[
  {
    "path": ".gitignore",
    "content": "**/*.pyc\n**/.DS_Store\n**/.idea\n**/.ipynb_checkpoints\n"
  },
  {
    "path": "AUTHORS",
    "content": "\n# This is the official list of TensorFlow authors for copyright purposes.\n# This file is distinct from the CONTRIBUTORS files.\n# See the latter for an explanation.\n# Names should be added to this file as:\n# Name or Organization <email address>\n# The email address is not required for organizations.\nGoogle Inc.\n"
  },
  {
    "path": "CODEOWNERS",
    "content": "# https://help.github.com/articles/about-codeowners/\n# Last matching pattern takes preecedence.\n\n# Default owners for everything in repo.\n*  @tensorflow/docs-team\n\n# Courses\n/courses/ @josbecker @MarkDaoust\n\n# TF Lite\n/lite/ @khanhlvg @lu-wang-g @miaout17 @terryheo\n/tensorflow_examples/lite/ @ziyeqinghan @khanhlvg @lu-wang-g @miaout17 @terryheo"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "﻿# Contributing\n\nYou don't need to be a developer to make a significant\nimpact on TensorFlow documentation and examples-—just a\n[GitHub account](https://github.com/). \n\nQuestions about TensorFlow usage are best addressed on\n[StackOverflow](https://stackoverflow.com/questions/tagged/tensorflow) or the\n[discuss@tensorflow.org](https://groups.google.com/a/tensorflow.org/forum/#!forum/discuss)\nmailing list.\n\nTo contribute to the TensorFlow code repositories, see the\n[Contributing to TensorFlow](https://www.tensorflow.org/community/contribute) guide\nand the\n[TensorFlow contribution guidelines](https://github.com/tensorflow/tensorflow/blob/master/CONTRIBUTING.md).\n\n## Contributor License Agreements\n\nWe love patches! To publish your changes, you must sign either the individual or\ncorporate Contributor License Agreement (CLA):\n\n* If you are an individual writing original documentation or source code and\n  you're sure you own the intellectual property, sign an\n  [individual CLA](http://code.google.com/legal/individual-cla-v1.0.html).\n* If you work for a company that wants to allow you to contribute your work, sign\n  a [corporate CLA](http://code.google.com/legal/corporate-cla-v1.0.html).\n\nWe can accept your pull requests after you sign the CLA. We can only receive\noriginal documentation and source code from you and other people that have\nsigned the CLA.\n\n\n# Pull requests\n\nTo contribute documentation or code, please send us a pull request. If you are new to\npull requests, read GitHub's\n[Creating a pull request from a fork](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)\nguide.\n\nNotebooks can be viewed, edited, and run in\n[Colab](https://colab.research.google.com/notebooks/welcome.ipynb) by passing\nthe GitHub path as a URL parameter. For example, open the notebook at\nhttps://github.com/tensorflow/docs/blob/r1.11/site/en/tutorials/keras/basic_classification.ipynb\nin Colab here:\nhttps://colab.research.google.com/github/tensorflow/docs/blob/r1.11/site/en/tutorials/keras/basic_classification.ipynb\n\nThe [Open in Colab](https://chrome.google.com/webstore/detail/open-in-colab/iogfkhleblhcpcekbiedikdehleodpjo)\nChrome extension will automatically perform the URL substitution.\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2018 The TensorFlow Authors.  All rights reserved.\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2017, The TensorFlow Authors.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# TensorFlow Examples\n\n<div align=\"center\">\n  <img src=\"https://www.tensorflow.org/images/tf_logo_social.png\" /><br /><br />\n</div>\n\n<h2>Most important links!</h2>\n\n* [Community examples](./community)\n* [Course materials](./courses/udacity_deep_learning) for the [Deep Learning](https://www.udacity.com/course/deep-learning--ud730) class on Udacity\n\nIf you are looking to learn TensorFlow, don't miss the\n[core TensorFlow documentation](http://github.com/tensorflow/docs)\nwhich is largely runnable code.\nThose notebooks can be opened in Colab from\n[tensorflow.org](https://tensorflow.org).\n\n<h2>What is this repo?</h2>\n\nThis is the TensorFlow example repo.  It has several classes of material:\n\n* Showcase examples and documentation for our fantastic [TensorFlow Community](https://tensorflow.org/community)\n* Provide examples mentioned on TensorFlow.org\n* Publish material supporting official TensorFlow courses\n* Publish supporting material for the [TensorFlow Blog](https://blog.tensorflow.org) and [TensorFlow YouTube Channel](https://youtube.com/tensorflow)\n\nWe welcome community contributions, see [CONTRIBUTING.md](CONTRIBUTING.md) and, for style help,\n[Writing TensorFlow documentation](https://www.tensorflow.org/community/contribute/docs_style)\nguide.\n\nTo file an issue, use the tracker in the\n[tensorflow/tensorflow](https://github.com/tensorflow/tensorflow/issues/new?template=20-documentation-issue.md) repo.\n\n## License\n\n[Apache License 2.0](LICENSE)\n"
  },
  {
    "path": "__init__.py",
    "content": "\"\"\"tensorflow_examples is a package for examples of TensorFlow.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\n"
  },
  {
    "path": "courses/udacity_deep_learning/.gitignore",
    "content": "notMNIST_large*\nnotMNIST_small*"
  },
  {
    "path": "courses/udacity_deep_learning/1_notmnist.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"5hIbr52I7Z7U\"\n      },\n      \"source\": [\n        \"Deep Learning\\n\",\n        \"=============\\n\",\n        \"\\n\",\n        \"Assignment 1\\n\",\n        \"------------\\n\",\n        \"\\n\",\n        \"The objective of this assignment is to learn about simple data curation practices, and familiarize you with some of the data we'll be reusing later.\\n\",\n        \"\\n\",\n        \"This notebook uses the [notMNIST](http://yaroslavvb.blogspot.com/2011/09/notmnist-dataset.html) dataset to be used with python experiments. This dataset is designed to look like the classic [MNIST](http://yann.lecun.com/exdb/mnist/) dataset, while looking a little more like real data: it's a harder task, and the data is a lot less 'clean' than MNIST.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"apJbCsBHl-2A\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# These are all the modules we'll be using later. Make sure you can import them\\n\",\n        \"# before proceeding further.\\n\",\n        \"from __future__ import print_function\\n\",\n        \"import imageio\\n\",\n        \"import matplotlib.pyplot as plt\\n\",\n        \"import numpy as np\\n\",\n        \"import os\\n\",\n        \"import sys\\n\",\n        \"import tarfile\\n\",\n        \"from IPython.display import display, Image\\n\",\n        \"from sklearn.linear_model import LogisticRegression\\n\",\n        \"from six.moves.urllib.request import urlretrieve\\n\",\n        \"from six.moves import cPickle as pickle\\n\",\n        \"\\n\",\n        \"# Config the matplotlib backend as plotting inline in IPython\\n\",\n        \"%matplotlib inline\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"jNWGtZaXn-5j\"\n      },\n      \"source\": [\n        \"First, we'll download the dataset to our local machine. The data consists of characters rendered in a variety of fonts on a 28x28 image. The labels are limited to 'A' through 'J' (10 classes). The training set has about 500k and the testset 19000 labeled examples. Given these sizes, it should be possible to train models quickly on any machine.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"EYRJ4ICW6-da\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"Found and verified notMNIST_large.tar.gz\\n\",\n            \"Found and verified notMNIST_small.tar.gz\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"url = 'https://commondatastorage.googleapis.com/books1000/'\\n\",\n        \"last_percent_reported = None\\n\",\n        \"data_root = '.' # Change me to store data elsewhere\\n\",\n        \"\\n\",\n        \"def download_progress_hook(count, blockSize, totalSize):\\n\",\n        \"  \\\"\\\"\\\"A hook to report the progress of a download. This is mostly intended for users with\\n\",\n        \"  slow internet connections. Reports every 5% change in download progress.\\n\",\n        \"  \\\"\\\"\\\"\\n\",\n        \"  global last_percent_reported\\n\",\n        \"  percent = int(count * blockSize * 100 / totalSize)\\n\",\n        \"\\n\",\n        \"  if last_percent_reported != percent:\\n\",\n        \"    if percent % 5 == 0:\\n\",\n        \"      sys.stdout.write(\\\"%s%%\\\" % percent)\\n\",\n        \"      sys.stdout.flush()\\n\",\n        \"    else:\\n\",\n        \"      sys.stdout.write(\\\".\\\")\\n\",\n        \"      sys.stdout.flush()\\n\",\n        \"      \\n\",\n        \"    last_percent_reported = percent\\n\",\n        \"        \\n\",\n        \"def maybe_download(filename, expected_bytes, force=False):\\n\",\n        \"  \\\"\\\"\\\"Download a file if not present, and make sure it's the right size.\\\"\\\"\\\"\\n\",\n        \"  dest_filename = os.path.join(data_root, filename)\\n\",\n        \"  if force or not os.path.exists(dest_filename):\\n\",\n        \"    print('Attempting to download:', filename) \\n\",\n        \"    filename, _ = urlretrieve(url + filename, dest_filename, reporthook=download_progress_hook)\\n\",\n        \"    print('\\\\nDownload Complete!')\\n\",\n        \"  statinfo = os.stat(dest_filename)\\n\",\n        \"  if statinfo.st_size == expected_bytes:\\n\",\n        \"    print('Found and verified', dest_filename)\\n\",\n        \"  else:\\n\",\n        \"    raise Exception(\\n\",\n        \"      'Failed to verify ' + dest_filename + '. Can you get to it with a browser?')\\n\",\n        \"  return dest_filename\\n\",\n        \"\\n\",\n        \"train_filename = maybe_download('notMNIST_large.tar.gz', 247336696)\\n\",\n        \"test_filename = maybe_download('notMNIST_small.tar.gz', 8458043)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"cC3p0oEyF8QT\"\n      },\n      \"source\": [\n        \"Extract the dataset from the compressed .tar.gz file.\\n\",\n        \"This should give you a set of directories, labeled A through J.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"H8CBE-WZ8nmj\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"['notMNIST_large/A', 'notMNIST_large/B', 'notMNIST_large/C', 'notMNIST_large/D', 'notMNIST_large/E', 'notMNIST_large/F', 'notMNIST_large/G', 'notMNIST_large/H', 'notMNIST_large/I', 'notMNIST_large/J']\\n\",\n            \"['notMNIST_small/A', 'notMNIST_small/B', 'notMNIST_small/C', 'notMNIST_small/D', 'notMNIST_small/E', 'notMNIST_small/F', 'notMNIST_small/G', 'notMNIST_small/H', 'notMNIST_small/I', 'notMNIST_small/J']\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"num_classes = 10\\n\",\n        \"np.random.seed(133)\\n\",\n        \"\\n\",\n        \"def maybe_extract(filename, force=False):\\n\",\n        \"  root = os.path.splitext(os.path.splitext(filename)[0])[0]  # remove .tar.gz\\n\",\n        \"  if os.path.isdir(root) and not force:\\n\",\n        \"    # You may override by setting force=True.\\n\",\n        \"    print('%s already present - Skipping extraction of %s.' % (root, filename))\\n\",\n        \"  else:\\n\",\n        \"    print('Extracting data for %s. This may take a while. Please wait.' % root)\\n\",\n        \"    tar = tarfile.open(filename)\\n\",\n        \"    sys.stdout.flush()\\n\",\n        \"    tar.extractall(data_root)\\n\",\n        \"    tar.close()\\n\",\n        \"  data_folders = [\\n\",\n        \"    os.path.join(root, d) for d in sorted(os.listdir(root))\\n\",\n        \"    if os.path.isdir(os.path.join(root, d))]\\n\",\n        \"  if len(data_folders) != num_classes:\\n\",\n        \"    raise Exception(\\n\",\n        \"      'Expected %d folders, one per class. Found %d instead.' % (\\n\",\n        \"        num_classes, len(data_folders)))\\n\",\n        \"  print(data_folders)\\n\",\n        \"  return data_folders\\n\",\n        \"  \\n\",\n        \"train_folders = maybe_extract(train_filename)\\n\",\n        \"test_folders = maybe_extract(test_filename)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"4riXK3IoHgx6\"\n      },\n      \"source\": [\n        \"---\\n\",\n        \"Problem 1\\n\",\n        \"---------\\n\",\n        \"\\n\",\n        \"Let's take a peek at some of the data to make sure it looks sensible. Each exemplar should be an image of a character A through J rendered in a different font. Display a sample of the images that we just downloaded. Hint: you can use the package IPython.display.\\n\",\n        \"\\n\",\n        \"---\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"PBdkjESPK8tw\"\n      },\n      \"source\": [\n        \"Now let's load the data in a more manageable format. Since, depending on your computer setup you might not be able to fit it all in memory, we'll load each class into a separate dataset, store them on disk and curate them independently. Later we'll merge them into a single dataset of manageable size.\\n\",\n        \"\\n\",\n        \"We'll convert the entire dataset into a 3D array (image index, x, y) of floating point values, normalized to have approximately zero mean and standard deviation ~0.5 to make training easier down the road. \\n\",\n        \"\\n\",\n        \"A few images might not be readable, we'll just skip them.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"h7q0XhG3MJdf\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"notMNIST_large/A\\n\",\n            \"Could not read: notMNIST_large/A/Um9tYW5hIEJvbGQucGZi.png : cannot identify image file - it's ok, skipping.\\n\",\n            \"Could not read: notMNIST_large/A/RnJlaWdodERpc3BCb29rSXRhbGljLnR0Zg==.png : cannot identify image file - it's ok, skipping.\\n\",\n            \"Could not read: notMNIST_large/A/SG90IE11c3RhcmQgQlROIFBvc3Rlci50dGY=.png : cannot identify image file - it's ok, skipping.\\n\",\n            \"Full dataset tensor: (52909, 28, 28)\\n\",\n            \"Mean: -0.12848\\n\",\n            \"Standard deviation: 0.425576\\n\",\n            \"notMNIST_large/B\\n\",\n            \"Could not read: notMNIST_large/B/TmlraXNFRi1TZW1pQm9sZEl0YWxpYy5vdGY=.png : cannot identify image file - it's ok, skipping.\\n\",\n            \"Full dataset tensor: (52911, 28, 28)\\n\",\n            \"Mean: -0.00755947\\n\",\n            \"Standard deviation: 0.417272\\n\",\n            \"notMNIST_large/C\\n\",\n            \"Full dataset tensor: (52912, 28, 28)\\n\",\n            \"Mean: -0.142321\\n\",\n            \"Standard deviation: 0.421305\\n\",\n            \"notMNIST_large/D\\n\",\n            \"Could not read: notMNIST_large/D/VHJhbnNpdCBCb2xkLnR0Zg==.png : cannot identify image file - it's ok, skipping.\\n\",\n            \"Full dataset tensor: (52911, 28, 28)\\n\",\n            \"Mean: -0.0574553\\n\",\n            \"Standard deviation: 0.434072\\n\",\n            \"notMNIST_large/E\\n\",\n            \"Full dataset tensor: (52912, 28, 28)\\n\",\n            \"Mean: -0.0701406\\n\",\n            \"Standard deviation: 0.42882\\n\",\n            \"notMNIST_large/F\\n\",\n            \"Full dataset tensor: (52912, 28, 28)\\n\",\n            \"Mean: -0.125914\\n\",\n            \"Standard deviation: 0.429645\\n\",\n            \"notMNIST_large/G\\n\",\n            \"Full dataset tensor: (52912, 28, 28)\\n\",\n            \"Mean: -0.0947771\\n\",\n            \"Standard deviation: 0.421674\\n\",\n            \"notMNIST_large/H\\n\",\n            \"Full dataset tensor: (52912, 28, 28)\\n\",\n            \"Mean: -0.0687667\\n\",\n            \"Standard deviation: 0.430344\\n\",\n            \"notMNIST_large/I\\n\",\n            \"Full dataset tensor: (52912, 28, 28)\\n\",\n            \"Mean: 0.0307405\\n\",\n            \"Standard deviation: 0.449686\\n\",\n            \"notMNIST_large/J\\n\",\n            \"Full dataset tensor: (52911, 28, 28)\\n\",\n            \"Mean: -0.153479\\n\",\n            \"Standard deviation: 0.397169\\n\",\n            \"notMNIST_small/A\\n\",\n            \"Could not read: notMNIST_small/A/RGVtb2NyYXRpY2FCb2xkT2xkc3R5bGUgQm9sZC50dGY=.png : cannot identify image file - it's ok, skipping.\\n\",\n            \"Full dataset tensor: (1872, 28, 28)\\n\",\n            \"Mean: -0.132588\\n\",\n            \"Standard deviation: 0.445923\\n\",\n            \"notMNIST_small/B\\n\",\n            \"Full dataset tensor: (1873, 28, 28)\\n\",\n            \"Mean: 0.00535619\\n\",\n            \"Standard deviation: 0.457054\\n\",\n            \"notMNIST_small/C\\n\",\n            \"Full dataset tensor: (1873, 28, 28)\\n\",\n            \"Mean: -0.141489\\n\",\n            \"Standard deviation: 0.441056\\n\",\n            \"notMNIST_small/D\\n\",\n            \"Full dataset tensor: (1873, 28, 28)\\n\",\n            \"Mean: -0.0492094\\n\",\n            \"Standard deviation: 0.460477\\n\",\n            \"notMNIST_small/E\\n\",\n            \"Full dataset tensor: (1873, 28, 28)\\n\",\n            \"Mean: -0.0598952\\n\",\n            \"Standard deviation: 0.456146\\n\",\n            \"notMNIST_small/F\\n\",\n            \"Could not read: notMNIST_small/F/Q3Jvc3NvdmVyIEJvbGRPYmxpcXVlLnR0Zg==.png : cannot identify image file - it's ok, skipping.\\n\",\n            \"Full dataset tensor: (1872, 28, 28)\\n\",\n            \"Mean: -0.118148\\n\",\n            \"Standard deviation: 0.451134\\n\",\n            \"notMNIST_small/G\\n\",\n            \"Full dataset tensor: (1872, 28, 28)\\n\",\n            \"Mean: -0.092519\\n\",\n            \"Standard deviation: 0.448468\\n\",\n            \"notMNIST_small/H\\n\",\n            \"Full dataset tensor: (1872, 28, 28)\\n\",\n            \"Mean: -0.0586729\\n\",\n            \"Standard deviation: 0.457387\\n\",\n            \"notMNIST_small/I\\n\",\n            \"Full dataset tensor: (1872, 28, 28)\\n\",\n            \"Mean: 0.0526481\\n\",\n            \"Standard deviation: 0.472657\\n\",\n            \"notMNIST_small/J\\n\",\n            \"Full dataset tensor: (1872, 28, 28)\\n\",\n            \"Mean: -0.15167\\n\",\n            \"Standard deviation: 0.449521\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"image_size = 28  # Pixel width and height.\\n\",\n        \"pixel_depth = 255.0  # Number of levels per pixel.\\n\",\n        \"\\n\",\n        \"def load_letter(folder, min_num_images):\\n\",\n        \"  \\\"\\\"\\\"Load the data for a single letter label.\\\"\\\"\\\"\\n\",\n        \"  image_files = os.listdir(folder)\\n\",\n        \"  dataset = np.ndarray(shape=(len(image_files), image_size, image_size),\\n\",\n        \"                         dtype=np.float32)\\n\",\n        \"  print(folder)\\n\",\n        \"  num_images = 0\\n\",\n        \"  for image in image_files:\\n\",\n        \"    image_file = os.path.join(folder, image)\\n\",\n        \"    try:\\n\",\n        \"      image_data = (imageio.imread(image_file).astype(float) - \\n\",\n        \"                    pixel_depth / 2) / pixel_depth\\n\",\n        \"      if image_data.shape != (image_size, image_size):\\n\",\n        \"        raise Exception('Unexpected image shape: %s' % str(image_data.shape))\\n\",\n        \"      dataset[num_images, :, :] = image_data\\n\",\n        \"      num_images = num_images + 1\\n\",\n        \"    except (IOError, ValueError) as e:\\n\",\n        \"      print('Could not read:', image_file, ':', e, '- it\\\\'s ok, skipping.')\\n\",\n        \"    \\n\",\n        \"  dataset = dataset[0:num_images, :, :]\\n\",\n        \"  if num_images < min_num_images:\\n\",\n        \"    raise Exception('Many fewer images than expected: %d < %d' %\\n\",\n        \"                    (num_images, min_num_images))\\n\",\n        \"    \\n\",\n        \"  print('Full dataset tensor:', dataset.shape)\\n\",\n        \"  print('Mean:', np.mean(dataset))\\n\",\n        \"  print('Standard deviation:', np.std(dataset))\\n\",\n        \"  return dataset\\n\",\n        \"        \\n\",\n        \"def maybe_pickle(data_folders, min_num_images_per_class, force=False):\\n\",\n        \"  dataset_names = []\\n\",\n        \"  for folder in data_folders:\\n\",\n        \"    set_filename = folder + '.pickle'\\n\",\n        \"    dataset_names.append(set_filename)\\n\",\n        \"    if os.path.exists(set_filename) and not force:\\n\",\n        \"      # You may override by setting force=True.\\n\",\n        \"      print('%s already present - Skipping pickling.' % set_filename)\\n\",\n        \"    else:\\n\",\n        \"      print('Pickling %s.' % set_filename)\\n\",\n        \"      dataset = load_letter(folder, min_num_images_per_class)\\n\",\n        \"      try:\\n\",\n        \"        with open(set_filename, 'wb') as f:\\n\",\n        \"          pickle.dump(dataset, f, pickle.HIGHEST_PROTOCOL)\\n\",\n        \"      except Exception as e:\\n\",\n        \"        print('Unable to save data to', set_filename, ':', e)\\n\",\n        \"  \\n\",\n        \"  return dataset_names\\n\",\n        \"\\n\",\n        \"train_datasets = maybe_pickle(train_folders, 45000)\\n\",\n        \"test_datasets = maybe_pickle(test_folders, 1800)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"vUdbskYE2d87\"\n      },\n      \"source\": [\n        \"---\\n\",\n        \"Problem 2\\n\",\n        \"---------\\n\",\n        \"\\n\",\n        \"Let's verify that the data still looks good. Displaying a sample of the labels and images from the ndarray. Hint: you can use matplotlib.pyplot.\\n\",\n        \"\\n\",\n        \"---\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"cYznx5jUwzoO\"\n      },\n      \"source\": [\n        \"---\\n\",\n        \"Problem 3\\n\",\n        \"---------\\n\",\n        \"Another check: we expect the data to be balanced across classes. Verify that.\\n\",\n        \"\\n\",\n        \"---\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"LA7M7K22ynCt\"\n      },\n      \"source\": [\n        \"Merge and prune the training data as needed. Depending on your computer setup, you might not be able to fit it all in memory, and you can tune `train_size` as needed. The labels will be stored into a separate array of integers 0 through 9.\\n\",\n        \"\\n\",\n        \"Also create a validation dataset for hyperparameter tuning.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"s3mWgZLpyuzq\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"Training (200000, 28, 28) (200000,)\\n\",\n            \"Validation (10000, 28, 28) (10000,)\\n\",\n            \"Testing (10000, 28, 28) (10000,)\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"def make_arrays(nb_rows, img_size):\\n\",\n        \"  if nb_rows:\\n\",\n        \"    dataset = np.ndarray((nb_rows, img_size, img_size), dtype=np.float32)\\n\",\n        \"    labels = np.ndarray(nb_rows, dtype=np.int32)\\n\",\n        \"  else:\\n\",\n        \"    dataset, labels = None, None\\n\",\n        \"  return dataset, labels\\n\",\n        \"\\n\",\n        \"def merge_datasets(pickle_files, train_size, valid_size=0):\\n\",\n        \"  num_classes = len(pickle_files)\\n\",\n        \"  valid_dataset, valid_labels = make_arrays(valid_size, image_size)\\n\",\n        \"  train_dataset, train_labels = make_arrays(train_size, image_size)\\n\",\n        \"  vsize_per_class = valid_size // num_classes\\n\",\n        \"  tsize_per_class = train_size // num_classes\\n\",\n        \"    \\n\",\n        \"  start_v, start_t = 0, 0\\n\",\n        \"  end_v, end_t = vsize_per_class, tsize_per_class\\n\",\n        \"  end_l = vsize_per_class+tsize_per_class\\n\",\n        \"  for label, pickle_file in enumerate(pickle_files):       \\n\",\n        \"    try:\\n\",\n        \"      with open(pickle_file, 'rb') as f:\\n\",\n        \"        letter_set = pickle.load(f)\\n\",\n        \"        # let's shuffle the letters to have random validation and training set\\n\",\n        \"        np.random.shuffle(letter_set)\\n\",\n        \"        if valid_dataset is not None:\\n\",\n        \"          valid_letter = letter_set[:vsize_per_class, :, :]\\n\",\n        \"          valid_dataset[start_v:end_v, :, :] = valid_letter\\n\",\n        \"          valid_labels[start_v:end_v] = label\\n\",\n        \"          start_v += vsize_per_class\\n\",\n        \"          end_v += vsize_per_class\\n\",\n        \"                    \\n\",\n        \"        train_letter = letter_set[vsize_per_class:end_l, :, :]\\n\",\n        \"        train_dataset[start_t:end_t, :, :] = train_letter\\n\",\n        \"        train_labels[start_t:end_t] = label\\n\",\n        \"        start_t += tsize_per_class\\n\",\n        \"        end_t += tsize_per_class\\n\",\n        \"    except Exception as e:\\n\",\n        \"      print('Unable to process data from', pickle_file, ':', e)\\n\",\n        \"      raise\\n\",\n        \"    \\n\",\n        \"  return valid_dataset, valid_labels, train_dataset, train_labels\\n\",\n        \"            \\n\",\n        \"            \\n\",\n        \"train_size = 200000\\n\",\n        \"valid_size = 10000\\n\",\n        \"test_size = 10000\\n\",\n        \"\\n\",\n        \"valid_dataset, valid_labels, train_dataset, train_labels = merge_datasets(\\n\",\n        \"  train_datasets, train_size, valid_size)\\n\",\n        \"_, _, test_dataset, test_labels = merge_datasets(test_datasets, test_size)\\n\",\n        \"\\n\",\n        \"print('Training:', train_dataset.shape, train_labels.shape)\\n\",\n        \"print('Validation:', valid_dataset.shape, valid_labels.shape)\\n\",\n        \"print('Testing:', test_dataset.shape, test_labels.shape)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"GPTCnjIcyuKN\"\n      },\n      \"source\": [\n        \"Next, we'll randomize the data. It's important to have the labels well shuffled for the training and test distributions to match.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"6WZ2l2tN2zOL\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def randomize(dataset, labels):\\n\",\n        \"  permutation = np.random.permutation(labels.shape[0])\\n\",\n        \"  shuffled_dataset = dataset[permutation,:,:]\\n\",\n        \"  shuffled_labels = labels[permutation]\\n\",\n        \"  return shuffled_dataset, shuffled_labels\\n\",\n        \"train_dataset, train_labels = randomize(train_dataset, train_labels)\\n\",\n        \"test_dataset, test_labels = randomize(test_dataset, test_labels)\\n\",\n        \"valid_dataset, valid_labels = randomize(valid_dataset, valid_labels)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"puDUTe6t6USl\"\n      },\n      \"source\": [\n        \"---\\n\",\n        \"Problem 4\\n\",\n        \"---------\\n\",\n        \"Convince yourself that the data is still good after shuffling!\\n\",\n        \"\\n\",\n        \"---\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"tIQJaJuwg5Hw\"\n      },\n      \"source\": [\n        \"Finally, let's save the data for later reuse:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"QiR_rETzem6C\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"pickle_file = os.path.join(data_root, 'notMNIST.pickle')\\n\",\n        \"\\n\",\n        \"try:\\n\",\n        \"  f = open(pickle_file, 'wb')\\n\",\n        \"  save = {\\n\",\n        \"    'train_dataset': train_dataset,\\n\",\n        \"    'train_labels': train_labels,\\n\",\n        \"    'valid_dataset': valid_dataset,\\n\",\n        \"    'valid_labels': valid_labels,\\n\",\n        \"    'test_dataset': test_dataset,\\n\",\n        \"    'test_labels': test_labels,\\n\",\n        \"    }\\n\",\n        \"  pickle.dump(save, f, pickle.HIGHEST_PROTOCOL)\\n\",\n        \"  f.close()\\n\",\n        \"except Exception as e:\\n\",\n        \"  print('Unable to save data to', pickle_file, ':', e)\\n\",\n        \"  raise\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"hQbLjrW_iT39\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"Compressed pickle size: 718193801\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"statinfo = os.stat(pickle_file)\\n\",\n        \"print('Compressed pickle size:', statinfo.st_size)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"gE_cRAQB33lk\"\n      },\n      \"source\": [\n        \"---\\n\",\n        \"Problem 5\\n\",\n        \"---------\\n\",\n        \"\\n\",\n        \"By construction, this dataset might contain a lot of overlapping samples, including training data that's also contained in the validation and test set! Overlap between training and test can skew the results if you expect to use your model in an environment where there is never an overlap, but are actually ok if you expect to see training samples recur when you use it.\\n\",\n        \"Measure how much overlap there is between training, validation and test samples.\\n\",\n        \"\\n\",\n        \"Optional questions:\\n\",\n        \"- What about near duplicates between datasets? (images that are almost identical)\\n\",\n        \"- Create a sanitized validation and test set, and compare your accuracy on those in subsequent assignments.\\n\",\n        \"---\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"L8oww1s4JMQx\"\n      },\n      \"source\": [\n        \"---\\n\",\n        \"Problem 6\\n\",\n        \"---------\\n\",\n        \"\\n\",\n        \"Let's get an idea of what an off-the-shelf classifier can give you on this data. It's always good to check that there is something to learn, and that it's a problem that is not so trivial that a canned solution solves it.\\n\",\n        \"\\n\",\n        \"Train a simple model on this data using 50, 100, 1000 and 5000 training samples. Hint: you can use the LogisticRegression model from sklearn.linear_model.\\n\",\n        \"\\n\",\n        \"Optional question: train an off-the-shelf model on all the data!\\n\",\n        \"\\n\",\n        \"---\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"name\": \"1_notmnist.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_deep_learning/2_fullyconnected.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"kR-4eNdK6lYS\"\n      },\n      \"source\": [\n        \"Deep Learning\\n\",\n        \"=============\\n\",\n        \"\\n\",\n        \"Assignment 2\\n\",\n        \"------------\\n\",\n        \"\\n\",\n        \"Previously in `1_notmnist.ipynb`, we created a pickle with formatted datasets for training, development and testing on the [notMNIST dataset](http://yaroslavvb.blogspot.com/2011/09/notmnist-dataset.html).\\n\",\n        \"\\n\",\n        \"The goal of this assignment is to progressively train deeper and more accurate models using TensorFlow.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"JLpLa8Jt7Vu4\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# These are all the modules we'll be using later. Make sure you can import them\\n\",\n        \"# before proceeding further.\\n\",\n        \"from __future__ import print_function\\n\",\n        \"import numpy as np\\n\",\n        \"import tensorflow as tf\\n\",\n        \"from six.moves import cPickle as pickle\\n\",\n        \"from six.moves import range\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"1HrCK6e17WzV\"\n      },\n      \"source\": [\n        \"First reload the data we generated in `1_notmnist.ipynb`.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"y3-cj1bpmuxc\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"Training set (200000, 28, 28) (200000,)\\n\",\n            \"Validation set (10000, 28, 28) (10000,)\\n\",\n            \"Test set (18724, 28, 28) (18724,)\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"pickle_file = 'notMNIST.pickle'\\n\",\n        \"\\n\",\n        \"with open(pickle_file, 'rb') as f:\\n\",\n        \"  save = pickle.load(f)\\n\",\n        \"  train_dataset = save['train_dataset']\\n\",\n        \"  train_labels = save['train_labels']\\n\",\n        \"  valid_dataset = save['valid_dataset']\\n\",\n        \"  valid_labels = save['valid_labels']\\n\",\n        \"  test_dataset = save['test_dataset']\\n\",\n        \"  test_labels = save['test_labels']\\n\",\n        \"  del save  # hint to help gc free up memory\\n\",\n        \"  print('Training set', train_dataset.shape, train_labels.shape)\\n\",\n        \"  print('Validation set', valid_dataset.shape, valid_labels.shape)\\n\",\n        \"  print('Test set', test_dataset.shape, test_labels.shape)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"L7aHrm6nGDMB\"\n      },\n      \"source\": [\n        \"Reformat into a shape that's more adapted to the models we're going to train:\\n\",\n        \"- data as a flat matrix,\\n\",\n        \"- labels as float 1-hot encodings.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"IRSyYiIIGIzS\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"Training set (200000, 784) (200000, 10)\\n\",\n            \"Validation set (10000, 784) (10000, 10)\\n\",\n            \"Test set (18724, 784) (18724, 10)\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"image_size = 28\\n\",\n        \"num_labels = 10\\n\",\n        \"\\n\",\n        \"def reformat(dataset, labels):\\n\",\n        \"  dataset = dataset.reshape((-1, image_size * image_size)).astype(np.float32)\\n\",\n        \"  # Map 0 to [1.0, 0.0, 0.0 ...], 1 to [0.0, 1.0, 0.0 ...]\\n\",\n        \"  labels = (np.arange(num_labels) == labels[:,None]).astype(np.float32)\\n\",\n        \"  return dataset, labels\\n\",\n        \"train_dataset, train_labels = reformat(train_dataset, train_labels)\\n\",\n        \"valid_dataset, valid_labels = reformat(valid_dataset, valid_labels)\\n\",\n        \"test_dataset, test_labels = reformat(test_dataset, test_labels)\\n\",\n        \"print('Training set', train_dataset.shape, train_labels.shape)\\n\",\n        \"print('Validation set', valid_dataset.shape, valid_labels.shape)\\n\",\n        \"print('Test set', test_dataset.shape, test_labels.shape)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"nCLVqyQ5vPPH\"\n      },\n      \"source\": [\n        \"We're first going to train a multinomial logistic regression using simple gradient descent.\\n\",\n        \"\\n\",\n        \"TensorFlow works like this:\\n\",\n        \"* First you describe the computation that you want to see performed: what the inputs, the variables, and the operations look like. These get created as nodes over a computation graph. This description is all contained within the block below:\\n\",\n        \"\\n\",\n        \"      with graph.as_default():\\n\",\n        \"          ...\\n\",\n        \"\\n\",\n        \"* Then you can run the operations on this graph as many times as you want by calling `session.run()`, providing it outputs to fetch from the graph that get returned. This runtime operation is all contained in the block below:\\n\",\n        \"\\n\",\n        \"      with tf.Session(graph=graph) as session:\\n\",\n        \"          ...\\n\",\n        \"\\n\",\n        \"Let's load all the data into TensorFlow and build the computation graph corresponding to our training:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"Nfv39qvtvOl_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# With gradient descent training, even this much data is prohibitive.\\n\",\n        \"# Subset the training data for faster turnaround.\\n\",\n        \"train_subset = 10000\\n\",\n        \"\\n\",\n        \"graph = tf.Graph()\\n\",\n        \"with graph.as_default():\\n\",\n        \"\\n\",\n        \"  # Input data.\\n\",\n        \"  # Load the training, validation and test data into constants that are\\n\",\n        \"  # attached to the graph.\\n\",\n        \"  tf_train_dataset = tf.constant(train_dataset[:train_subset, :])\\n\",\n        \"  tf_train_labels = tf.constant(train_labels[:train_subset])\\n\",\n        \"  tf_valid_dataset = tf.constant(valid_dataset)\\n\",\n        \"  tf_test_dataset = tf.constant(test_dataset)\\n\",\n        \"  \\n\",\n        \"  # Variables.\\n\",\n        \"  # These are the parameters that we are going to be training. The weight\\n\",\n        \"  # matrix will be initialized using random values following a (truncated)\\n\",\n        \"  # normal distribution. The biases get initialized to zero.\\n\",\n        \"  weights = tf.Variable(\\n\",\n        \"    tf.truncated_normal([image_size * image_size, num_labels]))\\n\",\n        \"  biases = tf.Variable(tf.zeros([num_labels]))\\n\",\n        \"  \\n\",\n        \"  # Training computation.\\n\",\n        \"  # We multiply the inputs with the weight matrix, and add biases. We compute\\n\",\n        \"  # the softmax and cross-entropy (it's one operation in TensorFlow, because\\n\",\n        \"  # it's very common, and it can be optimized). We take the average of this\\n\",\n        \"  # cross-entropy across all training examples: that's our loss.\\n\",\n        \"  logits = tf.matmul(tf_train_dataset, weights) + biases\\n\",\n        \"  loss = tf.reduce_mean(\\n\",\n        \"    tf.nn.softmax_cross_entropy_with_logits(labels=tf_train_labels, logits=logits))\\n\",\n        \"  \\n\",\n        \"  # Optimizer.\\n\",\n        \"  # We are going to find the minimum of this loss using gradient descent.\\n\",\n        \"  optimizer = tf.train.GradientDescentOptimizer(0.5).minimize(loss)\\n\",\n        \"  \\n\",\n        \"  # Predictions for the training, validation, and test data.\\n\",\n        \"  # These are not part of training, but merely here so that we can report\\n\",\n        \"  # accuracy figures as we train.\\n\",\n        \"  train_prediction = tf.nn.softmax(logits)\\n\",\n        \"  valid_prediction = tf.nn.softmax(\\n\",\n        \"    tf.matmul(tf_valid_dataset, weights) + biases)\\n\",\n        \"  test_prediction = tf.nn.softmax(tf.matmul(tf_test_dataset, weights) + biases)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"KQcL4uqISHjP\"\n      },\n      \"source\": [\n        \"Let's run this computation and iterate:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"z2cjdenH869W\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"Initialized\\n\",\n            \"Loss at step 0 : 17.2939\\n\",\n            \"Training accuracy: 10.8%\\n\",\n            \"Validation accuracy: 13.8%\\n\",\n            \"Loss at step 100 : 2.26903\\n\",\n            \"Training accuracy: 72.3%\\n\",\n            \"Validation accuracy: 71.6%\\n\",\n            \"Loss at step 200 : 1.84895\\n\",\n            \"Training accuracy: 74.9%\\n\",\n            \"Validation accuracy: 73.9%\\n\",\n            \"Loss at step 300 : 1.60701\\n\",\n            \"Training accuracy: 76.0%\\n\",\n            \"Validation accuracy: 74.5%\\n\",\n            \"Loss at step 400 : 1.43912\\n\",\n            \"Training accuracy: 76.8%\\n\",\n            \"Validation accuracy: 74.8%\\n\",\n            \"Loss at step 500 : 1.31349\\n\",\n            \"Training accuracy: 77.5%\\n\",\n            \"Validation accuracy: 75.0%\\n\",\n            \"Loss at step 600 : 1.21501\\n\",\n            \"Training accuracy: 78.1%\\n\",\n            \"Validation accuracy: 75.4%\\n\",\n            \"Loss at step 700 : 1.13515\\n\",\n            \"Training accuracy: 78.6%\\n\",\n            \"Validation accuracy: 75.4%\\n\",\n            \"Loss at step 800 : 1.0687\\n\",\n            \"Training accuracy: 79.2%\\n\",\n            \"Validation accuracy: 75.6%\\n\",\n            \"Test accuracy: 82.9%\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"num_steps = 801\\n\",\n        \"\\n\",\n        \"def accuracy(predictions, labels):\\n\",\n        \"  return (100.0 * np.sum(np.argmax(predictions, 1) == np.argmax(labels, 1))\\n\",\n        \"          / predictions.shape[0])\\n\",\n        \"\\n\",\n        \"with tf.Session(graph=graph) as session:\\n\",\n        \"  # This is a one-time operation which ensures the parameters get initialized as\\n\",\n        \"  # we described in the graph: random weights for the matrix, zeros for the\\n\",\n        \"  # biases. \\n\",\n        \"  tf.global_variables_initializer().run()\\n\",\n        \"  print('Initialized')\\n\",\n        \"  for step in range(num_steps):\\n\",\n        \"    # Run the computations. We tell .run() that we want to run the optimizer,\\n\",\n        \"    # and get the loss value and the training predictions returned as numpy\\n\",\n        \"    # arrays.\\n\",\n        \"    _, l, predictions = session.run([optimizer, loss, train_prediction])\\n\",\n        \"    if (step % 100 == 0):\\n\",\n        \"      print('Loss at step %d: %f' % (step, l))\\n\",\n        \"      print('Training accuracy: %.1f%%' % accuracy(\\n\",\n        \"        predictions, train_labels[:train_subset, :]))\\n\",\n        \"      # Calling .eval() on valid_prediction is basically like calling run(), but\\n\",\n        \"      # just to get that one numpy array. Note that it recomputes all its graph\\n\",\n        \"      # dependencies.\\n\",\n        \"      print('Validation accuracy: %.1f%%' % accuracy(\\n\",\n        \"        valid_prediction.eval(), valid_labels))\\n\",\n        \"  print('Test accuracy: %.1f%%' % accuracy(test_prediction.eval(), test_labels))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"x68f-hxRGm3H\"\n      },\n      \"source\": [\n        \"Let's now switch to stochastic gradient descent training instead, which is much faster.\\n\",\n        \"\\n\",\n        \"The graph will be similar, except that instead of holding all the training data into a constant node, we create a `Placeholder` node which will be fed actual data at every call of `session.run()`.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"qhPMzWYRGrzM\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"batch_size = 128\\n\",\n        \"\\n\",\n        \"graph = tf.Graph()\\n\",\n        \"with graph.as_default():\\n\",\n        \"\\n\",\n        \"  # Input data. For the training data, we use a placeholder that will be fed\\n\",\n        \"  # at run time with a training minibatch.\\n\",\n        \"  tf_train_dataset = tf.placeholder(tf.float32,\\n\",\n        \"                                    shape=(batch_size, image_size * image_size))\\n\",\n        \"  tf_train_labels = tf.placeholder(tf.float32, shape=(batch_size, num_labels))\\n\",\n        \"  tf_valid_dataset = tf.constant(valid_dataset)\\n\",\n        \"  tf_test_dataset = tf.constant(test_dataset)\\n\",\n        \"  \\n\",\n        \"  # Variables.\\n\",\n        \"  weights = tf.Variable(\\n\",\n        \"    tf.truncated_normal([image_size * image_size, num_labels]))\\n\",\n        \"  biases = tf.Variable(tf.zeros([num_labels]))\\n\",\n        \"  \\n\",\n        \"  # Training computation.\\n\",\n        \"  logits = tf.matmul(tf_train_dataset, weights) + biases\\n\",\n        \"  loss = tf.reduce_mean(\\n\",\n        \"    tf.nn.softmax_cross_entropy_with_logits(labels=tf_train_labels, logits=logits))\\n\",\n        \"  \\n\",\n        \"  # Optimizer.\\n\",\n        \"  optimizer = tf.train.GradientDescentOptimizer(0.5).minimize(loss)\\n\",\n        \"  \\n\",\n        \"  # Predictions for the training, validation, and test data.\\n\",\n        \"  train_prediction = tf.nn.softmax(logits)\\n\",\n        \"  valid_prediction = tf.nn.softmax(\\n\",\n        \"    tf.matmul(tf_valid_dataset, weights) + biases)\\n\",\n        \"  test_prediction = tf.nn.softmax(tf.matmul(tf_test_dataset, weights) + biases)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"XmVZESmtG4JH\"\n      },\n      \"source\": [\n        \"Let's run it:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"FoF91pknG_YW\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"Initialized\\n\",\n            \"Minibatch loss at step 0 : 16.8091\\n\",\n            \"Minibatch accuracy: 12.5%\\n\",\n            \"Validation accuracy: 14.0%\\n\",\n            \"Minibatch loss at step 500 : 1.75256\\n\",\n            \"Minibatch accuracy: 77.3%\\n\",\n            \"Validation accuracy: 75.0%\\n\",\n            \"Minibatch loss at step 1000 : 1.32283\\n\",\n            \"Minibatch accuracy: 77.3%\\n\",\n            \"Validation accuracy: 76.6%\\n\",\n            \"Minibatch loss at step 1500 : 0.944533\\n\",\n            \"Minibatch accuracy: 83.6%\\n\",\n            \"Validation accuracy: 76.5%\\n\",\n            \"Minibatch loss at step 2000 : 1.03795\\n\",\n            \"Minibatch accuracy: 78.9%\\n\",\n            \"Validation accuracy: 77.8%\\n\",\n            \"Minibatch loss at step 2500 : 1.10219\\n\",\n            \"Minibatch accuracy: 80.5%\\n\",\n            \"Validation accuracy: 78.0%\\n\",\n            \"Minibatch loss at step 3000 : 0.758874\\n\",\n            \"Minibatch accuracy: 82.8%\\n\",\n            \"Validation accuracy: 78.8%\\n\",\n            \"Test accuracy: 86.1%\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"num_steps = 3001\\n\",\n        \"\\n\",\n        \"with tf.Session(graph=graph) as session:\\n\",\n        \"  tf.global_variables_initializer().run()\\n\",\n        \"  print(\\\"Initialized\\\")\\n\",\n        \"  for step in range(num_steps):\\n\",\n        \"    # Pick an offset within the training data, which has been randomized.\\n\",\n        \"    # Note: we could use better randomization across epochs.\\n\",\n        \"    offset = (step * batch_size) % (train_labels.shape[0] - batch_size)\\n\",\n        \"    # Generate a minibatch.\\n\",\n        \"    batch_data = train_dataset[offset:(offset + batch_size), :]\\n\",\n        \"    batch_labels = train_labels[offset:(offset + batch_size), :]\\n\",\n        \"    # Prepare a dictionary telling the session where to feed the minibatch.\\n\",\n        \"    # The key of the dictionary is the placeholder node of the graph to be fed,\\n\",\n        \"    # and the value is the numpy array to feed to it.\\n\",\n        \"    feed_dict = {tf_train_dataset : batch_data, tf_train_labels : batch_labels}\\n\",\n        \"    _, l, predictions = session.run(\\n\",\n        \"      [optimizer, loss, train_prediction], feed_dict=feed_dict)\\n\",\n        \"    if (step % 500 == 0):\\n\",\n        \"      print(\\\"Minibatch loss at step %d: %f\\\" % (step, l))\\n\",\n        \"      print(\\\"Minibatch accuracy: %.1f%%\\\" % accuracy(predictions, batch_labels))\\n\",\n        \"      print(\\\"Validation accuracy: %.1f%%\\\" % accuracy(\\n\",\n        \"        valid_prediction.eval(), valid_labels))\\n\",\n        \"  print(\\\"Test accuracy: %.1f%%\\\" % accuracy(test_prediction.eval(), test_labels))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"7omWxtvLLxik\"\n      },\n      \"source\": [\n        \"---\\n\",\n        \"Problem\\n\",\n        \"-------\\n\",\n        \"\\n\",\n        \"Turn the logistic regression example with SGD into a 1-hidden layer neural network with rectified linear units [nn.relu()](https://www.tensorflow.org/versions/r0.7/api_docs/python/nn.html#relu) and 1024 hidden nodes. This model should improve your validation / test accuracy.\\n\",\n        \"\\n\",\n        \"---\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"name\": \"2_fullyconnected.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_deep_learning/3_regularization.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"kR-4eNdK6lYS\"\n      },\n      \"source\": [\n        \"Deep Learning\\n\",\n        \"=============\\n\",\n        \"\\n\",\n        \"Assignment 3\\n\",\n        \"------------\\n\",\n        \"\\n\",\n        \"Previously in `2_fullyconnected.ipynb`, you trained a logistic regression and a neural network model.\\n\",\n        \"\\n\",\n        \"The goal of this assignment is to explore regularization techniques.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"JLpLa8Jt7Vu4\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# These are all the modules we'll be using later. Make sure you can import them\\n\",\n        \"# before proceeding further.\\n\",\n        \"from __future__ import print_function\\n\",\n        \"import numpy as np\\n\",\n        \"import tensorflow as tf\\n\",\n        \"from six.moves import cPickle as pickle\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"1HrCK6e17WzV\"\n      },\n      \"source\": [\n        \"First reload the data we generated in `1_notmnist.ipynb`.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"y3-cj1bpmuxc\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"Training set (200000, 28, 28) (200000,)\\n\",\n            \"Validation set (10000, 28, 28) (10000,)\\n\",\n            \"Test set (18724, 28, 28) (18724,)\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"pickle_file = 'notMNIST.pickle'\\n\",\n        \"\\n\",\n        \"with open(pickle_file, 'rb') as f:\\n\",\n        \"  save = pickle.load(f)\\n\",\n        \"  train_dataset = save['train_dataset']\\n\",\n        \"  train_labels = save['train_labels']\\n\",\n        \"  valid_dataset = save['valid_dataset']\\n\",\n        \"  valid_labels = save['valid_labels']\\n\",\n        \"  test_dataset = save['test_dataset']\\n\",\n        \"  test_labels = save['test_labels']\\n\",\n        \"  del save  # hint to help gc free up memory\\n\",\n        \"  print('Training set', train_dataset.shape, train_labels.shape)\\n\",\n        \"  print('Validation set', valid_dataset.shape, valid_labels.shape)\\n\",\n        \"  print('Test set', test_dataset.shape, test_labels.shape)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"L7aHrm6nGDMB\"\n      },\n      \"source\": [\n        \"Reformat into a shape that's more adapted to the models we're going to train:\\n\",\n        \"- data as a flat matrix,\\n\",\n        \"- labels as float 1-hot encodings.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"IRSyYiIIGIzS\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"Training set (200000, 784) (200000, 10)\\n\",\n            \"Validation set (10000, 784) (10000, 10)\\n\",\n            \"Test set (18724, 784) (18724, 10)\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"image_size = 28\\n\",\n        \"num_labels = 10\\n\",\n        \"\\n\",\n        \"def reformat(dataset, labels):\\n\",\n        \"  dataset = dataset.reshape((-1, image_size * image_size)).astype(np.float32)\\n\",\n        \"  # Map 1 to [0.0, 1.0, 0.0 ...], 2 to [0.0, 0.0, 1.0 ...]\\n\",\n        \"  labels = (np.arange(num_labels) == labels[:,None]).astype(np.float32)\\n\",\n        \"  return dataset, labels\\n\",\n        \"train_dataset, train_labels = reformat(train_dataset, train_labels)\\n\",\n        \"valid_dataset, valid_labels = reformat(valid_dataset, valid_labels)\\n\",\n        \"test_dataset, test_labels = reformat(test_dataset, test_labels)\\n\",\n        \"print('Training set', train_dataset.shape, train_labels.shape)\\n\",\n        \"print('Validation set', valid_dataset.shape, valid_labels.shape)\\n\",\n        \"print('Test set', test_dataset.shape, test_labels.shape)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"RajPLaL_ZW6w\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def accuracy(predictions, labels):\\n\",\n        \"  return (100.0 * np.sum(np.argmax(predictions, 1) == np.argmax(labels, 1))\\n\",\n        \"          / predictions.shape[0])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"sgLbUAQ1CW-1\"\n      },\n      \"source\": [\n        \"---\\n\",\n        \"Problem 1\\n\",\n        \"---------\\n\",\n        \"\\n\",\n        \"Introduce and tune L2 regularization for both logistic and neural network models. Remember that L2 amounts to adding a penalty on the norm of the weights to the loss. In TensorFlow, you can compute the L2 loss for a tensor `t` using `nn.l2_loss(t)`. The right amount of regularization should improve your validation / test accuracy.\\n\",\n        \"\\n\",\n        \"---\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"na8xX2yHZzNF\"\n      },\n      \"source\": [\n        \"---\\n\",\n        \"Problem 2\\n\",\n        \"---------\\n\",\n        \"Let's demonstrate an extreme case of overfitting. Restrict your training data to just a few batches. What happens?\\n\",\n        \"\\n\",\n        \"---\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ww3SCBUdlkRc\"\n      },\n      \"source\": [\n        \"---\\n\",\n        \"Problem 3\\n\",\n        \"---------\\n\",\n        \"Introduce Dropout on the hidden layer of the neural network. Remember: Dropout should only be introduced during training, not evaluation, otherwise your evaluation results would be stochastic as well. TensorFlow provides `nn.dropout()` for that, but you have to make sure it's only inserted during training.\\n\",\n        \"\\n\",\n        \"What happens to our extreme overfitting case?\\n\",\n        \"\\n\",\n        \"---\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"-b1hTz3VWZjw\"\n      },\n      \"source\": [\n        \"---\\n\",\n        \"Problem 4\\n\",\n        \"---------\\n\",\n        \"\\n\",\n        \"Try to get the best performance you can using a multi-layer model! The best reported test accuracy using a deep network is [97.1%](http://yaroslavvb.blogspot.com/2011/09/notmnist-dataset.html?showComment=1391023266211#c8758720086795711595).\\n\",\n        \"\\n\",\n        \"One avenue you can explore is to add multiple layers.\\n\",\n        \"\\n\",\n        \"Another one is to use learning rate decay:\\n\",\n        \"\\n\",\n        \"    global_step = tf.Variable(0)  # count the number of steps taken.\\n\",\n        \"    learning_rate = tf.train.exponential_decay(0.5, global_step, ...)\\n\",\n        \"    optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)\\n\",\n        \" \\n\",\n        \" ---\\n\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"name\": \"3_regularization.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_deep_learning/4_convolutions.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"4embtkV0pNxM\"\n      },\n      \"source\": [\n        \"Deep Learning\\n\",\n        \"=============\\n\",\n        \"\\n\",\n        \"Assignment 4\\n\",\n        \"------------\\n\",\n        \"\\n\",\n        \"Previously in `2_fullyconnected.ipynb` and `3_regularization.ipynb`, we trained fully connected networks to classify [notMNIST](http://yaroslavvb.blogspot.com/2011/09/notmnist-dataset.html) characters.\\n\",\n        \"\\n\",\n        \"The goal of this assignment is make the neural network convolutional.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"tm2CQN_Cpwj0\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# These are all the modules we'll be using later. Make sure you can import them\\n\",\n        \"# before proceeding further.\\n\",\n        \"from __future__ import print_function\\n\",\n        \"import numpy as np\\n\",\n        \"import tensorflow as tf\\n\",\n        \"from six.moves import cPickle as pickle\\n\",\n        \"from six.moves import range\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"y3-cj1bpmuxc\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"Training set (200000, 28, 28) (200000,)\\n\",\n            \"Validation set (10000, 28, 28) (10000,)\\n\",\n            \"Test set (18724, 28, 28) (18724,)\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"pickle_file = 'notMNIST.pickle'\\n\",\n        \"\\n\",\n        \"with open(pickle_file, 'rb') as f:\\n\",\n        \"  save = pickle.load(f)\\n\",\n        \"  train_dataset = save['train_dataset']\\n\",\n        \"  train_labels = save['train_labels']\\n\",\n        \"  valid_dataset = save['valid_dataset']\\n\",\n        \"  valid_labels = save['valid_labels']\\n\",\n        \"  test_dataset = save['test_dataset']\\n\",\n        \"  test_labels = save['test_labels']\\n\",\n        \"  del save  # hint to help gc free up memory\\n\",\n        \"  print('Training set', train_dataset.shape, train_labels.shape)\\n\",\n        \"  print('Validation set', valid_dataset.shape, valid_labels.shape)\\n\",\n        \"  print('Test set', test_dataset.shape, test_labels.shape)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"L7aHrm6nGDMB\"\n      },\n      \"source\": [\n        \"Reformat into a TensorFlow-friendly shape:\\n\",\n        \"- convolutions need the image data formatted as a cube (width by height by #channels)\\n\",\n        \"- labels as float 1-hot encodings.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"IRSyYiIIGIzS\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"Training set (200000, 28, 28, 1) (200000, 10)\\n\",\n            \"Validation set (10000, 28, 28, 1) (10000, 10)\\n\",\n            \"Test set (18724, 28, 28, 1) (18724, 10)\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"image_size = 28\\n\",\n        \"num_labels = 10\\n\",\n        \"num_channels = 1 # grayscale\\n\",\n        \"\\n\",\n        \"import numpy as np\\n\",\n        \"\\n\",\n        \"def reformat(dataset, labels):\\n\",\n        \"  dataset = dataset.reshape(\\n\",\n        \"    (-1, image_size, image_size, num_channels)).astype(np.float32)\\n\",\n        \"  labels = (np.arange(num_labels) == labels[:,None]).astype(np.float32)\\n\",\n        \"  return dataset, labels\\n\",\n        \"train_dataset, train_labels = reformat(train_dataset, train_labels)\\n\",\n        \"valid_dataset, valid_labels = reformat(valid_dataset, valid_labels)\\n\",\n        \"test_dataset, test_labels = reformat(test_dataset, test_labels)\\n\",\n        \"print('Training set', train_dataset.shape, train_labels.shape)\\n\",\n        \"print('Validation set', valid_dataset.shape, valid_labels.shape)\\n\",\n        \"print('Test set', test_dataset.shape, test_labels.shape)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"AgQDIREv02p1\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def accuracy(predictions, labels):\\n\",\n        \"  return (100.0 * np.sum(np.argmax(predictions, 1) == np.argmax(labels, 1))\\n\",\n        \"          / predictions.shape[0])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"5rhgjmROXu2O\"\n      },\n      \"source\": [\n        \"Let's build a small network with two convolutional layers, followed by one fully connected layer. Convolutional networks are more expensive computationally, so we'll limit its depth and number of fully connected nodes.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"IZYv70SvvOan\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"batch_size = 16\\n\",\n        \"patch_size = 5\\n\",\n        \"depth = 16\\n\",\n        \"num_hidden = 64\\n\",\n        \"\\n\",\n        \"graph = tf.Graph()\\n\",\n        \"\\n\",\n        \"with graph.as_default():\\n\",\n        \"\\n\",\n        \"  # Input data.\\n\",\n        \"  tf_train_dataset = tf.placeholder(\\n\",\n        \"    tf.float32, shape=(batch_size, image_size, image_size, num_channels))\\n\",\n        \"  tf_train_labels = tf.placeholder(tf.float32, shape=(batch_size, num_labels))\\n\",\n        \"  tf_valid_dataset = tf.constant(valid_dataset)\\n\",\n        \"  tf_test_dataset = tf.constant(test_dataset)\\n\",\n        \"  \\n\",\n        \"  # Variables.\\n\",\n        \"  layer1_weights = tf.Variable(tf.truncated_normal(\\n\",\n        \"      [patch_size, patch_size, num_channels, depth], stddev=0.1))\\n\",\n        \"  layer1_biases = tf.Variable(tf.zeros([depth]))\\n\",\n        \"  layer2_weights = tf.Variable(tf.truncated_normal(\\n\",\n        \"      [patch_size, patch_size, depth, depth], stddev=0.1))\\n\",\n        \"  layer2_biases = tf.Variable(tf.constant(1.0, shape=[depth]))\\n\",\n        \"  layer3_weights = tf.Variable(tf.truncated_normal(\\n\",\n        \"      [image_size // 4 * image_size // 4 * depth, num_hidden], stddev=0.1))\\n\",\n        \"  layer3_biases = tf.Variable(tf.constant(1.0, shape=[num_hidden]))\\n\",\n        \"  layer4_weights = tf.Variable(tf.truncated_normal(\\n\",\n        \"      [num_hidden, num_labels], stddev=0.1))\\n\",\n        \"  layer4_biases = tf.Variable(tf.constant(1.0, shape=[num_labels]))\\n\",\n        \"  \\n\",\n        \"  # Model.\\n\",\n        \"  def model(data):\\n\",\n        \"    conv = tf.nn.conv2d(data, layer1_weights, [1, 2, 2, 1], padding='SAME')\\n\",\n        \"    hidden = tf.nn.relu(conv + layer1_biases)\\n\",\n        \"    conv = tf.nn.conv2d(hidden, layer2_weights, [1, 2, 2, 1], padding='SAME')\\n\",\n        \"    hidden = tf.nn.relu(conv + layer2_biases)\\n\",\n        \"    shape = hidden.get_shape().as_list()\\n\",\n        \"    reshape = tf.reshape(hidden, [shape[0], shape[1] * shape[2] * shape[3]])\\n\",\n        \"    hidden = tf.nn.relu(tf.matmul(reshape, layer3_weights) + layer3_biases)\\n\",\n        \"    return tf.matmul(hidden, layer4_weights) + layer4_biases\\n\",\n        \"  \\n\",\n        \"  # Training computation.\\n\",\n        \"  logits = model(tf_train_dataset)\\n\",\n        \"  loss = tf.reduce_mean(\\n\",\n        \"    tf.nn.softmax_cross_entropy_with_logits(labels=tf_train_labels, logits=logits))\\n\",\n        \"    \\n\",\n        \"  # Optimizer.\\n\",\n        \"  optimizer = tf.train.GradientDescentOptimizer(0.05).minimize(loss)\\n\",\n        \"  \\n\",\n        \"  # Predictions for the training, validation, and test data.\\n\",\n        \"  train_prediction = tf.nn.softmax(logits)\\n\",\n        \"  valid_prediction = tf.nn.softmax(model(tf_valid_dataset))\\n\",\n        \"  test_prediction = tf.nn.softmax(model(tf_test_dataset))\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"noKFb2UovVFR\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"Initialized\\n\",\n            \"Minibatch loss at step 0 : 3.51275\\n\",\n            \"Minibatch accuracy: 6.2%\\n\",\n            \"Validation accuracy: 12.8%\\n\",\n            \"Minibatch loss at step 50 : 1.48703\\n\",\n            \"Minibatch accuracy: 43.8%\\n\",\n            \"Validation accuracy: 50.4%\\n\",\n            \"Minibatch loss at step 100 : 1.04377\\n\",\n            \"Minibatch accuracy: 68.8%\\n\",\n            \"Validation accuracy: 67.4%\\n\",\n            \"Minibatch loss at step 150 : 0.601682\\n\",\n            \"Minibatch accuracy: 68.8%\\n\",\n            \"Validation accuracy: 73.0%\\n\",\n            \"Minibatch loss at step 200 : 0.898649\\n\",\n            \"Minibatch accuracy: 75.0%\\n\",\n            \"Validation accuracy: 77.8%\\n\",\n            \"Minibatch loss at step 250 : 1.3637\\n\",\n            \"Minibatch accuracy: 56.2%\\n\",\n            \"Validation accuracy: 75.4%\\n\",\n            \"Minibatch loss at step 300 : 1.41968\\n\",\n            \"Minibatch accuracy: 62.5%\\n\",\n            \"Validation accuracy: 76.0%\\n\",\n            \"Minibatch loss at step 350 : 0.300648\\n\",\n            \"Minibatch accuracy: 81.2%\\n\",\n            \"Validation accuracy: 80.2%\\n\",\n            \"Minibatch loss at step 400 : 1.32092\\n\",\n            \"Minibatch accuracy: 56.2%\\n\",\n            \"Validation accuracy: 80.4%\\n\",\n            \"Minibatch loss at step 450 : 0.556701\\n\",\n            \"Minibatch accuracy: 81.2%\\n\",\n            \"Validation accuracy: 79.4%\\n\",\n            \"Minibatch loss at step 500 : 1.65595\\n\",\n            \"Minibatch accuracy: 43.8%\\n\",\n            \"Validation accuracy: 79.6%\\n\",\n            \"Minibatch loss at step 550 : 1.06995\\n\",\n            \"Minibatch accuracy: 75.0%\\n\",\n            \"Validation accuracy: 81.2%\\n\",\n            \"Minibatch loss at step 600 : 0.223684\\n\",\n            \"Minibatch accuracy: 100.0%\\n\",\n            \"Validation accuracy: 82.3%\\n\",\n            \"Minibatch loss at step 650 : 0.619602\\n\",\n            \"Minibatch accuracy: 87.5%\\n\",\n            \"Validation accuracy: 81.8%\\n\",\n            \"Minibatch loss at step 700 : 0.812091\\n\",\n            \"Minibatch accuracy: 75.0%\\n\",\n            \"Validation accuracy: 82.4%\\n\",\n            \"Minibatch loss at step 750 : 0.276302\\n\",\n            \"Minibatch accuracy: 87.5%\\n\",\n            \"Validation accuracy: 82.3%\\n\",\n            \"Minibatch loss at step 800 : 0.450241\\n\",\n            \"Minibatch accuracy: 81.2%\\n\",\n            \"Validation accuracy: 82.3%\\n\",\n            \"Minibatch loss at step 850 : 0.137139\\n\",\n            \"Minibatch accuracy: 93.8%\\n\",\n            \"Validation accuracy: 82.3%\\n\",\n            \"Minibatch loss at step 900 : 0.52664\\n\",\n            \"Minibatch accuracy: 75.0%\\n\",\n            \"Validation accuracy: 82.2%\\n\",\n            \"Minibatch loss at step 950 : 0.623835\\n\",\n            \"Minibatch accuracy: 87.5%\\n\",\n            \"Validation accuracy: 82.1%\\n\",\n            \"Minibatch loss at step 1000 : 0.243114\\n\",\n            \"Minibatch accuracy: 93.8%\\n\",\n            \"Validation accuracy: 82.9%\\n\",\n            \"Test accuracy: 90.0%\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"num_steps = 1001\\n\",\n        \"\\n\",\n        \"with tf.Session(graph=graph) as session:\\n\",\n        \"  tf.global_variables_initializer().run()\\n\",\n        \"  print('Initialized')\\n\",\n        \"  for step in range(num_steps):\\n\",\n        \"    offset = (step * batch_size) % (train_labels.shape[0] - batch_size)\\n\",\n        \"    batch_data = train_dataset[offset:(offset + batch_size), :, :, :]\\n\",\n        \"    batch_labels = train_labels[offset:(offset + batch_size), :]\\n\",\n        \"    feed_dict = {tf_train_dataset : batch_data, tf_train_labels : batch_labels}\\n\",\n        \"    _, l, predictions = session.run(\\n\",\n        \"      [optimizer, loss, train_prediction], feed_dict=feed_dict)\\n\",\n        \"    if (step % 50 == 0):\\n\",\n        \"      print('Minibatch loss at step %d: %f' % (step, l))\\n\",\n        \"      print('Minibatch accuracy: %.1f%%' % accuracy(predictions, batch_labels))\\n\",\n        \"      print('Validation accuracy: %.1f%%' % accuracy(\\n\",\n        \"        valid_prediction.eval(), valid_labels))\\n\",\n        \"  print('Test accuracy: %.1f%%' % accuracy(test_prediction.eval(), test_labels))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"KedKkn4EutIK\"\n      },\n      \"source\": [\n        \"---\\n\",\n        \"Problem 1\\n\",\n        \"---------\\n\",\n        \"\\n\",\n        \"The convolutional model above uses convolutions with stride 2 to reduce the dimensionality. Replace the strides by a max pooling operation (`nn.max_pool()`) of stride 2 and kernel size 2.\\n\",\n        \"\\n\",\n        \"---\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"klf21gpbAgb-\"\n      },\n      \"source\": [\n        \"---\\n\",\n        \"Problem 2\\n\",\n        \"---------\\n\",\n        \"\\n\",\n        \"Try to get the best performance you can using a convolutional net. Look for example at the classic [LeNet5](http://yann.lecun.com/exdb/lenet/) architecture, adding Dropout, and/or adding learning rate decay.\\n\",\n        \"\\n\",\n        \"---\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"name\": \"4_convolutions.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_deep_learning/5_word2vec.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"D7tqLMoKF6uq\"\n      },\n      \"source\": [\n        \"Deep Learning\\n\",\n        \"=============\\n\",\n        \"\\n\",\n        \"Assignment 5\\n\",\n        \"------------\\n\",\n        \"\\n\",\n        \"The goal of this assignment is to train a Word2Vec skip-gram model over [Text8](http://mattmahoney.net/dc/textdata) data.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"0K1ZyLn04QZf\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# These are all the modules we'll be using later. Make sure you can import them\\n\",\n        \"# before proceeding further.\\n\",\n        \"%matplotlib inline\\n\",\n        \"from __future__ import print_function\\n\",\n        \"import collections\\n\",\n        \"import math\\n\",\n        \"import numpy as np\\n\",\n        \"import os\\n\",\n        \"import random\\n\",\n        \"import tensorflow as tf\\n\",\n        \"import zipfile\\n\",\n        \"from matplotlib import pylab\\n\",\n        \"from six.moves import range\\n\",\n        \"from six.moves.urllib.request import urlretrieve\\n\",\n        \"from sklearn.manifold import TSNE\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"aCjPJE944bkV\"\n      },\n      \"source\": [\n        \"Download the data from the source website if necessary.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"RJ-o3UBUFtCw\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"Found and verified text8.zip\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"url = 'http://mattmahoney.net/dc/'\\n\",\n        \"\\n\",\n        \"def maybe_download(filename, expected_bytes):\\n\",\n        \"  \\\"\\\"\\\"Download a file if not present, and make sure it's the right size.\\\"\\\"\\\"\\n\",\n        \"  if not os.path.exists(filename):\\n\",\n        \"    filename, _ = urlretrieve(url + filename, filename)\\n\",\n        \"  statinfo = os.stat(filename)\\n\",\n        \"  if statinfo.st_size == expected_bytes:\\n\",\n        \"    print('Found and verified %s' % filename)\\n\",\n        \"  else:\\n\",\n        \"    print(statinfo.st_size)\\n\",\n        \"    raise Exception(\\n\",\n        \"      'Failed to verify ' + filename + '. Can you get to it with a browser?')\\n\",\n        \"  return filename\\n\",\n        \"\\n\",\n        \"filename = maybe_download('text8.zip', 31344016)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Zqz3XiqI4mZT\"\n      },\n      \"source\": [\n        \"Read the data into a string.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"Mvf09fjugFU_\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"Data size 17005207\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"def read_data(filename):\\n\",\n        \"  \\\"\\\"\\\"Extract the first file enclosed in a zip file as a list of words\\\"\\\"\\\"\\n\",\n        \"  with zipfile.ZipFile(filename) as f:\\n\",\n        \"    data = tf.compat.as_str(f.read(f.namelist()[0])).split()\\n\",\n        \"  return data\\n\",\n        \"  \\n\",\n        \"words = read_data(filename)\\n\",\n        \"print('Data size %d' % len(words))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Zdw6i4F8glpp\"\n      },\n      \"source\": [\n        \"Build the dictionary and replace rare words with UNK token.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"gAL1EECXeZsD\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"Most common words (+UNK) [['UNK', 418391], ('the', 1061396), ('of', 593677), ('and', 416629), ('one', 411764)]\\n\",\n            \"Sample data [5243, 3083, 12, 6, 195, 2, 3136, 46, 59, 156]\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"vocabulary_size = 50000\\n\",\n        \"\\n\",\n        \"def build_dataset(words):\\n\",\n        \"  count = [['UNK', -1]]\\n\",\n        \"  count.extend(collections.Counter(words).most_common(vocabulary_size - 1))\\n\",\n        \"  dictionary = dict()\\n\",\n        \"  for word, _ in count:\\n\",\n        \"    dictionary[word] = len(dictionary)\\n\",\n        \"  data = list()\\n\",\n        \"  unk_count = 0\\n\",\n        \"  for word in words:\\n\",\n        \"    if word in dictionary:\\n\",\n        \"      index = dictionary[word]\\n\",\n        \"    else:\\n\",\n        \"      index = 0  # dictionary['UNK']\\n\",\n        \"      unk_count = unk_count + 1\\n\",\n        \"    data.append(index)\\n\",\n        \"  count[0][1] = unk_count\\n\",\n        \"  reverse_dictionary = dict(zip(dictionary.values(), dictionary.keys())) \\n\",\n        \"  return data, count, dictionary, reverse_dictionary\\n\",\n        \"\\n\",\n        \"data, count, dictionary, reverse_dictionary = build_dataset(words)\\n\",\n        \"print('Most common words (+UNK)', count[:5])\\n\",\n        \"print('Sample data', data[:10])\\n\",\n        \"del words  # Hint to reduce memory.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"lFwoyygOmWsL\"\n      },\n      \"source\": [\n        \"Function to generate a training batch for the skip-gram model.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"w9APjA-zmfjV\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"data: ['anarchism', 'originated', 'as', 'a', 'term', 'of', 'abuse', 'first']\\n\",\n            \"\\n\",\n            \"with num_skips = 2 and skip_window = 1:\\n\",\n            \"    batch: ['originated', 'originated', 'as', 'as', 'a', 'a', 'term', 'term']\\n\",\n            \"    labels: ['as', 'anarchism', 'a', 'originated', 'term', 'as', 'a', 'of']\\n\",\n            \"\\n\",\n            \"with num_skips = 4 and skip_window = 2:\\n\",\n            \"    batch: ['as', 'as', 'as', 'as', 'a', 'a', 'a', 'a']\\n\",\n            \"    labels: ['anarchism', 'originated', 'term', 'a', 'as', 'of', 'originated', 'term']\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"data_index = 0\\n\",\n        \"\\n\",\n        \"def generate_batch(batch_size, num_skips, skip_window):\\n\",\n        \"  global data_index\\n\",\n        \"  assert batch_size % num_skips == 0\\n\",\n        \"  assert num_skips <= 2 * skip_window\\n\",\n        \"  batch = np.ndarray(shape=(batch_size), dtype=np.int32)\\n\",\n        \"  labels = np.ndarray(shape=(batch_size, 1), dtype=np.int32)\\n\",\n        \"  span = 2 * skip_window + 1 # [ skip_window target skip_window ]\\n\",\n        \"  buffer = collections.deque(maxlen=span)\\n\",\n        \"  for _ in range(span):\\n\",\n        \"    buffer.append(data[data_index])\\n\",\n        \"    data_index = (data_index + 1) % len(data)\\n\",\n        \"  for i in range(batch_size // num_skips):\\n\",\n        \"    target = skip_window  # target label at the center of the buffer\\n\",\n        \"    targets_to_avoid = [ skip_window ]\\n\",\n        \"    for j in range(num_skips):\\n\",\n        \"      while target in targets_to_avoid:\\n\",\n        \"        target = random.randint(0, span - 1)\\n\",\n        \"      targets_to_avoid.append(target)\\n\",\n        \"      batch[i * num_skips + j] = buffer[skip_window]\\n\",\n        \"      labels[i * num_skips + j, 0] = buffer[target]\\n\",\n        \"    buffer.append(data[data_index])\\n\",\n        \"    data_index = (data_index + 1) % len(data)\\n\",\n        \"  return batch, labels\\n\",\n        \"\\n\",\n        \"print('data:', [reverse_dictionary[di] for di in data[:8]])\\n\",\n        \"\\n\",\n        \"for num_skips, skip_window in [(2, 1), (4, 2)]:\\n\",\n        \"    data_index = 0\\n\",\n        \"    batch, labels = generate_batch(batch_size=8, num_skips=num_skips, skip_window=skip_window)\\n\",\n        \"    print('\\\\nwith num_skips = %d and skip_window = %d:' % (num_skips, skip_window))\\n\",\n        \"    print('    batch:', [reverse_dictionary[bi] for bi in batch])\\n\",\n        \"    print('    labels:', [reverse_dictionary[li] for li in labels.reshape(8)])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Ofd1MbBuwiva\"\n      },\n      \"source\": [\n        \"Train a skip-gram model.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"8pQKsV4Vwlzy\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"batch_size = 128\\n\",\n        \"embedding_size = 128 # Dimension of the embedding vector.\\n\",\n        \"skip_window = 1 # How many words to consider left and right.\\n\",\n        \"num_skips = 2 # How many times to reuse an input to generate a label.\\n\",\n        \"# We pick a random validation set to sample nearest neighbors. here we limit the\\n\",\n        \"# validation samples to the words that have a low numeric ID, which by\\n\",\n        \"# construction are also the most frequent. \\n\",\n        \"valid_size = 16 # Random set of words to evaluate similarity on.\\n\",\n        \"valid_window = 100 # Only pick dev samples in the head of the distribution.\\n\",\n        \"valid_examples = np.array(random.sample(range(valid_window), valid_size))\\n\",\n        \"num_sampled = 64 # Number of negative examples to sample.\\n\",\n        \"\\n\",\n        \"graph = tf.Graph()\\n\",\n        \"\\n\",\n        \"with graph.as_default(), tf.device('/cpu:0'):\\n\",\n        \"\\n\",\n        \"  # Input data.\\n\",\n        \"  train_dataset = tf.placeholder(tf.int32, shape=[batch_size])\\n\",\n        \"  train_labels = tf.placeholder(tf.int32, shape=[batch_size, 1])\\n\",\n        \"  valid_dataset = tf.constant(valid_examples, dtype=tf.int32)\\n\",\n        \"  \\n\",\n        \"  # Variables.\\n\",\n        \"  embeddings = tf.Variable(\\n\",\n        \"    tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))\\n\",\n        \"  softmax_weights = tf.Variable(\\n\",\n        \"    tf.truncated_normal([vocabulary_size, embedding_size],\\n\",\n        \"                         stddev=1.0 / math.sqrt(embedding_size)))\\n\",\n        \"  softmax_biases = tf.Variable(tf.zeros([vocabulary_size]))\\n\",\n        \"  \\n\",\n        \"  # Model.\\n\",\n        \"  # Look up embeddings for inputs.\\n\",\n        \"  embed = tf.nn.embedding_lookup(embeddings, train_dataset)\\n\",\n        \"  # Compute the softmax loss, using a sample of the negative labels each time.\\n\",\n        \"  loss = tf.reduce_mean(\\n\",\n        \"    tf.nn.sampled_softmax_loss(weights=softmax_weights, biases=softmax_biases, inputs=embed,\\n\",\n        \"                               labels=train_labels, num_sampled=num_sampled, num_classes=vocabulary_size))\\n\",\n        \"\\n\",\n        \"  # Optimizer.\\n\",\n        \"  # Note: The optimizer will optimize the softmax_weights AND the embeddings.\\n\",\n        \"  # This is because the embeddings are defined as a variable quantity and the\\n\",\n        \"  # optimizer's `minimize` method will by default modify all variable quantities \\n\",\n        \"  # that contribute to the tensor it is passed.\\n\",\n        \"  # See docs on `tf.train.Optimizer.minimize()` for more details.\\n\",\n        \"  optimizer = tf.train.AdagradOptimizer(1.0).minimize(loss)\\n\",\n        \"  \\n\",\n        \"  # Compute the similarity between minibatch examples and all embeddings.\\n\",\n        \"  # We use the cosine distance:\\n\",\n        \"  norm = tf.sqrt(tf.reduce_sum(tf.square(embeddings), 1, keepdims=True))\\n\",\n        \"  normalized_embeddings = embeddings / norm\\n\",\n        \"  valid_embeddings = tf.nn.embedding_lookup(\\n\",\n        \"    normalized_embeddings, valid_dataset)\\n\",\n        \"  similarity = tf.matmul(valid_embeddings, tf.transpose(normalized_embeddings))\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"1bQFGceBxrWW\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"Initialized\\n\",\n            \"Average loss at step 0 : 8.58149623871\\n\",\n            \"Nearest to been: unfavourably, marmara, ancestral, legal, bogart, glossaries, worst, rooms,\\n\",\n            \"Nearest to time: conformist, strawberries, sindhi, waterfall, xia, nominates, psp, sensitivity,\\n\",\n            \"Nearest to over: overlord, panda, golden, semigroup, rawlings, involved, shreveport, handling,\\n\",\n            \"Nearest to not: hymenoptera, reintroducing, lamiaceae, because, davao, omnipotent, combustion, debilitating,\\n\",\n            \"Nearest to three: catalog, koza, gn, braque, holstein, postgresql, luddite, justine,\\n\",\n            \"Nearest to if: chilled, vince, fiddler, represented, sandinistas, happiness, lya, glands,\\n\",\n            \"Nearest to there: coast, photosynthetic, kimmei, legally, inner, illyricum, formats, fullmetal,\\n\",\n            \"Nearest to between: chuvash, prinz, suitability, wolfe, guideline, computability, diminutive, paulo,\\n\",\n            \"Nearest to from: tanganyika, workshop, elphinstone, spearhead, resurrected, kevlar, shangri, loves,\\n\",\n            \"Nearest to state: sextus, wuppertal, glaring, inches, unrounded, courageous, adler, connie,\\n\",\n            \"Nearest to on: gino, phocas, rhine, jg, macrocosm, jackass, jays, theorie,\\n\",\n            \"Nearest to and: standings, towed, reyes, willard, equality, juggling, wladislaus, faked,\\n\",\n            \"Nearest to eight: gresham, dogg, moko, tennis, superseded, telegraphy, scramble, vinod,\\n\",\n            \"Nearest to they: prisons, divisor, coder, ribeira, willingness, factional, nne, lotta,\\n\",\n            \"Nearest to more: blues, fur, sterling, tangier, khwarizmi, discouraged, cal, deicide,\\n\",\n            \"Nearest to other: enemies, bogged, brassicaceae, lascaux, dispense, alexandrians, crimea, dou,\\n\",\n            \"Average loss at step 2000 : 4.39983723116\\n\",\n            \"Average loss at step 4000 : 3.86921076906\\n\",\n            \"Average loss at step 6000 : 3.72542127335\\n\",\n            \"Average loss at step 8000 : 3.57835536212\\n\",\n            \"Average loss at step 10000 : 3.61056993055\\n\",\n            \"Nearest to been: glossaries, legal, unfavourably, be, hadad, wore, scarcity, were,\\n\",\n            \"Nearest to time: strawberries, conformist, gleichschaltung, waterfall, molality, nominates, baal, dole,\\n\",\n            \"Nearest to over: golden, semigroup, catus, motorways, brick, shehri, mussolini, overlord,\\n\",\n            \"Nearest to not: hinayana, it, often, they, boots, also, noaa, lindsey,\\n\",\n            \"Nearest to three: four, seven, six, five, nine, eight, two, zero,\\n\",\n            \"Nearest to if: glands, euros, wallpaper, redefine, toho, confuse, unsound, shepherd,\\n\",\n            \"Nearest to there: it, they, fullmetal, pace, legally, harpsichord, mma, bug,\\n\",\n            \"Nearest to between: chuvash, wandering, from, kirsch, pursuant, eurocents, suitability, jackie,\\n\",\n            \"Nearest to from: into, in, workshop, to, at, misogynist, elphinstone, spearhead,\\n\",\n            \"Nearest to state: sextus, glaring, connie, adler, esoteric, didactic, handedness, presidents,\\n\",\n            \"Nearest to on: in, at, for, ruminants, wakefulness, torrey, foley, gino,\\n\",\n            \"Nearest to and: or, who, but, zelda, of, for, thirst, chisel,\\n\",\n            \"Nearest to eight: nine, six, seven, five, four, three, zero, two,\\n\",\n            \"Nearest to they: he, prisons, there, we, hydrate, it, not, cumbersome,\\n\",\n            \"Nearest to more: skye, blues, trypomastigotes, deicide, most, readable, used, sterling,\\n\",\n            \"Nearest to other: trochaic, hush, surveyors, joachim, differentiation, attackers, reverence, attestation,\\n\",\n            \"Average loss at step 12000 : 3.66169466591\\n\",\n            \"Average loss at step 14000 : 3.60342905837\\n\",\n            \"Average loss at step 16000 : 3.57761328053\\n\",\n            \"Average loss at step 18000 : 3.57667332476\\n\",\n            \"Average loss at step 20000 : 3.53310145146\\n\",\n            \"Nearest to been: be, become, was, hadad, unfavourably, were, wore, partido,\\n\",\n            \"Nearest to time: gleichschaltung, strawberries, year, nominates, conformist, etch, admittedly, treasuries,\\n\",\n            \"Nearest to over: golden, semigroup, motorways, rawlings, triangle, trey, ustawa, mattingly,\\n\",\n            \"Nearest to not: they, boots, often, dieppe, still, hinayana, nearly, be,\\n\",\n            \"Nearest to three: two, four, five, seven, eight, six, nine, one,\\n\",\n            \"Nearest to if: wallpaper, euros, before, toho, unsound, so, bg, pfc,\\n\",\n            \"Nearest to there: they, it, he, usually, which, we, not, transactions,\\n\",\n            \"Nearest to between: from, with, about, near, reactance, eurocents, wandering, voltaire,\\n\",\n            \"Nearest to from: into, workshop, by, between, in, on, elphinstone, under,\\n\",\n            \"Nearest to state: glaring, esoteric, succeeding, sextus, vorarlberg, presidents, depends, connie,\\n\",\n            \"Nearest to on: in, at, upon, during, from, janis, foley, nubian,\\n\",\n            \"Nearest to and: or, thirst, but, where, s, who, pfaff, including,\\n\",\n            \"Nearest to eight: nine, seven, six, five, four, three, zero, one,\\n\",\n            \"Nearest to they: there, he, we, not, it, you, prisons, who,\\n\",\n            \"Nearest to more: less, most, deicide, skye, trypomastigotes, interventionism, toed, drummond,\\n\",\n            \"Nearest to other: such, joachim, hush, attackers, surveyors, trochaic, differentiation, reverence,\\n\",\n            \"Average loss at step 22000 : 3.59519316927\\n\",\n            \"Average loss at step 24000 : 3.55378576797\\n\",\n            \"Average loss at step 26000 : 3.56455037558\\n\",\n            \"Average loss at step 28000 : 3.5040882225\\n\",\n            \"Average loss at step 30000 : 3.39208897972\\n\",\n            \"Nearest to been: become, be, were, was, spotless, hadad, by, hausdorff,\\n\",\n            \"Nearest to time: gleichschaltung, year, day, nominates, jesus, strawberries, way, admittedly,\\n\",\n            \"Nearest to over: golden, semigroup, motorways, rawlings, interventionism, counternarcotics, adaption, brick,\\n\",\n            \"Nearest to not: often, they, it, never, still, nor, boots, pki,\\n\",\n            \"Nearest to three: four, six, two, eight, five, seven, nine, zero,\\n\",\n            \"Nearest to if: when, before, so, should, toho, where, bg, wallpaper,\\n\",\n            \"Nearest to there: they, it, which, usually, he, that, also, now,\\n\",\n            \"Nearest to between: with, from, in, panasonic, presupposes, churchmen, hijacking, where,\\n\",\n            \"Nearest to from: into, elphinstone, workshop, between, through, speculates, sosa, in,\\n\",\n            \"Nearest to state: esoteric, glaring, presidents, vorarlberg, atmosphere, succeeding, lute, connie,\\n\",\n            \"Nearest to on: upon, in, janis, during, torrey, against, infield, catalans,\\n\",\n            \"Nearest to and: or, thirst, in, but, of, sobib, cleaves, including,\\n\",\n            \"Nearest to eight: nine, six, four, seven, three, zero, five, one,\\n\",\n            \"Nearest to they: we, there, he, you, it, these, who, i,\\n\",\n            \"Nearest to more: less, most, deicide, faster, toed, very, skye, tonic,\\n\",\n            \"Nearest to other: different, attackers, joachim, various, such, many, differentiation, these,\\n\",\n            \"Average loss at step 32000 : 3.49501452419\\n\",\n            \"Average loss at step 34000 : 3.48593705952\\n\",\n            \"Average loss at step 36000 : 3.50112806576\\n\",\n            \"Average loss at step\"\n          ]\n        },\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \" 38000 : 3.49244426501\\n\",\n            \"Average loss at step 40000 : 3.3890105716\\n\",\n            \"Nearest to been: become, be, were, was, jolie, hausdorff, spotless, had,\\n\",\n            \"Nearest to time: year, way, gleichschaltung, period, day, stanislav, stage, outcome,\\n\",\n            \"Nearest to over: through, semigroup, rawlings, golden, about, brick, on, motorways,\\n\",\n            \"Nearest to not: they, radiated, never, pki, still, omnipotent, hinayana, really,\\n\",\n            \"Nearest to three: four, six, five, two, seven, eight, one, nine,\\n\",\n            \"Nearest to if: when, before, where, then, bg, because, can, should,\\n\",\n            \"Nearest to there: they, it, he, usually, this, typically, still, often,\\n\",\n            \"Nearest to between: with, in, from, about, against, churchmen, johansen, presupposes,\\n\",\n            \"Nearest to from: into, through, elphinstone, in, workshop, between, suing, under,\\n\",\n            \"Nearest to state: esoteric, presidents, atmosphere, vorarlberg, lute, succeeding, glaring, didactic,\\n\",\n            \"Nearest to on: upon, at, in, during, unitarians, under, catalans, batavians,\\n\",\n            \"Nearest to and: or, but, s, incapacitation, including, while, of, which,\\n\",\n            \"Nearest to eight: nine, six, seven, four, five, three, one, two,\\n\",\n            \"Nearest to they: we, he, there, you, she, i, not, it,\\n\",\n            \"Nearest to more: less, most, deicide, toed, greater, faster, quite, longer,\\n\",\n            \"Nearest to other: various, different, attackers, joachim, clutter, nz, trochaic, apulia,\\n\",\n            \"Average loss at step 42000 : 3.45294014364\\n\",\n            \"Average loss at step 44000 : 3.47660055941\\n\",\n            \"Average loss at step 46000 : 3.47458503014\\n\",\n            \"Average loss at step 48000 : 3.47261548793\\n\",\n            \"Average loss at step 50000 : 3.45390708435\\n\",\n            \"Nearest to been: become, be, had, was, were, hausdorff, prem, remained,\\n\",\n            \"Nearest to time: way, year, period, stv, day, gleichschaltung, stage, outcome,\\n\",\n            \"Nearest to over: through, golden, semigroup, about, brick, counternarcotics, theremin, mattingly,\\n\",\n            \"Nearest to not: they, still, never, really, sometimes, it, kiwifruit, nearly,\\n\",\n            \"Nearest to three: five, four, six, seven, two, eight, one, nine,\\n\",\n            \"Nearest to if: when, before, where, because, connexion, though, so, whether,\\n\",\n            \"Nearest to there: they, it, he, this, now, often, usually, still,\\n\",\n            \"Nearest to between: with, from, fashioned, churchmen, panasonic, explores, within, racial,\\n\",\n            \"Nearest to from: into, through, under, elphinstone, between, workshop, circumpolar, idiom,\\n\",\n            \"Nearest to state: atmosphere, vorarlberg, esoteric, presidents, madhya, majority, moulin, bowmen,\\n\",\n            \"Nearest to on: upon, in, catalans, tezuka, minotaurs, wakefulness, batavians, guglielmo,\\n\",\n            \"Nearest to and: or, but, thirst, signifier, which, however, including, unattractive,\\n\",\n            \"Nearest to eight: six, nine, seven, five, four, three, zero, two,\\n\",\n            \"Nearest to they: we, there, he, you, it, she, these, not,\\n\",\n            \"Nearest to more: less, most, quite, very, further, faster, toed, deicide,\\n\",\n            \"Nearest to other: various, different, many, attackers, are, joachim, nihilo, reject,\\n\",\n            \"Average loss at step 52000 : 3.43597227755\\n\",\n            \"Average loss at step 54000 : 3.25126817495\\n\",\n            \"Average loss at step 56000 : 3.35102432287\\n\",\n            \"Average loss at step 58000 : 3.44654818082\\n\",\n            \"Average loss at step 60000 : 3.4287913968\\n\",\n            \"Nearest to been: become, be, was, prem, had, remained, hadad, stanislavsky,\\n\",\n            \"Nearest to time: year, way, period, stv, barely, name, stage, restoring,\\n\",\n            \"Nearest to over: about, through, golden, adaption, counternarcotics, up, mattingly, brick,\\n\",\n            \"Nearest to not: still, never, nor, kiwifruit, they, nearly, therefore, rarely,\\n\",\n            \"Nearest to three: two, five, four, six, seven, eight, one, nine,\\n\",\n            \"Nearest to if: when, though, before, where, although, because, can, could,\\n\",\n            \"Nearest to there: they, it, he, still, she, we, this, often,\\n\",\n            \"Nearest to between: with, from, churchmen, among, ethical, within, vma, panasonic,\\n\",\n            \"Nearest to from: through, into, under, during, between, in, suing, across,\\n\",\n            \"Nearest to state: atmosphere, infringe, madhya, vorarlberg, government, bowmen, vargas, republic,\\n\",\n            \"Nearest to on: upon, through, within, ridiculous, janis, in, under, over,\\n\",\n            \"Nearest to and: or, while, including, but, of, like, whose, bannister,\\n\",\n            \"Nearest to eight: nine, six, five, four, seven, zero, three, two,\\n\",\n            \"Nearest to they: we, there, you, he, it, these, she, prisons,\\n\",\n            \"Nearest to more: less, most, quite, further, toed, very, faster, rather,\\n\",\n            \"Nearest to other: different, various, many, nihilo, these, amour, including, screenplays,\\n\",\n            \"Average loss at step 62000 : 3.38358767056\\n\",\n            \"Average loss at step 64000 : 3.41693099326\\n\",\n            \"Average loss at step 66000 : 3.39588000977\\n\",\n            \"Average loss at step 68000 : 3.35567189544\\n\",\n            \"Average loss at step 70000 : 3.38878934443\\n\",\n            \"Nearest to been: become, be, was, prem, remained, were, being, discounts,\\n\",\n            \"Nearest to time: year, way, day, period, barely, ethos, stage, reason,\\n\",\n            \"Nearest to over: about, through, fortunately, semigroup, theremin, off, loudest, up,\\n\",\n            \"Nearest to not: still, nor, never, they, actually, nearly, unelected, therefore,\\n\",\n            \"Nearest to three: five, two, four, six, seven, eight, nine, zero,\\n\",\n            \"Nearest to if: when, though, before, where, because, then, after, since,\\n\",\n            \"Nearest to there: they, it, he, often, she, we, usually, still,\\n\",\n            \"Nearest to between: among, with, within, from, ethical, churchmen, racial, prentice,\\n\",\n            \"Nearest to from: through, into, within, during, under, until, between, across,\\n\",\n            \"Nearest to state: city, atmosphere, desks, surrounding, preservation, bohr, principal, republic,\\n\",\n            \"Nearest to on: upon, tezuka, through, within, wakefulness, catalans, at, ingeborg,\\n\",\n            \"Nearest to and: or, but, while, including, thirst, jerzy, massing, abadan,\\n\",\n            \"Nearest to eight: seven, six, nine, five, four, three, two, zero,\\n\",\n            \"Nearest to they: we, you, he, there, she, it, prisons, who,\\n\",\n            \"Nearest to more: less, most, quite, very, faster, smaller, further, larger,\\n\",\n            \"Nearest to other: various, different, some, screenplays, lab, many, including, debugging,\\n\",\n            \"Average loss at step 72000 : 3.41103189731\\n\",\n            \"Average loss at step 74000 : 3.44926435578\\n\",\n            \"Average loss at step 76000 : 3.4423020488\\n\",\n            \"Average loss at step 78000 : 3.41976813722\\n\",\n            \"Average loss at step 80000 : 3.39511853886\\n\",\n            \"Nearest to been: become, be, remained, was, grown, were, prem, already,\"\n          ]\n        },\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"\\n\",\n            \"Nearest to time: year, way, period, reason, barely, distance, stage, day,\\n\",\n            \"Nearest to over: about, fortunately, through, semigroup, further, mattingly, rawlings, golden,\\n\",\n            \"Nearest to not: still, they, nor, never, we, kiwifruit, noaa, really,\\n\",\n            \"Nearest to three: five, two, seven, four, eight, six, nine, zero,\\n\",\n            \"Nearest to if: when, where, though, before, since, because, although, follows,\\n\",\n            \"Nearest to there: they, it, he, we, she, still, typically, actually,\\n\",\n            \"Nearest to between: with, among, within, in, racial, around, from, serapeum,\\n\",\n            \"Nearest to from: into, through, in, within, under, using, during, towards,\\n\",\n            \"Nearest to state: city, atmosphere, ferro, vorarlberg, surrounding, republic, madhya, national,\\n\",\n            \"Nearest to on: upon, poll, in, from, tezuka, janis, through, within,\\n\",\n            \"Nearest to and: or, but, including, while, s, which, thirst, although,\\n\",\n            \"Nearest to eight: nine, seven, six, five, four, three, zero, two,\\n\",\n            \"Nearest to they: we, you, there, he, she, it, these, not,\\n\",\n            \"Nearest to more: less, most, smaller, very, faster, quite, rather, larger,\\n\",\n            \"Nearest to other: various, different, joachim, including, theos, smaller, individual, screenplays,\\n\",\n            \"Average loss at step 82000 : 3.40933967865\\n\",\n            \"Average loss at step 84000 : 3.41618054378\\n\",\n            \"Average loss at step 86000 : 3.31485116804\\n\",\n            \"Average loss at step 88000 : 3.37068593091\\n\",\n            \"Average loss at step 90000 : 3.2785516749\\n\",\n            \"Nearest to been: become, be, was, prem, remained, grown, recently, already,\\n\",\n            \"Nearest to time: year, way, period, day, barely, battle, buds, name,\\n\",\n            \"Nearest to over: through, about, fortunately, off, theremin, semigroup, extraterrestrial, mattingly,\\n\",\n            \"Nearest to not: nor, still, never, otherwise, generally, separately, gown, hydrate,\\n\",\n            \"Nearest to three: four, five, six, two, eight, seven, nine, zero,\\n\",\n            \"Nearest to if: when, where, before, though, because, since, then, while,\\n\",\n            \"Nearest to there: they, it, he, we, she, still, typically, fiorello,\\n\",\n            \"Nearest to between: with, among, within, from, churchmen, prentice, racial, panasonic,\\n\",\n            \"Nearest to from: through, into, across, during, towards, until, at, within,\\n\",\n            \"Nearest to state: bohr, city, atmosphere, ferro, bowmen, republic, retaliation, vorarlberg,\\n\",\n            \"Nearest to on: upon, in, tezuka, at, during, within, via, catalans,\\n\",\n            \"Nearest to and: or, including, but, while, like, thirst, with, schuman,\\n\",\n            \"Nearest to eight: seven, nine, six, five, four, three, zero, two,\\n\",\n            \"Nearest to they: we, there, he, you, she, it, prisons, these,\\n\",\n            \"Nearest to more: less, most, very, faster, larger, quite, smaller, better,\\n\",\n            \"Nearest to other: different, various, tamara, prosthetic, including, individual, failing, restaurants,\\n\",\n            \"Average loss at step 92000 : 3.40355363208\\n\",\n            \"Average loss at step 94000 : 3.35647508007\\n\",\n            \"Average loss at step 96000 : 3.34374570692\\n\",\n            \"Average loss at step 98000 : 3.4230104093\\n\",\n            \"Average loss at step 100000 : 3.36909827\\n\",\n            \"Nearest to been: become, be, grown, was, being, already, remained, prem,\\n\",\n            \"Nearest to time: way, year, day, period, years, days, mothersbaugh, separators,\\n\",\n            \"Nearest to over: through, about, semigroup, further, fortunately, off, into, theremin,\\n\",\n            \"Nearest to not: never, nor, still, dieppe, really, unelected, actually, now,\\n\",\n            \"Nearest to three: four, two, five, seven, six, eight, nine, zero,\\n\",\n            \"Nearest to if: when, though, where, before, is, abe, then, follows,\\n\",\n            \"Nearest to there: they, it, he, we, still, she, typically, often,\\n\",\n            \"Nearest to between: within, with, among, churchmen, around, explores, from, reactance,\\n\",\n            \"Nearest to from: into, through, within, across, in, between, using, workshop,\\n\",\n            \"Nearest to state: atmosphere, bohr, national, ferro, germ, desks, city, unpaid,\\n\",\n            \"Nearest to on: upon, in, within, tezuka, janis, batavians, about, macrocosm,\\n\",\n            \"Nearest to and: or, but, purview, thirst, sukkot, epr, including, honesty,\\n\",\n            \"Nearest to eight: seven, nine, six, four, five, three, zero, one,\\n\",\n            \"Nearest to they: we, there, you, he, she, prisons, it, these,\\n\",\n            \"Nearest to more: less, most, very, quite, faster, larger, rather, smaller,\\n\",\n            \"Nearest to other: various, different, tamara, theos, some, cope, many, others,\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"num_steps = 100001\\n\",\n        \"\\n\",\n        \"with tf.Session(graph=graph) as session:\\n\",\n        \"  tf.global_variables_initializer().run()\\n\",\n        \"  print('Initialized')\\n\",\n        \"  average_loss = 0\\n\",\n        \"  for step in range(num_steps):\\n\",\n        \"    batch_data, batch_labels = generate_batch(\\n\",\n        \"      batch_size, num_skips, skip_window)\\n\",\n        \"    feed_dict = {train_dataset : batch_data, train_labels : batch_labels}\\n\",\n        \"    _, l = session.run([optimizer, loss], feed_dict=feed_dict)\\n\",\n        \"    average_loss += l\\n\",\n        \"    if step % 2000 == 0:\\n\",\n        \"      if step > 0:\\n\",\n        \"        average_loss = average_loss / 2000\\n\",\n        \"      # The average loss is an estimate of the loss over the last 2000 batches.\\n\",\n        \"      print('Average loss at step %d: %f' % (step, average_loss))\\n\",\n        \"      average_loss = 0\\n\",\n        \"    # note that this is expensive (~20% slowdown if computed every 500 steps)\\n\",\n        \"    if step % 10000 == 0:\\n\",\n        \"      sim = similarity.eval()\\n\",\n        \"      for i in range(valid_size):\\n\",\n        \"        valid_word = reverse_dictionary[valid_examples[i]]\\n\",\n        \"        top_k = 8 # number of nearest neighbors\\n\",\n        \"        nearest = (-sim[i, :]).argsort()[1:top_k+1]\\n\",\n        \"        log = 'Nearest to %s:' % valid_word\\n\",\n        \"        for k in range(top_k):\\n\",\n        \"          close_word = reverse_dictionary[nearest[k]]\\n\",\n        \"          log = '%s %s,' % (log, close_word)\\n\",\n        \"        print(log)\\n\",\n        \"  final_embeddings = normalized_embeddings.eval()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"jjJXYA_XzV79\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"num_points = 400\\n\",\n        \"\\n\",\n        \"tsne = TSNE(perplexity=30, n_components=2, init='pca', n_iter=5000, method='exact')\\n\",\n        \"two_d_embeddings = tsne.fit_transform(final_embeddings[1:num_points+1, :])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"o_e0D_UezcDe\"\n      },\n      \"outputs\": [\n        {\n          \"data\": {\n            \"image/png\": \"iVBORw0KGgoAAAANSUhEUgAAA3MAAANpCAYAAAChBGCHAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3XdAldUfx/H3BdlbQEVzoyDukZaae5aZ5tbcIzUz9x5Z\\njhwNNXMVztTExFHqT9Ny50hFc+ZKEVBwAbLh/v4gSXILChc/r3+69/Lc53yfewL8cM5zjsFoNBoR\\nERERERERk2KW0QWIiIiIiIjI01OYExERERERMUEKcyIiIiIiIiZIYU5ERERERMQEKcyJiIiIiIiY\\nIIU5ERERERERE5TmMBceHk7fvn1p2LAhb775JgEBAdy6dYvOnTtTv359unTpQnh4eHrUKiIiIiIi\\nIv8wpHWfuaFDh/Lqq6/SvHlzEhISiI6OZvbs2bi4uNC9e3fmzZtHeHg4gwYNSq+aRUREREREXnpp\\nGpmLiIjg4MGDNG/eHIBs2bLh4ODAtm3baNq0KQBNmzbll19+SXulIiIiIiIikiJbWt4cGBhI9uzZ\\nGT58OKdOnaJ48eKMGDGC69ev4+bmBoCbmxvXr19Pl2JFREREREQkWZpG5hISEjhx4gRt2rTB398f\\nGxsb5s2bl+oYg8GAwWBIU5EiIiIiIiKSWprCXK5cuciZMyelSpUCoH79+pw4cQI3NzdCQ0MBuHbt\\nGtmzZ3/kedJ4256IiIiIiMhLJ03TLN3d3fHw8ODChQsULFiQvXv34unpiaenJ/7+/vTo0YM1a9ZQ\\np06dR57HYDAQGhqRllIkE3N3d1D/ZmHq36xLfZu1qX+zLvVt1qb+zbrc3R2e+j1pCnMAo0ePZtCg\\nQcTHx5MvXz4mTZpEYmIi/fr148cffyRPnjx89dVXaW1GRERERERE7pHmMOft7c2PP/543+sLFy5M\\n66lFRERERETkIdK8abiIiIiIiIi8eApzIiIiIiIiJkhhTkRERERExAQpzImIiIiIiJgghTkRERER\\nERETpDAnIiIiIiJighTmRERERERETJDCnIiIiIiIiAlSmBMRERERETFBCnMiIiIiIiImSGFORERE\\nRETEBCnMiYiIiIiImCCFOREREREREROkMCciIiIiImKCFOZERERERERMkMKciIiIiIiICVKYExER\\nERERMUEKcyIiIiIiIiZIYU5ERERERMQEKcyJiIiIiIiYIIU5ERERERERE6QwJyIiIiIiYoIU5kRE\\nREREREyQwpyIiIiIiIgJUpgTERERERExQQpzIiIiIiIiJkhhTkRERERExAQpzImIiIiIiJgghTkR\\nERERERETpDAnIiIiIiJighTmRERERERETJDCnIiIiIiIiAlSmBMRERERETFBCnMiIiIiIiImSGFO\\nRERERETEBCnMiYiIiIiImCCFOREREREREROkMCciIiIiImKCFOZERERERERMkMKciIiIiIiICVKY\\nExERERERMUEKcyIiIiIiIiZIYU5ERERERMQEKcyJiIiIiIiYIIU5ERERERERE6QwJyIiIiIiYoIU\\n5kREREREREyQwpyIiIiIiIgJUpgTERERERExQQpzIiIiIiIiJkhhTkRERERExAQpzImIiIiIiJgg\\nhTkRERERERETpDAnIiIiIiJighTmRERERERETJDCnIiIiIiIiAlSmBMRERERETFBCnMiIiIiIiIm\\nSGFORERERETEBCnMiYiIiIiImCCFOREREREREROkMCciIiIiImKCFOZERERERERMkMKciIiIiIiI\\nCVKYExERERERMUEKcyIiIiIiIiZIYU5ERERERMQEKcyJiIiIiIiYIIU5ERERERERE6QwJyIiIiIi\\nYoIU5kREREREREyQwpyIiIiIiIgJUpgTERERERExQQpzIiIiIiIiJkhhTkRERERExAQpzImIiIiI\\niJgghTkRERERERETpDAnIiIiIiJighTmRERERERETJDCnIiIiIiIiAlSmBMRERERETFBCnMiIiIi\\nIiImSGFORERERETEBCnMiYiIiIiImCCFOREREREREROULaMLEBERkcxnxYqlbNiwHoBGjZpQrVoN\\nBg78kFKlyvLnnwG4u+dg0qTPsbKy4sqVQL74Ygq3bt3E2tqaoUNHki9fgYy9ABGRl4BG5kRERCSV\\nU6dOsnHjT8yfv4i5cxeyfr0/ERHhBAZeplmzlixZshJ7ewe2b98GwJQpE+jffzDffbeE3r0/4vPP\\nJ2fwFYiIvBw0MiciIiKpHD16hGrVamJlZQ1A9eq1CAg4jIdHHjw9iwDg5eVNcHAQ0dHRHDt2lNGj\\nh6a8Pz4+IUPqFhF52SjMiYiISCoGg+GBr1taWqQ8NjMzJykpDqMxCQcHBxYsWPaiyhMRkX9omqWI\\niIikUrp0GXbs+I3Y2Biio6PZseNXSpcue99xRqMRW1s7cufOza+//pLy2tmzf73okkVEXkoamRMR\\n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\\ntt/uu1FQ2bDhLEFBPQExoMrp093Yu/ckLi7/y/djfWxO5s2bN9i8eT0gx9KyEiNGjEVNTQ0Xl9Y0\\nauTEpUsXSEh4J9yiq6tLr159cHRsDLz7jsXERHPunCKgmj17AdOmTSQlJQWAUaPGY2VlQ9++PYmI\\neMy4cSNo2bINwcFBbNu2mTlzFhAf/5axY4cTHR2NpqYWDx7cx9m5FdHRUcyc6Z2jR+GX8PBhJMuX\\nh5CRIaZLl7LY22d9efCz/WYICHwJX2faIiAgIPAfYsaMtqxaFcK0aTvZtcvim86XyzQmNzcvz+rV\\ny1i/fg0tWrThyJFDnDhxJJtJup1ddfbs2cWsWdOUfXxokr5u3RbWrduCv/82fH0XK9t9yiQ90+Kh\\nYPBzeZaWKlWa3bt30L27K4mJibi4uDFt2mxWrFhMz55d6dWrK7dv3/jo/rq6elhZ2TB+/BRiYqLx\\n919DerqU4cMV3mdt2rTj4cP7tGjRmEOH9pGQkMjz58/Q0dFFXV0DH5+pnDlzSilYkUlmiaa9vT2g\\nKNHM5MqVixw+fIBevbrSr19P4uPfEhn5FJFIRKVKVShc2ASRSET58hWJiYnh8uWbpKVpkXlvYmIa\\nsW/fgxzPJzExkd27d37RtXRxaU18/Nsv2vdX5enTCDp0cGXTpgB0dHTYunUTM2d6M3XqLPbt24dM\\nJlNeb5FIhJ6eHv7+2+jYsRMLF85Xrs9K9u+YkZERCxYsxc9vE97eM/nzT0Wpd//+g7GxsWPdui10\\n6tQ1yz5r167EwqIS/v5b6ddvINOnTwIUnnuXLoVQpkxr5s5dxLp1qz86p+5zvH79hp49b+Lv78bG\\nja707fuasLDHnz0fAYFfDSEzJyAgIJBLRCIRrVo5fJdjFRST9IJIcnIyY8eOICEhHplMym+/9ade\\nvYZs2bIBdXV1XFzcWLRoPg8e3GfhwuVcvXqFAwf+UqrnfS/EYjETJ2Y9ZoUKFVmyZFW2tosXr8yy\\n7Orahb//Pq3cZ9iwUezatYPw8LtKURszs5I0auSUo4rf6tX+BAVd5vTpEwQG7mDhwuXZ2mTyoRCF\\nl9coatSonWVdcHBQlkBfLFZBJpNSpIghItH7D+MS9D6irJ2QEM/u3QG0b++SbZtUqigJ/BCZTIZY\\nLM5xrlRBw929Pvv3r/+3zFKGo+MW2rbNfq75xYdzMtevX0Px4maUKFESUJQlBgbuoFOnLgA0adJM\\n+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\\nw9DPMeWzadOmYunSpcWzzz5bFEVh2yqyYsWK4sMPPyyKoigGBgaKCxcuXPO+w/rOXG1t7dArvhcv\\nXhz6cPG9e/emtbU1lUol48ePz4QJE/7yrh23vq1bt2bx4sWpVCpJkpqamiS2rSbr16/P8uXL/3LN\\nvtXh0UcfzciRf/zz8NBDD+XkyZNJ7FsNenp6MmHChIwfPz6VSiWtra3Zu3fvcB+LG1BbW5v7778/\\nSXL77bdn4sSJ6evrS1dXV2bNmpUkmTVrVvbs2TOcx+Q6nTx5Mvv27cvTTz89dM221eHixYs5ePBg\\n5syZkyQZPXp07rzzzmved1hjbtmyZeno6Mjjjz+eN954I8uWLUuSnDp1KvX19UPPq6+vT19f33Ad\\nk+vQ29ubgwcPZu7cuZk/f34OHTqUxLbVYs+ePamvr8/kyZP/ct2+1Wf79u157LHHkti3GvT19WXc\\nuHFDX9fV1dmwihw/fjzff/99GhoacubMmYwdOzZJMnbs2Jw5c2aYT8f1eP3117NixYqhF9iS2LZK\\nHD9+PDU1NXnppZcya9asrF69Or/99ts173vdHxr+by1cuDCnT5/+2/X29vZs2bIlq1evzpNPPplP\\nPvkkK1euzObNm//x+/jg8VvP1bYdHBzM+fPns23btvT09KS9vf2Kr/7a9tZ0tX03btyYTZs2DV0r\\nrvJxlfa9NV1p3xdeeCFTp05NkrzzzjupVCqZMWPGFb+PfcvFXtXr119/zZIlS7Jq1arccccdf3ls\\nxIgRti+hzz77LGPGjMkDDzyQr7766h+fY9vyunz5cg4fPpw1a9akoaEhr7322t9+j/nf7Ps/j7kr\\nxVmSLF++PO+9916SpLm5OatXr07yxyuFf97Wk/zxFvOft2By67jatlu3bk1TU1OSpKGhISNHjszZ\\ns2dtWyJX2veHH37I8ePH09bWluSPV/pnz56dbdu22bdErvbzmyQ7duzIvn378v777w9ds2/51dXV\\n5cSJE0Nf27A6DAwMZMmSJWlra0tjY2OSZMyYMfn5559TW1ubU6dODf26A+Xx7bffpqurK/v27Ut/\\nf38uXbqU5cuX27ZK1NfXp66uLg0NDUmSadOmZePGjRk7duw17Tust1nefffd+frrr5Mk3d3dueee\\ne5IkU6dOza5du9Lf359jx46lt7d36C9KOTQ2Nqa7uztJcvTo0QwMDKSmpsa2VeDee+/NF198ka6u\\nrnR1daWuri47duzI2LFj7Vsl9u/fn3fffTdvv/12/vOf/wxdt2/5Pfjgg+nt7c3x48fT39+f3bt3\\n54knnhjuY3EDiqLIqlWrMnHixCxYsGDo+tSpU/PRRx8lST7++OOhyKM8li5dmn379qWrqytvvvlm\\nHnnkkXR2dtq2StTW1mbcuHE5evRokuTLL7/MpEmTMmXKlGva93/+ztzVrFu3LuvWrUt/f39uu+22\\nvPLKK0mSSZMmpaWlJa2trRk1alTWrl3rLeSSmT17dlauXJkZM2akUqmko6MjiW2r0f/fz77V4dVX\\nX83AwEAWLVqUJHn44Yfz8ssv27cKjB49OmvWrMkzzzyT33//PXPmzMnEiROH+1jcgG+++SY7d+7M\\nfffdl5kzZyb5IwIWL16c9vb2bN++PXfddVfeeuutYT4pN4ttq8eaNWvy4osvZmBgIBMmTMj69esz\\nODh4TfuOKK72yy4AAADckob1NksAAACuj5gDAAAoITEHAABQQmIOAACghMQcAABACYk5AACAEhJz\\nAAAAJSTmAAAASuj/AKSSWUR2kw4CAAAAAElFTkSuQmCC\\n\",\n            \"text/plain\": [\n              \"<matplotlib.figure.Figure at 0x2ee65e10>\"\n            ]\n          },\n          \"metadata\": {},\n          \"output_type\": \"display_data\"\n        }\n      ],\n      \"source\": [\n        \"def plot(embeddings, labels):\\n\",\n        \"  assert embeddings.shape[0] >= len(labels), 'More labels than embeddings'\\n\",\n        \"  pylab.figure(figsize=(15,15))  # in inches\\n\",\n        \"  for i, label in enumerate(labels):\\n\",\n        \"    x, y = embeddings[i,:]\\n\",\n        \"    pylab.scatter(x, y)\\n\",\n        \"    pylab.annotate(label, xy=(x, y), xytext=(5, 2), textcoords='offset points',\\n\",\n        \"                   ha='right', va='bottom')\\n\",\n        \"  pylab.show()\\n\",\n        \"\\n\",\n        \"words = [reverse_dictionary[i] for i in range(1, num_points+1)]\\n\",\n        \"plot(two_d_embeddings, words)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"QB5EFrBnpNnc\"\n      },\n      \"source\": [\n        \"---\\n\",\n        \"\\n\",\n        \"Problem\\n\",\n        \"-------\\n\",\n        \"\\n\",\n        \"An alternative to skip-gram is another Word2Vec model called [CBOW](http://arxiv.org/abs/1301.3781) (Continuous Bag of Words). In the CBOW model, instead of predicting a context word from a word vector, you predict a word from the sum of all the word vectors in its context. Implement and evaluate a CBOW model trained on the text8 dataset.\\n\",\n        \"\\n\",\n        \"---\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"name\": \"5_word2vec.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_deep_learning/6_lstm.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"D7tqLMoKF6uq\"\n      },\n      \"source\": [\n        \"Deep Learning\\n\",\n        \"=============\\n\",\n        \"\\n\",\n        \"Assignment 6\\n\",\n        \"------------\\n\",\n        \"\\n\",\n        \"After training a skip-gram model in `5_word2vec.ipynb`, the goal of this notebook is to train a LSTM character model over [Text8](http://mattmahoney.net/dc/textdata) data.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"MvEblsgEXxrd\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# These are all the modules we'll be using later. Make sure you can import them\\n\",\n        \"# before proceeding further.\\n\",\n        \"from __future__ import print_function\\n\",\n        \"import os\\n\",\n        \"import numpy as np\\n\",\n        \"import random\\n\",\n        \"import string\\n\",\n        \"import tensorflow as tf\\n\",\n        \"import zipfile\\n\",\n        \"from six.moves import range\\n\",\n        \"from six.moves.urllib.request import urlretrieve\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"RJ-o3UBUFtCw\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"Found and verified text8.zip\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"url = 'http://mattmahoney.net/dc/'\\n\",\n        \"\\n\",\n        \"def maybe_download(filename, expected_bytes):\\n\",\n        \"  \\\"\\\"\\\"Download a file if not present, and make sure it's the right size.\\\"\\\"\\\"\\n\",\n        \"  if not os.path.exists(filename):\\n\",\n        \"    filename, _ = urlretrieve(url + filename, filename)\\n\",\n        \"  statinfo = os.stat(filename)\\n\",\n        \"  if statinfo.st_size == expected_bytes:\\n\",\n        \"    print('Found and verified %s' % filename)\\n\",\n        \"  else:\\n\",\n        \"    print(statinfo.st_size)\\n\",\n        \"    raise Exception(\\n\",\n        \"      'Failed to verify ' + filename + '. Can you get to it with a browser?')\\n\",\n        \"  return filename\\n\",\n        \"\\n\",\n        \"filename = maybe_download('text8.zip', 31344016)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"Mvf09fjugFU_\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"Data size 100000000\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"def read_data(filename):\\n\",\n        \"  with zipfile.ZipFile(filename) as f:\\n\",\n        \"    name = f.namelist()[0]\\n\",\n        \"    data = tf.compat.as_str(f.read(name))\\n\",\n        \"  return data\\n\",\n        \"  \\n\",\n        \"text = read_data(filename)\\n\",\n        \"print('Data size %d' % len(text))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ga2CYACE-ghb\"\n      },\n      \"source\": [\n        \"Create a small validation set.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"w-oBpfFG-j43\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"99999000 ons anarchists advocate social relations based upon voluntary as\\n\",\n            \"1000  anarchism originated as a term of abuse first used against earl\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"valid_size = 1000\\n\",\n        \"valid_text = text[:valid_size]\\n\",\n        \"train_text = text[valid_size:]\\n\",\n        \"train_size = len(train_text)\\n\",\n        \"print(train_size, train_text[:64])\\n\",\n        \"print(valid_size, valid_text[:64])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Zdw6i4F8glpp\"\n      },\n      \"source\": [\n        \"Utility functions to map characters to vocabulary IDs and back.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"gAL1EECXeZsD\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"1 26 0 Unexpected character: ï\\n\",\n            \"0\\n\",\n            \"a z  \\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"vocabulary_size = len(string.ascii_lowercase) + 1 # [a-z] + ' '\\n\",\n        \"first_letter = ord(string.ascii_lowercase[0])\\n\",\n        \"\\n\",\n        \"def char2id(char):\\n\",\n        \"  if char in string.ascii_lowercase:\\n\",\n        \"    return ord(char) - first_letter + 1\\n\",\n        \"  elif char == ' ':\\n\",\n        \"    return 0\\n\",\n        \"  else:\\n\",\n        \"    print('Unexpected character: %s' % char)\\n\",\n        \"    return 0\\n\",\n        \"  \\n\",\n        \"def id2char(dictid):\\n\",\n        \"  if dictid > 0:\\n\",\n        \"    return chr(dictid + first_letter - 1)\\n\",\n        \"  else:\\n\",\n        \"    return ' '\\n\",\n        \"\\n\",\n        \"print(char2id('a'), char2id('z'), char2id(' '), char2id('ï'))\\n\",\n        \"print(id2char(1), id2char(26), id2char(0))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"lFwoyygOmWsL\"\n      },\n      \"source\": [\n        \"Function to generate a training batch for the LSTM model.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"d9wMtjy5hCj9\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"['ons anarchi', 'when milita', 'lleria arch', ' abbeys and', 'married urr', 'hel and ric', 'y and litur', 'ay opened f', 'tion from t', 'migration t', 'new york ot', 'he boeing s', 'e listed wi', 'eber has pr', 'o be made t', 'yer who rec', 'ore signifi', 'a fierce cr', ' two six ei', 'aristotle s', 'ity can be ', ' and intrac', 'tion of the', 'dy to pass ', 'f certain d', 'at it will ', 'e convince ', 'ent told hi', 'ampaign and', 'rver side s', 'ious texts ', 'o capitaliz', 'a duplicate', 'gh ann es d', 'ine january', 'ross zero t', 'cal theorie', 'ast instanc', ' dimensiona', 'most holy m', 't s support', 'u is still ', 'e oscillati', 'o eight sub', 'of italy la', 's the tower', 'klahoma pre', 'erprise lin', 'ws becomes ', 'et in a naz', 'the fabian ', 'etchy to re', ' sharman ne', 'ised empero', 'ting in pol', 'd neo latin', 'th risky ri', 'encyclopedi', 'fense the a', 'duating fro', 'treet grid ', 'ations more', 'appeal of d', 'si have mad']\\n\",\n            \"['ists advoca', 'ary governm', 'hes nationa', 'd monasteri', 'raca prince', 'chard baer ', 'rgical lang', 'for passeng', 'the nationa', 'took place ', 'ther well k', 'seven six s', 'ith a gloss', 'robably bee', 'to recogniz', 'ceived the ', 'icant than ', 'ritic of th', 'ight in sig', 's uncaused ', ' lost as in', 'cellular ic', 'e size of t', ' him a stic', 'drugs confu', ' take to co', ' the priest', 'im to name ', 'd barred at', 'standard fo', ' such as es', 'ze on the g', 'e of the or', 'd hiver one', 'y eight mar', 'the lead ch', 'es classica', 'ce the non ', 'al analysis', 'mormons bel', 't or at lea', ' disagreed ', 'ing system ', 'btypes base', 'anguages th', 'r commissio', 'ess one nin', 'nux suse li', ' the first ', 'zi concentr', ' society ne', 'elatively s', 'etworks sha', 'or hirohito', 'litical ini', 'n most of t', 'iskerdoo ri', 'ic overview', 'air compone', 'om acnm acc', ' centerline', 'e than any ', 'devotional ', 'de such dev']\\n\",\n            \"[' a']\\n\",\n            \"['an']\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"batch_size=64\\n\",\n        \"num_unrollings=10\\n\",\n        \"\\n\",\n        \"class BatchGenerator(object):\\n\",\n        \"  def __init__(self, text, batch_size, num_unrollings):\\n\",\n        \"    self._text = text\\n\",\n        \"    self._text_size = len(text)\\n\",\n        \"    self._batch_size = batch_size\\n\",\n        \"    self._num_unrollings = num_unrollings\\n\",\n        \"    segment = self._text_size // batch_size\\n\",\n        \"    self._cursor = [ offset * segment for offset in range(batch_size)]\\n\",\n        \"    self._last_batch = self._next_batch()\\n\",\n        \"  \\n\",\n        \"  def _next_batch(self):\\n\",\n        \"    \\\"\\\"\\\"Generate a single batch from the current cursor position in the data.\\\"\\\"\\\"\\n\",\n        \"    batch = np.zeros(shape=(self._batch_size, vocabulary_size), dtype=np.float)\\n\",\n        \"    for b in range(self._batch_size):\\n\",\n        \"      batch[b, char2id(self._text[self._cursor[b]])] = 1.0\\n\",\n        \"      self._cursor[b] = (self._cursor[b] + 1) % self._text_size\\n\",\n        \"    return batch\\n\",\n        \"  \\n\",\n        \"  def next(self):\\n\",\n        \"    \\\"\\\"\\\"Generate the next array of batches from the data. The array consists of\\n\",\n        \"    the last batch of the previous array, followed by num_unrollings new ones.\\n\",\n        \"    \\\"\\\"\\\"\\n\",\n        \"    batches = [self._last_batch]\\n\",\n        \"    for step in range(self._num_unrollings):\\n\",\n        \"      batches.append(self._next_batch())\\n\",\n        \"    self._last_batch = batches[-1]\\n\",\n        \"    return batches\\n\",\n        \"\\n\",\n        \"def characters(probabilities):\\n\",\n        \"  \\\"\\\"\\\"Turn a 1-hot encoding or a probability distribution over the possible\\n\",\n        \"  characters back into its (most likely) character representation.\\\"\\\"\\\"\\n\",\n        \"  return [id2char(c) for c in np.argmax(probabilities, 1)]\\n\",\n        \"\\n\",\n        \"def batches2string(batches):\\n\",\n        \"  \\\"\\\"\\\"Convert a sequence of batches back into their (most likely) string\\n\",\n        \"  representation.\\\"\\\"\\\"\\n\",\n        \"  s = [''] * batches[0].shape[0]\\n\",\n        \"  for b in batches:\\n\",\n        \"    s = [''.join(x) for x in zip(s, characters(b))]\\n\",\n        \"  return s\\n\",\n        \"\\n\",\n        \"train_batches = BatchGenerator(train_text, batch_size, num_unrollings)\\n\",\n        \"valid_batches = BatchGenerator(valid_text, 1, 1)\\n\",\n        \"\\n\",\n        \"print(batches2string(train_batches.next()))\\n\",\n        \"print(batches2string(train_batches.next()))\\n\",\n        \"print(batches2string(valid_batches.next()))\\n\",\n        \"print(batches2string(valid_batches.next()))\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"KyVd8FxT5QBc\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def logprob(predictions, labels):\\n\",\n        \"  \\\"\\\"\\\"Log-probability of the true labels in a predicted batch.\\\"\\\"\\\"\\n\",\n        \"  predictions[predictions < 1e-10] = 1e-10\\n\",\n        \"  return np.sum(np.multiply(labels, -np.log(predictions))) / labels.shape[0]\\n\",\n        \"\\n\",\n        \"def sample_distribution(distribution):\\n\",\n        \"  \\\"\\\"\\\"Sample one element from a distribution assumed to be an array of normalized\\n\",\n        \"  probabilities.\\n\",\n        \"  \\\"\\\"\\\"\\n\",\n        \"  r = random.uniform(0, 1)\\n\",\n        \"  s = 0\\n\",\n        \"  for i in range(len(distribution)):\\n\",\n        \"    s += distribution[i]\\n\",\n        \"    if s >= r:\\n\",\n        \"      return i\\n\",\n        \"  return len(distribution) - 1\\n\",\n        \"\\n\",\n        \"def sample(prediction):\\n\",\n        \"  \\\"\\\"\\\"Turn a (column) prediction into 1-hot encoded samples.\\\"\\\"\\\"\\n\",\n        \"  p = np.zeros(shape=[1, vocabulary_size], dtype=np.float)\\n\",\n        \"  p[0, sample_distribution(prediction[0])] = 1.0\\n\",\n        \"  return p\\n\",\n        \"\\n\",\n        \"def random_distribution():\\n\",\n        \"  \\\"\\\"\\\"Generate a random column of probabilities.\\\"\\\"\\\"\\n\",\n        \"  b = np.random.uniform(0.0, 1.0, size=[1, vocabulary_size])\\n\",\n        \"  return b/np.sum(b, 1)[:,None]\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"K8f67YXaDr4C\"\n      },\n      \"source\": [\n        \"Simple LSTM Model.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"Q5rxZK6RDuGe\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"num_nodes = 64\\n\",\n        \"\\n\",\n        \"graph = tf.Graph()\\n\",\n        \"with graph.as_default():\\n\",\n        \"  \\n\",\n        \"  # Parameters:\\n\",\n        \"  # Input gate: input, previous output, and bias.\\n\",\n        \"  ix = tf.Variable(tf.truncated_normal([vocabulary_size, num_nodes], -0.1, 0.1))\\n\",\n        \"  im = tf.Variable(tf.truncated_normal([num_nodes, num_nodes], -0.1, 0.1))\\n\",\n        \"  ib = tf.Variable(tf.zeros([1, num_nodes]))\\n\",\n        \"  # Forget gate: input, previous output, and bias.\\n\",\n        \"  fx = tf.Variable(tf.truncated_normal([vocabulary_size, num_nodes], -0.1, 0.1))\\n\",\n        \"  fm = tf.Variable(tf.truncated_normal([num_nodes, num_nodes], -0.1, 0.1))\\n\",\n        \"  fb = tf.Variable(tf.zeros([1, num_nodes]))\\n\",\n        \"  # Memory cell: input, state and bias.                             \\n\",\n        \"  cx = tf.Variable(tf.truncated_normal([vocabulary_size, num_nodes], -0.1, 0.1))\\n\",\n        \"  cm = tf.Variable(tf.truncated_normal([num_nodes, num_nodes], -0.1, 0.1))\\n\",\n        \"  cb = tf.Variable(tf.zeros([1, num_nodes]))\\n\",\n        \"  # Output gate: input, previous output, and bias.\\n\",\n        \"  ox = tf.Variable(tf.truncated_normal([vocabulary_size, num_nodes], -0.1, 0.1))\\n\",\n        \"  om = tf.Variable(tf.truncated_normal([num_nodes, num_nodes], -0.1, 0.1))\\n\",\n        \"  ob = tf.Variable(tf.zeros([1, num_nodes]))\\n\",\n        \"  # Variables saving state across unrollings.\\n\",\n        \"  saved_output = tf.Variable(tf.zeros([batch_size, num_nodes]), trainable=False)\\n\",\n        \"  saved_state = tf.Variable(tf.zeros([batch_size, num_nodes]), trainable=False)\\n\",\n        \"  # Classifier weights and biases.\\n\",\n        \"  w = tf.Variable(tf.truncated_normal([num_nodes, vocabulary_size], -0.1, 0.1))\\n\",\n        \"  b = tf.Variable(tf.zeros([vocabulary_size]))\\n\",\n        \"  \\n\",\n        \"  # Definition of the cell computation.\\n\",\n        \"  def lstm_cell(i, o, state):\\n\",\n        \"    \\\"\\\"\\\"Create a LSTM cell. See e.g.: http://arxiv.org/pdf/1402.1128v1.pdf\\n\",\n        \"    Note that in this formulation, we omit the various connections between the\\n\",\n        \"    previous state and the gates.\\\"\\\"\\\"\\n\",\n        \"    input_gate = tf.sigmoid(tf.matmul(i, ix) + tf.matmul(o, im) + ib)\\n\",\n        \"    forget_gate = tf.sigmoid(tf.matmul(i, fx) + tf.matmul(o, fm) + fb)\\n\",\n        \"    update = tf.matmul(i, cx) + tf.matmul(o, cm) + cb\\n\",\n        \"    state = forget_gate * state + input_gate * tf.tanh(update)\\n\",\n        \"    output_gate = tf.sigmoid(tf.matmul(i, ox) + tf.matmul(o, om) + ob)\\n\",\n        \"    return output_gate * tf.tanh(state), state\\n\",\n        \"\\n\",\n        \"  # Input data.\\n\",\n        \"  train_data = list()\\n\",\n        \"  for _ in range(num_unrollings + 1):\\n\",\n        \"    train_data.append(\\n\",\n        \"      tf.placeholder(tf.float32, shape=[batch_size,vocabulary_size]))\\n\",\n        \"  train_inputs = train_data[:num_unrollings]\\n\",\n        \"  train_labels = train_data[1:]  # labels are inputs shifted by one time step.\\n\",\n        \"\\n\",\n        \"  # Unrolled LSTM loop.\\n\",\n        \"  outputs = list()\\n\",\n        \"  output = saved_output\\n\",\n        \"  state = saved_state\\n\",\n        \"  for i in train_inputs:\\n\",\n        \"    output, state = lstm_cell(i, output, state)\\n\",\n        \"    outputs.append(output)\\n\",\n        \"\\n\",\n        \"  # State saving across unrollings.\\n\",\n        \"  with tf.control_dependencies([saved_output.assign(output),\\n\",\n        \"                                saved_state.assign(state)]):\\n\",\n        \"    # Classifier.\\n\",\n        \"    logits = tf.nn.xw_plus_b(tf.concat(outputs, 0), w, b)\\n\",\n        \"    loss = tf.reduce_mean(\\n\",\n        \"      tf.nn.softmax_cross_entropy_with_logits(\\n\",\n        \"        labels=tf.concat(train_labels, 0), logits=logits))\\n\",\n        \"\\n\",\n        \"  # Optimizer.\\n\",\n        \"  global_step = tf.Variable(0)\\n\",\n        \"  learning_rate = tf.train.exponential_decay(\\n\",\n        \"    10.0, global_step, 5000, 0.1, staircase=True)\\n\",\n        \"  optimizer = tf.train.GradientDescentOptimizer(learning_rate)\\n\",\n        \"  gradients, v = zip(*optimizer.compute_gradients(loss))\\n\",\n        \"  gradients, _ = tf.clip_by_global_norm(gradients, 1.25)\\n\",\n        \"  optimizer = optimizer.apply_gradients(\\n\",\n        \"    zip(gradients, v), global_step=global_step)\\n\",\n        \"\\n\",\n        \"  # Predictions.\\n\",\n        \"  train_prediction = tf.nn.softmax(logits)\\n\",\n        \"  \\n\",\n        \"  # Sampling and validation eval: batch 1, no unrolling.\\n\",\n        \"  sample_input = tf.placeholder(tf.float32, shape=[1, vocabulary_size])\\n\",\n        \"  saved_sample_output = tf.Variable(tf.zeros([1, num_nodes]))\\n\",\n        \"  saved_sample_state = tf.Variable(tf.zeros([1, num_nodes]))\\n\",\n        \"  reset_sample_state = tf.group(\\n\",\n        \"    saved_sample_output.assign(tf.zeros([1, num_nodes])),\\n\",\n        \"    saved_sample_state.assign(tf.zeros([1, num_nodes])))\\n\",\n        \"  sample_output, sample_state = lstm_cell(\\n\",\n        \"    sample_input, saved_sample_output, saved_sample_state)\\n\",\n        \"  with tf.control_dependencies([saved_sample_output.assign(sample_output),\\n\",\n        \"                                saved_sample_state.assign(sample_state)]):\\n\",\n        \"    sample_prediction = tf.nn.softmax(tf.nn.xw_plus_b(sample_output, w, b))\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"RD9zQCZTEaEm\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"Initialized\\n\",\n            \"Average loss at step 0 : 3.29904174805 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 27.09\\n\",\n            \"================================================================================\\n\",\n            \"srk dwmrnuldtbbgg tapootidtu xsciu sgokeguw hi ieicjq lq piaxhazvc s fht wjcvdlh\\n\",\n            \"lhrvallvbeqqquc dxd y siqvnle bzlyw nr rwhkalezo siie o deb e lpdg  storq u nx o\\n\",\n            \"meieu nantiouie gdys qiuotblci loc hbiznauiccb cqzed acw l tsm adqxplku gn oaxet\\n\",\n            \"unvaouc oxchywdsjntdh zpklaejvxitsokeerloemee htphisb th eaeqseibumh aeeyj j orw\\n\",\n            \"ogmnictpycb whtup   otnilnesxaedtekiosqet  liwqarysmt  arj flioiibtqekycbrrgoysj\\n\",\n            \"================================================================================\\n\",\n            \"Validation set perplexity: 19.99\\n\",\n            \"Average loss at step 100 : 2.59553678274 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 9.57\\n\",\n            \"Validation set perplexity: 10.60\\n\",\n            \"Average loss at step 200 : 2.24747137785 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 7.68\\n\",\n            \"Validation set perplexity: 8.84\\n\",\n            \"Average loss at step 300 : 2.09438110709 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 7.41\\n\",\n            \"Validation set perplexity: 8.13\\n\",\n            \"Average loss at step 400 : 1.99440989017 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 6.46\\n\",\n            \"Validation set perplexity: 7.58\\n\",\n            \"Average loss at step 500 : 1.9320810616 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 6.30\\n\",\n            \"Validation set perplexity: 6.88\\n\",\n            \"Average loss at step 600 : 1.90935629249 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 7.21\\n\",\n            \"Validation set perplexity: 6.91\\n\",\n            \"Average loss at step 700 : 1.85583009005 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 6.13\\n\",\n            \"Validation set perplexity: 6.60\\n\",\n            \"Average loss at step 800 : 1.82152368546 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 6.01\\n\",\n            \"Validation set perplexity: 6.37\\n\",\n            \"Average loss at step 900 : 1.83169809818 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 7.20\\n\",\n            \"Validation set perplexity: 6.23\\n\",\n            \"Average loss at step 1000 : 1.82217029214 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 6.73\\n\",\n            \"================================================================================\\n\",\n            \"le action b of the tert sy ofter selvorang previgned stischdy yocal chary the co\\n\",\n            \"le relganis networks partucy cetinning wilnchan sics rumeding a fulch laks oftes\\n\",\n            \"hian andoris ret the ecause bistory l pidect one eight five lack du that the ses\\n\",\n            \"aiv dromery buskocy becomer worils resism disele retery exterrationn of hide in \\n\",\n            \"mer miter y sught esfectur of the upission vain is werms is vul ugher compted by\\n\",\n            \"================================================================================\\n\",\n            \"Validation set perplexity: 6.07\\n\",\n            \"Average loss at step 1100 : 1.77301145077 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 6.03\\n\",\n            \"Validation set perplexity: 5.89\\n\",\n            \"Average loss at step 1200 : 1.75306463003 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 6.50\\n\",\n            \"Validation set perplexity: 5.61\\n\",\n            \"Average loss at step 1300 : 1.72937195778 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.00\\n\",\n            \"Validation set perplexity: 5.60\\n\",\n            \"Average loss at step 1400 : 1.74773373723 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 6.48\\n\",\n            \"Validation set perplexity: 5.66\\n\",\n            \"Average loss at step 1500 : 1.7368799901 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.22\\n\",\n            \"Validation set perplexity: 5.44\\n\",\n            \"Average loss at step 1600 : 1.74528762937 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.85\\n\",\n            \"Validation set perplexity: 5.33\\n\",\n            \"Average loss at step 1700 : 1.70881183743 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.33\\n\",\n            \"Validation set perplexity: 5.56\\n\",\n            \"Average loss at step 1800 : 1.67776108027 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.33\\n\",\n            \"Validation set perplexity: 5.29\\n\",\n            \"Average loss at step 1900 : 1.64935536742 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.29\\n\",\n            \"Validation set perplexity: 5.15\\n\",\n            \"Average loss at step\"\n          ]\n        },\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \" 2000 : 1.69528644681 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.13\\n\",\n            \"================================================================================\\n\",\n            \"vers soqually have one five landwing to docial page kagan lower with ther batern\\n\",\n            \"ctor son alfortmandd tethre k skin the known purated to prooust caraying the fit\\n\",\n            \"je in beverb is the sournction bainedy wesce tu sture artualle lines digra forme\\n\",\n            \"m rousively haldio ourso ond anvary was for the seven solies hild buil  s  to te\\n\",\n            \"zall for is it is one nine eight eight one neval to the kime typer oene where he\\n\",\n            \"================================================================================\\n\",\n            \"Validation set perplexity: 5.25\\n\",\n            \"Average loss at step 2100 : 1.68808053017 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.17\\n\",\n            \"Validation set perplexity: 5.01\\n\",\n            \"Average loss at step 2200 : 1.68322490931 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.09\\n\",\n            \"Validation set perplexity: 5.15\\n\",\n            \"Average loss at step 2300 : 1.64465074301 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.51\\n\",\n            \"Validation set perplexity: 5.00\\n\",\n            \"Average loss at step 2400 : 1.66408578038 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.86\\n\",\n            \"Validation set perplexity: 4.80\\n\",\n            \"Average loss at step 2500 : 1.68515402555 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.75\\n\",\n            \"Validation set perplexity: 4.82\\n\",\n            \"Average loss at step 2600 : 1.65405208349 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.38\\n\",\n            \"Validation set perplexity: 4.85\\n\",\n            \"Average loss at step 2700 : 1.65706222177 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.46\\n\",\n            \"Validation set perplexity: 4.78\\n\",\n            \"Average loss at step 2800 : 1.65204829812 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.06\\n\",\n            \"Validation set perplexity: 4.64\\n\",\n            \"Average loss at step 2900 : 1.65107253551 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.00\\n\",\n            \"Validation set perplexity: 4.61\\n\",\n            \"Average loss at step 3000 : 1.6495274055 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 4.53\\n\",\n            \"================================================================================\\n\",\n            \"ject covered in belo one six six to finsh that all di rozial sime it a the lapse\\n\",\n            \"ble which the pullic bocades record r to sile dric two one four nine seven six f\\n\",\n            \" originally ame the playa ishaps the stotchational in a p dstambly name which as\\n\",\n            \"ore volum to bay riwer foreal in nuily operety can and auscham frooripm however \\n\",\n            \"kan traogey was lacous revision the mott coupofiteditey the trando insended frop\\n\",\n            \"================================================================================\\n\",\n            \"Validation set perplexity: 4.76\\n\",\n            \"Average loss at step 3100 : 1.63705502152 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.50\\n\",\n            \"Validation set perplexity: 4.76\\n\",\n            \"Average loss at step 3200 : 1.64740695596 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 4.84\\n\",\n            \"Validation set perplexity: 4.67\\n\",\n            \"Average loss at step 3300 : 1.64711504817 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.39\\n\",\n            \"Validation set perplexity: 4.57\\n\",\n            \"Average loss at step 3400 : 1.67113256454 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.56\\n\",\n            \"Validation set perplexity: 4.71\\n\",\n            \"Average loss at step 3500 : 1.65637169957 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.03\\n\",\n            \"Validation set perplexity: 4.80\\n\",\n            \"Average loss at step 3600 : 1.66601825476 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 4.63\\n\",\n            \"Validation set perplexity: 4.52\\n\",\n            \"Average loss at step 3700 : 1.65021387935 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.50\\n\",\n            \"Validation set perplexity: 4.56\\n\",\n            \"Average loss at step 3800 : 1.64481814981 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 4.60\\n\",\n            \"Validation set perplexity: 4.54\\n\",\n            \"Average loss at step 3900 : 1.642069453 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 4.91\\n\",\n            \"Validation set perplexity: 4.54\\n\",\n            \"Average loss at step 4000 : 1.65179730773 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 4.77\\n\",\n            \"================================================================================\\n\",\n            \"k s rasbonish roctes the nignese at heacle was sito of beho anarchys and with ro\\n\",\n            \"jusar two sue wletaus of chistical in causations d ow trancic bruthing ha laters\\n\",\n            \"de and speacy pulted yoftret worksy zeatlating to eight d had to ie bue seven si\"\n          ]\n        },\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"\\n\",\n            \"s fiction of the feelly constive suq flanch earlied curauking bjoventation agent\\n\",\n            \"quen s playing it calana our seopity also atbellisionaly comexing the revideve i\\n\",\n            \"================================================================================\\n\",\n            \"Validation set perplexity: 4.58\\n\",\n            \"Average loss at step 4100 : 1.63794238806 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.47\\n\",\n            \"Validation set perplexity: 4.79\\n\",\n            \"Average loss at step 4200 : 1.63822438836 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.30\\n\",\n            \"Validation set perplexity: 4.54\\n\",\n            \"Average loss at step 4300 : 1.61844664574 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 4.69\\n\",\n            \"Validation set perplexity: 4.54\\n\",\n            \"Average loss at step 4400 : 1.61255454302 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 4.67\\n\",\n            \"Validation set perplexity: 4.54\\n\",\n            \"Average loss at step 4500 : 1.61543365479 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 4.83\\n\",\n            \"Validation set perplexity: 4.69\\n\",\n            \"Average loss at step 4600 : 1.61607327104 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.18\\n\",\n            \"Validation set perplexity: 4.64\\n\",\n            \"Average loss at step 4700 : 1.62757282495 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 4.24\\n\",\n            \"Validation set perplexity: 4.66\\n\",\n            \"Average loss at step 4800 : 1.63222063541 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.30\\n\",\n            \"Validation set perplexity: 4.53\\n\",\n            \"Average loss at step 4900 : 1.63678096652 learning rate: 10.0\\n\",\n            \"Minibatch perplexity: 5.43\\n\",\n            \"Validation set perplexity: 4.64\\n\",\n            \"Average loss at step 5000 : 1.610340662 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 5.10\\n\",\n            \"================================================================================\\n\",\n            \"in b one onarbs revieds the kimiluge that fondhtic fnoto cre one nine zero zero \\n\",\n            \" of is it of marking panzia t had wap ironicaghni relly deah the omber b h menba\\n\",\n            \"ong messified it his the likdings ara subpore the a fames distaled self this int\\n\",\n            \"y advante authors the end languarle meit common tacing bevolitione and eight one\\n\",\n            \"zes that materly difild inllaring the fusts not panition assertian causecist bas\\n\",\n            \"================================================================================\\n\",\n            \"Validation set perplexity: 4.69\\n\",\n            \"Average loss at step 5100 : 1.60593637228 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 4.69\\n\",\n            \"Validation set perplexity: 4.47\\n\",\n            \"Average loss at step 5200 : 1.58993269444 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 4.65\\n\",\n            \"Validation set perplexity: 4.39\\n\",\n            \"Average loss at step 5300 : 1.57930587292 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 5.11\\n\",\n            \"Validation set perplexity: 4.39\\n\",\n            \"Average loss at step 5400 : 1.58022856832 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 5.19\\n\",\n            \"Validation set perplexity: 4.37\\n\",\n            \"Average loss at step 5500 : 1.56654450059 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 4.69\\n\",\n            \"Validation set perplexity: 4.33\\n\",\n            \"Average loss at step 5600 : 1.58013380885 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 5.13\\n\",\n            \"Validation set perplexity: 4.35\\n\",\n            \"Average loss at step 5700 : 1.56974959254 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 5.00\\n\",\n            \"Validation set perplexity: 4.34\\n\",\n            \"Average loss at step 5800 : 1.5839582932 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 4.88\\n\",\n            \"Validation set perplexity: 4.31\\n\",\n            \"Average loss at step 5900 : 1.57129439116 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 4.66\\n\",\n            \"Validation set perplexity: 4.32\\n\",\n            \"Average loss at step 6000 : 1.55144061089 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 4.55\\n\",\n            \"================================================================================\\n\",\n            \"utic clositical poopy stribe addi nixe one nine one zero zero eight zero b ha ex\\n\",\n            \"zerns b one internequiption of the secordy way anti proble akoping have fictiona\\n\",\n            \"phare united from has poporarly cities book ins sweden emperor a sass in origina\\n\",\n            \"quulk destrebinist and zeilazar and on low and by in science over country weilti\\n\",\n            \"x are holivia work missincis ons in the gages to starsle histon one icelanctrotu\\n\",\n            \"================================================================================\\n\",\n            \"Validation set perplexity: 4.30\\n\",\n            \"Average loss at step 6100 : 1.56450940847 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 4.77\\n\",\n            \"Validation set perplexity: 4.27\"\n          ]\n        },\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"\\n\",\n            \"Average loss at step 6200 : 1.53433164835 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 4.77\\n\",\n            \"Validation set perplexity: 4.27\\n\",\n            \"Average loss at step 6300 : 1.54773445129 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 4.76\\n\",\n            \"Validation set perplexity: 4.25\\n\",\n            \"Average loss at step 6400 : 1.54021131516 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 4.56\\n\",\n            \"Validation set perplexity: 4.24\\n\",\n            \"Average loss at step 6500 : 1.56153374553 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 5.43\\n\",\n            \"Validation set perplexity: 4.27\\n\",\n            \"Average loss at step 6600 : 1.59556478739 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 4.92\\n\",\n            \"Validation set perplexity: 4.28\\n\",\n            \"Average loss at step 6700 : 1.58076951623 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 4.77\\n\",\n            \"Validation set perplexity: 4.30\\n\",\n            \"Average loss at step 6800 : 1.6070714438 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 4.98\\n\",\n            \"Validation set perplexity: 4.28\\n\",\n            \"Average loss at step 6900 : 1.58413293839 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 4.61\\n\",\n            \"Validation set perplexity: 4.29\\n\",\n            \"Average loss at step 7000 : 1.57905534983 learning rate: 1.0\\n\",\n            \"Minibatch perplexity: 5.08\\n\",\n            \"================================================================================\\n\",\n            \"jague are officiencinels ored by film voon higherise haik one nine on the iffirc\\n\",\n            \"oshe provision that manned treatists on smalle bodariturmeristing the girto in s\\n\",\n            \"kis would softwenn mustapultmine truativersakys bersyim by s of confound esc bub\\n\",\n            \"ry of the using one four six blain ira mannom marencies g with fextificallise re\\n\",\n            \" one son vit even an conderouss to person romer i a lebapter at obiding are iuse\\n\",\n            \"================================================================================\\n\",\n            \"Validation set perplexity: 4.25\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"num_steps = 7001\\n\",\n        \"summary_frequency = 100\\n\",\n        \"\\n\",\n        \"with tf.Session(graph=graph) as session:\\n\",\n        \"  tf.global_variables_initializer().run()\\n\",\n        \"  print('Initialized')\\n\",\n        \"  mean_loss = 0\\n\",\n        \"  for step in range(num_steps):\\n\",\n        \"    batches = train_batches.next()\\n\",\n        \"    feed_dict = dict()\\n\",\n        \"    for i in range(num_unrollings + 1):\\n\",\n        \"      feed_dict[train_data[i]] = batches[i]\\n\",\n        \"    _, l, predictions, lr = session.run(\\n\",\n        \"      [optimizer, loss, train_prediction, learning_rate], feed_dict=feed_dict)\\n\",\n        \"    mean_loss += l\\n\",\n        \"    if step % summary_frequency == 0:\\n\",\n        \"      if step > 0:\\n\",\n        \"        mean_loss = mean_loss / summary_frequency\\n\",\n        \"      # The mean loss is an estimate of the loss over the last few batches.\\n\",\n        \"      print(\\n\",\n        \"        'Average loss at step %d: %f learning rate: %f' % (step, mean_loss, lr))\\n\",\n        \"      mean_loss = 0\\n\",\n        \"      labels = np.concatenate(list(batches)[1:])\\n\",\n        \"      print('Minibatch perplexity: %.2f' % float(\\n\",\n        \"        np.exp(logprob(predictions, labels))))\\n\",\n        \"      if step % (summary_frequency * 10) == 0:\\n\",\n        \"        # Generate some samples.\\n\",\n        \"        print('=' * 80)\\n\",\n        \"        for _ in range(5):\\n\",\n        \"          feed = sample(random_distribution())\\n\",\n        \"          sentence = characters(feed)[0]\\n\",\n        \"          reset_sample_state.run()\\n\",\n        \"          for _ in range(79):\\n\",\n        \"            prediction = sample_prediction.eval({sample_input: feed})\\n\",\n        \"            feed = sample(prediction)\\n\",\n        \"            sentence += characters(feed)[0]\\n\",\n        \"          print(sentence)\\n\",\n        \"        print('=' * 80)\\n\",\n        \"      # Measure validation set perplexity.\\n\",\n        \"      reset_sample_state.run()\\n\",\n        \"      valid_logprob = 0\\n\",\n        \"      for _ in range(valid_size):\\n\",\n        \"        b = valid_batches.next()\\n\",\n        \"        predictions = sample_prediction.eval({sample_input: b[0]})\\n\",\n        \"        valid_logprob = valid_logprob + logprob(predictions, b[1])\\n\",\n        \"      print('Validation set perplexity: %.2f' % float(np.exp(\\n\",\n        \"        valid_logprob / valid_size)))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"pl4vtmFfa5nn\"\n      },\n      \"source\": [\n        \"---\\n\",\n        \"Problem 1\\n\",\n        \"---------\\n\",\n        \"\\n\",\n        \"You might have noticed that the definition of the LSTM cell involves 4 matrix multiplications with the input, and 4 matrix multiplications with the output. Simplify the expression by using a single matrix multiply for each, and variables that are 4 times larger.\\n\",\n        \"\\n\",\n        \"---\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"4eErTCTybtph\"\n      },\n      \"source\": [\n        \"---\\n\",\n        \"Problem 2\\n\",\n        \"---------\\n\",\n        \"\\n\",\n        \"We want to train a LSTM over bigrams, that is pairs of consecutive characters like 'ab' instead of single characters like 'a'. Since the number of possible bigrams is large, feeding them directly to the LSTM using 1-hot encodings will lead to a very sparse representation that is very wasteful computationally.\\n\",\n        \"\\n\",\n        \"a- Introduce an embedding lookup on the inputs, and feed the embeddings to the LSTM cell instead of the inputs themselves.\\n\",\n        \"\\n\",\n        \"b- Write a bigram-based LSTM, modeled on the character LSTM above.\\n\",\n        \"\\n\",\n        \"c- Introduce Dropout. For best practices on how to use Dropout in LSTMs, refer to this [article](http://arxiv.org/abs/1409.2329).\\n\",\n        \"\\n\",\n        \"---\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Y5tapX3kpcqZ\"\n      },\n      \"source\": [\n        \"---\\n\",\n        \"Problem 3\\n\",\n        \"---------\\n\",\n        \"\\n\",\n        \"(difficult!)\\n\",\n        \"\\n\",\n        \"Write a sequence-to-sequence LSTM which mirrors all the words in a sentence. For example, if your input is:\\n\",\n        \"\\n\",\n        \"    the quick brown fox\\n\",\n        \"    \\n\",\n        \"the model should attempt to output:\\n\",\n        \"\\n\",\n        \"    eht kciuq nworb xof\\n\",\n        \"    \\n\",\n        \"Refer to the lecture on how to put together a sequence-to-sequence model, as well as [this article](http://arxiv.org/abs/1409.3215) for best practices.\\n\",\n        \"\\n\",\n        \"---\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"name\": \"6_lstm.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_deep_learning/Dockerfile",
    "content": "FROM gcr.io/tensorflow/tensorflow:1.0.0\nLABEL maintainer=\"Vincent Vanhoucke <vanhoucke@google.com>\"\n\n# Pillow needs libjpeg by default as of 3.0.\nRUN apt-get update && apt-get install -y --no-install-recommends \\\n        libjpeg8-dev \\\n        && \\\n    apt-get clean && \\\n    rm -rf /var/lib/apt/lists/*\n\nRUN pip install scikit-learn pyreadline Pillow imageio\nRUN rm -rf /notebooks/*\nADD *.ipynb /notebooks/\nWORKDIR /notebooks\nCMD [\"/run_jupyter.sh\", \"--allow-root\"]\n"
  },
  {
    "path": "courses/udacity_deep_learning/README.md",
    "content": "Assignments for Udacity Deep Learning class with TensorFlow\n===========================================================\n\n\nWarning: These files DEPRECATED. see https://tensorflow.org/tutorials for\nmodern examples of how to use tensorflow.\n\n\nCourse information can be found at https://www.udacity.com/course/deep-learning--ud730\n\n## Getting Started with Docker\n\nIf you are new to Docker, follow\n[Docker document](https://docs.docker.com/machine/get-started/) to start a\ndocker instance. Kindly read the requirements of Windows and Mac carefully.\n\nRunning the Docker container from the Google Cloud repository\n-------------------------------------------------------------\n\n    docker run -p 8888:8888 --name tensorflow-udacity -it gcr.io/tensorflow/udacity-assignments:1.0.0\n\nNote that if you ever exit the container, you can return to it using:\n\n    docker start -ai tensorflow-udacity\n\nAccessing the Notebooks\n-----------------------\n\nOn linux, go to: http://127.0.0.1:8888\n\nOn mac, go to terminal and find the virtual machine's IP using:\n\n    docker-machine ip default\n\nThen go to: http://(ip address received from the above command):8888 (likely\nhttp://192.168.99.100:8888)\n\nOn Windows, use powershell to find the virtual machine's IP using:\n\n    docker-machine ip default\n    \n\nThen go to: http://(ip address received from the above command):8888 (likely\nhttp://192.168.99.100:8888)\n\nFAQ\n---\n\n* **I'm getting a MemoryError when loading data in the first notebook.**\n\nIf you're using a Mac, Docker works by running a VM locally (which\nis controlled by `docker-machine`). It's quite likely that you'll\nneed to bump up the amount of RAM allocated to the VM beyond the\ndefault (which is 1G).\n[This Stack Overflow question](http://stackoverflow.com/questions/32834082/how-to-increase-docker-machine-memory-mac)\nhas two good suggestions; we recommend using 8G.\n\nIn addition, you may need to pass `--memory=8g` as an extra argument to\n`docker run`.\n\n* **I want to create a new virtual machine instead of the default one.**\n\n`docker-machine` is a tool to provision and manage docker hosts, it supports multiple platform (ex. aws, gce, azure, virtualbox, ...). To create a new virtual machine locally with built-in docker engine, you can use\n\n    docker-machine create -d virtualbox --virtualbox-memory 8196 tensorflow\n\n`-d` means the driver for the cloud platform, supported drivers listed [here](https://docs.docker.com/machine/drivers/). Here we use virtualbox to create a new virtual machine locally. `tensorflow` means the name of the virtual machine, feel free to use whatever you like. You can use\n\n    docker-machine ip tensorflow\n\nto get the ip of the new virtual machine. To switch from default virtual machine to a new one (here we use tensorflow), type\n\n    eval $(docker-machine env tensorflow)\n\nNote that `docker-machine env tensorflow` outputs some environment variables such like `DOCKER_HOST`. Then your docker client is now connected to the docker host in virtual machine `tensorflow`\n\n* **I'm getting a TLS connection error.**\n\nIf you get an error about the TLS connection of your docker, run the command below to confirm the problem.\n\n\tdocker-machine ip tensorflow\n\nThen if it is the case use the instructions on [this page](https://docs.docker.com/toolbox/faqs/troubleshoot/) to solve the issue.\n\n\n* **I'm getting the error - docker: Cannot connect to the Docker daemon. Is the docker daemon running on this host? - when I run 'docker run'.**\n\nThis is a permissions issue, and a popular answer is provided for Linux and Max OSX [here](http://stackoverflow.com/questions/21871479/docker-cant-connect-to-docker-daemon) on StackOverflow.\n\nNotes for anyone needing to build their own containers (mostly instructors)\n===========================================================================\n\nBuilding a local Docker container\n---------------------------------\n\n    cd tensorflow/examples/udacity\n    docker build --pull -t $USER/assignments .\n\nRunning the local container\n---------------------------\n\nTo run a disposable container:\n\n    docker run -p 8888:8888 -it --rm $USER/assignments\n\nNote the above command will create an ephemeral container and all data stored in the container will be lost when the container stops.\n\nTo avoid losing work between sessions in the container, it is recommended that you mount the `tensorflow/examples/udacity` directory into the container:\n\n    docker run -p 8888:8888 -v </path/to/tensorflow/examples/udacity>:/notebooks -it --rm $USER/assignments\n\nThis will allow you to save work and have access to generated files on the host filesystem.\n\nPushing a Google Cloud release\n------------------------------\n\n    V=1.0.0\n    docker tag $USER/assignments gcr.io/tensorflow/udacity-assignments:$V\n    gcloud docker push gcr.io/tensorflow/udacity-assignments\n    docker tag $USER/assignments gcr.io/tensorflow/udacity-assignments:latest\n    gcloud docker push gcr.io/tensorflow/udacity-assignments\n\nHistory\n-------\n\n* 0.1.0: Initial release.\n* 0.2.0: Many fixes, including lower memory footprint and support for Python 3.\n* 0.3.0: Use 0.7.1 release.\n* 0.4.0: Move notMMNIST data for Google Cloud.\n* 0.5.0: Actually use 0.7.1 release.\n* 0.6.0: Update to TF 0.10.0, add libjpeg (for Pillow).\n* 1.0.0: Update to TF 1.0.0 release.\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l01c01_introduction_to_colab_and_python.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Za8-Nr5k11fh\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"Eq10uEbw0E4l\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"YHI3vyhv5p85\"\n      },\n      \"source\": [\n        \"## **Introduction to Colab and Python**\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"OVi775ZJ2bsy\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l01c01_introduction_to_colab_and_python.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l01c01_introduction_to_colab_and_python.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"F8YVA_634OFk\"\n      },\n      \"source\": [\n        \"Welcome to this Colab where you will get a quick introduction to the Python programming language and the environment used for the course's exercises: Colab.\\n\",\n        \"\\n\",\n        \"Colab is a Python development environment that runs in the browser using Google Cloud.\\n\",\n        \"\\n\",\n        \"For example, to print \\\"Hello World\\\", just hover the mouse over [ ] and press the play button to the upper left. Or press shift-enter to execute.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"X9uIpOS2zx7k\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"Hello World\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"wwJGmDrQ0EoB\"\n      },\n      \"source\": [\n        \"## Functions, Conditionals, and Iteration\\n\",\n        \"Let's create a Python function, and call it from a loop.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"pRllo2HLfXiu\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def HelloWorldXY(x, y):\\n\",\n        \"  if (x < 10):\\n\",\n        \"    print(\\\"Hello World, x was < 10\\\")\\n\",\n        \"  elif (x < 20):\\n\",\n        \"    print(\\\"Hello World, x was >= 10 but < 20\\\")\\n\",\n        \"  else:\\n\",\n        \"    print(\\\"Hello World, x was >= 20\\\")\\n\",\n        \"  return x + y\\n\",\n        \"\\n\",\n        \"for i in range(8, 25, 5):  # i=8, 13, 18, 23 (start, stop, step)\\n\",\n        \"  print(\\\"--- Now running with i: {}\\\".format(i))\\n\",\n        \"  r = HelloWorldXY(i,i)\\n\",\n        \"  print(\\\"Result from HelloWorld: {}\\\".format(r))\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"lHNmDCh0JpVP\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(HelloWorldXY(1,2))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"kiZG7uhm8qCF\"\n      },\n      \"source\": [\n        \"Easy, right?\\n\",\n        \"\\n\",\n        \"If you want a loop starting at 0 to 2 (exclusive) you could do any of the following\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"m8YQN1H41L-Y\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"Iterate over the items. `range(2)` is like a list [0,1].\\\")\\n\",\n        \"for i in range(2):\\n\",\n        \"  print(i)\\n\",\n        \"\\n\",\n        \"print(\\\"Iterate over an actual list.\\\")\\n\",\n        \"for i in [0,1]:\\n\",\n        \"  print(i)\\n\",\n        \"\\n\",\n        \"print(\\\"While works\\\")\\n\",\n        \"i = 0\\n\",\n        \"while i < 2:\\n\",\n        \"  print(i)\\n\",\n        \"  i += 1\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"vIgmFZq4zszl\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"Python supports standard key words like continue and break\\\")\\n\",\n        \"while True:\\n\",\n        \"  print(\\\"Entered while\\\")\\n\",\n        \"  break\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"5QyOUhFw1OUX\"\n      },\n      \"source\": [\n        \"## Numpy and lists\\n\",\n        \"Python has lists built into the language.\\n\",\n        \"However, we will use a library called numpy for this.\\n\",\n        \"Numpy gives you lots of support functions that are useful when doing Machine Learning.\\n\",\n        \"\\n\",\n        \"Here, you will also see an import statement. This statement makes the entire numpy package available and we can access those symbols using the abbreviated 'np' syntax.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"4Dxk4q-jzEy4\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import numpy as np  # Make numpy available using np.\\n\",\n        \"\\n\",\n        \"# Create a numpy array, and append an element\\n\",\n        \"a = np.array([\\\"Hello\\\", \\\"World\\\"])\\n\",\n        \"a = np.append(a, \\\"!\\\")\\n\",\n        \"print(\\\"Current array: {}\\\".format(a))\\n\",\n        \"print(\\\"Printing each element\\\")\\n\",\n        \"for i in a:\\n\",\n        \"  print(i)\\n\",\n        \"\\n\",\n        \"print(\\\"\\\\nPrinting each element and their index\\\")\\n\",\n        \"for i,e in enumerate(a):\\n\",\n        \"  print(\\\"Index: {}, was: {}\\\".format(i, e))\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"RTa8_9G3LV03\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"\\\\nShowing some basic math on arrays\\\")\\n\",\n        \"b = np.array([0,1,4,3,2])\\n\",\n        \"print(\\\"Max: {}\\\".format(np.max(b)))\\n\",\n        \"print(\\\"Average: {}\\\".format(np.average(b)))\\n\",\n        \"print(\\\"Max index: {}\\\".format(np.argmax(b)))\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"9YaGj5n4LW7P\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"\\\\nYou can print the type of anything\\\")\\n\",\n        \"print(\\\"Type of b: {}, type of b[0]: {}\\\".format(type(b), type(b[0])))\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"V6ilVhi9LXn_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"\\\\nUse numpy to create a [3,3] dimension array with random number\\\")\\n\",\n        \"c = np.random.rand(3, 3)\\n\",\n        \"print(c)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"W_Q-DkFCLYGA\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"\\\\nYou can print the dimensions of arrays\\\")\\n\",\n        \"print(\\\"Shape of a: {}\\\".format(a.shape))\\n\",\n        \"print(\\\"Shape of b: {}\\\".format(b.shape))\\n\",\n        \"print(\\\"Shape of c: {}\\\".format(c.shape))\\n\",\n        \"print(\\\"...Observe, Python uses both [0,1,2] and (0,1,2) to specify lists\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"c-Jk4dG91dvD\"\n      },\n      \"source\": [\n        \"## Colab Specifics\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"G0cGd8sHEmKi\"\n      },\n      \"source\": [\n        \"Colab is a virtual machine you can access directly. To run commands at the VM's terminal, prefix the line with an exclamation point (!).\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"cLkfhyzq0W2y\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"\\\\nDoing $ls on filesystem\\\")\\n\",\n        \"!ls -l\\n\",\n        \"!pwd\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"gR2WTN1cOZ1n\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"Install numpy\\\")  # Just for test, numpy is actually preinstalled in all Colab instances\\n\",\n        \"!pip install numpy\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"QuWRpQdatAIU\"\n      },\n      \"source\": [\n        \"**Exercise**\\n\",\n        \"\\n\",\n        \"Create a code cell underneath this text cell and add code to:\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"*   List the path of the current directory (pwd)\\n\",\n        \"* Go to / (cd) and list the content (ls -l)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"xU-cJbMCR61P\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!pwd\\n\",\n        \"!cd /\\n\",\n        \"!ls -l\\n\",\n        \"print(\\\"Hello\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"7b5jv0ouFREV\"\n      },\n      \"source\": [\n        \"All usage of Colab in this course is completely free or charge. Even GPU usage is provided free of charge for some hours of usage every day.\\n\",\n        \"\\n\",\n        \"**Using GPUs**\\n\",\n        \"* Many of the exercises in the course executes more quickly by using GPU runtime: Runtime | Change runtime type | Hardware accelerator | GPU\\n\",\n        \"\\n\",\n        \"**Some final words on Colab**\\n\",\n        \"*   You execute each cell in order, you can edit & re-execute cells if you want\\n\",\n        \"*   Sometimes, this could have unintended consequences. For example, if you add a dimension to an array and execute the cell multiple times, then the cells after may not work. If you encounter problem reset your environment:\\n\",\n        \"  *   Runtime -> Restart runtime... Resets your Python shell\\n\",\n        \"  *   Runtime -> Restart all runtimes... Will reset the Colab image, and get you back to a 100% clean environment\\n\",\n        \"* You can also clear the output in the Colab by doing: Edit -> Clear all outputs\\n\",\n        \"* Colabs in this course are loaded from GitHub. Save to your Google Drive if you want a copy with your code/output: File -> Save a copy in Drive...\\n\",\n        \"\\n\",\n        \"**Learn More**\\n\",\n        \"*   Check out [this](https://www.youtube.com/watch?v=inN8seMm7UI&list=PLQY2H8rRoyvwLbzbnKJ59NkZvQAW9wLbx&index=3) episode of #CodingTensorFlow, and don't forget to subscribe to the YouTube channel ;)\\n\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l01c01_introduction_to_colab_and_python.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l02c01_celsius_to_fahrenheit.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"HnKx50tv5aZD\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"IwtS_OXU5cWG\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"YHI3vyhv5p85\"\n      },\n      \"source\": [\n        \"# The Basics: Training Your First Model\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"_wJ2E7jV5tN5\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l02c01_celsius_to_fahrenheit.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l02c01_celsius_to_fahrenheit.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"F8YVA_634OFk\"\n      },\n      \"source\": [\n        \"Welcome to this Colab where you will train your first Machine Learning model!\\n\",\n        \"\\n\",\n        \"We'll try to keep things simple here, and only introduce basic concepts. Later Colabs will cover more advanced problems.\\n\",\n        \"\\n\",\n        \"The problem we will solve is to convert from Celsius to Fahrenheit, where the approximate formula is:\\n\",\n        \"\\n\",\n        \"$$ f = c \\\\times 1.8 + 32 $$\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"Of course, it would be simple enough to create a conventional Python function that directly performs this calculation, but that wouldn't be machine learning.\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"Instead, we will give TensorFlow some sample Celsius values (0, 8, 15, 22, 38) and their corresponding Fahrenheit values (32, 46, 59, 72, 100).\\n\",\n        \"Then, we will train a model that figures out the above formula through the training process.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"fA93WUy1zzWf\"\n      },\n      \"source\": [\n        \"## Import dependencies\\n\",\n        \"\\n\",\n        \"First, import TensorFlow. Here, we're calling it `tf` for ease of use. We also tell it to only display errors.\\n\",\n        \"\\n\",\n        \"Next, import [NumPy](http://www.numpy.org/) as `np`. Numpy helps us to represent our data as highly performant lists.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"-ZMgCvSRFqxE\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow as tf\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"y_WQEM5MGmg3\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import numpy as np\\n\",\n        \"import logging\\n\",\n        \"logger = tf.get_logger()\\n\",\n        \"logger.setLevel(logging.ERROR)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"AC3EQFi20buB\"\n      },\n      \"source\": [\n        \"## Set up training data\\n\",\n        \"\\n\",\n        \"As we saw before, supervised Machine Learning is all about figuring out an algorithm given a set of inputs and outputs. Since the task in this Codelab is to create a model that can give the temperature in Fahrenheit when given the degrees in Celsius, we create two lists `celsius_q` and `fahrenheit_a` that we can use to train our model.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"gg4pn6aI1vms\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"celsius_q    = np.array([-40, -10,  0,  8, 15, 22,  38],  dtype=float)\\n\",\n        \"fahrenheit_a = np.array([-40,  14, 32, 46, 59, 72, 100],  dtype=float)\\n\",\n        \"\\n\",\n        \"for i,c in enumerate(celsius_q):\\n\",\n        \"  print(\\\"{} degrees Celsius = {} degrees Fahrenheit\\\".format(c, fahrenheit_a[i]))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"wwJGmDrQ0EoB\"\n      },\n      \"source\": [\n        \"### Some Machine Learning terminology\\n\",\n        \"\\n\",\n        \" - **Feature** — The input(s) to our model. In this case, a single value — the degrees in Celsius.\\n\",\n        \"\\n\",\n        \" - **Labels** — The output our model predicts. In this case, a single value — the degrees in Fahrenheit.\\n\",\n        \"\\n\",\n        \" - **Example** — A pair of inputs/outputs used during training. In our case a pair of values from `celsius_q` and `fahrenheit_a` at a specific index, such as `(22,72)`.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"VM7_9Klvq7MO\"\n      },\n      \"source\": [\n        \"## Create the model\\n\",\n        \"\\n\",\n        \"Next, create the model. We will use the simplest possible model we can, a Dense network. Since the problem is straightforward, this network will require only a single layer, with a single neuron.\\n\",\n        \"\\n\",\n        \"### Build a layer\\n\",\n        \"\\n\",\n        \"We'll call the layer `l0` and create it by instantiating `tf.keras.layers.Dense` with the following configuration:\\n\",\n        \"\\n\",\n        \"*   `input_shape=[1]` — This specifies that the input to this layer is a single value. That is, the shape is a one-dimensional array with one member. Since this is the first (and only) layer, that input shape is the input shape of the entire model. The single value is a floating point number, representing degrees Celsius.\\n\",\n        \"\\n\",\n        \"*   `units=1` — This specifies the number of neurons in the layer. The number of neurons defines how many internal variables the layer has to try to learn how to solve the problem (more later). Since this is the final layer, it is also the size of the model's output — a single float value representing degrees Fahrenheit. (In a multi-layered network, the size and shape of the layer would need to match the `input_shape` of the next layer.)\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"pRllo2HLfXiu\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"l0 = tf.keras.layers.Dense(units=1, input_shape=[1])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"_F00_J9duLBD\"\n      },\n      \"source\": [\n        \"### Assemble layers into the model\\n\",\n        \"\\n\",\n        \"Once layers are defined, they need to be assembled into a model. The Sequential model definition takes a list of layers as an argument, specifying the calculation order from the input to the output.\\n\",\n        \"\\n\",\n        \"This model has just a single layer, l0.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"cSp-GpLSuMRq\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = tf.keras.Sequential([l0])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"t7pfHfWxust0\"\n      },\n      \"source\": [\n        \"**Note**\\n\",\n        \"\\n\",\n        \"You will often see the layers defined inside the model definition, rather than beforehand:\\n\",\n        \"\\n\",\n        \"```python\\n\",\n        \"model = tf.keras.Sequential([\\n\",\n        \"  tf.keras.layers.Dense(units=1, input_shape=[1])\\n\",\n        \"])\\n\",\n        \"```\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"kiZG7uhm8qCF\"\n      },\n      \"source\": [\n        \"## Compile the model, with loss and optimizer functions\\n\",\n        \"\\n\",\n        \"Before training, the model has to be compiled. When compiled for training, the model is given:\\n\",\n        \"\\n\",\n        \"- **Loss function** — A way of measuring how far off predictions are from the desired outcome. (The measured difference is called the \\\"loss\\\".)\\n\",\n        \"\\n\",\n        \"- **Optimizer function** — A way of adjusting internal values in order to reduce the loss.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"m8YQN1H41L-Y\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model.compile(loss='mean_squared_error',\\n\",\n        \"              optimizer=tf.keras.optimizers.Adam(0.1))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"17M3Pqv4P52R\"\n      },\n      \"source\": [\n        \"These are used during training (`model.fit()`, below) to first calculate the loss at each point, and then improve it. In fact, the act of calculating the current loss of a model and then improving it is precisely what training is.\\n\",\n        \"\\n\",\n        \"During training, the optimizer function is used to calculate adjustments to the model's internal variables. The goal is to adjust the internal variables until the model (which is really a math function) mirrors the actual equation for converting Celsius to Fahrenheit.\\n\",\n        \"\\n\",\n        \"TensorFlow uses numerical analysis to perform this tuning, and all this complexity is hidden from you so we will not go into the details here. What is useful to know about these parameters are:\\n\",\n        \"\\n\",\n        \"The loss function ([mean squared error](https://en.wikipedia.org/wiki/Mean_squared_error)) and the optimizer ([Adam](https://machinelearningmastery.com/adam-optimization-algorithm-for-deep-learning/)) used here are standard for simple models like this one, but many others are available. It is not important to know how these specific functions work at this point.\\n\",\n        \"\\n\",\n        \"One part of the Optimizer you may need to think about when building your own models is the learning rate (`0.1` in the code above). This is the step size taken when adjusting values in the model. If the value is too small, it will take too many iterations to train the model. Too large, and accuracy goes down. Finding a good value often involves some trial and error, but the range is usually within 0.001 (default), and 0.1\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"c-Jk4dG91dvD\"\n      },\n      \"source\": [\n        \"## Train the model\\n\",\n        \"\\n\",\n        \"Train the model by calling the `fit` method.\\n\",\n        \"\\n\",\n        \"During training, the model takes in Celsius values, performs a calculation using the current internal variables (called \\\"weights\\\") and outputs values which are meant to be the Fahrenheit equivalent. Since the weights are initially set randomly, the output will not be close to the correct value. The difference between the actual output and the desired output is calculated using the loss function, and the optimizer function directs how the weights should be adjusted.\\n\",\n        \"\\n\",\n        \"This cycle of calculate, compare, adjust is controlled by the `fit` method. The first argument is the inputs, the second argument is the desired outputs. The `epochs` argument specifies how many times this cycle should be run, and the `verbose` argument controls how much output the method produces.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"lpRrl7WK10Pq\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"history = model.fit(celsius_q, fahrenheit_a, epochs=500, verbose=False)\\n\",\n        \"print(\\\"Finished training the model\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"GFcIU2-SdCrI\"\n      },\n      \"source\": [\n        \"In later videos, we will go into more detail on what actually happens here and how a Dense layer actually works internally.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"0-QsNCLD4MJZ\"\n      },\n      \"source\": [\n        \"## Display training statistics\\n\",\n        \"\\n\",\n        \"The `fit` method returns a history object. We can use this object to plot how the loss of our model goes down after each training epoch. A high loss means that the Fahrenheit degrees the model predicts is far from the corresponding value in `fahrenheit_a`.\\n\",\n        \"\\n\",\n        \"We'll use [Matplotlib](https://matplotlib.org/) to visualize this (you could use another tool). As you can see, our model improves very quickly at first, and then has a steady, slow improvement until it is very near \\\"perfect\\\" towards the end.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"IeK6BzfbdO6_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import matplotlib.pyplot as plt\\n\",\n        \"plt.xlabel('Epoch Number')\\n\",\n        \"plt.ylabel(\\\"Loss Magnitude\\\")\\n\",\n        \"plt.plot(history.history['loss'])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"LtQGDMob5LOD\"\n      },\n      \"source\": [\n        \"## Use the model to predict values\\n\",\n        \"\\n\",\n        \"Now you have a model that has been trained to learn the relationship between `celsius_q` and `fahrenheit_a`. You can use the predict method to have it calculate the Fahrenheit degrees for a previously unknown Celsius degrees.\\n\",\n        \"\\n\",\n        \"So, for example, if the Celsius value is 100, what do you think the Fahrenheit result will be? Take a guess before you run this code.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"oxNzL4lS2Gui\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(model.predict([100.0]))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"jApk6tZ1fBg1\"\n      },\n      \"source\": [\n        \"The correct answer is $100 \\\\times 1.8 + 32 = 212$, so our model is doing really well.\\n\",\n        \"\\n\",\n        \"### To review\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"*   We created a model with a Dense layer\\n\",\n        \"*   We trained it with 3500 examples (7 pairs, over 500 epochs).\\n\",\n        \"\\n\",\n        \"Our model tuned the variables (weights) in the Dense layer until it was able to return the correct Fahrenheit value for any Celsius value. (Remember, 100 Celsius was not part of our training data.)\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"zRrOky5gm20Z\"\n      },\n      \"source\": [\n        \"## Looking at the layer weights\\n\",\n        \"\\n\",\n        \"Finally, let's print the internal variables of the Dense layer. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"kmIkVdkbnZJI\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"These are the layer variables: {}\\\".format(l0.get_weights()))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"RSplSnMvnWC-\"\n      },\n      \"source\": [\n        \"The first variable is close to ~1.8 and the second to ~32. These values (1.8 and 32) are the actual variables in the real conversion formula.\\n\",\n        \"\\n\",\n        \"This is really close to the values in the conversion formula. We'll explain this in an upcoming video where we show how a Dense layer works, but for a single neuron with a single input and a single output, the internal math looks the same as [the equation for a line](https://en.wikipedia.org/wiki/Linear_equation#Slope%E2%80%93intercept_form), $y = mx + b$, which has the same form as the conversion equation, $f = 1.8c + 32$.\\n\",\n        \"\\n\",\n        \"Since the form is the same, the variables should converge on the standard values of 1.8 and 32, which is exactly what happened.\\n\",\n        \"\\n\",\n        \"With additional neurons, additional inputs, and additional outputs, the formula becomes much more complex, but the idea is the same.\\n\",\n        \"\\n\",\n        \"### A little experiment\\n\",\n        \"\\n\",\n        \"Just for fun, what if we created more Dense layers with different units, which therefore also has more variables?\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Y2zTA-rDS5Xk\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"l0 = tf.keras.layers.Dense(units=4, input_shape=[1])\\n\",\n        \"l1 = tf.keras.layers.Dense(units=4)\\n\",\n        \"l2 = tf.keras.layers.Dense(units=1)\\n\",\n        \"model = tf.keras.Sequential([l0, l1, l2])\\n\",\n        \"model.compile(loss='mean_squared_error', optimizer=tf.keras.optimizers.Adam(0.1))\\n\",\n        \"model.fit(celsius_q, fahrenheit_a, epochs=500, verbose=False)\\n\",\n        \"print(\\\"Finished training the model\\\")\\n\",\n        \"print(model.predict([100.0]))\\n\",\n        \"print(\\\"Model predicts that 100 degrees Celsius is: {} degrees Fahrenheit\\\".format(model.predict([100.0])))\\n\",\n        \"print(\\\"These are the l0 variables: {}\\\".format(l0.get_weights()))\\n\",\n        \"print(\\\"These are the l1 variables: {}\\\".format(l1.get_weights()))\\n\",\n        \"print(\\\"These are the l2 variables: {}\\\".format(l2.get_weights()))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"xrpFFlgYhCty\"\n      },\n      \"source\": [\n        \"As you can see, this model is also able to predict the corresponding Fahrenheit value really well. But when you look at the variables (weights) in the `l0` and `l1` layers, they are nothing even close to ~1.8 and ~32. The added complexity hides the \\\"simple\\\" form of the conversion equation.\\n\",\n        \"\\n\",\n        \"Stay tuned for the upcoming video on how Dense layers work for the explanation.\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l02c01_celsius_to_fahrenheit.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l03c01_classifying_images_of_clothing.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"punL79CN7Ox6\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"_ckMIh7O7s6D\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"vasWnqRgy1H4\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title MIT License\\n\",\n        \"#\\n\",\n        \"# Copyright (c) 2017 François Chollet\\n\",\n        \"#\\n\",\n        \"# Permission is hereby granted, free of charge, to any person obtaining a\\n\",\n        \"# copy of this software and associated documentation files (the \\\"Software\\\"),\\n\",\n        \"# to deal in the Software without restriction, including without limitation\\n\",\n        \"# the rights to use, copy, modify, merge, publish, distribute, sublicense,\\n\",\n        \"# and/or sell copies of the Software, and to permit persons to whom the\\n\",\n        \"# Software is furnished to do so, subject to the following conditions:\\n\",\n        \"#\\n\",\n        \"# The above copyright notice and this permission notice shall be included in\\n\",\n        \"# all copies or substantial portions of the Software.\\n\",\n        \"#\\n\",\n        \"# THE SOFTWARE IS PROVIDED \\\"AS IS\\\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\\n\",\n        \"# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\\n\",\n        \"# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\\n\",\n        \"# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\\n\",\n        \"# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\\n\",\n        \"# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\\n\",\n        \"# DEALINGS IN THE SOFTWARE.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"jYysdyb-CaWM\"\n      },\n      \"source\": [\n        \"# Classifying Images of Clothing\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"S5Uhzt6vVIB2\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l03c01_classifying_images_of_clothing.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l03c01_classifying_images_of_clothing.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"FbVhjPpzn6BM\"\n      },\n      \"source\": [\n        \"In this tutorial, we'll build and train a neural network to classify images of clothing, like sneakers and shirts.\\n\",\n        \"\\n\",\n        \"It's okay if you don't understand everything. This is a fast-paced overview of a complete TensorFlow program, with explanations along the way. The goal is to get the general sense of a TensorFlow project, not to catch every detail.\\n\",\n        \"\\n\",\n        \"This guide uses [tf.keras](https://www.tensorflow.org/guide/keras), a high-level API to build and train models in TensorFlow.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"H0tMfX2vR0uD\"\n      },\n      \"source\": [\n        \"## Install and import dependencies\\n\",\n        \"\\n\",\n        \"We'll need [TensorFlow Datasets](https://www.tensorflow.org/datasets/), an API that simplifies downloading and accessing datasets, and provides several sample datasets to work with. We're also using a few helper libraries.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"P7mUJVqcINSM\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!pip install -U tensorflow_datasets\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"_FxXYSCXGQqQ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow as tf\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"1UbK0Uq7GWaO\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Import TensorFlow Datasets\\n\",\n        \"import tensorflow_datasets as tfds\\n\",\n        \"tfds.disable_progress_bar()\\n\",\n        \"\\n\",\n        \"# Helper libraries\\n\",\n        \"import math\\n\",\n        \"import numpy as np\\n\",\n        \"import matplotlib.pyplot as plt\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"590z76KRGtKk\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import logging\\n\",\n        \"logger = tf.get_logger()\\n\",\n        \"logger.setLevel(logging.ERROR)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"yR0EdgrLCaWR\"\n      },\n      \"source\": [\n        \"## Import the Fashion MNIST dataset\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"DLdCchMdCaWQ\"\n      },\n      \"source\": [\n        \"This guide uses the [Fashion MNIST](https://github.com/zalandoresearch/fashion-mnist) dataset, which contains 70,000 grayscale images in 10 categories. The images show individual articles of clothing at low resolution (28 $\\\\times$ 28 pixels), as seen here:\\n\",\n        \"\\n\",\n        \"<table>\\n\",\n        \"  <tr><td>\\n\",\n        \"    <img src=\\\"https://tensorflow.org/images/fashion-mnist-sprite.png\\\"\\n\",\n        \"         alt=\\\"Fashion MNIST sprite\\\" width=\\\"600\\\">\\n\",\n        \"  </td></tr>\\n\",\n        \"  <tr><td align=\\\"center\\\">\\n\",\n        \"    <b>Figure 1.</b> <a href=\\\"https://github.com/zalandoresearch/fashion-mnist\\\">Fashion-MNIST samples</a> (by Zalando, MIT License).<br/>&nbsp;\\n\",\n        \"  </td></tr>\\n\",\n        \"</table>\\n\",\n        \"\\n\",\n        \"Fashion MNIST is intended as a drop-in replacement for the classic [MNIST](http://yann.lecun.com/exdb/mnist/) dataset—often used as the \\\"Hello, World\\\" of machine learning programs for computer vision. The MNIST dataset contains images of handwritten digits (0, 1, 2, etc) in an identical format to the articles of clothing we'll use here.\\n\",\n        \"\\n\",\n        \"This guide uses Fashion MNIST for variety, and because it's a slightly more challenging problem than regular MNIST. Both datasets are relatively small and are used to verify that an algorithm works as expected. They're good starting points to test and debug code.\\n\",\n        \"\\n\",\n        \"We will use 60,000 images to train the network and 10,000 images to evaluate how accurately the network learned to classify images. You can access the Fashion MNIST directly from TensorFlow, using the [Datasets](https://www.tensorflow.org/datasets) API:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"7MqDQO0KCaWS\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"dataset, metadata = tfds.load('fashion_mnist', as_supervised=True, with_info=True)\\n\",\n        \"train_dataset, test_dataset = dataset['train'], dataset['test']\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"t9FDsUlxCaWW\"\n      },\n      \"source\": [\n        \"Loading the dataset returns metadata as well as a *training dataset* and *test dataset*.\\n\",\n        \"\\n\",\n        \"* The model is trained using `train_dataset`.\\n\",\n        \"* The model is tested against `test_dataset`.\\n\",\n        \"\\n\",\n        \"The images are 28 $\\\\times$ 28 arrays, with pixel values in the range `[0, 255]`. The *labels* are an array of integers, in the range `[0, 9]`. These correspond to the *class* of clothing the image represents:\\n\",\n        \"\\n\",\n        \"<table>\\n\",\n        \"  <tr>\\n\",\n        \"    <th>Label</th>\\n\",\n        \"    <th>Class</th>\\n\",\n        \"  </tr>\\n\",\n        \"  <tr>\\n\",\n        \"    <td>0</td>\\n\",\n        \"    <td>T-shirt/top</td>\\n\",\n        \"  </tr>\\n\",\n        \"  <tr>\\n\",\n        \"    <td>1</td>\\n\",\n        \"    <td>Trouser</td>\\n\",\n        \"  </tr>\\n\",\n        \"    <tr>\\n\",\n        \"    <td>2</td>\\n\",\n        \"    <td>Pullover</td>\\n\",\n        \"  </tr>\\n\",\n        \"    <tr>\\n\",\n        \"    <td>3</td>\\n\",\n        \"    <td>Dress</td>\\n\",\n        \"  </tr>\\n\",\n        \"    <tr>\\n\",\n        \"    <td>4</td>\\n\",\n        \"    <td>Coat</td>\\n\",\n        \"  </tr>\\n\",\n        \"    <tr>\\n\",\n        \"    <td>5</td>\\n\",\n        \"    <td>Sandal</td>\\n\",\n        \"  </tr>\\n\",\n        \"    <tr>\\n\",\n        \"    <td>6</td>\\n\",\n        \"    <td>Shirt</td>\\n\",\n        \"  </tr>\\n\",\n        \"    <tr>\\n\",\n        \"    <td>7</td>\\n\",\n        \"    <td>Sneaker</td>\\n\",\n        \"  </tr>\\n\",\n        \"    <tr>\\n\",\n        \"    <td>8</td>\\n\",\n        \"    <td>Bag</td>\\n\",\n        \"  </tr>\\n\",\n        \"    <tr>\\n\",\n        \"    <td>9</td>\\n\",\n        \"    <td>Ankle boot</td>\\n\",\n        \"  </tr>\\n\",\n        \"</table>\\n\",\n        \"\\n\",\n        \"Each image is mapped to a single label. Since the *class names* are not included with the dataset, store them here to use later when plotting the images:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"IjnLH5S2CaWx\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"class_names = metadata.features['label'].names\\n\",\n        \"print(\\\"Class names: {}\\\".format(class_names))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Brm0b_KACaWX\"\n      },\n      \"source\": [\n        \"### Explore the data\\n\",\n        \"\\n\",\n        \"Let's explore the format of the dataset before training the model. The following shows there are 60,000 images in the training set, and 10000 images in the test set:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"MaOTZxFzi48X\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"num_train_examples = metadata.splits['train'].num_examples\\n\",\n        \"num_test_examples = metadata.splits['test'].num_examples\\n\",\n        \"print(\\\"Number of training examples: {}\\\".format(num_train_examples))\\n\",\n        \"print(\\\"Number of test examples:     {}\\\".format(num_test_examples))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ES6uQoLKCaWr\"\n      },\n      \"source\": [\n        \"## Preprocess the data\\n\",\n        \"\\n\",\n        \"The value of each pixel in the image data is an integer in the range `[0,255]`. For the model to work properly, these values need to be normalized to the range `[0,1]`. So here we create a normalization function, and then apply it to each image in the test and train datasets.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"nAsH3Zm-76pB\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def normalize(images, labels):\\n\",\n        \"  images = tf.cast(images, tf.float32)\\n\",\n        \"  images /= 255\\n\",\n        \"  return images, labels\\n\",\n        \"\\n\",\n        \"# The map function applies the normalize function to each element in the train\\n\",\n        \"# and test datasets\\n\",\n        \"train_dataset =  train_dataset.map(normalize)\\n\",\n        \"test_dataset  =  test_dataset.map(normalize)\\n\",\n        \"\\n\",\n        \"# The first time you use the dataset, the images will be loaded from disk\\n\",\n        \"# Caching will keep them in memory, making training faster\\n\",\n        \"train_dataset =  train_dataset.cache()\\n\",\n        \"test_dataset  =  test_dataset.cache()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"lIQbEiJGXM-q\"\n      },\n      \"source\": [\n        \"### Explore the processed data\\n\",\n        \"\\n\",\n        \"Let's plot an image to see what it looks like.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"oSzE9l7PjHx0\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Take a single image, and remove the color dimension by reshaping\\n\",\n        \"for image, label in test_dataset.take(1):\\n\",\n        \"  break\\n\",\n        \"image = image.numpy().reshape((28,28))\\n\",\n        \"\\n\",\n        \"# Plot the image - voila a piece of fashion clothing\\n\",\n        \"plt.figure()\\n\",\n        \"plt.imshow(image, cmap=plt.cm.binary)\\n\",\n        \"plt.colorbar()\\n\",\n        \"plt.grid(False)\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Ee638AlnCaWz\"\n      },\n      \"source\": [\n        \"Display the first 25 images from the *training set* and display the class name below each image. Verify that the data is in the correct format and we're ready to build and train the network.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"oZTImqg_CaW1\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.figure(figsize=(10,10))\\n\",\n        \"for i, (image, label) in enumerate(train_dataset.take(25)):\\n\",\n        \"    image = image.numpy().reshape((28,28))\\n\",\n        \"    plt.subplot(5,5,i+1)\\n\",\n        \"    plt.xticks([])\\n\",\n        \"    plt.yticks([])\\n\",\n        \"    plt.grid(False)\\n\",\n        \"    plt.imshow(image, cmap=plt.cm.binary)\\n\",\n        \"    plt.xlabel(class_names[label])\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"59veuiEZCaW4\"\n      },\n      \"source\": [\n        \"## Build the model\\n\",\n        \"\\n\",\n        \"Building the neural network requires configuring the layers of the model, then compiling the model.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Gxg1XGm0eOBy\"\n      },\n      \"source\": [\n        \"### Setup the layers\\n\",\n        \"\\n\",\n        \"The basic building block of a neural network is the *layer*. A layer extracts a representation from the data fed into it. Hopefully, a series of connected layers results in a representation that is meaningful for the problem at hand.\\n\",\n        \"\\n\",\n        \"Much of deep learning consists of chaining together simple layers. Most layers, like `tf.keras.layers.Dense`, have internal parameters which are adjusted (\\\"learned\\\") during training.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"9ODch-OFCaW4\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = tf.keras.Sequential([\\n\",\n        \"    tf.keras.layers.Flatten(input_shape=(28, 28, 1)),\\n\",\n        \"    tf.keras.layers.Dense(128, activation=tf.nn.relu),\\n\",\n        \"    tf.keras.layers.Dense(10, activation=tf.nn.softmax)\\n\",\n        \"])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"gut8A_7rCaW6\"\n      },\n      \"source\": [\n        \"This network has three layers:\\n\",\n        \"\\n\",\n        \"* **input** `tf.keras.layers.Flatten` — This layer transforms the images from a 2d-array of 28 $\\\\times$ 28 pixels, to a 1d-array of 784 pixels (28\\\\*28). Think of this layer as unstacking rows of pixels in the image and lining them up. This layer has no parameters to learn, as it only reformats the data.\\n\",\n        \"\\n\",\n        \"* **\\\"hidden\\\"** `tf.keras.layers.Dense`— A densely connected layer of 128 neurons. Each neuron (or node) takes input from all 784 nodes in the previous layer, weighting that input according to hidden parameters which will be learned during training, and outputs a single value to the next layer.\\n\",\n        \"\\n\",\n        \"* **output**  `tf.keras.layers.Dense` — A 128-neuron, followed by 10-node *softmax* layer. Each node represents a class of clothing. As in the previous layer, the final layer takes input from the 128 nodes in the layer before it, and outputs a value in the range `[0, 1]`, representing the probability that the image belongs to that class. The sum of all 10 node values is 1.\\n\",\n        \"\\n\",\n        \"> Note: Using `softmax` activation and `SparseCategoricalCrossentropy()` has issues and which are patched by the `tf.keras` model. A safer approach, in general, is to use a linear output (no activation function) with `SparseCategoricalCrossentropy(from_logits=True)`.\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"### Compile the model\\n\",\n        \"\\n\",\n        \"Before the model is ready for training, it needs a few more settings. These are added during the model's *compile* step:\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"* *Loss function* — An algorithm for measuring how far the model's outputs are from the desired output. The goal of training is this measures loss.\\n\",\n        \"* *Optimizer* —An algorithm for adjusting the inner parameters of the model in order to minimize loss.\\n\",\n        \"* *Metrics* —Used to monitor the training and testing steps. The following example uses *accuracy*, the fraction of the images that are correctly classified.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Lhan11blCaW7\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model.compile(optimizer='adam',\\n\",\n        \"              loss=tf.keras.losses.SparseCategoricalCrossentropy(),\\n\",\n        \"              metrics=['accuracy'])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"qKF6uW-BCaW-\"\n      },\n      \"source\": [\n        \"## Train the model\\n\",\n        \"\\n\",\n        \"First, we define the iteration behavior for the train dataset:\\n\",\n        \"1. Repeat forever by specifying `dataset.repeat()` (the `epochs` parameter described below limits how long we perform training).\\n\",\n        \"2. The `dataset.shuffle(60000)` randomizes the order so our model cannot learn anything from the order of the examples.\\n\",\n        \"3. And `dataset.batch(32)` tells `model.fit` to use batches of 32 images and labels when updating the model variables.\\n\",\n        \"\\n\",\n        \"Training is performed by calling the `model.fit` method:\\n\",\n        \"1. Feed the training data to the model using `train_dataset`.\\n\",\n        \"2. The model learns to associate images and labels.\\n\",\n        \"3. The `epochs=5` parameter limits training to 5 full iterations of the training dataset, so a total of 5 * 60000 = 300000 examples.\\n\",\n        \"\\n\",\n        \"(Don't worry about `steps_per_epoch`, the requirement to have this flag will soon be removed.)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"o_Dp8971McQ1\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"BATCH_SIZE = 32\\n\",\n        \"train_dataset = train_dataset.cache().repeat().shuffle(num_train_examples).batch(BATCH_SIZE)\\n\",\n        \"test_dataset = test_dataset.cache().batch(BATCH_SIZE)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"xvwvpA64CaW_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model.fit(train_dataset, epochs=5, steps_per_epoch=math.ceil(num_train_examples/BATCH_SIZE))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"W3ZVOhugCaXA\"\n      },\n      \"source\": [\n        \"As the model trains, the loss and accuracy metrics are displayed. This model reaches an accuracy of about 0.88 (or 88%) on the training data.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"oEw4bZgGCaXB\"\n      },\n      \"source\": [\n        \"## Evaluate accuracy\\n\",\n        \"\\n\",\n        \"Next, compare how the model performs on the test dataset. Use all examples we have in the test dataset to assess accuracy.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"VflXLEeECaXC\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"test_loss, test_accuracy = model.evaluate(test_dataset, steps=math.ceil(num_test_examples/32))\\n\",\n        \"print('Accuracy on test dataset:', test_accuracy)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"yWfgsmVXCaXG\"\n      },\n      \"source\": [\n        \"As it turns out, the accuracy on the test dataset is smaller than the accuracy on the training dataset. This is completely normal, since the model was trained on the `train_dataset`. When the model sees images it has never seen during training, (that is, from the `test_dataset`), we can expect performance to go down. \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"xsoS7CPDCaXH\"\n      },\n      \"source\": [\n        \"## Make predictions and explore\\n\",\n        \"\\n\",\n        \"With the model trained, we can use it to make predictions about some images.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Ccoz4conNCpl\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"for test_images, test_labels in test_dataset.take(1):\\n\",\n        \"  test_images = test_images.numpy()\\n\",\n        \"  test_labels = test_labels.numpy()\\n\",\n        \"  predictions = model.predict(test_images)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Gl91RPhdCaXI\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"predictions.shape\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"x9Kk1voUCaXJ\"\n      },\n      \"source\": [\n        \"Here, the model has predicted the label for each image in the testing set. Let's take a look at the first prediction:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"3DmJEUinCaXK\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"predictions[0]\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"-hw1hgeSCaXN\"\n      },\n      \"source\": [\n        \"A prediction is an array of 10 numbers. These describe the \\\"confidence\\\" of the model that the image corresponds to each of the 10 different articles of clothing. We can see which label has the highest confidence value:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"qsqenuPnCaXO\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"np.argmax(predictions[0])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"E51yS7iCCaXO\"\n      },\n      \"source\": [\n        \"So the model is most confident that this image is a shirt, or `class_names[6]`. And we can check the test label to see this is correct:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Sd7Pgsu6CaXP\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"test_labels[0]\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ygh2yYC972ne\"\n      },\n      \"source\": [\n        \"We can graph this to look at the full set of 10 class predictions\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"DvYmmrpIy6Y1\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def plot_image(i, predictions_array, true_labels, images):\\n\",\n        \"  predictions_array, true_label, img = predictions_array[i], true_labels[i], images[i]\\n\",\n        \"  plt.grid(False)\\n\",\n        \"  plt.xticks([])\\n\",\n        \"  plt.yticks([])\\n\",\n        \"  \\n\",\n        \"  plt.imshow(img[...,0], cmap=plt.cm.binary)\\n\",\n        \"\\n\",\n        \"  predicted_label = np.argmax(predictions_array)\\n\",\n        \"  if predicted_label == true_label:\\n\",\n        \"    color = 'blue'\\n\",\n        \"  else:\\n\",\n        \"    color = 'red'\\n\",\n        \"  \\n\",\n        \"  plt.xlabel(\\\"{} {:2.0f}% ({})\\\".format(class_names[predicted_label],\\n\",\n        \"                                100*np.max(predictions_array),\\n\",\n        \"                                class_names[true_label]),\\n\",\n        \"                                color=color)\\n\",\n        \"\\n\",\n        \"def plot_value_array(i, predictions_array, true_label):\\n\",\n        \"  predictions_array, true_label = predictions_array[i], true_label[i]\\n\",\n        \"  plt.grid(False)\\n\",\n        \"  plt.xticks([])\\n\",\n        \"  plt.yticks([])\\n\",\n        \"  thisplot = plt.bar(range(10), predictions_array, color=\\\"#777777\\\")\\n\",\n        \"  plt.ylim([0, 1]) \\n\",\n        \"  predicted_label = np.argmax(predictions_array)\\n\",\n        \"  \\n\",\n        \"  thisplot[predicted_label].set_color('red')\\n\",\n        \"  thisplot[true_label].set_color('blue')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"d4Ov9OFDMmOD\"\n      },\n      \"source\": [\n        \"Let's look at the 0th image, predictions, and prediction array. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"HV5jw-5HwSmO\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"i = 0\\n\",\n        \"plt.figure(figsize=(6,3))\\n\",\n        \"plt.subplot(1,2,1)\\n\",\n        \"plot_image(i, predictions, test_labels, test_images)\\n\",\n        \"plt.subplot(1,2,2)\\n\",\n        \"plot_value_array(i, predictions, test_labels)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Ko-uzOufSCSe\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"i = 12\\n\",\n        \"plt.figure(figsize=(6,3))\\n\",\n        \"plt.subplot(1,2,1)\\n\",\n        \"plot_image(i, predictions, test_labels, test_images)\\n\",\n        \"plt.subplot(1,2,2)\\n\",\n        \"plot_value_array(i, predictions, test_labels)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"kgdvGD52CaXR\"\n      },\n      \"source\": [\n        \"Let's plot several images with their predictions. Correct prediction labels are blue and incorrect prediction labels are red. The number gives the percent (out of 100) for the predicted label. Note that it can be wrong even when very confident. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"hQlnbqaw2Qu_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Plot the first X test images, their predicted label, and the true label\\n\",\n        \"# Color correct predictions in blue, incorrect predictions in red\\n\",\n        \"num_rows = 5\\n\",\n        \"num_cols = 3\\n\",\n        \"num_images = num_rows*num_cols\\n\",\n        \"plt.figure(figsize=(2*2*num_cols, 2*num_rows))\\n\",\n        \"for i in range(num_images):\\n\",\n        \"  plt.subplot(num_rows, 2*num_cols, 2*i+1)\\n\",\n        \"  plot_image(i, predictions, test_labels, test_images)\\n\",\n        \"  plt.subplot(num_rows, 2*num_cols, 2*i+2)\\n\",\n        \"  plot_value_array(i, predictions, test_labels)\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"R32zteKHCaXT\"\n      },\n      \"source\": [\n        \"Finally, use the trained model to make a prediction about a single image. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"yRJ7JU7JCaXT\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Grab an image from the test dataset\\n\",\n        \"img = test_images[0]\\n\",\n        \"\\n\",\n        \"print(img.shape)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"vz3bVp21CaXV\"\n      },\n      \"source\": [\n        \"`tf.keras` models are optimized to make predictions on a *batch*, or collection, of examples at once. So even though we're using a single image, we need to add it to a list:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"lDFh5yF_CaXW\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Add the image to a batch where it's the only member.\\n\",\n        \"img = np.array([img])\\n\",\n        \"\\n\",\n        \"print(img.shape)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"EQ5wLTkcCaXY\"\n      },\n      \"source\": [\n        \"Now predict the image:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"o_rzNSdrCaXY\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"predictions_single = model.predict(img)\\n\",\n        \"\\n\",\n        \"print(predictions_single)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"6Ai-cpLjO-3A\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plot_value_array(0, predictions_single, test_labels)\\n\",\n        \"_ = plt.xticks(range(10), class_names, rotation=45)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"cU1Y2OAMCaXb\"\n      },\n      \"source\": [\n        \"`model.predict` returns a list of lists, one for each image in the batch of data. Grab the predictions for our (only) image in the batch:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"2tRmdq_8CaXb\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"np.argmax(predictions_single[0])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"YFc2HbEVCaXd\"\n      },\n      \"source\": [\n        \"And, as before, the model predicts a label of 6 (shirt).\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"-KtnHECKZni_\"\n      },\n      \"source\": [\n        \"# Exercises\\n\",\n        \"\\n\",\n        \"Experiment with different models and see how the accuracy results differ. In particular change the following parameters:\\n\",\n        \"*   Set training epochs set to 1\\n\",\n        \"*   Number of neurons in the Dense layer following the Flatten one. For example, go really low (e.g. 10) in ranges up to 512 and see how accuracy changes\\n\",\n        \"*   Add additional Dense layers between the Flatten and the final `Dense(10)`, experiment with different units in these layers\\n\",\n        \"*   Don't normalize the pixel values, and see the effect that has\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"Remember to enable GPU to make everything run faster (Runtime -> Change runtime type -> Hardware accelerator -> GPU).\\n\",\n        \"Also, if you run into trouble, simply reset the entire environment and start from the beginning:\\n\",\n        \"*   Edit -> Clear all outputs\\n\",\n        \"*   Runtime -> Reset all runtimes\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l03c01_classifying_images_of_clothing.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l04c01_image_classification_with_cnns.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"6uQP3ZbC8J5o\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"_ckMIh7O7s6D\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"vasWnqRgy1H4\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title MIT License\\n\",\n        \"#\\n\",\n        \"# Copyright (c) 2017 François Chollet\\n\",\n        \"#\\n\",\n        \"# Permission is hereby granted, free of charge, to any person obtaining a\\n\",\n        \"# copy of this software and associated documentation files (the \\\"Software\\\"),\\n\",\n        \"# to deal in the Software without restriction, including without limitation\\n\",\n        \"# the rights to use, copy, modify, merge, publish, distribute, sublicense,\\n\",\n        \"# and/or sell copies of the Software, and to permit persons to whom the\\n\",\n        \"# Software is furnished to do so, subject to the following conditions:\\n\",\n        \"#\\n\",\n        \"# The above copyright notice and this permission notice shall be included in\\n\",\n        \"# all copies or substantial portions of the Software.\\n\",\n        \"#\\n\",\n        \"# THE SOFTWARE IS PROVIDED \\\"AS IS\\\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\\n\",\n        \"# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\\n\",\n        \"# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\\n\",\n        \"# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\\n\",\n        \"# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\\n\",\n        \"# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\\n\",\n        \"# DEALINGS IN THE SOFTWARE.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"jYysdyb-CaWM\"\n      },\n      \"source\": [\n        \"# Image Classification with Convolutional Neural Networks\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"S5Uhzt6vVIB2\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l04c01_image_classification_with_cnns.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l04c01_image_classification_with_cnns.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"FbVhjPpzn6BM\"\n      },\n      \"source\": [\n        \"In this tutorial, we'll build and train a neural network to classify images of clothing, like sneakers and shirts.\\n\",\n        \"\\n\",\n        \"It's okay if you don't understand everything. This is a fast-paced overview of a complete TensorFlow program, with explanations along the way. The goal is to get the general sense of a TensorFlow project, not to catch every detail.\\n\",\n        \"\\n\",\n        \"This guide uses [tf.keras](https://www.tensorflow.org/guide/keras), a high-level API to build and train models in TensorFlow.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"H0tMfX2vR0uD\"\n      },\n      \"source\": [\n        \"## Install and import dependencies\\n\",\n        \"\\n\",\n        \"We'll need [TensorFlow Datasets](https://www.tensorflow.org/datasets/), an API that simplifies downloading and accessing datasets, and provides several sample datasets to work with. We're also using a few helper libraries.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"5HDhfftMGc_i\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow as tf\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"uusvhUp9Gg37\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Import TensorFlow Datasets\\n\",\n        \"import tensorflow_datasets as tfds\\n\",\n        \"tfds.disable_progress_bar()\\n\",\n        \"\\n\",\n        \"# Helper libraries\\n\",\n        \"import math\\n\",\n        \"import numpy as np\\n\",\n        \"import matplotlib.pyplot as plt\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"UXZ44qIaG0Ru\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import logging\\n\",\n        \"logger = tf.get_logger()\\n\",\n        \"logger.setLevel(logging.ERROR)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"yR0EdgrLCaWR\"\n      },\n      \"source\": [\n        \"## Import the Fashion MNIST dataset\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"DLdCchMdCaWQ\"\n      },\n      \"source\": [\n        \"This guide uses the [Fashion MNIST](https://github.com/zalandoresearch/fashion-mnist) dataset, which contains 70,000 grayscale images in 10 categories. The images show individual articles of clothing at low resolution (28 $\\\\times$ 28 pixels), as seen here:\\n\",\n        \"\\n\",\n        \"<table>\\n\",\n        \"  <tr><td>\\n\",\n        \"    <img src=\\\"https://tensorflow.org/images/fashion-mnist-sprite.png\\\"\\n\",\n        \"         alt=\\\"Fashion MNIST sprite\\\" width=\\\"600\\\">\\n\",\n        \"  </td></tr>\\n\",\n        \"  <tr><td align=\\\"center\\\">\\n\",\n        \"    <b>Figure 1.</b> <a href=\\\"https://github.com/zalandoresearch/fashion-mnist\\\">Fashion-MNIST samples</a> (by Zalando, MIT License).<br/>&nbsp;\\n\",\n        \"  </td></tr>\\n\",\n        \"</table>\\n\",\n        \"\\n\",\n        \"Fashion MNIST is intended as a drop-in replacement for the classic [MNIST](http://yann.lecun.com/exdb/mnist/) dataset—often used as the \\\"Hello, World\\\" of machine learning programs for computer vision. The MNIST dataset contains images of handwritten digits (0, 1, 2, etc) in an identical format to the articles of clothing we'll use here.\\n\",\n        \"\\n\",\n        \"This guide uses Fashion MNIST for variety, and because it's a slightly more challenging problem than regular MNIST. Both datasets are relatively small and are used to verify that an algorithm works as expected. They're good starting points to test and debug code.\\n\",\n        \"\\n\",\n        \"We will use 60,000 images to train the network and 10,000 images to evaluate how accurately the network learned to classify images. You can access the Fashion MNIST directly from TensorFlow, using the [Datasets](https://www.tensorflow.org/datasets) API:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"7MqDQO0KCaWS\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"dataset, metadata = tfds.load('fashion_mnist', as_supervised=True, with_info=True)\\n\",\n        \"train_dataset, test_dataset = dataset['train'], dataset['test']\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"t9FDsUlxCaWW\"\n      },\n      \"source\": [\n        \"Loading the dataset returns metadata as well as a *training dataset* and *test dataset*.\\n\",\n        \"\\n\",\n        \"* The model is trained using `train_dataset`.\\n\",\n        \"* The model is tested against `test_dataset`.\\n\",\n        \"\\n\",\n        \"The images are 28 $\\\\times$ 28 arrays, with pixel values in the range `[0, 255]`. The *labels* are an array of integers, in the range `[0, 9]`. These correspond to the *class* of clothing the image represents:\\n\",\n        \"\\n\",\n        \"<table>\\n\",\n        \"  <tr>\\n\",\n        \"    <th>Label</th>\\n\",\n        \"    <th>Class</th>\\n\",\n        \"  </tr>\\n\",\n        \"  <tr>\\n\",\n        \"    <td>0</td>\\n\",\n        \"    <td>T-shirt/top</td>\\n\",\n        \"  </tr>\\n\",\n        \"  <tr>\\n\",\n        \"    <td>1</td>\\n\",\n        \"    <td>Trouser</td>\\n\",\n        \"  </tr>\\n\",\n        \"    <tr>\\n\",\n        \"    <td>2</td>\\n\",\n        \"    <td>Pullover</td>\\n\",\n        \"  </tr>\\n\",\n        \"    <tr>\\n\",\n        \"    <td>3</td>\\n\",\n        \"    <td>Dress</td>\\n\",\n        \"  </tr>\\n\",\n        \"    <tr>\\n\",\n        \"    <td>4</td>\\n\",\n        \"    <td>Coat</td>\\n\",\n        \"  </tr>\\n\",\n        \"    <tr>\\n\",\n        \"    <td>5</td>\\n\",\n        \"    <td>Sandal</td>\\n\",\n        \"  </tr>\\n\",\n        \"    <tr>\\n\",\n        \"    <td>6</td>\\n\",\n        \"    <td>Shirt</td>\\n\",\n        \"  </tr>\\n\",\n        \"    <tr>\\n\",\n        \"    <td>7</td>\\n\",\n        \"    <td>Sneaker</td>\\n\",\n        \"  </tr>\\n\",\n        \"    <tr>\\n\",\n        \"    <td>8</td>\\n\",\n        \"    <td>Bag</td>\\n\",\n        \"  </tr>\\n\",\n        \"    <tr>\\n\",\n        \"    <td>9</td>\\n\",\n        \"    <td>Ankle boot</td>\\n\",\n        \"  </tr>\\n\",\n        \"</table>\\n\",\n        \"\\n\",\n        \"Each image is mapped to a single label. Since the *class names* are not included with the dataset, store them here to use later when plotting the images:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"IjnLH5S2CaWx\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',\\n\",\n        \"               'Sandal',      'Shirt',   'Sneaker',  'Bag',   'Ankle boot']\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Brm0b_KACaWX\"\n      },\n      \"source\": [\n        \"### Explore the data\\n\",\n        \"\\n\",\n        \"Let's explore the format of the dataset before training the model. The following shows there are 60,000 images in the training set, and 10000 images in the test set:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"MaOTZxFzi48X\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"num_train_examples = metadata.splits['train'].num_examples\\n\",\n        \"num_test_examples = metadata.splits['test'].num_examples\\n\",\n        \"print(\\\"Number of training examples: {}\\\".format(num_train_examples))\\n\",\n        \"print(\\\"Number of test examples:     {}\\\".format(num_test_examples))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ES6uQoLKCaWr\"\n      },\n      \"source\": [\n        \"## Preprocess the data\\n\",\n        \"\\n\",\n        \"The value of each pixel in the image data is an integer in the range `[0,255]`. For the model to work properly, these values need to be normalized to the range `[0,1]`. So here we create a normalization function, and then apply it to each image in the test and train datasets.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"nAsH3Zm-76pB\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def normalize(images, labels):\\n\",\n        \"  images = tf.cast(images, tf.float32)\\n\",\n        \"  images /= 255\\n\",\n        \"  return images, labels\\n\",\n        \"\\n\",\n        \"# The map function applies the normalize function to each element in the train\\n\",\n        \"# and test datasets\\n\",\n        \"train_dataset =  train_dataset.map(normalize)\\n\",\n        \"test_dataset  =  test_dataset.map(normalize)\\n\",\n        \"\\n\",\n        \"# The first time you use the dataset, the images will be loaded from disk\\n\",\n        \"# Caching will keep them in memory, making training faster\\n\",\n        \"train_dataset =  train_dataset.cache()\\n\",\n        \"test_dataset  =  test_dataset.cache()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"lIQbEiJGXM-q\"\n      },\n      \"source\": [\n        \"### Explore the processed data\\n\",\n        \"\\n\",\n        \"Let's plot an image to see what it looks like.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"oSzE9l7PjHx0\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Take a single image, and remove the color dimension by reshaping\\n\",\n        \"for image, label in test_dataset.take(1):\\n\",\n        \"  break\\n\",\n        \"image = image.numpy().reshape((28,28))\\n\",\n        \"\\n\",\n        \"# Plot the image - voila a piece of fashion clothing\\n\",\n        \"plt.figure()\\n\",\n        \"plt.imshow(image, cmap=plt.cm.binary)\\n\",\n        \"plt.colorbar()\\n\",\n        \"plt.grid(False)\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Ee638AlnCaWz\"\n      },\n      \"source\": [\n        \"Display the first 25 images from the *training set* and display the class name below each image. Verify that the data is in the correct format and we're ready to build and train the network.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"oZTImqg_CaW1\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.figure(figsize=(10,10))\\n\",\n        \"i = 0\\n\",\n        \"for (image, label) in test_dataset.take(25):\\n\",\n        \"    image = image.numpy().reshape((28,28))\\n\",\n        \"    plt.subplot(5,5,i+1)\\n\",\n        \"    plt.xticks([])\\n\",\n        \"    plt.yticks([])\\n\",\n        \"    plt.grid(False)\\n\",\n        \"    plt.imshow(image, cmap=plt.cm.binary)\\n\",\n        \"    plt.xlabel(class_names[label])\\n\",\n        \"    i += 1\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"59veuiEZCaW4\"\n      },\n      \"source\": [\n        \"## Build the model\\n\",\n        \"\\n\",\n        \"Building the neural network requires configuring the layers of the model, then compiling the model.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Gxg1XGm0eOBy\"\n      },\n      \"source\": [\n        \"### Setup the layers\\n\",\n        \"\\n\",\n        \"The basic building block of a neural network is the *layer*. A layer extracts a representation from the data fed into it. Hopefully, a series of connected layers results in a representation that is meaningful for the problem at hand.\\n\",\n        \"\\n\",\n        \"Much of deep learning consists of chaining together simple layers. Most layers, like `tf.keras.layers.Dense`, have internal parameters which are adjusted (\\\"learned\\\") during training.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"9ODch-OFCaW4\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = tf.keras.Sequential([\\n\",\n        \"    tf.keras.layers.Conv2D(32, (3,3), padding='same', activation=tf.nn.relu,\\n\",\n        \"                           input_shape=(28, 28, 1)),\\n\",\n        \"    tf.keras.layers.MaxPooling2D((2, 2), strides=2),\\n\",\n        \"    tf.keras.layers.Conv2D(64, (3,3), padding='same', activation=tf.nn.relu),\\n\",\n        \"    tf.keras.layers.MaxPooling2D((2, 2), strides=2),\\n\",\n        \"    tf.keras.layers.Flatten(),\\n\",\n        \"    tf.keras.layers.Dense(128, activation=tf.nn.relu),\\n\",\n        \"    tf.keras.layers.Dense(10, activation=tf.nn.softmax)\\n\",\n        \"])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"gut8A_7rCaW6\"\n      },\n      \"source\": [\n        \"This network layers are:\\n\",\n        \"\\n\",\n        \"* **\\\"convolutions\\\"** `tf.keras.layers.Conv2D and MaxPooling2D`— Network start with two pairs of Conv/MaxPool. The first layer is a Conv2D filters (3,3) being applied to the input image, retaining the original image size by using padding, and creating 32 output (convoluted) images (so this layer creates 32 convoluted images of the same size as input). After that, the 32 outputs are reduced in size using a MaxPooling2D (2,2) with a stride of 2. The next Conv2D also has a (3,3) kernel, takes the 32 images as input and creates 64 outputs which are again reduced in size by a MaxPooling2D layer. So far in the course, we have described what a Convolution does, but we haven't yet covered how you chain multiples of these together. We will get back to this in lesson 4 when we use color images. At this point, it's enough if you understand the kind of operation a convolutional filter performs\\n\",\n        \"\\n\",\n        \"* **output** `tf.keras.layers.Dense` — A 128-neuron, followed by 10-node *softmax* layer. Each node represents a class of clothing. As in the previous layer, the final layer takes input from the 128 nodes in the layer before it, and outputs a value in the range `[0, 1]`, representing the probability that the image belongs to that class. The sum of all 10 node values is 1.\\n\",\n        \"\\n\",\n        \"> Note: Using `softmax` activation and `SparseCategoricalCrossentropy()` has issues and which are patched by the `tf.keras` model. A safer approach, in general, is to use a linear output (no activation function) with `SparseCategoricalCrossentropy(from_logits=True)`.\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"### Compile the model\\n\",\n        \"\\n\",\n        \"Before the model is ready for training, it needs a few more settings. These are added during the model's *compile* step:\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"* *Loss function* — An algorithm for measuring how far the model's outputs are from the desired output. The goal of training is this measures loss.\\n\",\n        \"* *Optimizer* —An algorithm for adjusting the inner parameters of the model in order to minimize loss.\\n\",\n        \"* *Metrics* —Used to monitor the training and testing steps. The following example uses *accuracy*, the fraction of the images that are correctly classified.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Lhan11blCaW7\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model.compile(optimizer='adam',\\n\",\n        \"              loss=tf.keras.losses.SparseCategoricalCrossentropy(),\\n\",\n        \"              metrics=['accuracy'])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"qKF6uW-BCaW-\"\n      },\n      \"source\": [\n        \"## Train the model\\n\",\n        \"\\n\",\n        \"First, we define the iteration behavior for the train dataset:\\n\",\n        \"1. Repeat forever by specifying `dataset.repeat()` (the `epochs` parameter described below limits how long we perform training).\\n\",\n        \"2. The `dataset.shuffle(60000)` randomizes the order so our model cannot learn anything from the order of the examples.\\n\",\n        \"3. And `dataset.batch(32)` tells `model.fit` to use batches of 32 images and labels when updating the model variables.\\n\",\n        \"\\n\",\n        \"Training is performed by calling the `model.fit` method:\\n\",\n        \"1. Feed the training data to the model using `train_dataset`.\\n\",\n        \"2. The model learns to associate images and labels.\\n\",\n        \"3. The `epochs=5` parameter limits training to 5 full iterations of the training dataset, so a total of 5 * 60000 = 300000 examples.\\n\",\n        \"\\n\",\n        \"(Don't worry about `steps_per_epoch`, the requirement to have this flag will soon be removed.)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"o_Dp8971McQ1\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"BATCH_SIZE = 32\\n\",\n        \"train_dataset = train_dataset.cache().repeat().shuffle(num_train_examples).batch(BATCH_SIZE)\\n\",\n        \"test_dataset = test_dataset.cache().batch(BATCH_SIZE)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"xvwvpA64CaW_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model.fit(train_dataset, epochs=10, steps_per_epoch=math.ceil(num_train_examples/BATCH_SIZE))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"W3ZVOhugCaXA\"\n      },\n      \"source\": [\n        \"As the model trains, the loss and accuracy metrics are displayed. This model reaches an accuracy of about 0.97 (or 97%) on the training data.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"oEw4bZgGCaXB\"\n      },\n      \"source\": [\n        \"## Evaluate accuracy\\n\",\n        \"\\n\",\n        \"Next, compare how the model performs on the test dataset. Use all examples we have in the test dataset to assess accuracy.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"VflXLEeECaXC\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"test_loss, test_accuracy = model.evaluate(test_dataset, steps=math.ceil(num_test_examples/32))\\n\",\n        \"print('Accuracy on test dataset:', test_accuracy)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"yWfgsmVXCaXG\"\n      },\n      \"source\": [\n        \"As it turns out, the accuracy on the test dataset is smaller than the accuracy on the training dataset. This is completely normal, since the model was trained on the `train_dataset`. When the model sees images it has never seen during training, (that is, from the `test_dataset`), we can expect performance to go down. \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"xsoS7CPDCaXH\"\n      },\n      \"source\": [\n        \"## Make predictions and explore\\n\",\n        \"\\n\",\n        \"With the model trained, we can use it to make predictions about some images.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Ccoz4conNCpl\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"for test_images, test_labels in test_dataset.take(1):\\n\",\n        \"  test_images = test_images.numpy()\\n\",\n        \"  test_labels = test_labels.numpy()\\n\",\n        \"  predictions = model.predict(test_images)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Gl91RPhdCaXI\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"predictions.shape\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"x9Kk1voUCaXJ\"\n      },\n      \"source\": [\n        \"Here, the model has predicted the probability of each label for each image in the testing set. Let's take a look at the first prediction:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"3DmJEUinCaXK\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"predictions[0]\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"-hw1hgeSCaXN\"\n      },\n      \"source\": [\n        \"A prediction is an array of 10 numbers. These describe the \\\"confidence\\\" of the model that the image corresponds to each of the 10 different articles of clothing. We can see which label has the highest confidence value:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"qsqenuPnCaXO\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"np.argmax(predictions[0])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"E51yS7iCCaXO\"\n      },\n      \"source\": [\n        \"So the model is usually most confident that this image is a Shirt, or `class_names[6]`. Let's check the label:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Sd7Pgsu6CaXP\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"test_labels[0]\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ygh2yYC972ne\"\n      },\n      \"source\": [\n        \"We can graph this to look at the full set of 10 class predictions\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"DvYmmrpIy6Y1\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def plot_image(i, predictions_array, true_labels, images):\\n\",\n        \"  predictions_array, true_label, img = predictions_array[i], true_labels[i], images[i]\\n\",\n        \"  plt.grid(False)\\n\",\n        \"  plt.xticks([])\\n\",\n        \"  plt.yticks([])\\n\",\n        \"  \\n\",\n        \"  plt.imshow(img[...,0], cmap=plt.cm.binary)\\n\",\n        \"\\n\",\n        \"  predicted_label = np.argmax(predictions_array)\\n\",\n        \"  if predicted_label == true_label:\\n\",\n        \"    color = 'blue'\\n\",\n        \"  else:\\n\",\n        \"    color = 'red'\\n\",\n        \"  \\n\",\n        \"  plt.xlabel(\\\"{} {:2.0f}% ({})\\\".format(class_names[predicted_label],\\n\",\n        \"                                100*np.max(predictions_array),\\n\",\n        \"                                class_names[true_label]),\\n\",\n        \"                                color=color)\\n\",\n        \"\\n\",\n        \"def plot_value_array(i, predictions_array, true_label):\\n\",\n        \"  predictions_array, true_label = predictions_array[i], true_label[i]\\n\",\n        \"  plt.grid(False)\\n\",\n        \"  plt.xticks([])\\n\",\n        \"  plt.yticks([])\\n\",\n        \"  thisplot = plt.bar(range(10), predictions_array, color=\\\"#777777\\\")\\n\",\n        \"  plt.ylim([0, 1])\\n\",\n        \"  predicted_label = np.argmax(predictions_array)\\n\",\n        \"  \\n\",\n        \"  thisplot[predicted_label].set_color('red')\\n\",\n        \"  thisplot[true_label].set_color('blue')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"d4Ov9OFDMmOD\"\n      },\n      \"source\": [\n        \"Let's look at the 0th image, predictions, and prediction array. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"HV5jw-5HwSmO\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"i = 0\\n\",\n        \"plt.figure(figsize=(6,3))\\n\",\n        \"plt.subplot(1,2,1)\\n\",\n        \"plot_image(i, predictions, test_labels, test_images)\\n\",\n        \"plt.subplot(1,2,2)\\n\",\n        \"plot_value_array(i, predictions, test_labels)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Ko-uzOufSCSe\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"i = 12\\n\",\n        \"plt.figure(figsize=(6,3))\\n\",\n        \"plt.subplot(1,2,1)\\n\",\n        \"plot_image(i, predictions, test_labels, test_images)\\n\",\n        \"plt.subplot(1,2,2)\\n\",\n        \"plot_value_array(i, predictions, test_labels)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"kgdvGD52CaXR\"\n      },\n      \"source\": [\n        \"Let's plot several images with their predictions. Correct prediction labels are blue and incorrect prediction labels are red. The number gives the percent (out of 100) for the predicted label. Note that it can be wrong even when very confident. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"hQlnbqaw2Qu_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Plot the first X test images, their predicted label, and the true label\\n\",\n        \"# Color correct predictions in blue, incorrect predictions in red\\n\",\n        \"num_rows = 5\\n\",\n        \"num_cols = 3\\n\",\n        \"num_images = num_rows*num_cols\\n\",\n        \"plt.figure(figsize=(2*2*num_cols, 2*num_rows))\\n\",\n        \"for i in range(num_images):\\n\",\n        \"  plt.subplot(num_rows, 2*num_cols, 2*i+1)\\n\",\n        \"  plot_image(i, predictions, test_labels, test_images)\\n\",\n        \"  plt.subplot(num_rows, 2*num_cols, 2*i+2)\\n\",\n        \"  plot_value_array(i, predictions, test_labels)\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"R32zteKHCaXT\"\n      },\n      \"source\": [\n        \"Finally, use the trained model to make a prediction about a single image. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"yRJ7JU7JCaXT\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Grab an image from the test dataset\\n\",\n        \"img = test_images[0]\\n\",\n        \"\\n\",\n        \"print(img.shape)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"vz3bVp21CaXV\"\n      },\n      \"source\": [\n        \"`tf.keras` models are optimized to make predictions on a *batch*, or collection, of examples at once. So even though we're using a single image, we need to add it to a list:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"lDFh5yF_CaXW\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Add the image to a batch where it's the only member.\\n\",\n        \"img = np.array([img])\\n\",\n        \"\\n\",\n        \"print(img.shape)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"EQ5wLTkcCaXY\"\n      },\n      \"source\": [\n        \"Now predict the image:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"o_rzNSdrCaXY\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"predictions_single = model.predict(img)\\n\",\n        \"\\n\",\n        \"print(predictions_single)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"6Ai-cpLjO-3A\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plot_value_array(0, predictions_single, test_labels)\\n\",\n        \"_ = plt.xticks(range(10), class_names, rotation=45)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"cU1Y2OAMCaXb\"\n      },\n      \"source\": [\n        \"`model.predict` returns a list of lists, one for each image in the batch of data. Grab the predictions for our (only) image in the batch:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"2tRmdq_8CaXb\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"np.argmax(predictions_single[0])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"YFc2HbEVCaXd\"\n      },\n      \"source\": [\n        \"And, as before, the model predicts a label of 6 (shirt).\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"-KtnHECKZni_\"\n      },\n      \"source\": [\n        \"# Exercises\\n\",\n        \"\\n\",\n        \"Experiment with different models and see how the accuracy results differ. In particular change the following parameters:\\n\",\n        \"*   Set training epochs set to 1\\n\",\n        \"*   Number of neurons in the Dense layer following the Flatten one. For example, go really low (e.g. 10) in ranges up to 512 and see how accuracy changes\\n\",\n        \"*   Add additional Dense layers between the Flatten and the final Dense(10), experiment with different units in these layers\\n\",\n        \"*   Don't normalize the pixel values, and see the effect that has\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"Remember to enable GPU to make everything run faster (Runtime -> Change runtime type -> Hardware accelerator -> GPU).\\n\",\n        \"Also, if you run into trouble, simply reset the entire environment and start from the beginning:\\n\",\n        \"*   Edit -> Clear all outputs\\n\",\n        \"*   Runtime -> Reset all runtimes\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l04c01_image_classification_with_cnns.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l05c01_dogs_vs_cats_without_augmentation.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"TBFXQGKYUc4X\"\n      },\n      \"source\": [\n        \"##### Copyright 2019 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"1z4xy2gTUc4a\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"FE7KNzPPVrVV\"\n      },\n      \"source\": [\n        \"# Dogs vs Cats Image Classification Without Image Augmentation\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"KwQtSOz0VrVX\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l05c01_dogs_vs_cats_without_augmentation.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l05c01_dogs_vs_cats_without_augmentation.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"gN7G9GFmVrVY\"\n      },\n      \"source\": [\n        \"In this tutorial, we will discuss how to classify images into pictures of cats or pictures of dogs. We'll build an image classifier using `tf.keras.Sequential` model and load data using `tf.keras.preprocessing.image.ImageDataGenerator`.\\n\",\n        \"\\n\",\n        \"## Specific concepts that will be covered:\\n\",\n        \"In the process, we will build practical experience and develop intuition around the following concepts\\n\",\n        \"\\n\",\n        \"* Building _data input pipelines_ using the `tf.keras.preprocessing.image.ImageDataGenerator` class — How can we efficiently work with data on disk to interface with our model?\\n\",\n        \"* _Overfitting_ - what is it, how to identify it?\\n\",\n        \"\\n\",\n        \"<hr>\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"**Before you begin**\\n\",\n        \"\\n\",\n        \"Before running the code in this notebook, reset the runtime by going to **Runtime -> Reset all runtimes** in the menu above. If you have been working through several notebooks, this will help you avoid reaching Colab's memory limits.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"zF9uvbXNVrVY\"\n      },\n      \"source\": [\n        \"# Importing packages\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"VddxeYBEVrVZ\"\n      },\n      \"source\": [\n        \"Let's start by importing required packages:\\n\",\n        \"\\n\",\n        \"*   os — to read files and directory structure\\n\",\n        \"*   numpy — for some matrix math outside of TensorFlow\\n\",\n        \"*   matplotlib.pyplot — to plot the graph and display images in our training and validation data\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"oSdjGwVWGshH\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow as tf\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"nlORkUyFGxWH\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"from tensorflow.keras.preprocessing.image import ImageDataGenerator\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"wqtiIPRbG4FA\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import os\\n\",\n        \"import matplotlib.pyplot as plt\\n\",\n        \"import numpy as np\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"GHHqtPisG3R1\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import logging\\n\",\n        \"logger = tf.get_logger()\\n\",\n        \"logger.setLevel(logging.ERROR)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"UZZI6lNkVrVm\"\n      },\n      \"source\": [\n        \"# Data Loading\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"DPHx8-t-VrVo\"\n      },\n      \"source\": [\n        \"To build our image classifier, we begin by downloading the dataset. The dataset we are using is a filtered version of <a href=\\\"https://www.kaggle.com/c/dogs-vs-cats/data\\\" target=\\\"_blank\\\">Dogs vs. Cats</a> dataset from Kaggle (ultimately, this dataset is provided by Microsoft Research).\\n\",\n        \"\\n\",\n        \"In previous Colabs, we've used <a href=\\\"https://www.tensorflow.org/datasets\\\" target=\\\"_blank\\\">TensorFlow Datasets</a>, which is a very easy and convenient way to use datasets. In this Colab however, we will make use of the class `tf.keras.preprocessing.image.ImageDataGenerator` which will read data from disk. We therefore need to directly download *Dogs vs. Cats* from a URL and unzip it to the Colab filesystem.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"rpUSoFjuVrVp\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"_URL = 'https://download.mlcc.google.com/mledu-datasets/cats_and_dogs_filtered.zip'\\n\",\n        \"zip_dir = tf.keras.utils.get_file('cats_and_dogs_filterted.zip', origin=_URL, extract=True)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Giv0wMQzVrVw\"\n      },\n      \"source\": [\n        \"The dataset we have downloaded has the following directory structure.\\n\",\n        \"\\n\",\n        \"<pre style=\\\"font-size: 10.0pt; font-family: Arial; line-height: 2; letter-spacing: 1.0pt;\\\" >\\n\",\n        \"<b>cats_and_dogs_filtered</b>\\n\",\n        \"|__ <b>train</b>\\n\",\n        \"    |______ <b>cats</b>: [cat.0.jpg, cat.1.jpg, cat.2.jpg ...]\\n\",\n        \"    |______ <b>dogs</b>: [dog.0.jpg, dog.1.jpg, dog.2.jpg ...]\\n\",\n        \"|__ <b>validation</b>\\n\",\n        \"    |______ <b>cats</b>: [cat.2000.jpg, cat.2001.jpg, cat.2002.jpg ...]\\n\",\n        \"    |______ <b>dogs</b>: [dog.2000.jpg, dog.2001.jpg, dog.2002.jpg ...]\\n\",\n        \"</pre>\\n\",\n        \"\\n\",\n        \"We can list the directories with the following terminal command:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"ssD23VbTZeVA\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"zip_dir_base = os.path.dirname(zip_dir)\\n\",\n        \"!find $zip_dir_base -type d -print\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"VpmywIlsVrVx\"\n      },\n      \"source\": [\n        \"We'll now assign variables with the proper file path for the training and validation sets.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"sRucI3QqVrVy\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"base_dir = os.path.join(zip_dir, 'cats_and_dogs_filtered')\\n\",\n        \"train_dir = os.path.join(base_dir, 'train')\\n\",\n        \"validation_dir = os.path.join(base_dir, 'validation')\\n\",\n        \"\\n\",\n        \"train_cats_dir = os.path.join(train_dir, 'cats')  # directory with our training cat pictures\\n\",\n        \"train_dogs_dir = os.path.join(train_dir, 'dogs')  # directory with our training dog pictures\\n\",\n        \"validation_cats_dir = os.path.join(validation_dir, 'cats')  # directory with our validation cat pictures\\n\",\n        \"validation_dogs_dir = os.path.join(validation_dir, 'dogs')  # directory with our validation dog pictures\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ZdrHHTy2VrV3\"\n      },\n      \"source\": [\n        \"### Understanding our data\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"LblUYjl-VrV3\"\n      },\n      \"source\": [\n        \"Let's look at how many cats and dogs images we have in our training and validation directory\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"vc4u8e9hVrV4\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"num_cats_tr = len(os.listdir(train_cats_dir))\\n\",\n        \"num_dogs_tr = len(os.listdir(train_dogs_dir))\\n\",\n        \"\\n\",\n        \"num_cats_val = len(os.listdir(validation_cats_dir))\\n\",\n        \"num_dogs_val = len(os.listdir(validation_dogs_dir))\\n\",\n        \"\\n\",\n        \"total_train = num_cats_tr + num_dogs_tr\\n\",\n        \"total_val = num_cats_val + num_dogs_val\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"g4GGzGt0VrV7\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print('total training cat images:', num_cats_tr)\\n\",\n        \"print('total training dog images:', num_dogs_tr)\\n\",\n        \"\\n\",\n        \"print('total validation cat images:', num_cats_val)\\n\",\n        \"print('total validation dog images:', num_dogs_val)\\n\",\n        \"print(\\\"--\\\")\\n\",\n        \"print(\\\"Total training images:\\\", total_train)\\n\",\n        \"print(\\\"Total validation images:\\\", total_val)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"tdsI_L-NVrV_\"\n      },\n      \"source\": [\n        \"# Setting Model Parameters\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"8Lp-0ejxOtP1\"\n      },\n      \"source\": [\n        \"For convenience, we'll set up variables that will be used later while pre-processing our dataset and training our network.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"3NqNselLVrWA\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"BATCH_SIZE = 100  # Number of training examples to process before updating our models variables\\n\",\n        \"IMG_SHAPE  = 150  # Our training data consists of images with width of 150 pixels and height of 150 pixels\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"INn-cOn1VrWC\"\n      },\n      \"source\": [\n        \"# Data Preparation \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"5Jfk6aSAVrWD\"\n      },\n      \"source\": [\n        \"Images must be formatted into appropriately pre-processed floating point tensors before being fed into the network. The steps involved in preparing these images are:\\n\",\n        \"\\n\",\n        \"1. Read images from the disk\\n\",\n        \"2. Decode contents of these images and convert it into proper grid format as per their RGB content\\n\",\n        \"3. Convert them into floating point tensors\\n\",\n        \"4. Rescale the tensors from values between 0 and 255 to values between 0 and 1, as neural networks prefer to deal with small input values.\\n\",\n        \"\\n\",\n        \"Fortunately, all these tasks can be done using the class **tf.keras.preprocessing.image.ImageDataGenerator**.\\n\",\n        \"\\n\",\n        \"We can set this up in a couple of lines of code.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"syDdF_LWVrWE\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"train_image_generator      = ImageDataGenerator(rescale=1./255)  # Generator for our training data\\n\",\n        \"validation_image_generator = ImageDataGenerator(rescale=1./255)  # Generator for our validation data\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"RLciCR_FVrWH\"\n      },\n      \"source\": [\n        \"After defining our generators for training and validation images, **flow_from_directory** method will load images from the disk, apply rescaling, and resize them using single line of code.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Pw94ajOOVrWI\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"train_data_gen = train_image_generator.flow_from_directory(batch_size=BATCH_SIZE,\\n\",\n        \"                                                           directory=train_dir,\\n\",\n        \"                                                           shuffle=True,\\n\",\n        \"                                                           target_size=(IMG_SHAPE,IMG_SHAPE), #(150,150)\\n\",\n        \"                                                           class_mode='binary')\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"2oUoKUzRVrWM\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"val_data_gen = validation_image_generator.flow_from_directory(batch_size=BATCH_SIZE,\\n\",\n        \"                                                              directory=validation_dir,\\n\",\n        \"                                                              shuffle=False,\\n\",\n        \"                                                              target_size=(IMG_SHAPE,IMG_SHAPE), #(150,150)\\n\",\n        \"                                                              class_mode='binary')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"hyexPJ8CVrWP\"\n      },\n      \"source\": [\n        \"### Visualizing Training images\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"60CnhEL4VrWQ\"\n      },\n      \"source\": [\n        \"We can visualize our training images by getting a batch of images from the training generator, and then plotting a few of them using `matplotlib`.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"3f0Z7NZgVrWQ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"sample_training_images, _ = next(train_data_gen) \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"49weMt5YVrWT\"\n      },\n      \"source\": [\n        \"The `next` function returns a batch from the dataset. One batch is a tuple of (*many images*, *many labels*). For right now, we're discarding the labels because we just want to look at the images.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"JMt2RES_VrWU\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# This function will plot images in the form of a grid with 1 row and 5 columns where images are placed in each column.\\n\",\n        \"def plotImages(images_arr):\\n\",\n        \"    fig, axes = plt.subplots(1, 5, figsize=(20,20))\\n\",\n        \"    axes = axes.flatten()\\n\",\n        \"    for img, ax in zip(images_arr, axes):\\n\",\n        \"        ax.imshow(img)\\n\",\n        \"    plt.tight_layout()\\n\",\n        \"    plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"d_VVg_gEVrWW\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plotImages(sample_training_images[:5])  # Plot images 0-4\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"b5Ej-HLGVrWZ\"\n      },\n      \"source\": [\n        \"# Model Creation\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"wEgW4i18VrWZ\"\n      },\n      \"source\": [\n        \"## Define the model\\n\",\n        \"\\n\",\n        \"The model consists of four convolution blocks with a max pool layer in each of them. Then we have a fully connected layer with 512 units, with a `relu` activation function. The model will output class probabilities for two classes — dogs and cats — using `softmax`. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"F15-uwLPVrWa\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = tf.keras.models.Sequential([\\n\",\n        \"    tf.keras.layers.Conv2D(32, (3,3), activation='relu', input_shape=(150, 150, 3)),\\n\",\n        \"    tf.keras.layers.MaxPooling2D(2, 2),\\n\",\n        \"\\n\",\n        \"    tf.keras.layers.Conv2D(64, (3,3), activation='relu'),\\n\",\n        \"    tf.keras.layers.MaxPooling2D(2,2),\\n\",\n        \"    \\n\",\n        \"    tf.keras.layers.Conv2D(128, (3,3), activation='relu'),\\n\",\n        \"    tf.keras.layers.MaxPooling2D(2,2),\\n\",\n        \"    \\n\",\n        \"    tf.keras.layers.Conv2D(128, (3,3), activation='relu'),\\n\",\n        \"    tf.keras.layers.MaxPooling2D(2,2),\\n\",\n        \"    \\n\",\n        \"    tf.keras.layers.Flatten(),\\n\",\n        \"    tf.keras.layers.Dense(512, activation='relu'),\\n\",\n        \"    tf.keras.layers.Dense(2)\\n\",\n        \"])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"PI5cdkMQVrWc\"\n      },\n      \"source\": [\n        \"### Compile the model\\n\",\n        \"\\n\",\n        \"As usual, we will use the `adam` optimizer. Since we output a softmax categorization, we'll use `sparse_categorical_crossentropy` as the loss function. We would also like to look at training and validation accuracy on each epoch as we train our network, so we are passing in the metrics argument.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"6Mg7_TXOVrWd\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model.compile(optimizer='adam',\\n\",\n        \"              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\\n\",\n        \"              metrics=['accuracy'])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"2YmQZ3TAVrWg\"\n      },\n      \"source\": [\n        \"### Model Summary\\n\",\n        \"\\n\",\n        \"Let's look at all the layers of our network using **summary** method.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Vtny8hmBVrWh\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model.summary()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"N06iqE8VVrWj\"\n      },\n      \"source\": [\n        \"### Train the model\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"oub9RtoFVrWk\"\n      },\n      \"source\": [\n        \"It's time we train our network.\\n\",\n        \"\\n\",\n        \"Since our batches are coming from a generator (`ImageDataGenerator`), we'll use `fit_generator` instead of `fit`.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"KSF2HqhDVrWk\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"EPOCHS = 100\\n\",\n        \"history = model.fit(\\n\",\n        \"    train_data_gen,\\n\",\n        \"    steps_per_epoch=int(np.ceil(total_train / float(BATCH_SIZE))),\\n\",\n        \"    epochs=EPOCHS,\\n\",\n        \"    validation_data=val_data_gen,\\n\",\n        \"    validation_steps=int(np.ceil(total_val / float(BATCH_SIZE)))\\n\",\n        \")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ojJNteAGVrWo\"\n      },\n      \"source\": [\n        \"### Visualizing results of the training\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"LZPYT-EmVrWo\"\n      },\n      \"source\": [\n        \"We'll now visualize the results we get after training our network.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"K6oA77ADVrWp\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"acc = history.history['accuracy']\\n\",\n        \"val_acc = history.history['val_accuracy']\\n\",\n        \"\\n\",\n        \"loss = history.history['loss']\\n\",\n        \"val_loss = history.history['val_loss']\\n\",\n        \"\\n\",\n        \"epochs_range = range(EPOCHS)\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(8, 8))\\n\",\n        \"plt.subplot(1, 2, 1)\\n\",\n        \"plt.plot(epochs_range, acc, label='Training Accuracy')\\n\",\n        \"plt.plot(epochs_range, val_acc, label='Validation Accuracy')\\n\",\n        \"plt.legend(loc='lower right')\\n\",\n        \"plt.title('Training and Validation Accuracy')\\n\",\n        \"\\n\",\n        \"plt.subplot(1, 2, 2)\\n\",\n        \"plt.plot(epochs_range, loss, label='Training Loss')\\n\",\n        \"plt.plot(epochs_range, val_loss, label='Validation Loss')\\n\",\n        \"plt.legend(loc='upper right')\\n\",\n        \"plt.title('Training and Validation Loss')\\n\",\n        \"plt.savefig('./foo.png')\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"kDnr50l2VrWu\"\n      },\n      \"source\": [\n        \"As we can see from the plots, training accuracy and validation accuracy are off by large margin and our model has achieved only around **70%** accuracy on the validation set (depending on the number of epochs you trained for).\\n\",\n        \"\\n\",\n        \"This is a clear indication of overfitting. Once the training and validation curves start to diverge, our model has started to memorize the training data and is unable to perform well on the validation data.\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l05c01_dogs_vs_cats_without_augmentation.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l05c02_dogs_vs_cats_with_augmentation.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"TBFXQGKYUc4X\"\n      },\n      \"source\": [\n        \"##### Copyright 2019 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"1z4xy2gTUc4a\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"FE7KNzPPVrVV\"\n      },\n      \"source\": [\n        \"# Dogs vs Cats Image Classification With Image Augmentation\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"KwQtSOz0VrVX\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l05c02_dogs_vs_cats_with_augmentation.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l05c02_dogs_vs_cats_with_augmentation.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"gN7G9GFmVrVY\"\n      },\n      \"source\": [\n        \"In this tutorial, we will discuss how to classify images into pictures of cats or pictures of dogs. We'll build an image classifier using `tf.keras.Sequential` model and load data using `tf.keras.preprocessing.image.ImageDataGenerator`.\\n\",\n        \"\\n\",\n        \"## Specific concepts that will be covered:\\n\",\n        \"In the process, we will build practical experience and develop intuition around the following concepts\\n\",\n        \"\\n\",\n        \"* Building _data input pipelines_ using the `tf.keras.preprocessing.image.ImageDataGenerator` class — How can we efficiently work with data on disk to interface with our model?\\n\",\n        \"* _Overfitting_ - what is it, how to identify it, and how can we prevent it?\\n\",\n        \"* _Data Augmentation_ and _Dropout_ - Key techniques to fight overfitting in computer vision tasks that we will incorporate into our data pipeline and image classifier model.\\n\",\n        \"\\n\",\n        \"## We will follow the general machine learning workflow:\\n\",\n        \"\\n\",\n        \"1. Examine and understand data\\n\",\n        \"2. Build an input pipeline\\n\",\n        \"3. Build our model\\n\",\n        \"4. Train our model\\n\",\n        \"5. Test our model\\n\",\n        \"6. Improve our model/Repeat the process\\n\",\n        \"\\n\",\n        \"<hr>\\n\",\n        \"\\n\",\n        \"**Before you begin**\\n\",\n        \"\\n\",\n        \"Before running the code in this notebook, reset the runtime by going to **Runtime -> Reset all runtimes** in the menu above. If you have been working through several notebooks, this will help you avoid reaching Colab's memory limits.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"zF9uvbXNVrVY\"\n      },\n      \"source\": [\n        \"# Importing packages\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"VddxeYBEVrVZ\"\n      },\n      \"source\": [\n        \"Let's start by importing required packages:\\n\",\n        \"\\n\",\n        \"*   os — to read files and directory structure\\n\",\n        \"*   numpy — for some matrix math outside of TensorFlow\\n\",\n        \"*   matplotlib.pyplot — to plot the graph and display images in our training and validation data\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"in3OdvpUG_9_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow as tf\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"L1WtoaOHVrVh\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"from tensorflow.keras.preprocessing.image import ImageDataGenerator\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"ede3_kbeHOjR\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import os\\n\",\n        \"import numpy as np\\n\",\n        \"import matplotlib.pyplot as plt\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"UZZI6lNkVrVm\"\n      },\n      \"source\": [\n        \"# Data Loading\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"DPHx8-t-VrVo\"\n      },\n      \"source\": [\n        \"To build our image classifier, we begin by downloading the dataset. The dataset we are using is a filtered version of <a href=\\\"https://www.kaggle.com/c/dogs-vs-cats/data\\\" target=\\\"_blank\\\">Dogs vs. Cats</a> dataset from Kaggle (ultimately, this dataset is provided by Microsoft Research).\\n\",\n        \"\\n\",\n        \"In previous Colabs, we've used <a href=\\\"https://www.tensorflow.org/datasets\\\" target=\\\"_blank\\\">TensorFlow Datasets</a>, which is a very easy and convenient way to use datasets. In this Colab however, we will make use of the class `tf.keras.preprocessing.image.ImageDataGenerator` which will read data from disk. We therefore need to directly download *Dogs vs. Cats* from a URL and unzip it to the Colab filesystem.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"OYmOylPlVrVt\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"_URL = 'https://download.mlcc.google.com/mledu-datasets/cats_and_dogs_filtered.zip'\\n\",\n        \"\\n\",\n        \"zip_dir = tf.keras.utils.get_file('cats_and_dogs_filterted.zip', origin=_URL, extract=True)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Giv0wMQzVrVw\"\n      },\n      \"source\": [\n        \"The dataset we have downloaded has following directory structure.\\n\",\n        \"\\n\",\n        \"<pre style=\\\"font-size: 10.0pt; font-family: Arial; line-height: 2; letter-spacing: 1.0pt;\\\" >\\n\",\n        \"<b>cats_and_dogs_filtered</b>\\n\",\n        \"|__ <b>train</b>\\n\",\n        \"    |______ <b>cats</b>: [cat.0.jpg, cat.1.jpg, cat.2.jpg ....]\\n\",\n        \"    |______ <b>dogs</b>: [dog.0.jpg, dog.1.jpg, dog.2.jpg ...]\\n\",\n        \"|__ <b>validation</b>\\n\",\n        \"    |______ <b>cats</b>: [cat.2000.jpg, cat.2001.jpg, cat.2002.jpg ....]\\n\",\n        \"    |______ <b>dogs</b>: [dog.2000.jpg, dog.2001.jpg, dog.2002.jpg ...]\\n\",\n        \"</pre>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"VpmywIlsVrVx\"\n      },\n      \"source\": [\n        \"We'll now assign variables with the proper file path for the training and validation sets.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"sRucI3QqVrVy\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"base_dir = os.path.join(os.path.dirname(zip_dir), 'cats_and_dogs_filtered')\\n\",\n        \"train_dir = os.path.join(base_dir, 'train')\\n\",\n        \"validation_dir = os.path.join(base_dir, 'validation')\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Utv3nryxVrV0\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"train_cats_dir = os.path.join(train_dir, 'cats')  # directory with our training cat pictures\\n\",\n        \"train_dogs_dir = os.path.join(train_dir, 'dogs')  # directory with our training dog pictures\\n\",\n        \"validation_cats_dir = os.path.join(validation_dir, 'cats')  # directory with our validation cat pictures\\n\",\n        \"validation_dogs_dir = os.path.join(validation_dir, 'dogs')  # directory with our validation dog pictures\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ZdrHHTy2VrV3\"\n      },\n      \"source\": [\n        \"### Understanding our data\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"LblUYjl-VrV3\"\n      },\n      \"source\": [\n        \"Let's look at how many cats and dogs images we have in our training and validation directory\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"vc4u8e9hVrV4\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"num_cats_tr = len(os.listdir(train_cats_dir))\\n\",\n        \"num_dogs_tr = len(os.listdir(train_dogs_dir))\\n\",\n        \"\\n\",\n        \"num_cats_val = len(os.listdir(validation_cats_dir))\\n\",\n        \"num_dogs_val = len(os.listdir(validation_dogs_dir))\\n\",\n        \"\\n\",\n        \"total_train = num_cats_tr + num_dogs_tr\\n\",\n        \"total_val = num_cats_val + num_dogs_val\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"g4GGzGt0VrV7\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print('total training cat images:', num_cats_tr)\\n\",\n        \"print('total training dog images:', num_dogs_tr)\\n\",\n        \"\\n\",\n        \"print('total validation cat images:', num_cats_val)\\n\",\n        \"print('total validation dog images:', num_dogs_val)\\n\",\n        \"print(\\\"--\\\")\\n\",\n        \"print(\\\"Total training images:\\\", total_train)\\n\",\n        \"print(\\\"Total validation images:\\\", total_val)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"tdsI_L-NVrV_\"\n      },\n      \"source\": [\n        \"# Setting Model Parameters\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"8Lp-0ejxOtP1\"\n      },\n      \"source\": [\n        \"For convenience, let us set up variables that will be used later while pre-processing our dataset and training our network.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"3NqNselLVrWA\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"BATCH_SIZE = 100\\n\",\n        \"IMG_SHAPE  = 150 # Our training data consists of images with width of 150 pixels and height of 150 pixels\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"RLciCR_FVrWH\"\n      },\n      \"source\": [\n        \"After defining our generators for training and validation images, **flow_from_directory** method will load images from the disk and will apply rescaling and will resize them into required dimensions using single line of code.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"UOoVpxFwVrWy\"\n      },\n      \"source\": [\n        \"# Data Augmentation\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Wn_QLciWVrWy\"\n      },\n      \"source\": [\n        \"Overfitting often occurs when we have a small number of training examples. One way to fix this problem is to augment our dataset so that it has sufficient number and variety of training examples. Data augmentation takes the approach of generating more training data from existing training samples, by augmenting the samples through random transformations that yield believable-looking images. The goal is that at training time, your model will never see the exact same picture twice. This exposes the model to more aspects of the data, allowing it to generalize better.\\n\",\n        \"\\n\",\n        \"In **tf.keras** we can implement this using the same **ImageDataGenerator** class we used before. We can simply pass different transformations we would want to our dataset as a form of arguments and it will take care of applying it to the dataset during our training process.\\n\",\n        \"\\n\",\n        \"To start off, let's define a function that can display an image, so we can see the type of augmentation that has been performed. Then, we'll look at specific augmentations that we'll use during training.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"GBYLOFgOXPJ9\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# This function will plot images in the form of a grid with 1 row and 5 columns where images are placed in each column.\\n\",\n        \"def plotImages(images_arr):\\n\",\n        \"    fig, axes = plt.subplots(1, 5, figsize=(20,20))\\n\",\n        \"    axes = axes.flatten()\\n\",\n        \"    for img, ax in zip(images_arr, axes):\\n\",\n        \"        ax.imshow(img)\\n\",\n        \"    plt.tight_layout()\\n\",\n        \"    plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"rlVj6VqaVrW0\"\n      },\n      \"source\": [\n        \"### Flipping the image horizontally\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"xcdvx4TVVrW1\"\n      },\n      \"source\": [\n        \"We can begin by randomly applying horizontal flip augmentation to our dataset and seeing how individual images will look after the transformation. This is achieved by passing `horizontal_flip=True` as an argument to the `ImageDataGenerator` class.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Bi1_vHyBVrW2\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"image_gen = ImageDataGenerator(rescale=1./255, horizontal_flip=True)\\n\",\n        \"\\n\",\n        \"train_data_gen = image_gen.flow_from_directory(batch_size=BATCH_SIZE,\\n\",\n        \"                                               directory=train_dir,\\n\",\n        \"                                               shuffle=True,\\n\",\n        \"                                               target_size=(IMG_SHAPE,IMG_SHAPE))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"zJpRSxJ-VrW7\"\n      },\n      \"source\": [\n        \"To see the transformation in action, let's take one sample image from our training set and repeat it five times. The augmentation will be randomly applied (or not) to each repetition.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"RrKGd_jjVrW7\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"augmented_images = [train_data_gen[0][0][0] for i in range(5)]\\n\",\n        \"plotImages(augmented_images)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"i7n9xcqCVrXB\"\n      },\n      \"source\": [\n        \"### Rotating the image\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"qXnwkzFuVrXB\"\n      },\n      \"source\": [\n        \"The rotation augmentation will randomly rotate the image up to a specified number of degrees. Here, we'll set it to 45.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"1zip35pDVrXB\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"image_gen = ImageDataGenerator(rescale=1./255, rotation_range=45)\\n\",\n        \"\\n\",\n        \"train_data_gen = image_gen.flow_from_directory(batch_size=BATCH_SIZE,\\n\",\n        \"                                               directory=train_dir,\\n\",\n        \"                                               shuffle=True,\\n\",\n        \"                                               target_size=(IMG_SHAPE, IMG_SHAPE))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"deaqZLsfcZ15\"\n      },\n      \"source\": [\n        \"To see the transformation in action, let's once again take a sample image from our training set and repeat it. The augmentation will be randomly applied (or not) to each repetition.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"kVoWh4OIVrXD\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"augmented_images = [train_data_gen[0][0][0] for i in range(5)]\\n\",\n        \"plotImages(augmented_images)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"FOqGPL76VrXM\"\n      },\n      \"source\": [\n        \"### Applying Zoom\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"NvqXaD8BVrXN\"\n      },\n      \"source\": [\n        \"We can also apply Zoom augmentation to our dataset, zooming images up to 50% randomly.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"tGNKLa_YVrXR\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"image_gen = ImageDataGenerator(rescale=1./255, zoom_range=0.5)\\n\",\n        \"\\n\",\n        \"train_data_gen = image_gen.flow_from_directory(batch_size=BATCH_SIZE,\\n\",\n        \"                                               directory=train_dir,\\n\",\n        \"                                               shuffle=True,\\n\",\n        \"                                               target_size=(IMG_SHAPE, IMG_SHAPE))\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"WgPWieSZcctO\"\n      },\n      \"source\": [\n        \"One more time, take a sample image from our training set and repeat it. The augmentation will be randomly applied (or not) to each repetition.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"VOvTs32FVrXU\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"augmented_images = [train_data_gen[0][0][0] for i in range(5)]\\n\",\n        \"plotImages(augmented_images)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"usS13KCNVrXd\"\n      },\n      \"source\": [\n        \"### Putting it all together\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"OC8fIsalVrXd\"\n      },\n      \"source\": [\n        \"We can apply all these augmentations, and even others, with just one line of code, by passing the augmentations as arguments with proper values.\\n\",\n        \"\\n\",\n        \"Here, we have applied rescale, rotation of 45 degrees, width shift, height shift, horizontal flip, and zoom augmentation to our training images.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"gnr2xujaVrXe\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"image_gen_train = ImageDataGenerator(\\n\",\n        \"      rescale=1./255,\\n\",\n        \"      rotation_range=40,\\n\",\n        \"      width_shift_range=0.2,\\n\",\n        \"      height_shift_range=0.2,\\n\",\n        \"      shear_range=0.2,\\n\",\n        \"      zoom_range=0.2,\\n\",\n        \"      horizontal_flip=True,\\n\",\n        \"      fill_mode='nearest')\\n\",\n        \"\\n\",\n        \"train_data_gen = image_gen_train.flow_from_directory(batch_size=BATCH_SIZE,\\n\",\n        \"                                                     directory=train_dir,\\n\",\n        \"                                                     shuffle=True,\\n\",\n        \"                                                     target_size=(IMG_SHAPE,IMG_SHAPE),\\n\",\n        \"                                                     class_mode='binary')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"AW-pV5awVrXl\"\n      },\n      \"source\": [\n        \"Let's visualize how a single image would look like five different times, when we pass these augmentations randomly to our dataset. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"z2m68eMhVrXm\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"augmented_images = [train_data_gen[0][0][0] for i in range(5)]\\n\",\n        \"plotImages(augmented_images)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"J8cUd7FXVrXq\"\n      },\n      \"source\": [\n        \"### Creating Validation Data generator\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"a99fDBt7VrXr\"\n      },\n      \"source\": [\n        \"Generally, we only apply data augmentation to our training examples, since the original images should be representative of what our model needs to manage. So, in this case we are only rescaling our validation images and converting them into batches using ImageDataGenerator.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"54x0aNbKVrXr\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"image_gen_val = ImageDataGenerator(rescale=1./255)\\n\",\n        \"\\n\",\n        \"val_data_gen = image_gen_val.flow_from_directory(batch_size=BATCH_SIZE,\\n\",\n        \"                                                 directory=validation_dir,\\n\",\n        \"                                                 target_size=(IMG_SHAPE, IMG_SHAPE),\\n\",\n        \"                                                 class_mode='binary')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"b5Ej-HLGVrWZ\"\n      },\n      \"source\": [\n        \"# Model Creation\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"wEgW4i18VrWZ\"\n      },\n      \"source\": [\n        \"## Define the model\\n\",\n        \"\\n\",\n        \"The model consists of four convolution blocks with a max pool layer in each of them.\\n\",\n        \"\\n\",\n        \"Before the final Dense layers, we're also applying a Dropout probability of 0.5. It means that 50% of the values coming into the Dropout layer will be set to zero. This helps to prevent overfitting.\\n\",\n        \"\\n\",\n        \"Then we have a fully connected layer with 512 units, with a `relu` activation function. The model will output class probabilities for two classes — dogs and cats — using `softmax`. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"Evjf8jZk2zi-\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = tf.keras.models.Sequential([\\n\",\n        \"    tf.keras.layers.Conv2D(32, (3,3), activation='relu', input_shape=(150, 150, 3)),\\n\",\n        \"    tf.keras.layers.MaxPooling2D(2, 2),\\n\",\n        \"\\n\",\n        \"    tf.keras.layers.Conv2D(64, (3,3), activation='relu'),\\n\",\n        \"    tf.keras.layers.MaxPooling2D(2,2),\\n\",\n        \"\\n\",\n        \"    tf.keras.layers.Conv2D(128, (3,3), activation='relu'),\\n\",\n        \"    tf.keras.layers.MaxPooling2D(2,2),\\n\",\n        \"\\n\",\n        \"    tf.keras.layers.Conv2D(128, (3,3), activation='relu'),\\n\",\n        \"    tf.keras.layers.MaxPooling2D(2,2),\\n\",\n        \"\\n\",\n        \"    tf.keras.layers.Dropout(0.5),\\n\",\n        \"    tf.keras.layers.Flatten(),\\n\",\n        \"    tf.keras.layers.Dense(512, activation='relu'),\\n\",\n        \"    tf.keras.layers.Dense(2)\\n\",\n        \"])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"DADWLqMSJcH3\"\n      },\n      \"source\": [\n        \"### Compiling the model\\n\",\n        \"\\n\",\n        \"As usual, we will use the `adam` optimizer. Since we output a softmax categorization, we'll use `sparse_categorical_crossentropy` as the loss function. We would also like to look at training and validation accuracy on each epoch as we train our network, so we are passing in the metrics argument.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"08rRJ0sn3Tb1\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model.compile(optimizer='adam',\\n\",\n        \"              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\\n\",\n        \"              metrics=['accuracy'])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"uurnCp_H4Hj9\"\n      },\n      \"source\": [\n        \"### Model Summary\\n\",\n        \"\\n\",\n        \"Let's look at all the layers of our network using **summary** method.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"b66qAJF_4Jnw\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model.summary()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"N06iqE8VVrWj\"\n      },\n      \"source\": [\n        \"### Train the model\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"oub9RtoFVrWk\"\n      },\n      \"source\": [\n        \"It's time we train our network.\\n\",\n        \"\\n\",\n        \"Since our batches are coming from a generator (`ImageDataGenerator`), we'll use `fit_generator` instead of `fit`.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"tk5NT1PW3j_P\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"epochs=100\\n\",\n        \"history = model.fit_generator(\\n\",\n        \"    train_data_gen,\\n\",\n        \"    steps_per_epoch=int(np.ceil(total_train / float(BATCH_SIZE))),\\n\",\n        \"    epochs=epochs,\\n\",\n        \"    validation_data=val_data_gen,\\n\",\n        \"    validation_steps=int(np.ceil(total_val / float(BATCH_SIZE)))\\n\",\n        \")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ojJNteAGVrWo\"\n      },\n      \"source\": [\n        \"### Visualizing results of the training\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"LZPYT-EmVrWo\"\n      },\n      \"source\": [\n        \"We'll now visualize the results we get after training our network.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"8CfngybnFHQR\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"acc = history.history['accuracy']\\n\",\n        \"val_acc = history.history['val_accuracy']\\n\",\n        \"\\n\",\n        \"loss = history.history['loss']\\n\",\n        \"val_loss = history.history['val_loss']\\n\",\n        \"\\n\",\n        \"epochs_range = range(epochs)\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(8, 8))\\n\",\n        \"plt.subplot(1, 2, 1)\\n\",\n        \"plt.plot(epochs_range, acc, label='Training Accuracy')\\n\",\n        \"plt.plot(epochs_range, val_acc, label='Validation Accuracy')\\n\",\n        \"plt.legend(loc='lower right')\\n\",\n        \"plt.title('Training and Validation Accuracy')\\n\",\n        \"\\n\",\n        \"plt.subplot(1, 2, 2)\\n\",\n        \"plt.plot(epochs_range, loss, label='Training Loss')\\n\",\n        \"plt.plot(epochs_range, val_loss, label='Validation Loss')\\n\",\n        \"plt.legend(loc='upper right')\\n\",\n        \"plt.title('Training and Validation Loss')\\n\",\n        \"plt.show()\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l05c02_dogs_vs_cats_with_augmentation.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l05c03_exercise_flowers_with_data_augmentation.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"TBFXQGKYUc4X\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"1z4xy2gTUc4a\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"FE7KNzPPVrVV\"\n      },\n      \"source\": [\n        \"# Image Classification using tf.keras\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"KwQtSOz0VrVX\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l05c03_exercise_flowers_with_data_augmentation.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l05c03_exercise_flowers_with_data_augmentation.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"gN7G9GFmVrVY\"\n      },\n      \"source\": [\n        \"In this Colab you will classify images of flowers. You will build an image classifier using `tf.keras.Sequential` model and load data using `tf.keras.preprocessing.image.ImageDataGenerator`.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"zF9uvbXNVrVY\"\n      },\n      \"source\": [\n        \"# Importing Packages\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"VddxeYBEVrVZ\"\n      },\n      \"source\": [\n        \"Let's start by importing required packages. **os** package is used to read files and directory structure, **numpy** is used to convert python list to numpy array and to perform required matrix operations and **matplotlib.pyplot** is used to plot the graph and display images in our training and validation data.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"rtPGh2MAVrVa\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import os\\n\",\n        \"import numpy as np\\n\",\n        \"import glob\\n\",\n        \"import shutil\\n\",\n        \"\\n\",\n        \"import tensorflow as tf\\n\",\n        \"\\n\",\n        \"import matplotlib.pyplot as plt\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Jlchl4x2VrVg\"\n      },\n      \"source\": [\n        \"### TODO: Import TensorFlow and Keras Layers\\n\",\n        \"\\n\",\n        \"In the cell below, import Tensorflow as `tf` and the Keras layers and models you will use to build your CNN. Also, import the `ImageDataGenerator` from Keras so that you can perform image augmentation.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"L1WtoaOHVrVh\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#import packages\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"UZZI6lNkVrVm\"\n      },\n      \"source\": [\n        \"# Data Loading\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"DPHx8-t-VrVo\"\n      },\n      \"source\": [\n        \"In order to build our image classifier, we can begin by downloading the flowers dataset. We first need to download the archive version of the dataset and after the download we are storing it to \\\"/tmp/\\\" directory.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"_lPjfOmNVrVs\"\n      },\n      \"source\": [\n        \"After downloading the dataset, we need to extract its contents.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"OYmOylPlVrVt\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"_URL = \\\"https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz\\\"\\n\",\n        \"\\n\",\n        \"zip_file = tf.keras.utils.get_file(origin=_URL,\\n\",\n        \"                                   fname=\\\"flower_photos.tgz\\\",\\n\",\n        \"                                   extract=True)\\n\",\n        \"\\n\",\n        \"base_dir = os.path.join(os.path.dirname(zip_file), 'flower_photos')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"2yge5MKnnjMd\"\n      },\n      \"source\": [\n        \"The dataset we downloaded contains images of 5 types of flowers:\\n\",\n        \"\\n\",\n        \"1. Rose\\n\",\n        \"2. Daisy\\n\",\n        \"3. Dandelion\\n\",\n        \"4. Sunflowers\\n\",\n        \"5. Tulips\\n\",\n        \"\\n\",\n        \"So, let's create the labels for these 5 classes: \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"FiYVs1MEmNHf\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"classes = ['roses', 'daisy', 'dandelion', 'sunflowers', 'tulips']\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"G1ymuCPS0_eu\"\n      },\n      \"source\": [\n        \"Also, the dataset we have downloaded has following directory structure.\\n\",\n        \"\\n\",\n        \"<pre style=\\\"font-size: 10.0pt; font-family: Arial; line-height: 2; letter-spacing: 1.0pt;\\\" >\\n\",\n        \"<b>flower_photos</b>\\n\",\n        \"|__ <b>daisy</b>\\n\",\n        \"|__ <b>dandelion</b>\\n\",\n        \"|__ <b>roses</b>\\n\",\n        \"|__ <b>sunflowers</b>\\n\",\n        \"|__ <b>tulips</b>\\n\",\n        \"</pre>\\n\",\n        \"\\n\",\n        \"As you can see there are no folders containing training and validation data. Therefore, we will have to create our own training and validation set. Let's write some code that will do this.\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"The code below creates a `train` and a `val` folder each containing 5 folders (one for each type of flower). It then moves the images from the original folders to these new folders such that 80% of the images go to the training set and 20% of the images go into the validation set. In the end our directory will have the following structure:\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"<pre style=\\\"font-size: 10.0pt; font-family: Arial; line-height: 2; letter-spacing: 1.0pt;\\\" >\\n\",\n        \"<b>flower_photos</b>\\n\",\n        \"|__ <b>daisy</b>\\n\",\n        \"|__ <b>dandelion</b>\\n\",\n        \"|__ <b>roses</b>\\n\",\n        \"|__ <b>sunflowers</b>\\n\",\n        \"|__ <b>tulips</b>\\n\",\n        \"|__ <b>train</b>\\n\",\n        \"    |______ <b>daisy</b>: [1.jpg, 2.jpg, 3.jpg ....]\\n\",\n        \"    |______ <b>dandelion</b>: [1.jpg, 2.jpg, 3.jpg ....]\\n\",\n        \"    |______ <b>roses</b>: [1.jpg, 2.jpg, 3.jpg ....]\\n\",\n        \"    |______ <b>sunflowers</b>: [1.jpg, 2.jpg, 3.jpg ....]\\n\",\n        \"    |______ <b>tulips</b>: [1.jpg, 2.jpg, 3.jpg ....]\\n\",\n        \" |__ <b>val</b>\\n\",\n        \"    |______ <b>daisy</b>: [507.jpg, 508.jpg, 509.jpg ....]\\n\",\n        \"    |______ <b>dandelion</b>: [719.jpg, 720.jpg, 721.jpg ....]\\n\",\n        \"    |______ <b>roses</b>: [514.jpg, 515.jpg, 516.jpg ....]\\n\",\n        \"    |______ <b>sunflowers</b>: [560.jpg, 561.jpg, 562.jpg .....]\\n\",\n        \"    |______ <b>tulips</b>: [640.jpg, 641.jpg, 642.jpg ....]\\n\",\n        \"</pre>\\n\",\n        \"\\n\",\n        \"Since we don't delete the original folders, they will still be in our `flower_photos` directory, but they will be empty. The code below also prints the total number of flower images we have for each type of flower. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"a-AL030LmcdD\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"for cl in classes:\\n\",\n        \"  img_path = os.path.join(base_dir, cl)\\n\",\n        \"  images = glob.glob(img_path + '/*.jpg')\\n\",\n        \"  print(\\\"{}: {} Images\\\".format(cl, len(images)))\\n\",\n        \"  train, val = images[:round(len(images)*0.8)], images[round(len(images)*0.8):]\\n\",\n        \"\\n\",\n        \"  for t in train:\\n\",\n        \"    if not os.path.exists(os.path.join(base_dir, 'train', cl)):\\n\",\n        \"      os.makedirs(os.path.join(base_dir, 'train', cl))\\n\",\n        \"    shutil.move(t, os.path.join(base_dir, 'train', cl))\\n\",\n        \"\\n\",\n        \"  for v in val:\\n\",\n        \"    if not os.path.exists(os.path.join(base_dir, 'val', cl)):\\n\",\n        \"      os.makedirs(os.path.join(base_dir, 'val', cl))\\n\",\n        \"    shutil.move(v, os.path.join(base_dir, 'val', cl))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"8Lp-0ejxOtP1\"\n      },\n      \"source\": [\n        \"For convenience, let us set up the path for the training and validation sets\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"uh68rmWspp0U\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"train_dir = os.path.join(base_dir, 'train')\\n\",\n        \"val_dir = os.path.join(base_dir, 'val')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"UOoVpxFwVrWy\"\n      },\n      \"source\": [\n        \"# Data Augmentation\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Wn_QLciWVrWy\"\n      },\n      \"source\": [\n        \"Overfitting generally occurs when we have small number of training examples. One way to fix this problem is to augment our dataset so that it has sufficient number of training examples. Data augmentation takes the approach of generating more training data from existing training samples, by augmenting the samples via a number of random transformations that yield believable-looking images. The goal is that at training time, your model will never see the exact same picture twice. This helps expose the model to more aspects of the data and generalize better.\\n\",\n        \"\\n\",\n        \"In **tf.keras** we can implement this using the same **ImageDataGenerator** class we used before. We can simply pass different transformations we would want to our dataset as a form of arguments and it will take care of applying it to the dataset during our training process. \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"2uJ1G030VrWz\"\n      },\n      \"source\": [\n        \"## Experiment with Various Image Transformations\\n\",\n        \"\\n\",\n        \"In this section you will get some practice doing some basic image transformations. Before we begin making transformations let's define our `batch_size` and our image size. Remember that the input to our CNN are images of the same size. We therefore have to resize the images in our dataset to the same size.\\n\",\n        \"\\n\",\n        \"### TODO: Set Batch and Image Size\\n\",\n        \"\\n\",\n        \"In the cell below, create a `batch_size` of 100 images and set a value to `IMG_SHAPE` such that our training data consists of images with width of 150 pixels and height of 150 pixels.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"QyPkET61yMMX\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"batch_size =\\n\",\n        \"IMG_SHAPE = \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"rlVj6VqaVrW0\"\n      },\n      \"source\": [\n        \"### TODO: Apply Random Horizontal Flip\\n\",\n        \"\\n\",\n        \"In the cell below, use ImageDataGenerator to create a transformation that rescales the images by 255 and then applies a random horizontal flip. Then use the `.flow_from_directory` method to apply the above transformation to the images in our training set. Make sure you indicate the batch size, the path to the directory of the training images, the target size for the images, and to shuffle the images. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Bi1_vHyBVrW2\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"image_gen = \\n\",\n        \"\\n\",\n        \"train_data_gen = \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"zJpRSxJ-VrW7\"\n      },\n      \"source\": [\n        \"Let's take 1 sample image from our training examples and repeat it 5 times so that the augmentation can be applied to the same image 5 times over randomly, to see the augmentation in action.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"jqb9OGoVKIOi\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# This function will plot images in the form of a grid with 1 row and 5 columns where images are placed in each column.\\n\",\n        \"def plotImages(images_arr):\\n\",\n        \"    fig, axes = plt.subplots(1, 5, figsize=(20,20))\\n\",\n        \"    axes = axes.flatten()\\n\",\n        \"    for img, ax in zip( images_arr, axes):\\n\",\n        \"        ax.imshow(img)\\n\",\n        \"    plt.tight_layout()\\n\",\n        \"    plt.show()\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"augmented_images = [train_data_gen[0][0][0] for i in range(5)]\\n\",\n        \"plotImages(augmented_images)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"qXnwkzFuVrXB\"\n      },\n      \"source\": [\n        \"### TODO: Apply Random Rotation\\n\",\n        \"\\n\",\n        \"In the cell below, use ImageDataGenerator to create a transformation that rescales the images by 255 and then applies a random 45 degree rotation. Then use the `.flow_from_directory` method to apply the above transformation to the images in our training set. Make sure you indicate the batch size, the path to the directory of the training images, the target size for the images, and to shuffle the images. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"1zip35pDVrXB\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"image_gen = \\n\",\n        \"\\n\",\n        \"train_data_gen = \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"m2lNPWFc3Bre\"\n      },\n      \"source\": [\n        \"Let's take 1 sample image from our training examples and repeat it 5 times so that the augmentation can be applied to the same image 5 times over randomly, to see the augmentation in action.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"wmBx8NhrVrXK\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"augmented_images = [train_data_gen[0][0][0] for i in range(5)]\\n\",\n        \"plotImages(augmented_images)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"NvqXaD8BVrXN\"\n      },\n      \"source\": [\n        \"### TODO: Apply Random Zoom\\n\",\n        \"\\n\",\n        \"In the cell below, use ImageDataGenerator to create a transformation that rescales the images by 255 and then applies a random zoom of up to 50%. Then use the `.flow_from_directory` method to apply the above transformation to the images in our training set. Make sure you indicate the batch size, the path to the directory of the training images, the target size for the images, and to shuffle the images. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"tGNKLa_YVrXR\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"image_gen = \\n\",\n        \"\\n\",\n        \"train_data_gen = \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"XuoAX0Za4zTk\"\n      },\n      \"source\": [\n        \"Let's take 1 sample image from our training examples and repeat it 5 times so that the augmentation can be applied to the same image 5 times over randomly, to see the augmentation in action.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"-KQWw8IZVrXZ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"augmented_images = [train_data_gen[0][0][0] for i in range(5)]\\n\",\n        \"plotImages(augmented_images)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"OC8fIsalVrXd\"\n      },\n      \"source\": [\n        \"### TODO: Put It All Together\\n\",\n        \"\\n\",\n        \"In the cell below, use ImageDataGenerator to create a transformation that rescales the images by 255 and that applies:\\n\",\n        \"\\n\",\n        \"- random 45 degree rotation\\n\",\n        \"- random zoom of up to 50%\\n\",\n        \"- random horizontal flip\\n\",\n        \"- width shift of 0.15\\n\",\n        \"- height shift of 0.15\\n\",\n        \"\\n\",\n        \"Then use the `.flow_from_directory` method to apply the above transformation to the images in our training set. Make sure you indicate the batch size, the path to the directory of the training images, the target size for the images, to shuffle the images, and to set the class mode to `sparse`.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"gnr2xujaVrXe\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"image_gen_train = \\n\",\n        \"\\n\",\n        \"\\n\",\n        \"train_data_gen = \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"AW-pV5awVrXl\"\n      },\n      \"source\": [\n        \"Let's visualize how a single image would look like 5 different times, when we pass these augmentations randomly to our dataset. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"z2m68eMhVrXm\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"augmented_images = [train_data_gen[0][0][0] for i in range(5)]\\n\",\n        \"plotImages(augmented_images)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"a99fDBt7VrXr\"\n      },\n      \"source\": [\n        \"### TODO: Create a Data Generator for the Validation Set\\n\",\n        \"\\n\",\n        \"Generally, we only apply data augmentation to our training examples. So, in the cell below, use ImageDataGenerator to create a transformation that only rescales the images by 255. Then use the `.flow_from_directory` method to apply the above transformation to the images in our validation set. Make sure you indicate the batch size, the path to the directory of the validation images, the target size for the images, and to set the class mode to `sparse`. Remember that it is not necessary to shuffle the images in the validation set. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"54x0aNbKVrXr\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"image_gen_val = \\n\",\n        \"val_data_gen = \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"wEgW4i18VrWZ\"\n      },\n      \"source\": [\n        \"# TODO: Create the CNN\\n\",\n        \"\\n\",\n        \"In the cell below, create a convolutional neural network that consists of 3 convolution blocks. Each convolutional block contains a `Conv2D` layer followed by a max pool layer. The first convolutional block should have 16 filters, the second one should have 32 filters, and the third one should have 64 filters. All convolutional filters should be 3 x 3. All max pool layers should have a `pool_size` of `(2, 2)`.\\n\",\n        \"\\n\",\n        \"After the 3 convolutional blocks you should have a flatten layer followed by a fully connected layer with 512 units. The CNN should output class probabilities based on 5 classes which is done by the **softmax** activation function. All other layers should use a **relu** activation function. You should also add Dropout layers with a probability of 20%, where appropriate. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Evjf8jZk2zi-\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"DADWLqMSJcH3\"\n      },\n      \"source\": [\n        \"# TODO: Compile the Model\\n\",\n        \"\\n\",\n        \"In the cell below, compile your model using the ADAM optimizer, the sparse cross entropy function as a loss function. We would also like to look at training and validation accuracy on each epoch as we train our network, so make sure you also pass the metrics argument.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"08rRJ0sn3Tb1\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Compile the model\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"oub9RtoFVrWk\"\n      },\n      \"source\": [\n        \"# TODO: Train the Model\\n\",\n        \"\\n\",\n        \"In the cell below, train your model using the **fit_generator** function instead of the usual **fit** function. We have to use the `fit_generator` function because we are using the **ImageDataGenerator** class to generate batches of training and validation data for our model. Train the model for 80 epochs and make sure you use the proper parameters in the `fit_generator` function.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"tk5NT1PW3j_P\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"epochs = \\n\",\n        \"\\n\",\n        \"history = \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"LZPYT-EmVrWo\"\n      },\n      \"source\": [\n        \"# TODO: Plot Training and Validation Graphs.\\n\",\n        \"\\n\",\n        \"In the cell below, plot the training and validation accuracy/loss graphs.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"8CfngybnFHQR\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"acc = \\n\",\n        \"val_acc = \\n\",\n        \"\\n\",\n        \"loss = \\n\",\n        \"val_loss = \\n\",\n        \"\\n\",\n        \"epochs_range = \\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"E9FROGm-KGEG\"\n      },\n      \"source\": [\n        \"# TODO: Experiment with Different Parameters\\n\",\n        \"\\n\",\n        \"So far you've created a CNN with 3 convolutional layers and followed by a fully connected layer with 512 units. In the cells below create a new CNN with a different architecture. Feel free to experiment by changing as many parameters as you like. For example, you can add more convolutional layers, or more fully connected layers. You can also experiment with different filter sizes in your convolutional layers, different number of units in your fully connected layers, different dropout rates, etc... You can also experiment by performing image augmentation with more image transformations that we have seen so far. Take a look at the [ImageDataGenerator Documentation](https://keras.io/preprocessing/image/) to see a full list of all the available image transformations. For example, you can add shear transformations, or you can vary the brightness of the images, etc... Experiment as much as you can and compare the accuracy of your various models. Which parameters give you the best result?\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l05c03_exercise_flowers_with_data_augmentation.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l05c04_exercise_flowers_with_data_augmentation_solution.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"TBFXQGKYUc4X\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"1z4xy2gTUc4a\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"FE7KNzPPVrVV\"\n      },\n      \"source\": [\n        \"# Image Classification using tf.keras\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"KwQtSOz0VrVX\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l05c04_exercise_flowers_with_data_augmentation_solution.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l05c04_exercise_flowers_with_data_augmentation_solution.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"gN7G9GFmVrVY\"\n      },\n      \"source\": [\n        \"In this Colab you will classify images of flowers. You will build an image classifier using `tf.keras.Sequential` model and load data using `tf.keras.preprocessing.image.ImageDataGenerator`.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"zF9uvbXNVrVY\"\n      },\n      \"source\": [\n        \"# Importing Packages\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"VddxeYBEVrVZ\"\n      },\n      \"source\": [\n        \"Let's start by importing required packages. **os** package is used to read files and directory structure, **numpy** is used to convert python list to numpy array and to perform required matrix operations and **matplotlib.pyplot** is used to plot the graph and display images in our training and validation data.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"rtPGh2MAVrVa\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import os\\n\",\n        \"import numpy as np\\n\",\n        \"import glob\\n\",\n        \"import shutil\\n\",\n        \"import matplotlib.pyplot as plt\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Jlchl4x2VrVg\"\n      },\n      \"source\": [\n        \"### TODO: Import TensorFlow and Keras Layers\\n\",\n        \"\\n\",\n        \"In the cell below, import Tensorflow and the Keras layers and models you will use to build your CNN. Also, import the `ImageDataGenerator` from Keras so that you can perform image augmentation.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"KzKpWdemHbC_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow as tf\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"L1WtoaOHVrVh\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"from tensorflow.keras.models import Sequential\\n\",\n        \"from tensorflow.keras.layers import Dense, Conv2D, Flatten, Dropout, MaxPooling2D\\n\",\n        \"from tensorflow.keras.preprocessing.image import ImageDataGenerator\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"UZZI6lNkVrVm\"\n      },\n      \"source\": [\n        \"# Data Loading\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"DPHx8-t-VrVo\"\n      },\n      \"source\": [\n        \"In order to build our image classifier, we can begin by downloading the flowers dataset. We first need to download the archive version of the dataset and after the download we are storing it to \\\"/tmp/\\\" directory.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"_lPjfOmNVrVs\"\n      },\n      \"source\": [\n        \"After downloading the dataset, we need to extract its contents.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"OYmOylPlVrVt\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"_URL = \\\"https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz\\\"\\n\",\n        \"\\n\",\n        \"zip_file = tf.keras.utils.get_file(origin=_URL,\\n\",\n        \"                                   fname=\\\"flower_photos.tgz\\\",\\n\",\n        \"                                   extract=True)\\n\",\n        \"\\n\",\n        \"base_dir = os.path.join(os.path.dirname(zip_file), 'flower_photos')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"2yge5MKnnjMd\"\n      },\n      \"source\": [\n        \"The dataset we downloaded contains images of 5 types of flowers:\\n\",\n        \"\\n\",\n        \"1. Rose\\n\",\n        \"2. Daisy\\n\",\n        \"3. Dandelion\\n\",\n        \"4. Sunflowers\\n\",\n        \"5. Tulips\\n\",\n        \"\\n\",\n        \"So, let's create the labels for these 5 classes: \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"FiYVs1MEmNHf\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"classes = ['roses', 'daisy', 'dandelion', 'sunflowers', 'tulips']\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"G1ymuCPS0_eu\"\n      },\n      \"source\": [\n        \"Also, the dataset we have downloaded has following directory structure. n\\n\",\n        \"<pre style=\\\"font-size: 10.0pt; font-family: Arial; line-height: 2; letter-spacing: 1.0pt;\\\" >\\n\",\n        \"<b>flower_photos</b>\\n\",\n        \"|__ <b>daisy</b>\\n\",\n        \"|__ <b>dandelion</b>\\n\",\n        \"|__ <b>roses</b>\\n\",\n        \"|__ <b>sunflowers</b>\\n\",\n        \"|__ <b>tulips</b>\\n\",\n        \"</pre>\\n\",\n        \"\\n\",\n        \"As you can see there are no folders containing training and validation data. Therefore, we will have to create our own training and validation set. Let's write some code that will do this.\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"The code below creates a `train` and a `val` folder each containing 5 folders (one for each type of flower). It then moves the images from the original folders to these new folders such that 80% of the images go to the training set and 20% of the images go into the validation set. In the end our directory will have the following structure:\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"<pre style=\\\"font-size: 10.0pt; font-family: Arial; line-height: 2; letter-spacing: 1.0pt;\\\" >\\n\",\n        \"<b>flower_photos</b>\\n\",\n        \"|__ <b>daisy</b>\\n\",\n        \"|__ <b>dandelion</b>\\n\",\n        \"|__ <b>roses</b>\\n\",\n        \"|__ <b>sunflowers</b>\\n\",\n        \"|__ <b>tulips</b>\\n\",\n        \"|__ <b>train</b>\\n\",\n        \"    |______ <b>daisy</b>: [1.jpg, 2.jpg, 3.jpg ....]\\n\",\n        \"    |______ <b>dandelion</b>: [1.jpg, 2.jpg, 3.jpg ....]\\n\",\n        \"    |______ <b>roses</b>: [1.jpg, 2.jpg, 3.jpg ....]\\n\",\n        \"    |______ <b>sunflowers</b>: [1.jpg, 2.jpg, 3.jpg ....]\\n\",\n        \"    |______ <b>tulips</b>: [1.jpg, 2.jpg, 3.jpg ....]\\n\",\n        \" |__ <b>val</b>\\n\",\n        \"    |______ <b>daisy</b>: [507.jpg, 508.jpg, 509.jpg ....]\\n\",\n        \"    |______ <b>dandelion</b>: [719.jpg, 720.jpg, 721.jpg ....]\\n\",\n        \"    |______ <b>roses</b>: [514.jpg, 515.jpg, 516.jpg ....]\\n\",\n        \"    |______ <b>sunflowers</b>: [560.jpg, 561.jpg, 562.jpg .....]\\n\",\n        \"    |______ <b>tulips</b>: [640.jpg, 641.jpg, 642.jpg ....]\\n\",\n        \"</pre>\\n\",\n        \"\\n\",\n        \"Since we don't delete the original folders, they will still be in our `flower_photos` directory, but they will be empty. The code below also prints the total number of flower images we have for each type of flower. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"a-AL030LmcdD\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"for cl in classes:\\n\",\n        \"  img_path = os.path.join(base_dir, cl)\\n\",\n        \"  images = glob.glob(img_path + '/*.jpg')\\n\",\n        \"  print(\\\"{}: {} Images\\\".format(cl, len(images)))\\n\",\n        \"  num_train = int(round(len(images)*0.8))\\n\",\n        \"  train, val = images[:num_train], images[num_train:]\\n\",\n        \"\\n\",\n        \"  for t in train:\\n\",\n        \"    if not os.path.exists(os.path.join(base_dir, 'train', cl)):\\n\",\n        \"      os.makedirs(os.path.join(base_dir, 'train', cl))\\n\",\n        \"    shutil.move(t, os.path.join(base_dir, 'train', cl))\\n\",\n        \"\\n\",\n        \"  for v in val:\\n\",\n        \"    if not os.path.exists(os.path.join(base_dir, 'val', cl)):\\n\",\n        \"      os.makedirs(os.path.join(base_dir, 'val', cl))\\n\",\n        \"    shutil.move(v, os.path.join(base_dir, 'val', cl))\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"yP85YhYol8ER\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"round(len(images)*0.8)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"8Lp-0ejxOtP1\"\n      },\n      \"source\": [\n        \"For convenience, let us set up the path for the training and validation sets\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"uh68rmWspp0U\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"train_dir = os.path.join(base_dir, 'train')\\n\",\n        \"val_dir = os.path.join(base_dir, 'val')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"UOoVpxFwVrWy\"\n      },\n      \"source\": [\n        \"# Data Augmentation\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Wn_QLciWVrWy\"\n      },\n      \"source\": [\n        \"Overfitting generally occurs when we have small number of training examples. One way to fix this problem is to augment our dataset so that it has sufficient number of training examples. Data augmentation takes the approach of generating more training data from existing training samples, by augmenting the samples via a number of random transformations that yield believable-looking images. The goal is that at training time, your model will never see the exact same picture twice. This helps expose the model to more aspects of the data and generalize better.\\n\",\n        \"\\n\",\n        \"In **tf.keras** we can implement this using the same **ImageDataGenerator** class we used before. We can simply pass different transformations we would want to our dataset as a form of arguments and it will take care of applying it to the dataset during our training process. \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"2uJ1G030VrWz\"\n      },\n      \"source\": [\n        \"## Experiment with Various Image Transformations\\n\",\n        \"\\n\",\n        \"In this section you will get some practice doing some basic image transformations. Before we begin making transformations let's define our `batch_size` and our image size. Remember that the input to our CNN are images of the same size. We therefore have to resize the images in our dataset to the same size.\\n\",\n        \"\\n\",\n        \"### TODO: Set Batch and Image Size\\n\",\n        \"\\n\",\n        \"In the cell below, create a `batch_size` of 100 images and set a value to `IMG_SHAPE` such that our training data consists of images with width of 150 pixels and height of 150 pixels.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"QyPkET61yMMX\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"batch_size = 100\\n\",\n        \"IMG_SHAPE = 150 \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"rlVj6VqaVrW0\"\n      },\n      \"source\": [\n        \"### TODO: Apply Random Horizontal Flip\\n\",\n        \"\\n\",\n        \"In the cell below, use ImageDataGenerator to create a transformation that rescales the images by 255 and then applies a random horizontal flip. Then use the `.flow_from_directory` method to apply the above transformation to the images in our training set. Make sure you indicate the batch size, the path to the directory of the training images, the target size for the images, and to shuffle the images. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Bi1_vHyBVrW2\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"image_gen = ImageDataGenerator(rescale=1./255, horizontal_flip=True)\\n\",\n        \"\\n\",\n        \"train_data_gen = image_gen.flow_from_directory(\\n\",\n        \"                                                batch_size=batch_size,\\n\",\n        \"                                                directory=train_dir,\\n\",\n        \"                                                shuffle=True,\\n\",\n        \"                                                target_size=(IMG_SHAPE,IMG_SHAPE)\\n\",\n        \"                                                )\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"zJpRSxJ-VrW7\"\n      },\n      \"source\": [\n        \"Let's take 1 sample image from our training examples and repeat it 5 times so that the augmentation can be applied to the same image 5 times over randomly, to see the augmentation in action.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"jqb9OGoVKIOi\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# This function will plot images in the form of a grid with 1 row and 5 columns where images are placed in each column.\\n\",\n        \"def plotImages(images_arr):\\n\",\n        \"    fig, axes = plt.subplots(1, 5, figsize=(20,20))\\n\",\n        \"    axes = axes.flatten()\\n\",\n        \"    for img, ax in zip( images_arr, axes):\\n\",\n        \"        ax.imshow(img)\\n\",\n        \"    plt.tight_layout()\\n\",\n        \"    plt.show()\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"augmented_images = [train_data_gen[0][0][0] for i in range(5)]\\n\",\n        \"plotImages(augmented_images)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"qXnwkzFuVrXB\"\n      },\n      \"source\": [\n        \"### TODO: Apply Random Rotation\\n\",\n        \"\\n\",\n        \"In the cell below, use ImageDataGenerator to create a transformation that rescales the images by 255 and then applies a random 45 degree rotation. Then use the `.flow_from_directory` method to apply the above transformation to the images in our training set. Make sure you indicate the batch size, the path to the directory of the training images, the target size for the images, and to shuffle the images. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"1zip35pDVrXB\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"image_gen = ImageDataGenerator(rescale=1./255, rotation_range=45)\\n\",\n        \"\\n\",\n        \"train_data_gen = image_gen.flow_from_directory(batch_size=batch_size,\\n\",\n        \"                                               directory=train_dir,\\n\",\n        \"                                               shuffle=True,\\n\",\n        \"                                               target_size=(IMG_SHAPE, IMG_SHAPE))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"m2lNPWFc3Bre\"\n      },\n      \"source\": [\n        \"Let's take 1 sample image from our training examples and repeat it 5 times so that the augmentation can be applied to the same image 5 times over randomly, to see the augmentation in action.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"wmBx8NhrVrXK\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"augmented_images = [train_data_gen[0][0][0] for i in range(5)]\\n\",\n        \"plotImages(augmented_images)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"NvqXaD8BVrXN\"\n      },\n      \"source\": [\n        \"### TODO: Apply Random Zoom\\n\",\n        \"\\n\",\n        \"In the cell below, use ImageDataGenerator to create a transformation that rescales the images by 255 and then applies a random zoom of up to 50%. Then use the `.flow_from_directory` method to apply the above transformation to the images in our training set. Make sure you indicate the batch size, the path to the directory of the training images, the target size for the images, and to shuffle the images. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"tGNKLa_YVrXR\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"image_gen = ImageDataGenerator(rescale=1./255, zoom_range=0.5)\\n\",\n        \"\\n\",\n        \"train_data_gen = image_gen.flow_from_directory(\\n\",\n        \"                                                batch_size=batch_size,\\n\",\n        \"                                                directory=train_dir,\\n\",\n        \"                                                shuffle=True,\\n\",\n        \"                                                target_size=(IMG_SHAPE, IMG_SHAPE)\\n\",\n        \"                                                )\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"XuoAX0Za4zTk\"\n      },\n      \"source\": [\n        \"Let's take 1 sample image from our training examples and repeat it 5 times so that the augmentation can be applied to the same image 5 times over randomly, to see the augmentation in action.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"-KQWw8IZVrXZ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"augmented_images = [train_data_gen[0][0][0] for i in range(5)]\\n\",\n        \"plotImages(augmented_images)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"OC8fIsalVrXd\"\n      },\n      \"source\": [\n        \"### TODO: Put It All Together\\n\",\n        \"\\n\",\n        \"In the cell below, use ImageDataGenerator to create a transformation that rescales the images by 255 and that applies:\\n\",\n        \"\\n\",\n        \"- random 45 degree rotation\\n\",\n        \"- random zoom of up to 50%\\n\",\n        \"- random horizontal flip\\n\",\n        \"- width shift of 0.15\\n\",\n        \"- height shift of 0.15\\n\",\n        \"\\n\",\n        \"Then use the `.flow_from_directory` method to apply the above transformation to the images in our training set. Make sure you indicate the batch size, the path to the directory of the training images, the target size for the images, to shuffle the images, and to set the class mode to `sparse`.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"gnr2xujaVrXe\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"image_gen_train = ImageDataGenerator(\\n\",\n        \"                    rescale=1./255,\\n\",\n        \"                    rotation_range=45,\\n\",\n        \"                    width_shift_range=.15,\\n\",\n        \"                    height_shift_range=.15,\\n\",\n        \"                    horizontal_flip=True,\\n\",\n        \"                    zoom_range=0.5\\n\",\n        \"                    )\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"train_data_gen = image_gen_train.flow_from_directory(\\n\",\n        \"                                                batch_size=batch_size,\\n\",\n        \"                                                directory=train_dir,\\n\",\n        \"                                                shuffle=True,\\n\",\n        \"                                                target_size=(IMG_SHAPE,IMG_SHAPE),\\n\",\n        \"                                                class_mode='sparse'\\n\",\n        \"                                                )\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"AW-pV5awVrXl\"\n      },\n      \"source\": [\n        \"Let's visualize how a single image would look like 5 different times, when we pass these augmentations randomly to our dataset. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"z2m68eMhVrXm\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"augmented_images = [train_data_gen[0][0][0] for i in range(5)]\\n\",\n        \"plotImages(augmented_images)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"a99fDBt7VrXr\"\n      },\n      \"source\": [\n        \"### TODO: Create a Data Generator for the Validation Set\\n\",\n        \"\\n\",\n        \"Generally, we only apply data augmentation to our training examples. So, in the cell below, use ImageDataGenerator to create a transformation that only rescales the images by 255. Then use the `.flow_from_directory` method to apply the above transformation to the images in our validation set. Make sure you indicate the batch size, the path to the directory of the validation images, the target size for the images, and to set the class mode to `sparse`. Remember that it is not necessary to shuffle the images in the validation set. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"54x0aNbKVrXr\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"image_gen_val = ImageDataGenerator(rescale=1./255)\\n\",\n        \"\\n\",\n        \"val_data_gen = image_gen_val.flow_from_directory(batch_size=batch_size,\\n\",\n        \"                                                 directory=val_dir,\\n\",\n        \"                                                 target_size=(IMG_SHAPE, IMG_SHAPE),\\n\",\n        \"                                                 class_mode='sparse')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"wEgW4i18VrWZ\"\n      },\n      \"source\": [\n        \"# TODO: Create the CNN\\n\",\n        \"\\n\",\n        \"In the cell below, create a convolutional neural network that consists of 3 convolution blocks. Each convolutional block contains a `Conv2D` layer followed by a max pool layer. The first convolutional block should have 16 filters, the second one should have 32 filters, and the third one should have 64 filters. All convolutional filters should be 3 x 3. All max pool layers should have a `pool_size` of `(2, 2)`.\\n\",\n        \"\\n\",\n        \"After the 3 convolutional blocks you should have a flatten layer followed by a fully connected layer with 512 units. The CNN should output class probabilities based on 5 classes which is done by the **softmax** activation function. All other layers should use a **relu** activation function. You should also add Dropout layers with a probability of 20%, where appropriate.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Evjf8jZk2zi-\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = Sequential()\\n\",\n        \"\\n\",\n        \"model.add(Conv2D(16, 3, padding='same', activation='relu', input_shape=(IMG_SHAPE,IMG_SHAPE, 3)))\\n\",\n        \"model.add(MaxPooling2D(pool_size=(2, 2)))\\n\",\n        \"\\n\",\n        \"model.add(Conv2D(32, 3, padding='same', activation='relu'))\\n\",\n        \"model.add(MaxPooling2D(pool_size=(2, 2)))\\n\",\n        \"\\n\",\n        \"model.add(Conv2D(64, 3, padding='same', activation='relu'))\\n\",\n        \"model.add(MaxPooling2D(pool_size=(2, 2)))\\n\",\n        \"\\n\",\n        \"model.add(Flatten())\\n\",\n        \"model.add(Dropout(0.2))\\n\",\n        \"model.add(Dense(512, activation='relu'))\\n\",\n        \"\\n\",\n        \"model.add(Dropout(0.2))\\n\",\n        \"model.add(Dense(5))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"DADWLqMSJcH3\"\n      },\n      \"source\": [\n        \"# TODO: Compile the Model\\n\",\n        \"\\n\",\n        \"In the cell below, compile your model using the ADAM optimizer, the sparse cross entropy function as a loss function. We would also like to look at training and validation accuracy on each epoch as we train our network, so make sure you also pass the metrics argument.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"08rRJ0sn3Tb1\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model.compile(optimizer='adam',\\n\",\n        \"              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\\n\",\n        \"              metrics=['accuracy'])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"oub9RtoFVrWk\"\n      },\n      \"source\": [\n        \"# TODO: Train the Model\\n\",\n        \"\\n\",\n        \"In the cell below, train your model using the **fit_generator** function instead of the usual **fit** function. We have to use the `fit_generator` function because we are using the **ImageDataGenerator** class to generate batches of training and validation data for our model. Train the model for 80 epochs and make sure you use the proper parameters in the `fit_generator` function.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"tk5NT1PW3j_P\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"epochs = 80\\n\",\n        \"\\n\",\n        \"history = model.fit_generator(\\n\",\n        \"    train_data_gen,\\n\",\n        \"    steps_per_epoch=int(np.ceil(train_data_gen.n / float(batch_size))),\\n\",\n        \"    epochs=epochs,\\n\",\n        \"    validation_data=val_data_gen,\\n\",\n        \"    validation_steps=int(np.ceil(val_data_gen.n / float(batch_size)))\\n\",\n        \")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"LZPYT-EmVrWo\"\n      },\n      \"source\": [\n        \"# TODO: Plot Training and Validation Graphs.\\n\",\n        \"\\n\",\n        \"In the cell below, plot the training and validation accuracy/loss graphs.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"8CfngybnFHQR\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"acc = history.history['accuracy']\\n\",\n        \"val_acc = history.history['val_accuracy']\\n\",\n        \"\\n\",\n        \"loss = history.history['loss']\\n\",\n        \"val_loss = history.history['val_loss']\\n\",\n        \"\\n\",\n        \"epochs_range = range(epochs)\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(8, 8))\\n\",\n        \"plt.subplot(1, 2, 1)\\n\",\n        \"plt.plot(epochs_range, acc, label='Training Accuracy')\\n\",\n        \"plt.plot(epochs_range, val_acc, label='Validation Accuracy')\\n\",\n        \"plt.legend(loc='lower right')\\n\",\n        \"plt.title('Training and Validation Accuracy')\\n\",\n        \"\\n\",\n        \"plt.subplot(1, 2, 2)\\n\",\n        \"plt.plot(epochs_range, loss, label='Training Loss')\\n\",\n        \"plt.plot(epochs_range, val_loss, label='Validation Loss')\\n\",\n        \"plt.legend(loc='upper right')\\n\",\n        \"plt.title('Training and Validation Loss')\\n\",\n        \"plt.show()\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l05c04_exercise_flowers_with_data_augmentation_solution.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l06c01_tensorflow_hub_and_transfer_learning.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"W_tvPdyfA-BL\"\n      },\n      \"source\": [\n        \"##### Copyright 2019 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"0O_LFhwSBCjm\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"9-3Pry4jh1-E\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l06c01_tensorflow_hub_and_transfer_learning.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l06c01_tensorflow_hub_and_transfer_learning.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"NxjpzKTvg_dd\"\n      },\n      \"source\": [\n        \"# TensorFlow Hub and Transfer Learning\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"crU-iluJIEzw\"\n      },\n      \"source\": [\n        \"[TensorFlow Hub](http://tensorflow.org/hub) is an online repository of already trained TensorFlow models that you can use.\\n\",\n        \"These models can either be used as is, or they can be used for Transfer Learning.\\n\",\n        \"\\n\",\n        \"Transfer learning is a process where you take an existing trained model, and extend it to do additional work. This involves leaving the bulk of the model unchanged, while adding and retraining the final layers, in order to get a different set of possible outputs.\\n\",\n        \"\\n\",\n        \"In this Colab we will do both.\\n\",\n        \"\\n\",\n        \"Here, you can see all the models available in [TensorFlow Module Hub](https://tfhub.dev/).\\n\",\n        \"\\n\",\n        \"## Concepts that will be covered in this Colab\\n\",\n        \"\\n\",\n        \"1. Use a TensorFlow Hub model for prediction.\\n\",\n        \"2. Use a TensorFlow Hub model for Dogs vs. Cats dataset.\\n\",\n        \"3. Do simple transfer learning with TensorFlow Hub.\\n\",\n        \"\\n\",\n        \"Before starting this Colab, you should reset the Colab environment by selecting `Runtime -> Reset all runtimes...` from menu above.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"7RVsYZLEpEWs\"\n      },\n      \"source\": [\n        \"# Imports\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ZUCEcRdhnyWn\"\n      },\n      \"source\": [\n        \"Some normal imports we've seen before. The new one is importing tensorflow_hub which was installed above, and which this Colab will make heavy use of.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"8z5hqr0hHtLv\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow as tf\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"WZnAHGETHu7e\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import matplotlib.pylab as plt\\n\",\n        \"\\n\",\n        \"import tensorflow_hub as hub\\n\",\n        \"import tensorflow_datasets as tfds\\n\",\n        \"\\n\",\n        \"from tensorflow.keras import layers\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"FVM2fKGEHIJN\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import logging\\n\",\n        \"logger = tf.get_logger()\\n\",\n        \"logger.setLevel(logging.ERROR)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"s4YuF5HvpM1W\"\n      },\n      \"source\": [\n        \"# Part 1: Use a TensorFlow Hub MobileNet for prediction\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"4Sh2sPc10V0b\"\n      },\n      \"source\": [\n        \"In this part of the Colab, we'll take a trained model, load it into to Keras, and try it out.\\n\",\n        \"\\n\",\n        \"The model that we'll use is MobileNet v2 (but any model from [tf2 compatible image classifier URL from tfhub.dev](https://tfhub.dev/s?q=tf2&module-type=image-classification) would work).\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"xEY_Ow5loN6q\"\n      },\n      \"source\": [\n        \"## Download the classifier\\n\",\n        \"\\n\",\n        \"Download the MobileNet model and create a Keras model from it.\\n\",\n        \"MobileNet is expecting images of 224 $\\\\times$ 224 pixels, in 3 color channels (RGB).\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"y_6bGjoPtzau\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"CLASSIFIER_URL =\\\"https://tfhub.dev/google/tf2-preview/mobilenet_v2/classification/2\\\"\\n\",\n        \"IMAGE_RES = 224\\n\",\n        \"\\n\",\n        \"model = tf.keras.Sequential([\\n\",\n        \"    hub.KerasLayer(CLASSIFIER_URL, input_shape=(IMAGE_RES, IMAGE_RES, 3))\\n\",\n        \"])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"pwZXaoV0uXp2\"\n      },\n      \"source\": [\n        \"## Run it on a single image\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"TQItP1i55-di\"\n      },\n      \"source\": [\n        \"MobileNet has been trained on the ImageNet dataset. ImageNet has 1000 different output classes, and one of them is military uniforms.\\n\",\n        \"Let's get an image containing a military uniform that is not part of ImageNet, and see if our model can predict that it is a military uniform.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"w5wDjXNjuXGD\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import numpy as np\\n\",\n        \"import PIL.Image as Image\\n\",\n        \"\\n\",\n        \"grace_hopper = tf.keras.utils.get_file('image.jpg','https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg')\\n\",\n        \"grace_hopper = Image.open(grace_hopper).resize((IMAGE_RES, IMAGE_RES))\\n\",\n        \"grace_hopper \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"BEmmBnGbLxPp\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"grace_hopper = np.array(grace_hopper)/255.0\\n\",\n        \"grace_hopper.shape\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"0Ic8OEEo2b73\"\n      },\n      \"source\": [\n        \"Remember, models always want a batch of images to process. So here, we add a batch dimension, and pass the image to the model for prediction.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"EMquyn29v8q3\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"result = model.predict(grace_hopper[np.newaxis, ...])\\n\",\n        \"result.shape\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"NKzjqENF6jDF\"\n      },\n      \"source\": [\n        \"The result is a 1001 element vector of logits, rating the probability of each class for the image.\\n\",\n        \"\\n\",\n        \"So the top class ID can be found with argmax. But how can we know what class this actually is and in particular if that class ID in the ImageNet dataset denotes a military uniform or something else?\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"rgXb44vt6goJ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"predicted_class = np.argmax(result[0], axis=-1)\\n\",\n        \"predicted_class\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"YrxLMajMoxkf\"\n      },\n      \"source\": [\n        \"## Decode the predictions\\n\",\n        \"\\n\",\n        \"To see what our predicted_class is in the ImageNet dataset, download the ImageNet labels and fetch the row that the model predicted.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"ij6SrDxcxzry\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"labels_path = tf.keras.utils.get_file('ImageNetLabels.txt','https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt')\\n\",\n        \"imagenet_labels = np.array(open(labels_path).read().splitlines())\\n\",\n        \"\\n\",\n        \"plt.imshow(grace_hopper)\\n\",\n        \"plt.axis('off')\\n\",\n        \"predicted_class_name = imagenet_labels[predicted_class]\\n\",\n        \"_ = plt.title(\\\"Prediction: \\\" + predicted_class_name.title())\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"a6TNYYAM4u2-\"\n      },\n      \"source\": [\n        \"Bingo. Our model correctly predicted military uniform!\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"amfzqn1Oo7Om\"\n      },\n      \"source\": [\n        \"# Part 2: Use a TensorFlow Hub models for the Cats vs. Dogs dataset\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"K-nIpVJ94xrw\"\n      },\n      \"source\": [\n        \"Now we'll use the full MobileNet model and see how it can perform on the Dogs vs. Cats dataset.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Z93vvAdGxDMD\"\n      },\n      \"source\": [\n        \"## Dataset\\n\",\n        \"\\n\",\n        \"We can use TensorFlow Datasets to load the Dogs vs Cats dataset.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"DrIUV3V0xDL_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"(train_examples, validation_examples), info = tfds.load(\\n\",\n        \"    'cats_vs_dogs', \\n\",\n        \"    with_info=True, \\n\",\n        \"    as_supervised=True, \\n\",\n        \"    split=['train[:80%]', 'train[80%:]'],\\n\",\n        \")\\n\",\n        \"\\n\",\n        \"num_examples = info.splits['train'].num_examples\\n\",\n        \"num_classes = info.features['label'].num_classes\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"UlFZ_hwjCLgS\"\n      },\n      \"source\": [\n        \"The images in the Dogs vs. Cats dataset are not all the same size.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"W4lDPkn2cpWZ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"for i, example_image in enumerate(train_examples.take(3)):\\n\",\n        \"  print(\\\"Image {} shape: {}\\\".format(i+1, example_image[0].shape))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"mbgpD3E6gM2P\"\n      },\n      \"source\": [\n        \"So we need to reformat all images to the resolution expected by MobileNet (224, 224).\\n\",\n        \"\\n\",\n        \"The `.repeat()` and `steps_per_epoch` here is not required, but saves ~15s per epoch, since the shuffle-buffer only has to cold-start once.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"we_ftzQxNf7e\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def format_image(image, label):\\n\",\n        \"  image = tf.image.resize(image, (IMAGE_RES, IMAGE_RES))/255.0\\n\",\n        \"  return image, label\\n\",\n        \"\\n\",\n        \"BATCH_SIZE = 32\\n\",\n        \"\\n\",\n        \"train_batches      = train_examples.shuffle(num_examples//4).map(format_image).batch(BATCH_SIZE).prefetch(1)\\n\",\n        \"validation_batches = validation_examples.map(format_image).batch(BATCH_SIZE).prefetch(1)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"0gTN7M_GxDLx\"\n      },\n      \"source\": [\n        \"## Run the classifier on a batch of images\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"O3fvrZR8xDLv\"\n      },\n      \"source\": [\n        \"Remember our `model` object is still the full MobileNet model trained on ImageNet, so it has 1000 possible output classes.\\n\",\n        \"ImageNet has a lot of dogs and cats in it, so let's see if it can predict the images in our Dogs vs. Cats dataset.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"kii_jWZYOn0B\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"image_batch, label_batch = next(iter(train_batches.take(1)))\\n\",\n        \"image_batch = image_batch.numpy()\\n\",\n        \"label_batch = label_batch.numpy()\\n\",\n        \"\\n\",\n        \"result_batch = model.predict(image_batch)\\n\",\n        \"\\n\",\n        \"predicted_class_names = imagenet_labels[np.argmax(result_batch, axis=-1)]\\n\",\n        \"predicted_class_names\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"QmvSWg9nxDLa\"\n      },\n      \"source\": [\n        \"The labels seem to match names of Dogs and Cats. Let's now plot the images from our Dogs vs Cats dataset and put the ImageNet labels next to them.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"IXTB22SpxDLP\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.figure(figsize=(10,9))\\n\",\n        \"for n in range(30):\\n\",\n        \"  plt.subplot(6,5,n+1)\\n\",\n        \"  plt.subplots_adjust(hspace = 0.3)\\n\",\n        \"  plt.imshow(image_batch[n])\\n\",\n        \"  plt.title(predicted_class_names[n])\\n\",\n        \"  plt.axis('off')\\n\",\n        \"_ = plt.suptitle(\\\"ImageNet predictions\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"JzV457OXreQP\"\n      },\n      \"source\": [\n        \"# Part 3: Do simple transfer learning with TensorFlow Hub\\n\",\n        \"\\n\",\n        \"Let's now use TensorFlow Hub to do Transfer Learning.\\n\",\n        \"\\n\",\n        \"With transfer learning we reuse parts of an already trained model and change the final layer, or several layers, of the model, and then retrain those layers on our own dataset.\\n\",\n        \"\\n\",\n        \"In addition to complete models, TensorFlow Hub also distributes models without the last classification layer. These can be used to easily do transfer learning. We will continue using MobileNet v2 because in later parts of this course, we will take this model and deploy it on a mobile device using [TensorFlow Lite](https://www.tensorflow.org/lite). Any [image feature vector URL from tfhub.dev](https://tfhub.dev/s?module-type=image-feature-vector&q=tf2) would work here.\\n\",\n        \"\\n\",\n        \"We'll also continue to use the Dogs vs Cats dataset, so we will be able to compare the performance of this model against the ones we created from scratch earlier.\\n\",\n        \"\\n\",\n        \"Note that we're calling the partial model from TensorFlow Hub (without the final classification layer) a `feature_extractor`. The reasoning for this term is that it will take the input all the way to a layer containing a number of features. So it has done the bulk of the work in identifying the content of an image, except for creating the final probability distribution. That is, it has extracted the features of the image.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"5wB030nezBwI\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"URL = \\\"https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/2\\\"\\n\",\n        \"feature_extractor = hub.KerasLayer(URL,\\n\",\n        \"                                   input_shape=(IMAGE_RES, IMAGE_RES,3))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"pkSvAPvKOWg2\"\n      },\n      \"source\": [\n        \"Let's run a batch of images through this, and see the final shape. 32 is the number of images, and 1280 is the number of neurons in the last layer of the partial model from TensorFlow Hub.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Of7i-35F09ls\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"feature_batch = feature_extractor(image_batch)\\n\",\n        \"print(feature_batch.shape)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"CtFmF7A5E4tk\"\n      },\n      \"source\": [\n        \"Freeze the variables in the feature extractor layer, so that the training only modifies the final classifier layer.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Jg5ar6rcE4H-\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"feature_extractor.trainable = False\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"RPVeouTksO9q\"\n      },\n      \"source\": [\n        \"## Attach a classification head\\n\",\n        \"\\n\",\n        \"Now wrap the hub layer in a `tf.keras.Sequential` model, and add a new classification layer.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"mGcY27fY1q3Q\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = tf.keras.Sequential([\\n\",\n        \"  feature_extractor,\\n\",\n        \"  layers.Dense(2)\\n\",\n        \"])\\n\",\n        \"\\n\",\n        \"model.summary()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"OHbXQqIquFxQ\"\n      },\n      \"source\": [\n        \"## Train the model\\n\",\n        \"\\n\",\n        \"We now train this model like any other, by first calling `compile` followed by `fit`.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"3n0Wb9ylKd8R\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model.compile(\\n\",\n        \"  optimizer='adam',\\n\",\n        \"  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\\n\",\n        \"  metrics=['accuracy'])\\n\",\n        \"\\n\",\n        \"EPOCHS = 6\\n\",\n        \"history = model.fit(train_batches,\\n\",\n        \"                    epochs=EPOCHS,\\n\",\n        \"                    validation_data=validation_batches)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"76as-K8-vFQJ\"\n      },\n      \"source\": [\n        \"You can see we get ~97% validation accuracy, which is absolutely awesome. This is a huge improvement over the model we created in the previous lesson, where we were able to get ~83% accuracy. The reason for this difference is that MobileNet was carefully designed over a long time by experts, then trained on a massive dataset (ImageNet).\\n\",\n        \"\\n\",\n        \"Although not equivalent to TensorFlow Hub, you can check out how to create MobileNet in Keras [here](https://github.com/keras-team/keras-applications/blob/master/keras_applications/mobilenet.py).\\n\",\n        \"\\n\",\n        \"Let's plot the training and validation accuracy/loss graphs.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"d28dhbFpr98b\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"acc = history.history['accuracy']\\n\",\n        \"val_acc = history.history['val_accuracy']\\n\",\n        \"\\n\",\n        \"loss = history.history['loss']\\n\",\n        \"val_loss = history.history['val_loss']\\n\",\n        \"\\n\",\n        \"epochs_range = range(EPOCHS)\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(8, 8))\\n\",\n        \"plt.subplot(1, 2, 1)\\n\",\n        \"plt.plot(epochs_range, acc, label='Training Accuracy')\\n\",\n        \"plt.plot(epochs_range, val_acc, label='Validation Accuracy')\\n\",\n        \"plt.legend(loc='lower right')\\n\",\n        \"plt.title('Training and Validation Accuracy')\\n\",\n        \"\\n\",\n        \"plt.subplot(1, 2, 2)\\n\",\n        \"plt.plot(epochs_range, loss, label='Training Loss')\\n\",\n        \"plt.plot(epochs_range, val_loss, label='Validation Loss')\\n\",\n        \"plt.legend(loc='upper right')\\n\",\n        \"plt.title('Training and Validation Loss')\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"5zmoDisGvNye\"\n      },\n      \"source\": [\n        \"What is a bit curious here is that validation performance is better than training performance, right from the start to the end of execution.\\n\",\n        \"\\n\",\n        \"One reason for this is that validation performance is measured at the end of the epoch, but training performance is the average values across the epoch.\\n\",\n        \"\\n\",\n        \"The bigger reason though is that we're reusing a large part of MobileNet which is already trained on Dogs and Cats images. While doing training, the network is still performing image augmentation on the training images, but not on the validation dataset. This means the training images may be harder to classify compared to the normal images in the validation dataset.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"kb__ZN8uFn-D\"\n      },\n      \"source\": [\n        \"## Check the predictions\\n\",\n        \"\\n\",\n        \"To redo the plot from before, first get the ordered list of class names.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"W_Zvg2i0fzJu\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"class_names = np.array(info.features['label'].names)\\n\",\n        \"class_names\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"4Olg6MsNGJTL\"\n      },\n      \"source\": [\n        \"Run the image batch through the model and convert the indices to class names.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"fCLVCpEjJ_VP\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"predicted_batch = model.predict(image_batch)\\n\",\n        \"predicted_batch = tf.squeeze(predicted_batch).numpy()\\n\",\n        \"predicted_ids = np.argmax(predicted_batch, axis=-1)\\n\",\n        \"predicted_class_names = class_names[predicted_ids]\\n\",\n        \"predicted_class_names\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"CkGbZxl9GZs-\"\n      },\n      \"source\": [\n        \"Let's look at the true labels and predicted ones.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"nL9IhOmGI5dJ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"Labels: \\\", label_batch)\\n\",\n        \"print(\\\"Predicted labels: \\\", predicted_ids)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"wC_AYRJU9NQe\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.figure(figsize=(10,9))\\n\",\n        \"for n in range(30):\\n\",\n        \"  plt.subplot(6,5,n+1)\\n\",\n        \"  plt.subplots_adjust(hspace = 0.3)\\n\",\n        \"  plt.imshow(image_batch[n])\\n\",\n        \"  color = \\\"blue\\\" if predicted_ids[n] == label_batch[n] else \\\"red\\\"\\n\",\n        \"  plt.title(predicted_class_names[n].title(), color=color)\\n\",\n        \"  plt.axis('off')\\n\",\n        \"_ = plt.suptitle(\\\"Model predictions (blue: correct, red: incorrect)\\\")\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l06c01_tensorflow_hub_and_transfer_learning.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l06c02_exercise_flowers_with_transfer_learning.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"W_tvPdyfA-BL\"\n      },\n      \"source\": [\n        \"##### Copyright 2019 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"0O_LFhwSBCjm\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"9-3Pry4jh1-E\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l06c02_exercise_flowers_with_transfer_learning.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l06c02_exercise_flowers_with_transfer_learning.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"NxjpzKTvg_dd\"\n      },\n      \"source\": [\n        \"# TensorFlow Hub\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"crU-iluJIEzw\"\n      },\n      \"source\": [\n        \"[TensorFlow Hub](http://tensorflow.org/hub) is an online repository of already trained TensorFlow models that you can use.\\n\",\n        \"These models can either be used as is, or they can be used for Transfer Learning.\\n\",\n        \"\\n\",\n        \"Transfer learning is a process where you take an existing trained model, and extend it to do additional work. This involves leaving the bulk of the model unchanged, while adding and retraining the final layers, in order to get a different set of possible outputs.\\n\",\n        \"\\n\",\n        \"Here, you can see all the models available in [TensorFlow Module Hub](https://tfhub.dev/).\\n\",\n        \"\\n\",\n        \"Before starting this Colab, you should reset the Colab environment by selecting `Runtime -> Reset all runtimes...` from menu above.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"7RVsYZLEpEWs\"\n      },\n      \"source\": [\n        \"# Imports\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ZUCEcRdhnyWn\"\n      },\n      \"source\": [\n        \"Some normal imports we've seen before. The new one is importing tensorflow_hub which this Colab will make heavy use of.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"zIuDCLW_IAG_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow as tf\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"dHenfza_ICJL\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import numpy as np\\n\",\n        \"import matplotlib.pyplot as plt\\n\",\n        \"\\n\",\n        \"import tensorflow_hub as hub\\n\",\n        \"import tensorflow_datasets as tfds\\n\",\n        \"\\n\",\n        \"from tensorflow.keras import layers\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"gEsgwsqbHFn2\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import logging\\n\",\n        \"logger = tf.get_logger()\\n\",\n        \"logger.setLevel(logging.ERROR)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"amfzqn1Oo7Om\"\n      },\n      \"source\": [\n        \"# TODO: Download the Flowers Dataset using TensorFlow Datasets\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Z93vvAdGxDMD\"\n      },\n      \"source\": [\n        \"In the cell below you will download the Flowers dataset using TensorFlow Datasets. If you look at the [TensorFlow Datasets documentation](https://www.tensorflow.org/datasets/datasets#tf_flowers) you will see that the name of the Flowers dataset is `tf_flowers`. You can also see that this dataset is only split into a TRAINING set. You will therefore have to use `tfds.splits` to split this training set into to a `training_set` and a `validation_set`. Do a `[70, 30]` split such that 70 corresponds to the `training_set` and 30 to the `validation_set`. Then load the `tf_flowers` dataset using `tfds.load`. Make sure the `tfds.load` function uses the all the parameters you need, and also make sure it returns the dataset info, so we can retrieve information about the datasets.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"oXiJjX0jfx1o\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"splits = \\n\",\n        \"\\n\",\n        \"(training_set, validation_set), dataset_info = \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"X0p1sOEHf0JF\"\n      },\n      \"source\": [\n        \"# TODO: Print Information about the Flowers Dataset\\n\",\n        \"\\n\",\n        \"Now that you have downloaded the dataset, use the dataset info to print the number of classes in the dataset, and also write some code that counts how many images we have in the training and validation sets. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"DrIUV3V0xDL_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print('Total Number of Classes: {}'.format(num_classes))\\n\",\n        \"print('Total Number of Training Images: {}'.format(num_training_examples))\\n\",\n        \"print('Total Number of Validation Images: {} \\\\n'.format(num_validation_examples))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"UlFZ_hwjCLgS\"\n      },\n      \"source\": [\n        \"The images in the Flowers dataset are not all the same size.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"W4lDPkn2cpWZ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"for i, example in enumerate(training_set.take(5)):\\n\",\n        \"  print('Image {} shape: {} label: {}'.format(i+1, example[0].shape, example[1]))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"mbgpD3E6gM2P\"\n      },\n      \"source\": [\n        \"# TODO: Reformat Images and Create Batches\\n\",\n        \"\\n\",\n        \"In the cell below create a function that reformats all images to the resolution expected by MobileNet v2 (224, 224) and normalizes them. The function should take in an `image` and a `label` as arguments and should return the new `image` and corresponding `label`. Then create training and validation batches of size `32`.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"we_ftzQxNf7e\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"IMAGE_RES = \\n\",\n        \"\\n\",\n        \"def format_image(image, label):\\n\",\n        \"  \\n\",\n        \"  return image, label\\n\",\n        \"\\n\",\n        \"BATCH_SIZE = \\n\",\n        \"\\n\",\n        \"train_batches = \\n\",\n        \"\\n\",\n        \"validation_batches = \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"JzV457OXreQP\"\n      },\n      \"source\": [\n        \"# Do Simple Transfer Learning with TensorFlow Hub\\n\",\n        \"\\n\",\n        \"Let's now use TensorFlow Hub to do Transfer Learning. Remember, in transfer learning we reuse parts of an already trained model and change the final layer, or several layers, of the model, and then retrain those layers on our own dataset.\\n\",\n        \"\\n\",\n        \"### TODO: Create a Feature Extractor\\n\",\n        \"In the cell below create a `feature_extractor` using MobileNet v2. Remember that the partial model from TensorFlow Hub (without the final classification layer) is called a feature vector. Go to the [TensorFlow Hub documentation](https://tfhub.dev/s?module-type=image-feature-vector&q=tf2) to see a list of available feature vectors. Click on the `tf2-preview/mobilenet_v2/feature_vector`. Read the documentation and get the corresponding `URL` to get the MobileNet v2 feature vector. Finally, create a `feature_extractor` by using `hub.KerasLayer` with the correct `input_shape` parameter.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"5wB030nezBwI\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"URL = \\n\",\n        \"feature_extractor = \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"CtFmF7A5E4tk\"\n      },\n      \"source\": [\n        \"### TODO: Freeze the Pre-Trained Model\\n\",\n        \"\\n\",\n        \"In the cell below freeze the variables in the feature extractor layer, so that the training only modifies the final classifier layer.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Jg5ar6rcE4H-\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"feature_extractor\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"RPVeouTksO9q\"\n      },\n      \"source\": [\n        \"### TODO: Attach a classification head\\n\",\n        \"\\n\",\n        \"In the cell below create a `tf.keras.Sequential` model, and add the pre-trained model and the new classification layer. Remember that the classification layer must have the same number of classes as our Flowers dataset. Finally print a summary of the Sequential model.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"mGcY27fY1q3Q\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = \\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"OHbXQqIquFxQ\"\n      },\n      \"source\": [\n        \"### TODO: Train the model\\n\",\n        \"\\n\",\n        \"In the cell bellow train this model like any other, by first calling `compile` and then followed by `fit`. Make sure you use the proper parameters when applying both methods. Train the model for only 6 epochs.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"3n0Wb9ylKd8R\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"EPOCHS = \\n\",\n        \"\\n\",\n        \"history = \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"76as-K8-vFQJ\"\n      },\n      \"source\": [\n        \"You can see we get ~88% validation accuracy with only 6 epochs of training, which is absolutely awesome. This is a huge improvement over the model we created in the previous lesson, where we were able to get ~76% accuracy with 80 epochs of training. The reason for this difference is that MobileNet v2 was carefully designed over a long time by experts, then trained on a massive dataset (ImageNet).\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"SLxTcprUqJaq\"\n      },\n      \"source\": [\n        \"# TODO: Plot Training and Validation Graphs\\n\",\n        \"\\n\",\n        \"In the cell below, plot the training and validation accuracy/loss graphs.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"d28dhbFpr98b\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"acc = \\n\",\n        \"val_acc = \\n\",\n        \"\\n\",\n        \"loss = \\n\",\n        \"val_loss = \\n\",\n        \"\\n\",\n        \"epochs_range = \\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"5zmoDisGvNye\"\n      },\n      \"source\": [\n        \"What is a bit curious here is that validation performance is better than training performance, right from the start to the end of execution.\\n\",\n        \"\\n\",\n        \"One reason for this is that validation performance is measured at the end of the epoch, but training performance is the average values across the epoch.\\n\",\n        \"\\n\",\n        \"The bigger reason though is that we're reusing a large part of MobileNet which is already trained on Flower images. \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"kb__ZN8uFn-D\"\n      },\n      \"source\": [\n        \"# TODO: Check Predictions\\n\",\n        \"\\n\",\n        \"In the cell below get the label names from the dataset info and convert them into a NumPy array. Print the array to make sure you have the correct label names.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"W_Zvg2i0fzJu\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"class_names = \\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"4Olg6MsNGJTL\"\n      },\n      \"source\": [\n        \"### TODO: Create an Image Batch and Make Predictions\\n\",\n        \"\\n\",\n        \"In the cell below, use the `next()` function to create an `image_batch` and its corresponding `label_batch`. Convert both the `image_batch` and `label_batch` to numpy arrays using the `.numpy()` method. Then use the `.predict()` method to run the image batch through your model and make predictions. Then use the `np.argmax()` function to get the indices of the best prediction for each image. Finally convert the indices of the best predictions to class names.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"fCLVCpEjJ_VP\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"image_batch, label_batch = \\n\",\n        \"\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"predicted_batch = \\n\",\n        \"predicted_batch = tf.squeeze(predicted_batch).numpy()\\n\",\n        \"\\n\",\n        \"predicted_ids = \\n\",\n        \"predicted_class_names = \\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"CkGbZxl9GZs-\"\n      },\n      \"source\": [\n        \"### TODO: Print True Labels and Predicted Indices\\n\",\n        \"\\n\",\n        \"In the cell below, print the true labels and the indices of predicted labels.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"nL9IhOmGI5dJ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"gJDyzEfYuFcW\"\n      },\n      \"source\": [\n        \"# Plot Model Predictions\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"wC_AYRJU9NQe\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.figure(figsize=(10,9))\\n\",\n        \"for n in range(30):\\n\",\n        \"  plt.subplot(6,5,n+1)\\n\",\n        \"  plt.subplots_adjust(hspace = 0.3)\\n\",\n        \"  plt.imshow(image_batch[n])\\n\",\n        \"  color = \\\"blue\\\" if predicted_ids[n] == label_batch[n] else \\\"red\\\"\\n\",\n        \"  plt.title(predicted_class_names[n].title(), color=color)\\n\",\n        \"  plt.axis('off')\\n\",\n        \"_ = plt.suptitle(\\\"Model predictions (blue: correct, red: incorrect)\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"7QBKxS5CuKhc\"\n      },\n      \"source\": [\n        \"# TODO: Perform Transfer Learning with the Inception Model\\n\",\n        \"\\n\",\n        \"Go to the [TensorFlow Hub documentation](https://tfhub.dev/s?module-type=image-feature-vector&q=tf2) and click on `tf2-preview/inception_v3/feature_vector`. This feature vector corresponds to the Inception v3 model. In the cells below, use transfer learning to create a CNN that uses Inception v3 as the pretrained model to classify the images from the Flowers dataset. Note that Inception, takes as input, images that are 299 x 299 pixels. Compare the accuracy you get with Inception v3 to the accuracy you got with MobileNet v2.\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l06c02_exercise_flowers_with_transfer_learning.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l06c03_exercise_flowers_with_transfer_learning_solution.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"W_tvPdyfA-BL\"\n      },\n      \"source\": [\n        \"##### Copyright 2019 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"0O_LFhwSBCjm\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"9-3Pry4jh1-E\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l06c03_exercise_flowers_with_transfer_learning_solution.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l06c03_exercise_flowers_with_transfer_learning_solution.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"NxjpzKTvg_dd\"\n      },\n      \"source\": [\n        \"# TensorFlow Hub\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"crU-iluJIEzw\"\n      },\n      \"source\": [\n        \"[TensorFlow Hub](http://tensorflow.org/hub) is an online repository of already trained TensorFlow models that you can use.\\n\",\n        \"These models can either be used as is, or they can be used for Transfer Learning.\\n\",\n        \"\\n\",\n        \"Transfer learning is a process where you take an existing trained model, and extend it to do additional work. This involves leaving the bulk of the model unchanged, while adding and retraining the final layers, in order to get a different set of possible outputs.\\n\",\n        \"\\n\",\n        \"Here, you can see all the models available in [TensorFlow Module Hub](https://tfhub.dev/).\\n\",\n        \"\\n\",\n        \"Before starting this Colab, you should reset the Colab environment by selecting `Runtime -> Reset all runtimes...` from menu above.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"7RVsYZLEpEWs\"\n      },\n      \"source\": [\n        \"# Imports\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ZUCEcRdhnyWn\"\n      },\n      \"source\": [\n        \"Some normal imports we've seen before. The new one is importing tensorflow_hub which this Colab will make heavy use of.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"RxwCQNZWIL8y\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow as tf\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"ivDUkUNdINH2\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import numpy as np\\n\",\n        \"import matplotlib.pyplot as plt\\n\",\n        \"\\n\",\n        \"import tensorflow_hub as hub\\n\",\n        \"import tensorflow_datasets as tfds\\n\",\n        \"\\n\",\n        \"from tensorflow.keras import layers\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"NU3IAZ02G6VA\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import logging\\n\",\n        \"logger = tf.get_logger()\\n\",\n        \"logger.setLevel(logging.ERROR)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"amfzqn1Oo7Om\"\n      },\n      \"source\": [\n        \"# TODO: Download the Flowers Dataset using TensorFlow Datasets\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Z93vvAdGxDMD\"\n      },\n      \"source\": [\n        \"In the cell below you will download the Flowers dataset using TensorFlow Datasets. If you look at the [TensorFlow Datasets documentation](https://www.tensorflow.org/datasets/datasets#tf_flowers) you will see that the name of the Flowers dataset is `tf_flowers`. You can also see that this dataset is only split into a TRAINING set. You will therefore have to use `tfds.splits` to split this training set into to a `training_set` and a `validation_set`. Do a `[70, 30]` split such that 70 corresponds to the `training_set` and 30 to the `validation_set`. Then load the `tf_flowers` dataset using `tfds.load`. Make sure the `tfds.load` function uses the all the parameters you need, and also make sure it returns the dataset info, so we can retrieve information about the datasets.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"oXiJjX0jfx1o\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"(training_set, validation_set), dataset_info = tfds.load(\\n\",\n        \"    'tf_flowers',\\n\",\n        \"    split=['train[:70%]', 'train[70%:]'],\\n\",\n        \"    with_info=True,\\n\",\n        \"    as_supervised=True,\\n\",\n        \")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"X0p1sOEHf0JF\"\n      },\n      \"source\": [\n        \"# TODO: Print Information about the Flowers Dataset\\n\",\n        \"\\n\",\n        \"Now that you have downloaded the dataset, use the dataset info to print the number of classes in the dataset, and also write some code that counts how many images we have in the training and validation sets. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"DrIUV3V0xDL_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"num_classes = dataset_info.features['label'].num_classes\\n\",\n        \"\\n\",\n        \"num_training_examples = 0\\n\",\n        \"num_validation_examples = 0\\n\",\n        \"\\n\",\n        \"for example in training_set:\\n\",\n        \"  num_training_examples += 1\\n\",\n        \"\\n\",\n        \"for example in validation_set:\\n\",\n        \"  num_validation_examples += 1\\n\",\n        \"\\n\",\n        \"print('Total Number of Classes: {}'.format(num_classes))\\n\",\n        \"print('Total Number of Training Images: {}'.format(num_training_examples))\\n\",\n        \"print('Total Number of Validation Images: {} \\\\n'.format(num_validation_examples))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"UlFZ_hwjCLgS\"\n      },\n      \"source\": [\n        \"The images in the Flowers dataset are not all the same size.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"W4lDPkn2cpWZ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"for i, example in enumerate(training_set.take(5)):\\n\",\n        \"  print('Image {} shape: {} label: {}'.format(i+1, example[0].shape, example[1]))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"mbgpD3E6gM2P\"\n      },\n      \"source\": [\n        \"# TODO: Reformat Images and Create Batches\\n\",\n        \"\\n\",\n        \"In the cell below create a function that reformats all images to the resolution expected by MobileNet v2 (224, 224) and normalizes them. The function should take in an `image` and a `label` as arguments and should return the new `image` and corresponding `label`. Then create training and validation batches of size `32`.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"we_ftzQxNf7e\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"IMAGE_RES = 224\\n\",\n        \"\\n\",\n        \"def format_image(image, label):\\n\",\n        \"  image = tf.image.resize(image, (IMAGE_RES, IMAGE_RES))/255.0\\n\",\n        \"  return image, label\\n\",\n        \"\\n\",\n        \"BATCH_SIZE = 32\\n\",\n        \"\\n\",\n        \"train_batches = training_set.shuffle(num_training_examples//4).map(format_image).batch(BATCH_SIZE).prefetch(1)\\n\",\n        \"\\n\",\n        \"validation_batches = validation_set.map(format_image).batch(BATCH_SIZE).prefetch(1)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"JzV457OXreQP\"\n      },\n      \"source\": [\n        \"# Do Simple Transfer Learning with TensorFlow Hub\\n\",\n        \"\\n\",\n        \"Let's now use TensorFlow Hub to do Transfer Learning. Remember, in transfer learning we reuse parts of an already trained model and change the final layer, or several layers, of the model, and then retrain those layers on our own dataset.\\n\",\n        \"\\n\",\n        \"### TODO: Create a Feature Extractor\\n\",\n        \"In the cell below create a `feature_extractor` using MobileNet v2. Remember that the partial model from TensorFlow Hub (without the final classification layer) is called a feature vector. Go to the [TensorFlow Hub documentation](https://tfhub.dev/s?module-type=image-feature-vector&q=tf2) to see a list of available feature vectors. Click on the `tf2-preview/mobilenet_v2/feature_vector`. Read the documentation and get the corresponding `URL` to get the MobileNet v2 feature vector. Finally, create a `feature_extractor` by using `hub.KerasLayer` with the correct `input_shape` parameter.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"5wB030nezBwI\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"URL = \\\"https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/4\\\"\\n\",\n        \"feature_extractor = hub.KerasLayer(URL,\\n\",\n        \"                                   input_shape=(IMAGE_RES, IMAGE_RES, 3))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"CtFmF7A5E4tk\"\n      },\n      \"source\": [\n        \"### TODO: Freeze the Pre-Trained Model\\n\",\n        \"\\n\",\n        \"In the cell below freeze the variables in the feature extractor layer, so that the training only modifies the final classifier layer.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Jg5ar6rcE4H-\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"feature_extractor.trainable = False\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"RPVeouTksO9q\"\n      },\n      \"source\": [\n        \"### TODO: Attach a classification head\\n\",\n        \"\\n\",\n        \"In the cell below create a `tf.keras.Sequential` model, and add the pre-trained model and the new classification layer. Remember that the classification layer must have the same number of classes as our Flowers dataset. Finally print a summary of the Sequential model.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"mGcY27fY1q3Q\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = tf.keras.Sequential([\\n\",\n        \"  feature_extractor,\\n\",\n        \"  layers.Dense(num_classes)\\n\",\n        \"])\\n\",\n        \"\\n\",\n        \"model.summary()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"OHbXQqIquFxQ\"\n      },\n      \"source\": [\n        \"### TODO: Train the model\\n\",\n        \"\\n\",\n        \"In the cell bellow train this model like any other, by first calling `compile` and then followed by `fit`. Make sure you use the proper parameters when applying both methods. Train the model for only 6 epochs.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"3n0Wb9ylKd8R\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model.compile(\\n\",\n        \"  optimizer='adam',\\n\",\n        \"  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\\n\",\n        \"  metrics=['accuracy'])\\n\",\n        \"\\n\",\n        \"EPOCHS = 6\\n\",\n        \"\\n\",\n        \"history = model.fit(train_batches,\\n\",\n        \"                    epochs=EPOCHS,\\n\",\n        \"                    validation_data=validation_batches)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"76as-K8-vFQJ\"\n      },\n      \"source\": [\n        \"You can see we get ~88% validation accuracy with only 6 epochs of training, which is absolutely awesome. This is a huge improvement over the model we created in the previous lesson, where we were able to get ~76% accuracy with 80 epochs of training. The reason for this difference is that MobileNet v2 was carefully designed over a long time by experts, then trained on a massive dataset (ImageNet).\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"SLxTcprUqJaq\"\n      },\n      \"source\": [\n        \"# TODO: Plot Training and Validation Graphs\\n\",\n        \"\\n\",\n        \"In the cell below, plot the training and validation accuracy/loss graphs.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"d28dhbFpr98b\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"acc = history.history['accuracy']\\n\",\n        \"val_acc = history.history['val_accuracy']\\n\",\n        \"\\n\",\n        \"loss = history.history['loss']\\n\",\n        \"val_loss = history.history['val_loss']\\n\",\n        \"\\n\",\n        \"epochs_range = range(EPOCHS)\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(8, 8))\\n\",\n        \"plt.subplot(1, 2, 1)\\n\",\n        \"plt.plot(epochs_range, acc, label='Training Accuracy')\\n\",\n        \"plt.plot(epochs_range, val_acc, label='Validation Accuracy')\\n\",\n        \"plt.legend(loc='lower right')\\n\",\n        \"plt.title('Training and Validation Accuracy')\\n\",\n        \"\\n\",\n        \"plt.subplot(1, 2, 2)\\n\",\n        \"plt.plot(epochs_range, loss, label='Training Loss')\\n\",\n        \"plt.plot(epochs_range, val_loss, label='Validation Loss')\\n\",\n        \"plt.legend(loc='upper right')\\n\",\n        \"plt.title('Training and Validation Loss')\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"5zmoDisGvNye\"\n      },\n      \"source\": [\n        \"What is a bit curious here is that validation performance is better than training performance, right from the start to the end of execution.\\n\",\n        \"\\n\",\n        \"One reason for this is that validation performance is measured at the end of the epoch, but training performance is the average values across the epoch.\\n\",\n        \"\\n\",\n        \"The bigger reason though is that we're reusing a large part of MobileNet which is already trained on Flower images. \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"kb__ZN8uFn-D\"\n      },\n      \"source\": [\n        \"# TODO: Check Predictions\\n\",\n        \"\\n\",\n        \"In the cell below get the label names from the dataset info and convert them into a NumPy array. Print the array to make sure you have the correct label names.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"W_Zvg2i0fzJu\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"class_names = np.array(dataset_info.features['label'].names)\\n\",\n        \"\\n\",\n        \"print(class_names)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"4Olg6MsNGJTL\"\n      },\n      \"source\": [\n        \"### TODO: Create an Image Batch and Make Predictions\\n\",\n        \"\\n\",\n        \"In the cell below, use the `next()` function to create an `image_batch` and its corresponding `label_batch`. Convert both the `image_batch` and `label_batch` to numpy arrays using the `.numpy()` method. Then use the `.predict()` method to run the image batch through your model and make predictions. Then use the `np.argmax()` function to get the indices of the best prediction for each image. Finally convert the indices of the best predictions to class names.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"fCLVCpEjJ_VP\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"image_batch, label_batch = next(iter(train_batches))\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"image_batch = image_batch.numpy()\\n\",\n        \"label_batch = label_batch.numpy()\\n\",\n        \"\\n\",\n        \"predicted_batch = model.predict(image_batch)\\n\",\n        \"predicted_batch = tf.squeeze(predicted_batch).numpy()\\n\",\n        \"\\n\",\n        \"predicted_ids = np.argmax(predicted_batch, axis=-1)\\n\",\n        \"predicted_class_names = class_names[predicted_ids]\\n\",\n        \"\\n\",\n        \"print(predicted_class_names)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"CkGbZxl9GZs-\"\n      },\n      \"source\": [\n        \"### TODO: Print True Labels and Predicted Indices\\n\",\n        \"\\n\",\n        \"In the cell below, print the true labels and the indices of predicted labels.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"nL9IhOmGI5dJ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"Labels:           \\\", label_batch)\\n\",\n        \"print(\\\"Predicted labels: \\\", predicted_ids)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"gJDyzEfYuFcW\"\n      },\n      \"source\": [\n        \"# Plot Model Predictions\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"wC_AYRJU9NQe\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.figure(figsize=(10,9))\\n\",\n        \"for n in range(30):\\n\",\n        \"  plt.subplot(6,5,n+1)\\n\",\n        \"  plt.subplots_adjust(hspace = 0.3)\\n\",\n        \"  plt.imshow(image_batch[n])\\n\",\n        \"  color = \\\"blue\\\" if predicted_ids[n] == label_batch[n] else \\\"red\\\"\\n\",\n        \"  plt.title(predicted_class_names[n].title(), color=color)\\n\",\n        \"  plt.axis('off')\\n\",\n        \"_ = plt.suptitle(\\\"Model predictions (blue: correct, red: incorrect)\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"7QBKxS5CuKhc\"\n      },\n      \"source\": [\n        \"# TODO: Perform Transfer Learning with the Inception Model\\n\",\n        \"\\n\",\n        \"Go to the [TensorFlow Hub documentation](https://tfhub.dev/s?module-type=image-feature-vector&q=tf2) and click on `tf2-preview/inception_v3/feature_vector`. This feature vector corresponds to the Inception v3 model. In the cells below, use transfer learning to create a CNN that uses Inception v3 as the pretrained model to classify the images from the Flowers dataset. Note that Inception, takes as input, images that are 299 x 299 pixels. Compare the accuracy you get with Inception v3 to the accuracy you got with MobileNet v2.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"wVII2H9ZNNQf\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"IMAGE_RES = 299\\n\",\n        \"\\n\",\n        \"(training_set, validation_set), dataset_info = tfds.load(\\n\",\n        \"    'tf_flowers', \\n\",\n        \"    with_info=True, \\n\",\n        \"    as_supervised=True, \\n\",\n        \"    split=['train[:70%]', 'train[70%:]'],\\n\",\n        \")\\n\",\n        \"train_batches = training_set.shuffle(num_training_examples//4).map(format_image).batch(BATCH_SIZE).prefetch(1)\\n\",\n        \"validation_batches = validation_set.map(format_image).batch(BATCH_SIZE).prefetch(1)\\n\",\n        \"\\n\",\n        \"URL = \\\"https://tfhub.dev/google/tf2-preview/inception_v3/feature_vector/4\\\"\\n\",\n        \"feature_extractor = hub.KerasLayer(URL,\\n\",\n        \"  input_shape=(IMAGE_RES, IMAGE_RES, 3),\\n\",\n        \"  trainable=False)\\n\",\n        \"\\n\",\n        \"model_inception = tf.keras.Sequential([\\n\",\n        \"  feature_extractor,\\n\",\n        \"  tf.keras.layers.Dense(num_classes)\\n\",\n        \"])\\n\",\n        \"\\n\",\n        \"model_inception.summary()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"idcaQKWAPgL0\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model_inception.compile(\\n\",\n        \"  optimizer='adam', \\n\",\n        \"  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\\n\",\n        \"  metrics=['accuracy'])\\n\",\n        \"\\n\",\n        \"EPOCHS = 6\\n\",\n        \"\\n\",\n        \"history = model_inception.fit(train_batches,\\n\",\n        \"                    epochs=EPOCHS,\\n\",\n        \"                    validation_data=validation_batches)\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l06c03_exercise_flowers_with_transfer_learning_solution.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l07c01_saving_and_loading_models.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"W_tvPdyfA-BL\"\n      },\n      \"source\": [\n        \"##### Copyright 2019 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"0O_LFhwSBCjm\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"9-3Pry4jh1-E\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l07c01_saving_and_loading_models.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l07c01_saving_and_loading_models.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"NxjpzKTvg_dd\"\n      },\n      \"source\": [\n        \"# Saving and Loading Models\\n\",\n        \"\\n\",\n        \"In this tutorial we will learn how we can take a trained model, save it, and then load it back to keep training it or use it to perform inference. In particular, we will use transfer learning to train a classifier to classify images of cats and dogs, just like we did in the previous lesson. We will then take our trained model and save it as an HDF5 file, which is the format used by Keras. We will then load this model, use it to perform predictions, and then continue to train the model. Finally, we will save our trained model as a TensorFlow SavedModel and then we will download it to a local disk, so that it can later be used for deployment in different platforms.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"crU-iluJIEzw\"\n      },\n      \"source\": [\n        \"## Concepts that will be covered in this Colab\\n\",\n        \"\\n\",\n        \"1. Saving models in HDF5 format for Keras\\n\",\n        \"2. Saving models in the TensorFlow SavedModel format\\n\",\n        \"3. Loading models\\n\",\n        \"4. Download models to Local Disk\\n\",\n        \"\\n\",\n        \"Before starting this Colab, you should reset the Colab environment by selecting `Runtime -> Reset all runtimes...` from menu above.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"7RVsYZLEpEWs\"\n      },\n      \"source\": [\n        \"# Imports\\n\",\n        \"\\n\",\n        \"In this Colab we will use the TensorFlow 2.0 Beta version. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"e3BXzUGabcI9\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!pip install -U tensorflow_hub\\n\",\n        \"!pip install -U tensorflow_datasets\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"a28UtPNlizGI\"\n      },\n      \"source\": [\n        \"Some normal imports we've seen before. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"OGNpmn43C0O6\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import time\\n\",\n        \"import numpy as np\\n\",\n        \"import matplotlib.pylab as plt\\n\",\n        \"\\n\",\n        \"import tensorflow as tf\\n\",\n        \"import tensorflow_hub as hub\\n\",\n        \"import tensorflow_datasets as tfds\\n\",\n        \"tfds.disable_progress_bar()\\n\",\n        \"\\n\",\n        \"from tensorflow.keras import layers\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"amfzqn1Oo7Om\"\n      },\n      \"source\": [\n        \"# Part 1: Load the Cats vs. Dogs Dataset\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Z93vvAdGxDMD\"\n      },\n      \"source\": [\n        \"We will use TensorFlow Datasets to load the Dogs vs Cats dataset. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"DrIUV3V0xDL_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"(train_examples, validation_examples), info = tfds.load(\\n\",\n        \"    'cats_vs_dogs',\\n\",\n        \"    split=['train[:80%]', 'train[80%:]'],\\n\",\n        \"    with_info=True,\\n\",\n        \"    as_supervised=True,\\n\",\n        \")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"mbgpD3E6gM2P\"\n      },\n      \"source\": [\n        \"The images in the Dogs vs. Cats dataset are not all the same size. So, we need to reformat all images to the resolution expected by MobileNet (224, 224)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"we_ftzQxNf7e\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def format_image(image, label):\\n\",\n        \"  # `hub` image modules exepct their data normalized to the [0,1] range.\\n\",\n        \"  image = tf.image.resize(image, (IMAGE_RES, IMAGE_RES))/255.0\\n\",\n        \"  return  image, label\\n\",\n        \"\\n\",\n        \"num_examples = info.splits['train'].num_examples\\n\",\n        \"\\n\",\n        \"BATCH_SIZE = 32\\n\",\n        \"IMAGE_RES = 224\\n\",\n        \"\\n\",\n        \"train_batches      = train_examples.cache().shuffle(num_examples//4).map(format_image).batch(BATCH_SIZE).prefetch(1)\\n\",\n        \"validation_batches = validation_examples.cache().map(format_image).batch(BATCH_SIZE).prefetch(1)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"JzV457OXreQP\"\n      },\n      \"source\": [\n        \"# Part 2: Transfer Learning with TensorFlow Hub\\n\",\n        \"\\n\",\n        \"We will now use TensorFlow Hub to do Transfer Learning.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"5wB030nezBwI\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"URL = \\\"https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/4\\\"\\n\",\n        \"feature_extractor = hub.KerasLayer(URL,\\n\",\n        \"                                   input_shape=(IMAGE_RES, IMAGE_RES,3))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"CtFmF7A5E4tk\"\n      },\n      \"source\": [\n        \"Freeze the variables in the feature extractor layer, so that the training only modifies the final classifier layer.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Jg5ar6rcE4H-\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"feature_extractor.trainable = False\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"RPVeouTksO9q\"\n      },\n      \"source\": [\n        \"## Attach a classification head\\n\",\n        \"\\n\",\n        \"Now wrap the hub layer in a `tf.keras.Sequential` model, and add a new classification layer.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"mGcY27fY1q3Q\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = tf.keras.Sequential([\\n\",\n        \"  feature_extractor,\\n\",\n        \"  layers.Dense(2)\\n\",\n        \"])\\n\",\n        \"\\n\",\n        \"model.summary()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"OHbXQqIquFxQ\"\n      },\n      \"source\": [\n        \"## Train the model\\n\",\n        \"\\n\",\n        \"We now train this model like any other, by first calling `compile` followed by `fit`.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"3n0Wb9ylKd8R\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model.compile(\\n\",\n        \"  optimizer='adam', \\n\",\n        \"  loss=tf.losses.SparseCategoricalCrossentropy(from_logits=True),\\n\",\n        \"  metrics=['accuracy'])\\n\",\n        \"\\n\",\n        \"EPOCHS = 3\\n\",\n        \"history = model.fit(train_batches,\\n\",\n        \"                    epochs=EPOCHS,\\n\",\n        \"                    validation_data=validation_batches)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"kb__ZN8uFn-D\"\n      },\n      \"source\": [\n        \"## Check the predictions\\n\",\n        \"\\n\",\n        \"Get the ordered list of class names.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"W_Zvg2i0fzJu\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"class_names = np.array(info.features['label'].names)\\n\",\n        \"class_names\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"4Olg6MsNGJTL\"\n      },\n      \"source\": [\n        \"Run an image batch through the model and convert the indices to class names.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"fCLVCpEjJ_VP\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"image_batch, label_batch = next(iter(train_batches.take(1)))\\n\",\n        \"image_batch = image_batch.numpy()\\n\",\n        \"label_batch = label_batch.numpy()\\n\",\n        \"\\n\",\n        \"predicted_batch = model.predict(image_batch)\\n\",\n        \"predicted_batch = tf.squeeze(predicted_batch).numpy()\\n\",\n        \"predicted_ids = np.argmax(predicted_batch, axis=-1)\\n\",\n        \"predicted_class_names = class_names[predicted_ids]\\n\",\n        \"predicted_class_names\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"CkGbZxl9GZs-\"\n      },\n      \"source\": [\n        \"Let's look at the true labels and predicted ones.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"nL9IhOmGI5dJ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"Labels: \\\", label_batch)\\n\",\n        \"print(\\\"Predicted labels: \\\", predicted_ids)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"wC_AYRJU9NQe\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.figure(figsize=(10,9))\\n\",\n        \"for n in range(30):\\n\",\n        \"  plt.subplot(6,5,n+1)\\n\",\n        \"  plt.imshow(image_batch[n])\\n\",\n        \"  color = \\\"blue\\\" if predicted_ids[n] == label_batch[n] else \\\"red\\\"\\n\",\n        \"  plt.title(predicted_class_names[n].title(), color=color)\\n\",\n        \"  plt.axis('off')\\n\",\n        \"_ = plt.suptitle(\\\"Model predictions (blue: correct, red: incorrect)\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"mmPQYQLx3cYq\"\n      },\n      \"source\": [\n        \"# Part 3: Save as Keras `.h5` model\\n\",\n        \"\\n\",\n        \"Now that we've trained the model,  we can save it as an HDF5 file, which is the format used by Keras. Our HDF5 file will have the extension '.h5', and it's name will correpond to the current time stamp.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"tCnNWTkZ3Ckz\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"t = time.time()\\n\",\n        \"\\n\",\n        \"export_path_keras = \\\"./{}.h5\\\".format(int(t))\\n\",\n        \"print(export_path_keras)\\n\",\n        \"\\n\",\n        \"model.save(export_path_keras)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"9tdJWVHmnKxJ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!ls\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"JgqmH1WVUKli\"\n      },\n      \"source\": [\n        \" You can later recreate the same model from this file, even if you no longer have access to the code that created the model.\\n\",\n        \"\\n\",\n        \"This file includes:\\n\",\n        \"\\n\",\n        \"- The model's architecture\\n\",\n        \"- The model's weight values (which were learned during training)\\n\",\n        \"- The model's training config (what you passed to `compile`), if any\\n\",\n        \"- The optimizer and its state, if any (this enables you to restart training where you left off)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"yXole25Z799G\"\n      },\n      \"source\": [\n        \"# Part 4:  Load the Keras `.h5` Model\\n\",\n        \"\\n\",\n        \"We will now load the model we just saved into a new model called `reloaded`. We will need to provide the file path and the `custom_objects` parameter. This parameter tells keras how to load the `hub.KerasLayer` from the `feature_extractor` we used for transfer learning.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Rx-z3Qwx5RnB\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"reloaded = tf.keras.models.load_model(\\n\",\n        \"  export_path_keras, \\n\",\n        \"  # `custom_objects` tells keras how to load a `hub.KerasLayer`\\n\",\n        \"  custom_objects={'KerasLayer': hub.KerasLayer})\\n\",\n        \"\\n\",\n        \"reloaded.summary()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"XxDl2vPkf2ST\"\n      },\n      \"source\": [\n        \"We can check that the reloaded model and the previous model give the same result\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"MFljA-Hd85Tu\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"result_batch = model.predict(image_batch)\\n\",\n        \"reloaded_result_batch = reloaded.predict(image_batch)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"YeCZUUJK9Svv\"\n      },\n      \"source\": [\n        \"The difference in output should be zero:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"S3p5-uD39PC1\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"(abs(result_batch - reloaded_result_batch)).max()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"KqU79kKFo7S2\"\n      },\n      \"source\": [\n        \"As we can see, the reult is 0.0, which indicates that both models made the same predictions on the same batch of images.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"nKunO4soA4Dm\"\n      },\n      \"source\": [\n        \"# Keep Training\\n\",\n        \"\\n\",\n        \"Besides making predictions, we can also take our `reloaded` model and keep training it. To do this, you can just train the `reloaded` as usual, using the `.fit` method.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"NEv-fnHEAplx\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"EPOCHS = 3\\n\",\n        \"history = reloaded.fit(train_batches,\\n\",\n        \"                    epochs=EPOCHS,\\n\",\n        \"                    validation_data=validation_batches)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"5OKoZaAHFH_s\"\n      },\n      \"source\": [\n        \"# Part 5: Export as SavedModel\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"3V47IwQbFTYT\"\n      },\n      \"source\": [\n        \"You can also export a whole model to the TensorFlow SavedModel format. SavedModel is a standalone serialization format for Tensorflow objects, supported by TensorFlow serving as well as TensorFlow implementations other than Python. A SavedModel contains a complete TensorFlow program, including weights and computation. It does not require the original model building code to run, which makes it useful for sharing or deploying (with TFLite, TensorFlow.js, TensorFlow Serving, or TFHub).\\n\",\n        \"\\n\",\n        \"The SavedModel files that were created contain:\\n\",\n        \"\\n\",\n        \"* A TensorFlow checkpoint containing the model weights.\\n\",\n        \"* A SavedModel proto containing the underlying Tensorflow graph. Separate graphs are saved for prediction (serving), train, and evaluation. If the model wasn't compiled before, then only the inference graph gets exported.\\n\",\n        \"* The model's architecture config, if available.\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"Let's save our original `model` as a TensorFlow SavedModel. To do this we will use the `tf.saved_model.save()` function. This functions takes in the model we want to save and the path to the folder where we want to save our model. \\n\",\n        \"\\n\",\n        \"This function will create a folder where you will find an `assets` folder, a `variables` folder, and the `saved_model.pb` file. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"LtpeKMfoGXrj\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"t = time.time()\\n\",\n        \"\\n\",\n        \"export_path_sm = \\\"./{}\\\".format(int(t))\\n\",\n        \"print(export_path_sm)\\n\",\n        \"\\n\",\n        \"tf.saved_model.save(model, export_path_sm)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"5h6B1wITlu-9\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!ls {export_path_sm}\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ktpsqHxJPIQW\"\n      },\n      \"source\": [\n        \"# Part 6: Load SavedModel\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"v0FDcCn9ncDb\"\n      },\n      \"source\": [\n        \"Now, let's load our SavedModel and use it to make predictions. We use the `tf.saved_model.load()` function to load our SavedModels. The object returned by `tf.saved_model.load` is 100% independent of the code that created it.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"c7_PM7lofG2V\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"reloaded_sm = tf.saved_model.load(export_path_sm)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"esEODdtM0kHB\"\n      },\n      \"source\": [\n        \"Now, let's use the `reloaded_sm` (reloaded SavedModel) to make predictions on a batch of images.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"lpQldy_bm3Ty\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"reload_sm_result_batch = reloaded_sm(image_batch, training=False).numpy()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"65upCyVN0u3Q\"\n      },\n      \"source\": [\n        \"We can check that the reloaded SavedModel and the previous model give the same result.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"hoCwmkGznR_0\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"(abs(result_batch - reload_sm_result_batch)).max()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"wxuETjjs01pL\"\n      },\n      \"source\": [\n        \"As we can see, the result is 0.0, which indicates that both models made the same predictions on the same batch of images.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"7Nom-ka0yuqB\"\n      },\n      \"source\": [\n        \"# Part 7: Loading the SavedModel as a Keras Model\\n\",\n        \"\\n\",\n        \"The object returned by `tf.saved_model.load` is not a Keras object (i.e. doesn't have `.fit`, `.predict`, `.summary`, etc. methods). Therefore, you can't simply take your `reloaded_sm` model and keep training it by running `.fit`. To be able to get back a full keras model from the Tensorflow SavedModel format we must use the `tf.keras.models.load_model` function. This function will work the same as before, except now we pass the path to the folder containing our SavedModel.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Vi1jaqh8yvt6\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"t = time.time()\\n\",\n        \"\\n\",\n        \"export_path_sm = \\\"./{}\\\".format(int(t))\\n\",\n        \"print(export_path_sm)\\n\",\n        \"tf.saved_model.save(model, export_path_sm)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"1VPE2_QQGmAP\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"reload_sm_keras = tf.keras.models.load_model(\\n\",\n        \"  export_path_sm,\\n\",\n        \"  custom_objects={'KerasLayer': hub.KerasLayer})\\n\",\n        \"\\n\",\n        \"reload_sm_keras.summary()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"thTbiHE72GL4\"\n      },\n      \"source\": [\n        \"Now, let's use the `reloaded_sm)keras` (reloaded Keras model from our SavedModel) to make predictions on a batch of images.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"-0oCJrNLKdKj\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"result_batch = model.predict(image_batch)\\n\",\n        \"reload_sm_keras_result_batch = reload_sm_keras.predict(image_batch)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"jUQaxzFj2Q8k\"\n      },\n      \"source\": [\n        \"We can check that the reloaded Keras model and the previous model give the same result.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"DJCD9JJxKg9F\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"(abs(result_batch - reload_sm_keras_result_batch)).max()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"NFa6jNc4PW9F\"\n      },\n      \"source\": [\n        \"# Part 8:  Download your model\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"2W_y1qMVQeCm\"\n      },\n      \"source\": [\n        \"You can download the SavedModel to your local disk by creating a zip file. We wil use the `-r` (recursice) option to zip all subfolders. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"WPRFoU1xPCGF\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!zip -r model.zip {export_path_sm}\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"MBZL85RsQBTj\"\n      },\n      \"source\": [\n        \"The zip file is saved in the current working directory. You can see what the current working directory is by running:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"ALP-DfwSQRL8\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!ls\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"IR89aU-SRmsL\"\n      },\n      \"source\": [\n        \"Once the file is zipped, you can download  it to your local disk. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"lOXYrlDkNjKQ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"try:\\n\",\n        \"  from google.colab import files\\n\",\n        \"  files.download('./model.zip')\\n\",\n        \"except ImportError:\\n\",\n        \"  pass\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"RzLutxq_SeLQ\"\n      },\n      \"source\": [\n        \"The `files.download` command will  search for files in your current working directory. If the file you want to download is in a directory other than the current working directory, you have to include the path to the directory where the file is located.\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l07c01_saving_and_loading_models.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l08c01_common_patterns.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Za8-Nr5k11fh\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"Eq10uEbw0E4l\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Ivi7Bm7thgCT\"\n      },\n      \"source\": [\n        \"# Common patterns\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"tFYaTP1Shi91\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l08c01_common_patterns.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l08c01_common_patterns.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"vidayERjaO5q\"\n      },\n      \"source\": [\n        \"## Setup\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"gqWabzlJ63nL\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import numpy as np\\n\",\n        \"import matplotlib.pyplot as plt\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"sJwA96JU00pW\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def plot_series(time, series, format=\\\"-\\\", start=0, end=None, label=None):\\n\",\n        \"    plt.plot(time[start:end], series[start:end], format, label=label)\\n\",\n        \"    plt.xlabel(\\\"Time\\\")\\n\",\n        \"    plt.ylabel(\\\"Value\\\")\\n\",\n        \"    if label:\\n\",\n        \"        plt.legend(fontsize=14)\\n\",\n        \"    plt.grid(True)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"yVo6CcpRaW7u\"\n      },\n      \"source\": [\n        \"## Trend and Seasonality\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"t30Ts2KjiOIY\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def trend(time, slope=0):\\n\",\n        \"    return slope * time\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"iJjc3G1Maefn\"\n      },\n      \"source\": [\n        \"Let's create a time series that just trends upward:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"BLt-pLiZ0nfB\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"time = np.arange(4 * 365 + 1)\\n\",\n        \"baseline = 10\\n\",\n        \"series = baseline + trend(time, 0.1)\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time, series)\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"3-4hV2WHTC_F\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"time\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"eOK7NnaOTGa7\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"series\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"WKD4nh9sauBf\"\n      },\n      \"source\": [\n        \"Now let's generate a time series with a seasonal pattern:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"89gdEnPY1Niy\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def seasonal_pattern(season_time):\\n\",\n        \"    \\\"\\\"\\\"Just an arbitrary pattern, you can change it if you wish\\\"\\\"\\\"\\n\",\n        \"    return np.where(season_time < 0.4,\\n\",\n        \"                    np.cos(season_time * 2 * np.pi),\\n\",\n        \"                    1 / np.exp(3 * season_time))\\n\",\n        \"\\n\",\n        \"def seasonality(time, period, amplitude=1, phase=0):\\n\",\n        \"    \\\"\\\"\\\"Repeats the same pattern at each period\\\"\\\"\\\"\\n\",\n        \"    season_time = ((time + phase) % period) / period\\n\",\n        \"    return amplitude * seasonal_pattern(season_time)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"7kaNezUk1S9l\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"amplitude = 40\\n\",\n        \"series = seasonality(time, period=365, amplitude=amplitude)\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time, series)\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"-Vo433h0bDLD\"\n      },\n      \"source\": [\n        \"Now let's create a time series with both trend and seasonality:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"AyqFdaIN1oy5\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"slope = 0.05\\n\",\n        \"series = baseline + trend(time, slope) + seasonality(time, period=365, amplitude=amplitude)\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time, series)\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"YVdJ2jNN8OHk\"\n      },\n      \"source\": [\n        \"## Noise\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"V4taP424sces\"\n      },\n      \"source\": [\n        \"In practice few real-life time series have such a smooth signal. They usually have some noise, and the signal-to-noise ratio can sometimes be very low. Let's generate some white noise:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"3kD3_zjVscBH\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def white_noise(time, noise_level=1, seed=None):\\n\",\n        \"    rnd = np.random.RandomState(seed)\\n\",\n        \"    return rnd.randn(len(time)) * noise_level\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"aLvBwrKrtDzo\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"noise_level = 5\\n\",\n        \"noise = white_noise(time, noise_level, seed=42)\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time, noise)\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"GHa6gicgbL74\"\n      },\n      \"source\": [\n        \"Now let's add this white noise to the time series:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"2bRDx8K816N9\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"series += noise\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time, series)\\n\",\n        \"plt.show()\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l08c01_common_patterns.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l08c02_naive_forecasting.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Za8-Nr5k11fh\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"Eq10uEbw0E4l\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"_3bi1D2IiCyW\"\n      },\n      \"source\": [\n        \"# Naive forecasting\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"m_6H00ELiA57\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l08c02_naive_forecasting.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l08c02_naive_forecasting.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"vidayERjaO5q\"\n      },\n      \"source\": [\n        \"## Setup\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"gqWabzlJ63nL\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import numpy as np\\n\",\n        \"import matplotlib.pyplot as plt\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"sJwA96JU00pW\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def plot_series(time, series, format=\\\"-\\\", start=0, end=None, label=None):\\n\",\n        \"    plt.plot(time[start:end], series[start:end], format, label=label)\\n\",\n        \"    plt.xlabel(\\\"Time\\\")\\n\",\n        \"    plt.ylabel(\\\"Value\\\")\\n\",\n        \"    if label:\\n\",\n        \"        plt.legend(fontsize=14)\\n\",\n        \"    plt.grid(True)\\n\",\n        \"    \\n\",\n        \"def trend(time, slope=0):\\n\",\n        \"    return slope * time\\n\",\n        \"\\n\",\n        \"def seasonal_pattern(season_time):\\n\",\n        \"    \\\"\\\"\\\"Just an arbitrary pattern, you can change it if you wish\\\"\\\"\\\"\\n\",\n        \"    return np.where(season_time < 0.4,\\n\",\n        \"                    np.cos(season_time * 2 * np.pi),\\n\",\n        \"                    1 / np.exp(3 * season_time))\\n\",\n        \"  \\n\",\n        \"def seasonality(time, period, amplitude=1, phase=0):\\n\",\n        \"    \\\"\\\"\\\"Repeats the same pattern at each period\\\"\\\"\\\"\\n\",\n        \"    season_time = ((time + phase) % period) / period\\n\",\n        \"    return amplitude * seasonal_pattern(season_time)\\n\",\n        \"    \\n\",\n        \"def white_noise(time, noise_level=1, seed=None):\\n\",\n        \"    rnd = np.random.RandomState(seed)\\n\",\n        \"    return rnd.randn(len(time)) * noise_level\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"yVo6CcpRaW7u\"\n      },\n      \"source\": [\n        \"## Trend and Seasonality\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"BLt-pLiZ0nfB\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"time = np.arange(4 * 365 + 1)\\n\",\n        \"\\n\",\n        \"slope = 0.05\\n\",\n        \"baseline = 10\\n\",\n        \"amplitude = 40\\n\",\n        \"series = baseline + trend(time, slope) + seasonality(time, period=365, amplitude=amplitude)\\n\",\n        \"\\n\",\n        \"noise_level = 5\\n\",\n        \"noise = white_noise(time, noise_level, seed=42)\\n\",\n        \"\\n\",\n        \"series += noise\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time, series)\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"a1sQpPjhtj0G\"\n      },\n      \"source\": [\n        \"All right, this looks realistic enough for now. Let's try to forecast it. We will split it into two periods: the training period and the validation period (in many cases, you would also want to have a test period). The split will be at time step 1000.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"_w0eKap5uFNP\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"split_time = 1000\\n\",\n        \"time_train = time[:split_time]\\n\",\n        \"x_train = series[:split_time]\\n\",\n        \"time_valid = time[split_time:]\\n\",\n        \"x_valid = series[split_time:]\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"bjD8ncEZbjEW\"\n      },\n      \"source\": [\n        \"## Naive Forecast\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Pj_-uCeYxcAb\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"naive_forecast = series[split_time - 1:-1]\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"JtxwHj9Ig0jT\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time_valid, x_valid, label=\\\"Series\\\")\\n\",\n        \"plot_series(time_valid, naive_forecast, label=\\\"Forecast\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"fw1SP5WeuixH\"\n      },\n      \"source\": [\n        \"Let's zoom in on the start of the validation period:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"D0MKg7FNug9V\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time_valid, x_valid, start=0, end=150, label=\\\"Series\\\")\\n\",\n        \"plot_series(time_valid, naive_forecast, start=1, end=151, label=\\\"Forecast\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"35gIlQLfu0TT\"\n      },\n      \"source\": [\n        \"You can see that the naive forecast lags 1 step behind the time series.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Uh_7244Gsxfx\"\n      },\n      \"source\": [\n        \"Now let's compute the mean absolute error between the forecasts and the predictions in the validation period:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"LjpLQeWY11H8\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"errors = naive_forecast - x_valid\\n\",\n        \"abs_errors = np.abs(errors)\\n\",\n        \"mae = abs_errors.mean()\\n\",\n        \"mae\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"WGPBC9QttI1u\"\n      },\n      \"source\": [\n        \"That's our baseline, now let's try a moving average.\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l08c02_naive_forecasting.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l08c03_moving_average.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Za8-Nr5k11fh\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"Eq10uEbw0E4l\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Nm71sonIiJjH\"\n      },\n      \"source\": [\n        \"# Moving average\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"C34f-r1Mhzkj\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l08c03_moving_average.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l08c03_moving_average.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"vidayERjaO5q\"\n      },\n      \"source\": [\n        \"## Setup\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"gqWabzlJ63nL\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import numpy as np\\n\",\n        \"import matplotlib.pyplot as plt\\n\",\n        \"import tensorflow as tf\\n\",\n        \"\\n\",\n        \"keras = tf.keras\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"sJwA96JU00pW\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def plot_series(time, series, format=\\\"-\\\", start=0, end=None, label=None):\\n\",\n        \"    plt.plot(time[start:end], series[start:end], format, label=label)\\n\",\n        \"    plt.xlabel(\\\"Time\\\")\\n\",\n        \"    plt.ylabel(\\\"Value\\\")\\n\",\n        \"    if label:\\n\",\n        \"        plt.legend(fontsize=14)\\n\",\n        \"    plt.grid(True)\\n\",\n        \"    \\n\",\n        \"def trend(time, slope=0):\\n\",\n        \"    return slope * time\\n\",\n        \"\\n\",\n        \"def seasonal_pattern(season_time):\\n\",\n        \"    \\\"\\\"\\\"Just an arbitrary pattern, you can change it if you wish\\\"\\\"\\\"\\n\",\n        \"    return np.where(season_time < 0.4,\\n\",\n        \"                    np.cos(season_time * 2 * np.pi),\\n\",\n        \"                    1 / np.exp(3 * season_time))\\n\",\n        \"\\n\",\n        \"def seasonality(time, period, amplitude=1, phase=0):\\n\",\n        \"    \\\"\\\"\\\"Repeats the same pattern at each period\\\"\\\"\\\"\\n\",\n        \"    season_time = ((time + phase) % period) / period\\n\",\n        \"    return amplitude * seasonal_pattern(season_time)\\n\",\n        \"\\n\",\n        \"def white_noise(time, noise_level=1, seed=None):\\n\",\n        \"    rnd = np.random.RandomState(seed)\\n\",\n        \"    return rnd.randn(len(time)) * noise_level\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"yVo6CcpRaW7u\"\n      },\n      \"source\": [\n        \"## Trend and Seasonality\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"BLt-pLiZ0nfB\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"time = np.arange(4 * 365 + 1)\\n\",\n        \"\\n\",\n        \"slope = 0.05\\n\",\n        \"baseline = 10\\n\",\n        \"amplitude = 40\\n\",\n        \"series = baseline + trend(time, slope) + seasonality(time, period=365, amplitude=amplitude)\\n\",\n        \"\\n\",\n        \"noise_level = 5\\n\",\n        \"noise = white_noise(time, noise_level, seed=42)\\n\",\n        \"\\n\",\n        \"series += noise\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time, series)\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"bjD8ncEZbjEW\"\n      },\n      \"source\": [\n        \"## Naive Forecast\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Pj_-uCeYxcAb\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"split_time = 1000\\n\",\n        \"time_train = time[:split_time]\\n\",\n        \"x_train = series[:split_time]\\n\",\n        \"time_valid = time[split_time:]\\n\",\n        \"x_valid = series[split_time:]\\n\",\n        \"\\n\",\n        \"naive_forecast = series[split_time - 1:-1]\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time_valid, x_valid, start=0, end=150, label=\\\"Series\\\")\\n\",\n        \"plot_series(time_valid, naive_forecast, start=1, end=151, label=\\\"Forecast\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Uh_7244Gsxfx\"\n      },\n      \"source\": [\n        \"Now let's compute the mean absolute error between the forecasts and the predictions in the validation period:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"byNnC7IbsnMZ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.metrics.mean_absolute_error(x_valid, naive_forecast).numpy()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"WGPBC9QttI1u\"\n      },\n      \"source\": [\n        \"That's our baseline, now let's try a moving average.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"MLtZbFoU8OH-\"\n      },\n      \"source\": [\n        \"## Moving Average\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"YGz5UsUdf2tV\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def moving_average_forecast(series, window_size):\\n\",\n        \"  \\\"\\\"\\\"Forecasts the mean of the last few values.\\n\",\n        \"     If window_size=1, then this is equivalent to naive forecast\\\"\\\"\\\"\\n\",\n        \"  forecast = []\\n\",\n        \"  for time in range(len(series) - window_size):\\n\",\n        \"    forecast.append(series[time:time + window_size].mean())\\n\",\n        \"  return np.array(forecast)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Le2gNBthBWPN\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def moving_average_forecast(series, window_size):\\n\",\n        \"  \\\"\\\"\\\"Forecasts the mean of the last few values.\\n\",\n        \"     If window_size=1, then this is equivalent to naive forecast\\n\",\n        \"     This implementation is *much* faster than the previous one\\\"\\\"\\\"\\n\",\n        \"  mov = np.cumsum(series)\\n\",\n        \"  mov[window_size:] = mov[window_size:] - mov[:-window_size]\\n\",\n        \"  return mov[window_size - 1:-1] / window_size\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"F50zyJGoDNJl\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"moving_avg = moving_average_forecast(series, 30)[split_time - 30:]\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time_valid, x_valid, label=\\\"Series\\\")\\n\",\n        \"plot_series(time_valid, moving_avg, label=\\\"Moving average (30 days)\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"wG7pTAd7z0e8\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.metrics.mean_absolute_error(x_valid, moving_avg).numpy()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"JMYPnJqwz8nS\"\n      },\n      \"source\": [\n        \"That's worse than naive forecast! The moving average does not anticipate trend or seasonality, so let's try to remove them by using differencing. Since the seasonality period is 365 days, we will subtract the value at time *t* – 365 from the value at time *t*.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"5pqySF7-rJR4\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"diff_series = (series[365:] - series[:-365])\\n\",\n        \"diff_time = time[365:]\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(diff_time, diff_series, label=\\\"Series(t) – Series(t–365)\\\")\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"WDNer84g8OIF\"\n      },\n      \"source\": [\n        \"Focusing on the validation period:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"-O21jlnA8OIG\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time_valid, diff_series[split_time - 365:], label=\\\"Series(t) – Series(t–365)\\\")\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"xPlPlS7DskWg\"\n      },\n      \"source\": [\n        \"Great, the trend and seasonality seem to be gone, so now we can use the moving average:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"QmZpz7arsjbb\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"diff_moving_avg = moving_average_forecast(diff_series, 50)[split_time - 365 - 50:]\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time_valid, diff_series[split_time - 365:], label=\\\"Series(t) – Series(t–365)\\\")\\n\",\n        \"plot_series(time_valid, diff_moving_avg, label=\\\"Moving Average of Diff\\\")\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Gno9S2lyecnc\"\n      },\n      \"source\": [\n        \"Now let's bring back the trend and seasonality by adding the past values from t – 365:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Dv6RWFq7TFGB\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"diff_moving_avg_plus_past = series[split_time - 365:-365] + diff_moving_avg\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time_valid, x_valid, label=\\\"Series\\\")\\n\",\n        \"plot_series(time_valid, diff_moving_avg_plus_past, label=\\\"Forecasts\\\")\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"59jmBrwcTFCx\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.metrics.mean_absolute_error(x_valid, diff_moving_avg_plus_past).numpy()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"vx9Et1Hkeusl\"\n      },\n      \"source\": [\n        \"Better than naive forecast, good. However the forecasts look a bit too random, because we're just adding past values, which were noisy. Let's use a moving averaging on past values to remove some of the noise:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"K81dtROoTE_r\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"diff_moving_avg_plus_smooth_past = moving_average_forecast(series[split_time - 370:-359], 11) + diff_moving_avg\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time_valid, x_valid, label=\\\"Series\\\")\\n\",\n        \"plot_series(time_valid, diff_moving_avg_plus_smooth_past, label=\\\"Forecasts\\\")\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"iN2MsBxWTE3m\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.metrics.mean_absolute_error(x_valid, diff_moving_avg_plus_smooth_past).numpy()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"WKnmJisHcvTW\"\n      },\n      \"source\": [\n        \"That's starting to look pretty good! Let's see if we can do better with a Machine Learning model.\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l08c03_moving_average.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l08c04_time_windows.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Za8-Nr5k11fh\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"Eq10uEbw0E4l\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Ou0PGp_4icRo\"\n      },\n      \"source\": [\n        \"# Time windows\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"93b0GzKph0jK\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l08c04_time_windows.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l08c04_time_windows.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"vidayERjaO5q\"\n      },\n      \"source\": [\n        \"## Setup\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"gqWabzlJ63nL\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow as tf\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ViWVB9qd8OIR\"\n      },\n      \"source\": [\n        \"## Time Windows\\n\",\n        \"\\n\",\n        \"First, we will train a model to forecast the next step given the previous 20 steps, therefore, we need to create a dataset of 20-step windows for training.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"bgJkwtq88OIS\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"dataset = tf.data.Dataset.range(10)\\n\",\n        \"for val in dataset:\\n\",\n        \"    print(val.numpy())\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"ad8C65JV8OIT\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"dataset = tf.data.Dataset.range(10)\\n\",\n        \"dataset = dataset.window(5, shift=1)\\n\",\n        \"for window_dataset in dataset:\\n\",\n        \"    for val in window_dataset:\\n\",\n        \"        print(val.numpy(), end=\\\" \\\")\\n\",\n        \"    print()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"AQtmODsi8OIU\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"dataset = tf.data.Dataset.range(10)\\n\",\n        \"dataset = dataset.window(5, shift=1, drop_remainder=True)\\n\",\n        \"for window_dataset in dataset:\\n\",\n        \"    for val in window_dataset:\\n\",\n        \"        print(val.numpy(), end=\\\" \\\")\\n\",\n        \"    print()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"kTRHiWxi8OIW\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"dataset = tf.data.Dataset.range(10)\\n\",\n        \"dataset = dataset.window(5, shift=1, drop_remainder=True)\\n\",\n        \"dataset = dataset.flat_map(lambda window: window.batch(5))\\n\",\n        \"for window in dataset:\\n\",\n        \"    print(window.numpy())\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"iPsQbWHb8OIX\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"dataset = tf.data.Dataset.range(10)\\n\",\n        \"dataset = dataset.window(5, shift=1, drop_remainder=True)\\n\",\n        \"dataset = dataset.flat_map(lambda window: window.batch(5))\\n\",\n        \"dataset = dataset.map(lambda window: (window[:-1], window[-1:]))\\n\",\n        \"for x, y in dataset:\\n\",\n        \"    print(x.numpy(), y.numpy())\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"hzp7RD6_8OIY\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"dataset = tf.data.Dataset.range(10)\\n\",\n        \"dataset = dataset.window(5, shift=1, drop_remainder=True)\\n\",\n        \"dataset = dataset.flat_map(lambda window: window.batch(5))\\n\",\n        \"dataset = dataset.map(lambda window: (window[:-1], window[-1:]))\\n\",\n        \"dataset = dataset.shuffle(buffer_size=10)\\n\",\n        \"for x, y in dataset:\\n\",\n        \"    print(x.numpy(), y.numpy())\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"y70nV0EI8OIZ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"dataset = tf.data.Dataset.range(10)\\n\",\n        \"dataset = dataset.window(5, shift=1, drop_remainder=True)\\n\",\n        \"dataset = dataset.flat_map(lambda window: window.batch(5))\\n\",\n        \"dataset = dataset.map(lambda window: (window[:-1], window[-1:]))\\n\",\n        \"dataset = dataset.shuffle(buffer_size=10)\\n\",\n        \"dataset = dataset.batch(2).prefetch(1)\\n\",\n        \"for x, y in dataset:\\n\",\n        \"    print(\\\"x =\\\", x.numpy())\\n\",\n        \"    print(\\\"y =\\\", y.numpy())\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"1tl-0BOKkEtk\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def window_dataset(series, window_size, batch_size=32,\\n\",\n        \"                   shuffle_buffer=1000):\\n\",\n        \"    dataset = tf.data.Dataset.from_tensor_slices(series)\\n\",\n        \"    dataset = dataset.window(window_size + 1, shift=1, drop_remainder=True)\\n\",\n        \"    dataset = dataset.flat_map(lambda window: window.batch(window_size + 1))\\n\",\n        \"    dataset = dataset.shuffle(shuffle_buffer)\\n\",\n        \"    dataset = dataset.map(lambda window: (window[:-1], window[-1]))\\n\",\n        \"    dataset = dataset.batch(batch_size).prefetch(1)\\n\",\n        \"    return dataset\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l08c04_time_windows.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l08c05_forecasting_with_machine_learning.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Za8-Nr5k11fh\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"Eq10uEbw0E4l\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"yHG6wUbvifuX\"\n      },\n      \"source\": [\n        \"# Forecasting with machine learning\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"bQNmSYMPh1TB\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l08c05_forecasting_with_machine_learning.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l08c05_forecasting_with_machine_learning.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"vidayERjaO5q\"\n      },\n      \"source\": [\n        \"## Setup\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"gqWabzlJ63nL\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import numpy as np\\n\",\n        \"import matplotlib.pyplot as plt\\n\",\n        \"import tensorflow as tf\\n\",\n        \"\\n\",\n        \"keras = tf.keras\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"cg1hfKCPldZG\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def plot_series(time, series, format=\\\"-\\\", start=0, end=None, label=None):\\n\",\n        \"    plt.plot(time[start:end], series[start:end], format, label=label)\\n\",\n        \"    plt.xlabel(\\\"Time\\\")\\n\",\n        \"    plt.ylabel(\\\"Value\\\")\\n\",\n        \"    if label:\\n\",\n        \"        plt.legend(fontsize=14)\\n\",\n        \"    plt.grid(True)\\n\",\n        \"    \\n\",\n        \"def trend(time, slope=0):\\n\",\n        \"    return slope * time\\n\",\n        \"  \\n\",\n        \"  \\n\",\n        \"def seasonal_pattern(season_time):\\n\",\n        \"    \\\"\\\"\\\"Just an arbitrary pattern, you can change it if you wish\\\"\\\"\\\"\\n\",\n        \"    return np.where(season_time < 0.4,\\n\",\n        \"                    np.cos(season_time * 2 * np.pi),\\n\",\n        \"                    1 / np.exp(3 * season_time))\\n\",\n        \"\\n\",\n        \"  \\n\",\n        \"def seasonality(time, period, amplitude=1, phase=0):\\n\",\n        \"    \\\"\\\"\\\"Repeats the same pattern at each period\\\"\\\"\\\"\\n\",\n        \"    season_time = ((time + phase) % period) / period\\n\",\n        \"    return amplitude * seasonal_pattern(season_time)\\n\",\n        \"  \\n\",\n        \"  \\n\",\n        \"def white_noise(time, noise_level=1, seed=None):\\n\",\n        \"    rnd = np.random.RandomState(seed)\\n\",\n        \"    return rnd.randn(len(time)) * noise_level\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"iL2DDjV3lel6\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"time = np.arange(4 * 365 + 1)\\n\",\n        \"\\n\",\n        \"slope = 0.05\\n\",\n        \"baseline = 10\\n\",\n        \"amplitude = 40\\n\",\n        \"series = baseline + trend(time, slope) + seasonality(time, period=365, amplitude=amplitude)\\n\",\n        \"\\n\",\n        \"noise_level = 5\\n\",\n        \"noise = white_noise(time, noise_level, seed=42)\\n\",\n        \"\\n\",\n        \"series += noise\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time, series)\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ViWVB9qd8OIR\"\n      },\n      \"source\": [\n        \"## Forecasting with Machine Learning\\n\",\n        \"\\n\",\n        \"First, we will train a model to forecast the next step given the previous 30 steps, therefore, we need to create a dataset of 30-step windows for training.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"1tl-0BOKkEtk\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def window_dataset(series, window_size, batch_size=32,\\n\",\n        \"                   shuffle_buffer=1000):\\n\",\n        \"    dataset = tf.data.Dataset.from_tensor_slices(series)\\n\",\n        \"    dataset = dataset.window(window_size + 1, shift=1, drop_remainder=True)\\n\",\n        \"    dataset = dataset.flat_map(lambda window: window.batch(window_size + 1))\\n\",\n        \"    dataset = dataset.shuffle(shuffle_buffer)\\n\",\n        \"    dataset = dataset.map(lambda window: (window[:-1], window[-1]))\\n\",\n        \"    dataset = dataset.batch(batch_size).prefetch(1)\\n\",\n        \"    return dataset\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Zmp1JXKxk9Vb\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"split_time = 1000\\n\",\n        \"time_train = time[:split_time]\\n\",\n        \"x_train = series[:split_time]\\n\",\n        \"time_valid = time[split_time:]\\n\",\n        \"x_valid = series[split_time:]\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"T1IvwAFn8OIc\"\n      },\n      \"source\": [\n        \"### Linear Model\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"ieOKdcEQ0A6k\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.backend.clear_session()\\n\",\n        \"tf.random.set_seed(42)\\n\",\n        \"np.random.seed(42)\\n\",\n        \"\\n\",\n        \"window_size = 30\\n\",\n        \"train_set = window_dataset(x_train, window_size)\\n\",\n        \"valid_set = window_dataset(x_valid, window_size)\\n\",\n        \"\\n\",\n        \"model = keras.models.Sequential([\\n\",\n        \"  keras.layers.Dense(1, input_shape=[window_size])\\n\",\n        \"])\\n\",\n        \"optimizer = keras.optimizers.SGD(lr=1e-5, momentum=0.9)\\n\",\n        \"model.compile(loss=keras.losses.Huber(),\\n\",\n        \"              optimizer=optimizer,\\n\",\n        \"              metrics=[\\\"mae\\\"])\\n\",\n        \"model.fit(train_set, epochs=100, validation_data=valid_set)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"N3N8AGRM8OIc\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.backend.clear_session()\\n\",\n        \"tf.random.set_seed(42)\\n\",\n        \"np.random.seed(42)\\n\",\n        \"\\n\",\n        \"window_size = 30\\n\",\n        \"train_set = window_dataset(x_train, window_size)\\n\",\n        \"\\n\",\n        \"model = keras.models.Sequential([\\n\",\n        \"  keras.layers.Dense(1, input_shape=[window_size])\\n\",\n        \"])\\n\",\n        \"\\n\",\n        \"lr_schedule = keras.callbacks.LearningRateScheduler(\\n\",\n        \"    lambda epoch: 1e-6 * 10**(epoch / 30))\\n\",\n        \"optimizer = keras.optimizers.SGD(lr=1e-6, momentum=0.9)\\n\",\n        \"model.compile(loss=keras.losses.Huber(),\\n\",\n        \"              optimizer=optimizer,\\n\",\n        \"              metrics=[\\\"mae\\\"])\\n\",\n        \"history = model.fit(train_set, epochs=100, callbacks=[lr_schedule])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"PF9e7IDm8OId\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.semilogx(history.history[\\\"lr\\\"], history.history[\\\"loss\\\"])\\n\",\n        \"plt.axis([1e-6, 1e-3, 0, 20])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"uMNwyIFE8OIf\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.backend.clear_session()\\n\",\n        \"tf.random.set_seed(42)\\n\",\n        \"np.random.seed(42)\\n\",\n        \"\\n\",\n        \"window_size = 30\\n\",\n        \"train_set = window_dataset(x_train, window_size)\\n\",\n        \"valid_set = window_dataset(x_valid, window_size)\\n\",\n        \"\\n\",\n        \"model = keras.models.Sequential([\\n\",\n        \"  keras.layers.Dense(1, input_shape=[window_size])\\n\",\n        \"])\\n\",\n        \"optimizer = keras.optimizers.SGD(lr=1e-5, momentum=0.9)\\n\",\n        \"model.compile(loss=keras.losses.Huber(),\\n\",\n        \"              optimizer=optimizer,\\n\",\n        \"              metrics=[\\\"mae\\\"])\\n\",\n        \"early_stopping = keras.callbacks.EarlyStopping(patience=10)\\n\",\n        \"model.fit(train_set, epochs=500,\\n\",\n        \"          validation_data=valid_set,\\n\",\n        \"          callbacks=[early_stopping])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"_eaAX9g_jS5W\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def model_forecast(model, series, window_size):\\n\",\n        \"    ds = tf.data.Dataset.from_tensor_slices(series)\\n\",\n        \"    ds = ds.window(window_size, shift=1, drop_remainder=True)\\n\",\n        \"    ds = ds.flat_map(lambda w: w.batch(window_size))\\n\",\n        \"    ds = ds.batch(32).prefetch(1)\\n\",\n        \"    forecast = model.predict(ds)\\n\",\n        \"    return forecast\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"FnIWROQ08OIj\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"lin_forecast = model_forecast(model, series[split_time - window_size:-1], window_size)[:, 0]\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"xd7Tj_fA8OIk\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"lin_forecast.shape\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"F-nftslfgQJs\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time_valid, x_valid)\\n\",\n        \"plot_series(time_valid, lin_forecast)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"W4E_jXktf7iv\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.metrics.mean_absolute_error(x_valid, lin_forecast).numpy()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"9nEM33dZ8OIp\"\n      },\n      \"source\": [\n        \"### Dense Model Forecasting\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"RhGTv4G_8OIp\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.backend.clear_session()\\n\",\n        \"tf.random.set_seed(42)\\n\",\n        \"np.random.seed(42)\\n\",\n        \"\\n\",\n        \"window_size = 30\\n\",\n        \"train_set = window_dataset(x_train, window_size)\\n\",\n        \"\\n\",\n        \"model = keras.models.Sequential([\\n\",\n        \"  keras.layers.Dense(10, activation=\\\"relu\\\", input_shape=[window_size]),\\n\",\n        \"  keras.layers.Dense(10, activation=\\\"relu\\\"),\\n\",\n        \"  keras.layers.Dense(1)\\n\",\n        \"])\\n\",\n        \"\\n\",\n        \"lr_schedule = keras.callbacks.LearningRateScheduler(\\n\",\n        \"    lambda epoch: 1e-7 * 10**(epoch / 20))\\n\",\n        \"optimizer = keras.optimizers.SGD(lr=1e-7, momentum=0.9)\\n\",\n        \"model.compile(loss=keras.losses.Huber(),\\n\",\n        \"              optimizer=optimizer,\\n\",\n        \"              metrics=[\\\"mae\\\"])\\n\",\n        \"history = model.fit(train_set, epochs=100, callbacks=[lr_schedule])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"5g-nC_em8OIq\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.semilogx(history.history[\\\"lr\\\"], history.history[\\\"loss\\\"])\\n\",\n        \"plt.axis([1e-7, 5e-3, 0, 30])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"B7t0VrCH8OIr\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.backend.clear_session()\\n\",\n        \"tf.random.set_seed(42)\\n\",\n        \"np.random.seed(42)\\n\",\n        \"\\n\",\n        \"window_size = 30\\n\",\n        \"train_set = window_dataset(x_train, window_size)\\n\",\n        \"valid_set = window_dataset(x_valid, window_size)\\n\",\n        \"\\n\",\n        \"model = keras.models.Sequential([\\n\",\n        \"  keras.layers.Dense(10, activation=\\\"relu\\\", input_shape=[window_size]),\\n\",\n        \"  keras.layers.Dense(10, activation=\\\"relu\\\"),\\n\",\n        \"  keras.layers.Dense(1)\\n\",\n        \"])\\n\",\n        \"\\n\",\n        \"optimizer = keras.optimizers.SGD(lr=1e-5, momentum=0.9)\\n\",\n        \"model.compile(loss=keras.losses.Huber(),\\n\",\n        \"              optimizer=optimizer,\\n\",\n        \"              metrics=[\\\"mae\\\"])\\n\",\n        \"early_stopping = keras.callbacks.EarlyStopping(patience=10)\\n\",\n        \"model.fit(train_set, epochs=500,\\n\",\n        \"          validation_data=valid_set,\\n\",\n        \"          callbacks=[early_stopping])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"RqQbX6DZ8OIu\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"dense_forecast = model_forecast(\\n\",\n        \"    model,\\n\",\n        \"    series[split_time - window_size:-1],\\n\",\n        \"    window_size)[:, 0]\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"98zwAuIo8OIv\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time_valid, x_valid)\\n\",\n        \"plot_series(time_valid, dense_forecast)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"EgkELN-58OIw\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.metrics.mean_absolute_error(x_valid, dense_forecast).numpy()\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"collapsed_sections\": [\n        \"vidayERjaO5q\"\n      ],\n      \"name\": \"l08c05_forecasting_with_machine_learning.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l08c06_forecasting_with_rnn.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Za8-Nr5k11fh\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"Eq10uEbw0E4l\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"nuRx7K-sirJr\"\n      },\n      \"source\": [\n        \"# Forecasting with an RNN\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"97jsq1rHh2Ds\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l08c06_forecasting_with_rnn.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l08c06_forecasting_with_rnn.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"vidayERjaO5q\"\n      },\n      \"source\": [\n        \"## Setup\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"gqWabzlJ63nL\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import numpy as np\\n\",\n        \"import matplotlib.pyplot as plt\\n\",\n        \"import tensorflow as tf\\n\",\n        \"\\n\",\n        \"keras = tf.keras\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"cg1hfKCPldZG\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def plot_series(time, series, format=\\\"-\\\", start=0, end=None, label=None):\\n\",\n        \"    plt.plot(time[start:end], series[start:end], format, label=label)\\n\",\n        \"    plt.xlabel(\\\"Time\\\")\\n\",\n        \"    plt.ylabel(\\\"Value\\\")\\n\",\n        \"    if label:\\n\",\n        \"        plt.legend(fontsize=14)\\n\",\n        \"    plt.grid(True)\\n\",\n        \"    \\n\",\n        \"def trend(time, slope=0):\\n\",\n        \"    return slope * time\\n\",\n        \"  \\n\",\n        \"  \\n\",\n        \"def seasonal_pattern(season_time):\\n\",\n        \"    \\\"\\\"\\\"Just an arbitrary pattern, you can change it if you wish\\\"\\\"\\\"\\n\",\n        \"    return np.where(season_time < 0.4,\\n\",\n        \"                    np.cos(season_time * 2 * np.pi),\\n\",\n        \"                    1 / np.exp(3 * season_time))\\n\",\n        \"\\n\",\n        \"  \\n\",\n        \"def seasonality(time, period, amplitude=1, phase=0):\\n\",\n        \"    \\\"\\\"\\\"Repeats the same pattern at each period\\\"\\\"\\\"\\n\",\n        \"    season_time = ((time + phase) % period) / period\\n\",\n        \"    return amplitude * seasonal_pattern(season_time)\\n\",\n        \"  \\n\",\n        \"  \\n\",\n        \"def white_noise(time, noise_level=1, seed=None):\\n\",\n        \"    rnd = np.random.RandomState(seed)\\n\",\n        \"    return rnd.randn(len(time)) * noise_level\\n\",\n        \"  \\n\",\n        \"  \\n\",\n        \"def window_dataset(series, window_size, batch_size=32,\\n\",\n        \"                   shuffle_buffer=1000):\\n\",\n        \"    dataset = tf.data.Dataset.from_tensor_slices(series)\\n\",\n        \"    dataset = dataset.window(window_size + 1, shift=1, drop_remainder=True)\\n\",\n        \"    dataset = dataset.flat_map(lambda window: window.batch(window_size + 1))\\n\",\n        \"    dataset = dataset.shuffle(shuffle_buffer)\\n\",\n        \"    dataset = dataset.map(lambda window: (window[:-1], window[-1]))\\n\",\n        \"    dataset = dataset.batch(batch_size).prefetch(1)\\n\",\n        \"    return dataset\\n\",\n        \"  \\n\",\n        \"def model_forecast(model, series, window_size):\\n\",\n        \"    ds = tf.data.Dataset.from_tensor_slices(series)\\n\",\n        \"    ds = ds.window(window_size, shift=1, drop_remainder=True)\\n\",\n        \"    ds = ds.flat_map(lambda w: w.batch(window_size))\\n\",\n        \"    ds = ds.batch(32).prefetch(1)\\n\",\n        \"    forecast = model.predict(ds)\\n\",\n        \"    return forecast\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"iL2DDjV3lel6\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"time = np.arange(4 * 365 + 1)\\n\",\n        \"\\n\",\n        \"slope = 0.05\\n\",\n        \"baseline = 10\\n\",\n        \"amplitude = 40\\n\",\n        \"series = baseline + trend(time, slope) + seasonality(time, period=365, amplitude=amplitude)\\n\",\n        \"\\n\",\n        \"noise_level = 5\\n\",\n        \"noise = white_noise(time, noise_level, seed=42)\\n\",\n        \"\\n\",\n        \"series += noise\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time, series)\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Zmp1JXKxk9Vb\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"split_time = 1000\\n\",\n        \"time_train = time[:split_time]\\n\",\n        \"x_train = series[:split_time]\\n\",\n        \"time_valid = time[split_time:]\\n\",\n        \"x_valid = series[split_time:]\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"vDs_w3kZ8OIw\"\n      },\n      \"source\": [\n        \"## Simple RNN Forecasting\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"YU4xRp9G8OIx\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.backend.clear_session()\\n\",\n        \"tf.random.set_seed(42)\\n\",\n        \"np.random.seed(42)\\n\",\n        \"\\n\",\n        \"window_size = 30\\n\",\n        \"train_set = window_dataset(x_train, window_size, batch_size=128)\\n\",\n        \"\\n\",\n        \"model = keras.models.Sequential([\\n\",\n        \"  keras.layers.Lambda(lambda x: tf.expand_dims(x, axis=-1),\\n\",\n        \"                      input_shape=[None]),\\n\",\n        \"  keras.layers.SimpleRNN(100, return_sequences=True),\\n\",\n        \"  keras.layers.SimpleRNN(100),\\n\",\n        \"  keras.layers.Dense(1),\\n\",\n        \"  keras.layers.Lambda(lambda x: x * 200.0)\\n\",\n        \"])\\n\",\n        \"lr_schedule = keras.callbacks.LearningRateScheduler(\\n\",\n        \"    lambda epoch: 1e-7 * 10**(epoch / 20))\\n\",\n        \"optimizer = keras.optimizers.SGD(lr=1e-7, momentum=0.9)\\n\",\n        \"model.compile(loss=keras.losses.Huber(),\\n\",\n        \"              optimizer=optimizer,\\n\",\n        \"              metrics=[\\\"mae\\\"])\\n\",\n        \"history = model.fit(train_set, epochs=100, callbacks=[lr_schedule])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"YJTlFAXF8OIy\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.semilogx(history.history[\\\"lr\\\"], history.history[\\\"loss\\\"])\\n\",\n        \"plt.axis([1e-7, 1e-4, 0, 30])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"T3yNjxWE8OIz\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.backend.clear_session()\\n\",\n        \"tf.random.set_seed(42)\\n\",\n        \"np.random.seed(42)\\n\",\n        \"\\n\",\n        \"window_size = 30\\n\",\n        \"train_set = window_dataset(x_train, window_size, batch_size=128)\\n\",\n        \"valid_set = window_dataset(x_valid, window_size, batch_size=128)\\n\",\n        \"\\n\",\n        \"model = keras.models.Sequential([\\n\",\n        \"  keras.layers.Lambda(lambda x: tf.expand_dims(x, axis=-1),\\n\",\n        \"                      input_shape=[None]),\\n\",\n        \"  keras.layers.SimpleRNN(100, return_sequences=True),\\n\",\n        \"  keras.layers.SimpleRNN(100),\\n\",\n        \"  keras.layers.Dense(1),\\n\",\n        \"  keras.layers.Lambda(lambda x: x * 200.0)\\n\",\n        \"])\\n\",\n        \"optimizer = keras.optimizers.SGD(lr=1.5e-6, momentum=0.9)\\n\",\n        \"model.compile(loss=keras.losses.Huber(),\\n\",\n        \"              optimizer=optimizer,\\n\",\n        \"              metrics=[\\\"mae\\\"])\\n\",\n        \"early_stopping = keras.callbacks.EarlyStopping(patience=50)\\n\",\n        \"model_checkpoint = keras.callbacks.ModelCheckpoint(\\n\",\n        \"    \\\"my_checkpoint\\\", save_best_only=True)\\n\",\n        \"model.fit(train_set, epochs=500,\\n\",\n        \"          validation_data=valid_set,\\n\",\n        \"          callbacks=[early_stopping, model_checkpoint])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"4KuPtKFe8OI0\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = keras.models.load_model(\\\"my_checkpoint\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"cxq09qOg8OI1\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"rnn_forecast = model_forecast(\\n\",\n        \"    model,\\n\",\n        \"    series[split_time - window_size:-1],\\n\",\n        \"    window_size)[:, 0]\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"PkC_JssS8OI2\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time_valid, x_valid)\\n\",\n        \"plot_series(time_valid, rnn_forecast)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"1mwfgEK08OI3\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.metrics.mean_absolute_error(x_valid, rnn_forecast).numpy()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"KNG7s8jt8OI4\"\n      },\n      \"source\": [\n        \"## Sequence-to-Sequence Forecasting\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"bsKGxfiE8OI4\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def seq2seq_window_dataset(series, window_size, batch_size=32,\\n\",\n        \"                           shuffle_buffer=1000):\\n\",\n        \"    series = tf.expand_dims(series, axis=-1)\\n\",\n        \"    ds = tf.data.Dataset.from_tensor_slices(series)\\n\",\n        \"    ds = ds.window(window_size + 1, shift=1, drop_remainder=True)\\n\",\n        \"    ds = ds.flat_map(lambda w: w.batch(window_size + 1))\\n\",\n        \"    ds = ds.shuffle(shuffle_buffer)\\n\",\n        \"    ds = ds.map(lambda w: (w[:-1], w[1:]))\\n\",\n        \"    return ds.batch(batch_size).prefetch(1)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"5Nk2C7WP8OI5\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"for X_batch, Y_batch in seq2seq_window_dataset(tf.range(10), 3,\\n\",\n        \"                                               batch_size=1):\\n\",\n        \"    print(\\\"X:\\\", X_batch.numpy())\\n\",\n        \"    print(\\\"Y:\\\", Y_batch.numpy())\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"4JSc-Btk8OI7\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.backend.clear_session()\\n\",\n        \"tf.random.set_seed(42)\\n\",\n        \"np.random.seed(42)\\n\",\n        \"\\n\",\n        \"window_size = 30\\n\",\n        \"train_set = seq2seq_window_dataset(x_train, window_size,\\n\",\n        \"                                   batch_size=128)\\n\",\n        \"\\n\",\n        \"model = keras.models.Sequential([\\n\",\n        \"  keras.layers.SimpleRNN(100, return_sequences=True,\\n\",\n        \"                         input_shape=[None, 1]),\\n\",\n        \"  keras.layers.SimpleRNN(100, return_sequences=True),\\n\",\n        \"  keras.layers.Dense(1),\\n\",\n        \"  keras.layers.Lambda(lambda x: x * 200)\\n\",\n        \"])\\n\",\n        \"lr_schedule = keras.callbacks.LearningRateScheduler(\\n\",\n        \"    lambda epoch: 1e-7 * 10**(epoch / 30))\\n\",\n        \"optimizer = keras.optimizers.SGD(lr=1e-7, momentum=0.9)\\n\",\n        \"model.compile(loss=keras.losses.Huber(),\\n\",\n        \"              optimizer=optimizer,\\n\",\n        \"              metrics=[\\\"mae\\\"])\\n\",\n        \"history = model.fit(train_set, epochs=100, callbacks=[lr_schedule])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"YGNsWceq8OI8\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.semilogx(history.history[\\\"lr\\\"], history.history[\\\"loss\\\"])\\n\",\n        \"plt.axis([1e-7, 1e-4, 0, 30])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"G9lDnb0X8OI9\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.backend.clear_session()\\n\",\n        \"tf.random.set_seed(42)\\n\",\n        \"np.random.seed(42)\\n\",\n        \"\\n\",\n        \"window_size = 30\\n\",\n        \"train_set = seq2seq_window_dataset(x_train, window_size,\\n\",\n        \"                                   batch_size=128)\\n\",\n        \"valid_set = seq2seq_window_dataset(x_valid, window_size,\\n\",\n        \"                                   batch_size=128)\\n\",\n        \"\\n\",\n        \"model = keras.models.Sequential([\\n\",\n        \"  keras.layers.SimpleRNN(100, return_sequences=True,\\n\",\n        \"                         input_shape=[None, 1]),\\n\",\n        \"  keras.layers.SimpleRNN(100, return_sequences=True),\\n\",\n        \"  keras.layers.Dense(1),\\n\",\n        \"  keras.layers.Lambda(lambda x: x * 200.0)\\n\",\n        \"])\\n\",\n        \"optimizer = keras.optimizers.SGD(lr=1e-6, momentum=0.9)\\n\",\n        \"model.compile(loss=keras.losses.Huber(),\\n\",\n        \"              optimizer=optimizer,\\n\",\n        \"              metrics=[\\\"mae\\\"])\\n\",\n        \"early_stopping = keras.callbacks.EarlyStopping(patience=10)\\n\",\n        \"model.fit(train_set, epochs=500,\\n\",\n        \"          validation_data=valid_set,\\n\",\n        \"          callbacks=[early_stopping])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"4mglBRex8OI_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"rnn_forecast = model_forecast(model, series[..., np.newaxis], window_size)\\n\",\n        \"rnn_forecast = rnn_forecast[split_time - window_size:-1, -1, 0]\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Zl_FkcdI8OJA\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time_valid, x_valid)\\n\",\n        \"plot_series(time_valid, rnn_forecast)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"cznEtSVK8OJB\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.metrics.mean_absolute_error(x_valid, rnn_forecast).numpy()\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"collapsed_sections\": [\n        \"vidayERjaO5q\"\n      ],\n      \"name\": \"l08c06_forecasting_with_rnn.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l08c07_forecasting_with_stateful_rnn.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Za8-Nr5k11fh\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"Eq10uEbw0E4l\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"eJoEl2FhixRp\"\n      },\n      \"source\": [\n        \"# Forecasting with a stateful RNN\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"w3Hibo73h236\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l08c07_forecasting_with_stateful_rnn.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l08c07_forecasting_with_stateful_rnn.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"vidayERjaO5q\"\n      },\n      \"source\": [\n        \"## Setup\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"gqWabzlJ63nL\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import numpy as np\\n\",\n        \"import matplotlib.pyplot as plt\\n\",\n        \"import tensorflow as tf\\n\",\n        \"\\n\",\n        \"keras = tf.keras\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"cg1hfKCPldZG\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def plot_series(time, series, format=\\\"-\\\", start=0, end=None, label=None):\\n\",\n        \"    plt.plot(time[start:end], series[start:end], format, label=label)\\n\",\n        \"    plt.xlabel(\\\"Time\\\")\\n\",\n        \"    plt.ylabel(\\\"Value\\\")\\n\",\n        \"    if label:\\n\",\n        \"        plt.legend(fontsize=14)\\n\",\n        \"    plt.grid(True)\\n\",\n        \"    \\n\",\n        \"def trend(time, slope=0):\\n\",\n        \"    return slope * time\\n\",\n        \"  \\n\",\n        \"  \\n\",\n        \"def seasonal_pattern(season_time):\\n\",\n        \"    \\\"\\\"\\\"Just an arbitrary pattern, you can change it if you wish\\\"\\\"\\\"\\n\",\n        \"    return np.where(season_time < 0.4,\\n\",\n        \"                    np.cos(season_time * 2 * np.pi),\\n\",\n        \"                    1 / np.exp(3 * season_time))\\n\",\n        \"\\n\",\n        \"  \\n\",\n        \"def seasonality(time, period, amplitude=1, phase=0):\\n\",\n        \"    \\\"\\\"\\\"Repeats the same pattern at each period\\\"\\\"\\\"\\n\",\n        \"    season_time = ((time + phase) % period) / period\\n\",\n        \"    return amplitude * seasonal_pattern(season_time)\\n\",\n        \"  \\n\",\n        \"  \\n\",\n        \"def white_noise(time, noise_level=1, seed=None):\\n\",\n        \"    rnd = np.random.RandomState(seed)\\n\",\n        \"    return rnd.randn(len(time)) * noise_level\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"iL2DDjV3lel6\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"time = np.arange(4 * 365 + 1)\\n\",\n        \"\\n\",\n        \"slope = 0.05\\n\",\n        \"baseline = 10\\n\",\n        \"amplitude = 40\\n\",\n        \"series = baseline + trend(time, slope) + seasonality(time, period=365, amplitude=amplitude)\\n\",\n        \"\\n\",\n        \"noise_level = 5\\n\",\n        \"noise = white_noise(time, noise_level, seed=42)\\n\",\n        \"\\n\",\n        \"series += noise\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time, series)\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Zmp1JXKxk9Vb\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"split_time = 1000\\n\",\n        \"time_train = time[:split_time]\\n\",\n        \"x_train = series[:split_time]\\n\",\n        \"time_valid = time[split_time:]\\n\",\n        \"x_valid = series[split_time:]\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"43Entmgk8OJC\"\n      },\n      \"source\": [\n        \"## Stateful RNN Forecasting\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"ezzIGpsq8OJC\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def sequential_window_dataset(series, window_size):\\n\",\n        \"    series = tf.expand_dims(series, axis=-1)\\n\",\n        \"    ds = tf.data.Dataset.from_tensor_slices(series)\\n\",\n        \"    ds = ds.window(window_size + 1, shift=window_size, drop_remainder=True)\\n\",\n        \"    ds = ds.flat_map(lambda window: window.batch(window_size + 1))\\n\",\n        \"    ds = ds.map(lambda window: (window[:-1], window[1:]))\\n\",\n        \"    return ds.batch(1).prefetch(1)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"cC2hh-Dn8OJD\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"for X_batch, y_batch in sequential_window_dataset(tf.range(10), 3):\\n\",\n        \"    print(X_batch.numpy(), y_batch.numpy())\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"CHCYhVvl8OJE\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"class ResetStatesCallback(keras.callbacks.Callback):\\n\",\n        \"    def on_epoch_begin(self, epoch, logs):\\n\",\n        \"        self.model.reset_states()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"g03h-KGJ8OJF\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.backend.clear_session()\\n\",\n        \"tf.random.set_seed(42)\\n\",\n        \"np.random.seed(42)\\n\",\n        \"\\n\",\n        \"window_size = 30\\n\",\n        \"train_set = sequential_window_dataset(x_train, window_size)\\n\",\n        \"\\n\",\n        \"model = keras.models.Sequential([\\n\",\n        \"  keras.layers.SimpleRNN(100, return_sequences=True, stateful=True,\\n\",\n        \"                         batch_input_shape=[1, None, 1]),\\n\",\n        \"  keras.layers.SimpleRNN(100, return_sequences=True, stateful=True),\\n\",\n        \"  keras.layers.Dense(1),\\n\",\n        \"  keras.layers.Lambda(lambda x: x * 200.0)\\n\",\n        \"])\\n\",\n        \"lr_schedule = keras.callbacks.LearningRateScheduler(\\n\",\n        \"    lambda epoch: 1e-8 * 10**(epoch / 30))\\n\",\n        \"reset_states = ResetStatesCallback()\\n\",\n        \"optimizer = keras.optimizers.SGD(lr=1e-8, momentum=0.9)\\n\",\n        \"model.compile(loss=keras.losses.Huber(),\\n\",\n        \"              optimizer=optimizer,\\n\",\n        \"              metrics=[\\\"mae\\\"])\\n\",\n        \"history = model.fit(train_set, epochs=100,\\n\",\n        \"                    callbacks=[lr_schedule, reset_states])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"rl_s5X448OJG\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.semilogx(history.history[\\\"lr\\\"], history.history[\\\"loss\\\"])\\n\",\n        \"plt.axis([1e-8, 1e-4, 0, 30])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Gi3suj8F8OJH\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.backend.clear_session()\\n\",\n        \"tf.random.set_seed(42)\\n\",\n        \"np.random.seed(42)\\n\",\n        \"\\n\",\n        \"window_size = 30\\n\",\n        \"train_set = sequential_window_dataset(x_train, window_size)\\n\",\n        \"valid_set = sequential_window_dataset(x_valid, window_size)\\n\",\n        \"\\n\",\n        \"model = keras.models.Sequential([\\n\",\n        \"  keras.layers.SimpleRNN(100, return_sequences=True, stateful=True,\\n\",\n        \"                         batch_input_shape=[1, None, 1]),\\n\",\n        \"  keras.layers.SimpleRNN(100, return_sequences=True, stateful=True),\\n\",\n        \"  keras.layers.Dense(1),\\n\",\n        \"  keras.layers.Lambda(lambda x: x * 200.0)\\n\",\n        \"])\\n\",\n        \"optimizer = keras.optimizers.SGD(lr=1e-7, momentum=0.9)\\n\",\n        \"model.compile(loss=keras.losses.Huber(),\\n\",\n        \"              optimizer=optimizer,\\n\",\n        \"              metrics=[\\\"mae\\\"])\\n\",\n        \"reset_states = ResetStatesCallback()\\n\",\n        \"model_checkpoint = keras.callbacks.ModelCheckpoint(\\n\",\n        \"    \\\"my_checkpoint.h5\\\", save_best_only=True)\\n\",\n        \"early_stopping = keras.callbacks.EarlyStopping(patience=50)\\n\",\n        \"model.fit(train_set, epochs=500,\\n\",\n        \"          validation_data=valid_set,\\n\",\n        \"          callbacks=[early_stopping, model_checkpoint, reset_states])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"lfW42jg-8OJI\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = keras.models.load_model(\\\"my_checkpoint.h5\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"JC3XHLxd8OJJ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model.reset_states()\\n\",\n        \"rnn_forecast = model.predict(series[np.newaxis, :, np.newaxis])\\n\",\n        \"rnn_forecast = rnn_forecast[0, split_time - 1:-1, 0]\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Cwsvi0cxmwBZ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"rnn_forecast.shape\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"s6eZmrn48OJK\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time_valid, x_valid)\\n\",\n        \"plot_series(time_valid, rnn_forecast)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"8z6Py2Lx8OJL\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.metrics.mean_absolute_error(x_valid, rnn_forecast).numpy()\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"collapsed_sections\": [\n        \"vidayERjaO5q\"\n      ],\n      \"name\": \"l08c07_forecasting_with_stateful_rnn.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l08c08_forecasting_with_lstm.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Za8-Nr5k11fh\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"Eq10uEbw0E4l\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"E5VI4y76i14x\"\n      },\n      \"source\": [\n        \"# Forecasting with an LSTM\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"-Da7hZzJh3mg\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l08c08_forecasting_with_lstm.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l08c08_forecasting_with_lstm.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"vidayERjaO5q\"\n      },\n      \"source\": [\n        \"## Setup\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"gqWabzlJ63nL\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import numpy as np\\n\",\n        \"import matplotlib.pyplot as plt\\n\",\n        \"import tensorflow as tf\\n\",\n        \"\\n\",\n        \"keras = tf.keras\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"cg1hfKCPldZG\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def plot_series(time, series, format=\\\"-\\\", start=0, end=None, label=None):\\n\",\n        \"    plt.plot(time[start:end], series[start:end], format, label=label)\\n\",\n        \"    plt.xlabel(\\\"Time\\\")\\n\",\n        \"    plt.ylabel(\\\"Value\\\")\\n\",\n        \"    if label:\\n\",\n        \"        plt.legend(fontsize=14)\\n\",\n        \"    plt.grid(True)\\n\",\n        \"    \\n\",\n        \"def trend(time, slope=0):\\n\",\n        \"    return slope * time\\n\",\n        \"  \\n\",\n        \"  \\n\",\n        \"def seasonal_pattern(season_time):\\n\",\n        \"    \\\"\\\"\\\"Just an arbitrary pattern, you can change it if you wish\\\"\\\"\\\"\\n\",\n        \"    return np.where(season_time < 0.4,\\n\",\n        \"                    np.cos(season_time * 2 * np.pi),\\n\",\n        \"                    1 / np.exp(3 * season_time))\\n\",\n        \"\\n\",\n        \"  \\n\",\n        \"def seasonality(time, period, amplitude=1, phase=0):\\n\",\n        \"    \\\"\\\"\\\"Repeats the same pattern at each period\\\"\\\"\\\"\\n\",\n        \"    season_time = ((time + phase) % period) / period\\n\",\n        \"    return amplitude * seasonal_pattern(season_time)\\n\",\n        \"  \\n\",\n        \"  \\n\",\n        \"def white_noise(time, noise_level=1, seed=None):\\n\",\n        \"    rnd = np.random.RandomState(seed)\\n\",\n        \"    return rnd.randn(len(time)) * noise_level\\n\",\n        \"  \\n\",\n        \"\\n\",\n        \"def sequential_window_dataset(series, window_size):\\n\",\n        \"    series = tf.expand_dims(series, axis=-1)\\n\",\n        \"    ds = tf.data.Dataset.from_tensor_slices(series)\\n\",\n        \"    ds = ds.window(window_size + 1, shift=window_size, drop_remainder=True)\\n\",\n        \"    ds = ds.flat_map(lambda window: window.batch(window_size + 1))\\n\",\n        \"    ds = ds.map(lambda window: (window[:-1], window[1:]))\\n\",\n        \"    return ds.batch(1).prefetch(1)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"iL2DDjV3lel6\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"time = np.arange(4 * 365 + 1)\\n\",\n        \"\\n\",\n        \"slope = 0.05\\n\",\n        \"baseline = 10\\n\",\n        \"amplitude = 40\\n\",\n        \"series = baseline + trend(time, slope) + seasonality(time, period=365, amplitude=amplitude)\\n\",\n        \"\\n\",\n        \"noise_level = 5\\n\",\n        \"noise = white_noise(time, noise_level, seed=42)\\n\",\n        \"\\n\",\n        \"series += noise\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time, series)\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Zmp1JXKxk9Vb\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"split_time = 1000\\n\",\n        \"time_train = time[:split_time]\\n\",\n        \"x_train = series[:split_time]\\n\",\n        \"time_valid = time[split_time:]\\n\",\n        \"x_valid = series[split_time:]\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"9fPenJpTtuDE\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"class ResetStatesCallback(keras.callbacks.Callback):\\n\",\n        \"    def on_epoch_begin(self, epoch, logs):\\n\",\n        \"        self.model.reset_states()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"EPjK0l9P8OJM\"\n      },\n      \"source\": [\n        \"## LSTM RNN Forecasting\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"cSoUmW-x8OJN\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.backend.clear_session()\\n\",\n        \"tf.random.set_seed(42)\\n\",\n        \"np.random.seed(42)\\n\",\n        \"\\n\",\n        \"window_size = 30\\n\",\n        \"train_set = sequential_window_dataset(x_train, window_size)\\n\",\n        \"\\n\",\n        \"model = keras.models.Sequential([\\n\",\n        \"  keras.layers.LSTM(100, return_sequences=True, stateful=True,\\n\",\n        \"                    batch_input_shape=[1, None, 1]),\\n\",\n        \"  keras.layers.LSTM(100, return_sequences=True, stateful=True),\\n\",\n        \"  keras.layers.Dense(1),\\n\",\n        \"  keras.layers.Lambda(lambda x: x * 200.0)\\n\",\n        \"])\\n\",\n        \"lr_schedule = keras.callbacks.LearningRateScheduler(\\n\",\n        \"    lambda epoch: 1e-8 * 10**(epoch / 20))\\n\",\n        \"reset_states = ResetStatesCallback()\\n\",\n        \"optimizer = keras.optimizers.SGD(lr=1e-8, momentum=0.9)\\n\",\n        \"model.compile(loss=keras.losses.Huber(),\\n\",\n        \"              optimizer=optimizer,\\n\",\n        \"              metrics=[\\\"mae\\\"])\\n\",\n        \"history = model.fit(train_set, epochs=100,\\n\",\n        \"                    callbacks=[lr_schedule, reset_states])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"KA0GM9sQ8OJO\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.semilogx(history.history[\\\"lr\\\"], history.history[\\\"loss\\\"])\\n\",\n        \"plt.axis([1e-8, 1e-4, 0, 30])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"hiHR5pPL8OJP\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.backend.clear_session()\\n\",\n        \"tf.random.set_seed(42)\\n\",\n        \"np.random.seed(42)\\n\",\n        \"\\n\",\n        \"window_size = 30\\n\",\n        \"train_set = sequential_window_dataset(x_train, window_size)\\n\",\n        \"valid_set = sequential_window_dataset(x_valid, window_size)\\n\",\n        \"\\n\",\n        \"model = keras.models.Sequential([\\n\",\n        \"  keras.layers.LSTM(100, return_sequences=True, stateful=True,\\n\",\n        \"                         batch_input_shape=[1, None, 1]),\\n\",\n        \"  keras.layers.LSTM(100, return_sequences=True, stateful=True),\\n\",\n        \"  keras.layers.Dense(1),\\n\",\n        \"  keras.layers.Lambda(lambda x: x * 200.0)\\n\",\n        \"])\\n\",\n        \"optimizer = keras.optimizers.SGD(lr=5e-7, momentum=0.9)\\n\",\n        \"model.compile(loss=keras.losses.Huber(),\\n\",\n        \"              optimizer=optimizer,\\n\",\n        \"              metrics=[\\\"mae\\\"])\\n\",\n        \"reset_states = ResetStatesCallback()\\n\",\n        \"model_checkpoint = keras.callbacks.ModelCheckpoint(\\n\",\n        \"    \\\"my_checkpoint.h5\\\", save_best_only=True)\\n\",\n        \"early_stopping = keras.callbacks.EarlyStopping(patience=50)\\n\",\n        \"model.fit(train_set, epochs=500,\\n\",\n        \"          validation_data=valid_set,\\n\",\n        \"          callbacks=[early_stopping, model_checkpoint, reset_states])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"nPeZUfQy8OJQ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = keras.models.load_model(\\\"my_checkpoint.h5\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"4tFrq5uW8OJR\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"rnn_forecast = model.predict(series[np.newaxis, :, np.newaxis])\\n\",\n        \"rnn_forecast = rnn_forecast[0, split_time - 1:-1, 0]\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"ZfaR6nqj8OJT\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time_valid, x_valid)\\n\",\n        \"plot_series(time_valid, rnn_forecast)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Wgf2u2Tp8OJV\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.metrics.mean_absolute_error(x_valid, rnn_forecast).numpy()\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [\n        \"vidayERjaO5q\"\n      ],\n      \"name\": \"l08c08_forecasting_with_lstm.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l08c09_forecasting_with_cnn.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Za8-Nr5k11fh\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"Eq10uEbw0E4l\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Tt5S6SiPi7ze\"\n      },\n      \"source\": [\n        \"# Forecasting with a CNN\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"W2iENc3Nh6g7\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l08c09_forecasting_with_cnn.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l08c09_forecasting_with_cnn.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"vidayERjaO5q\"\n      },\n      \"source\": [\n        \"## Setup\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"gqWabzlJ63nL\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import numpy as np\\n\",\n        \"import matplotlib.pyplot as plt\\n\",\n        \"import tensorflow as tf\\n\",\n        \"\\n\",\n        \"keras = tf.keras\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"cg1hfKCPldZG\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def plot_series(time, series, format=\\\"-\\\", start=0, end=None, label=None):\\n\",\n        \"    plt.plot(time[start:end], series[start:end], format, label=label)\\n\",\n        \"    plt.xlabel(\\\"Time\\\")\\n\",\n        \"    plt.ylabel(\\\"Value\\\")\\n\",\n        \"    if label:\\n\",\n        \"        plt.legend(fontsize=14)\\n\",\n        \"    plt.grid(True)\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"def trend(time, slope=0):\\n\",\n        \"    return slope * time\\n\",\n        \"  \\n\",\n        \"  \\n\",\n        \"def seasonal_pattern(season_time):\\n\",\n        \"    \\\"\\\"\\\"Just an arbitrary pattern, you can change it if you wish\\\"\\\"\\\"\\n\",\n        \"    return np.where(season_time < 0.4,\\n\",\n        \"                    np.cos(season_time * 2 * np.pi),\\n\",\n        \"                    1 / np.exp(3 * season_time))\\n\",\n        \"\\n\",\n        \"  \\n\",\n        \"def seasonality(time, period, amplitude=1, phase=0):\\n\",\n        \"    \\\"\\\"\\\"Repeats the same pattern at each period\\\"\\\"\\\"\\n\",\n        \"    season_time = ((time + phase) % period) / period\\n\",\n        \"    return amplitude * seasonal_pattern(season_time)\\n\",\n        \"  \\n\",\n        \"  \\n\",\n        \"def white_noise(time, noise_level=1, seed=None):\\n\",\n        \"    rnd = np.random.RandomState(seed)\\n\",\n        \"    return rnd.randn(len(time)) * noise_level\\n\",\n        \"  \\n\",\n        \"\\n\",\n        \"def seq2seq_window_dataset(series, window_size, batch_size=32,\\n\",\n        \"                           shuffle_buffer=1000):\\n\",\n        \"    series = tf.expand_dims(series, axis=-1)\\n\",\n        \"    ds = tf.data.Dataset.from_tensor_slices(series)\\n\",\n        \"    ds = ds.window(window_size + 1, shift=1, drop_remainder=True)\\n\",\n        \"    ds = ds.flat_map(lambda w: w.batch(window_size + 1))\\n\",\n        \"    ds = ds.shuffle(shuffle_buffer)\\n\",\n        \"    ds = ds.map(lambda w: (w[:-1], w[1:]))\\n\",\n        \"    return ds.batch(batch_size).prefetch(1)\\n\",\n        \"  \\n\",\n        \"\\n\",\n        \"def model_forecast(model, series, window_size):\\n\",\n        \"    ds = tf.data.Dataset.from_tensor_slices(series)\\n\",\n        \"    ds = ds.window(window_size, shift=1, drop_remainder=True)\\n\",\n        \"    ds = ds.flat_map(lambda w: w.batch(window_size))\\n\",\n        \"    ds = ds.batch(32).prefetch(1)\\n\",\n        \"    forecast = model.predict(ds)\\n\",\n        \"    return forecast\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"iL2DDjV3lel6\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"time = np.arange(4 * 365 + 1)\\n\",\n        \"\\n\",\n        \"slope = 0.05\\n\",\n        \"baseline = 10\\n\",\n        \"amplitude = 40\\n\",\n        \"series = baseline + trend(time, slope) + seasonality(time, period=365, amplitude=amplitude)\\n\",\n        \"\\n\",\n        \"noise_level = 5\\n\",\n        \"noise = white_noise(time, noise_level, seed=42)\\n\",\n        \"\\n\",\n        \"series += noise\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time, series)\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Zmp1JXKxk9Vb\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"split_time = 1000\\n\",\n        \"time_train = time[:split_time]\\n\",\n        \"x_train = series[:split_time]\\n\",\n        \"time_valid = time[split_time:]\\n\",\n        \"x_valid = series[split_time:]\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"DI2GlupZ8OJW\"\n      },\n      \"source\": [\n        \"## Preprocessing With 1D-Convolutional Layers\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"6GFE82ci8OJW\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.backend.clear_session()\\n\",\n        \"tf.random.set_seed(42)\\n\",\n        \"np.random.seed(42)\\n\",\n        \"\\n\",\n        \"window_size = 30\\n\",\n        \"train_set = seq2seq_window_dataset(x_train, window_size,\\n\",\n        \"                                   batch_size=128)\\n\",\n        \"\\n\",\n        \"model = keras.models.Sequential([\\n\",\n        \"  keras.layers.Conv1D(filters=32, kernel_size=5,\\n\",\n        \"                      strides=1, padding=\\\"causal\\\",\\n\",\n        \"                      activation=\\\"relu\\\",\\n\",\n        \"                      input_shape=[None, 1]),\\n\",\n        \"  keras.layers.LSTM(32, return_sequences=True),\\n\",\n        \"  keras.layers.LSTM(32, return_sequences=True),\\n\",\n        \"  keras.layers.Dense(1),\\n\",\n        \"  keras.layers.Lambda(lambda x: x * 200)\\n\",\n        \"])\\n\",\n        \"lr_schedule = keras.callbacks.LearningRateScheduler(\\n\",\n        \"    lambda epoch: 1e-8 * 10**(epoch / 20))\\n\",\n        \"optimizer = keras.optimizers.SGD(lr=1e-8, momentum=0.9)\\n\",\n        \"model.compile(loss=keras.losses.Huber(),\\n\",\n        \"              optimizer=optimizer,\\n\",\n        \"              metrics=[\\\"mae\\\"])\\n\",\n        \"history = model.fit(train_set, epochs=100, callbacks=[lr_schedule])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Uungw0H58OJX\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.semilogx(history.history[\\\"lr\\\"], history.history[\\\"loss\\\"])\\n\",\n        \"plt.axis([1e-8, 1e-4, 0, 30])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"UG7cO0yr8OJY\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.backend.clear_session()\\n\",\n        \"tf.random.set_seed(42)\\n\",\n        \"np.random.seed(42)\\n\",\n        \"\\n\",\n        \"window_size = 30\\n\",\n        \"train_set = seq2seq_window_dataset(x_train, window_size,\\n\",\n        \"                                   batch_size=128)\\n\",\n        \"valid_set = seq2seq_window_dataset(x_valid, window_size,\\n\",\n        \"                                   batch_size=128)\\n\",\n        \"\\n\",\n        \"model = keras.models.Sequential([\\n\",\n        \"  keras.layers.Conv1D(filters=32, kernel_size=5,\\n\",\n        \"                      strides=1, padding=\\\"causal\\\",\\n\",\n        \"                      activation=\\\"relu\\\",\\n\",\n        \"                      input_shape=[None, 1]),\\n\",\n        \"  keras.layers.LSTM(32, return_sequences=True),\\n\",\n        \"  keras.layers.LSTM(32, return_sequences=True),\\n\",\n        \"  keras.layers.Dense(1),\\n\",\n        \"  keras.layers.Lambda(lambda x: x * 200)\\n\",\n        \"])\\n\",\n        \"optimizer = keras.optimizers.SGD(lr=1e-5, momentum=0.9)\\n\",\n        \"model.compile(loss=keras.losses.Huber(),\\n\",\n        \"              optimizer=optimizer,\\n\",\n        \"              metrics=[\\\"mae\\\"])\\n\",\n        \"\\n\",\n        \"model_checkpoint = keras.callbacks.ModelCheckpoint(\\n\",\n        \"    \\\"my_checkpoint.h5\\\", save_best_only=True)\\n\",\n        \"early_stopping = keras.callbacks.EarlyStopping(patience=50)\\n\",\n        \"model.fit(train_set, epochs=500,\\n\",\n        \"          validation_data=valid_set,\\n\",\n        \"          callbacks=[early_stopping, model_checkpoint])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"BlqzLwfn8OJa\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = keras.models.load_model(\\\"my_checkpoint.h5\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Pj0rpT-48OJc\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"rnn_forecast = model_forecast(model, series[:,  np.newaxis], window_size)\\n\",\n        \"rnn_forecast = rnn_forecast[split_time - window_size:-1, -1, 0]\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"3vnDU8wm8OJd\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time_valid, x_valid)\\n\",\n        \"plot_series(time_valid, rnn_forecast)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"W6tWOoE88OJe\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.metrics.mean_absolute_error(x_valid, rnn_forecast).numpy()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"kfPTmghd8OJe\"\n      },\n      \"source\": [\n        \"## Fully Convolutional Forecasting\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"4-cPF5CX8OJf\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.backend.clear_session()\\n\",\n        \"tf.random.set_seed(42)\\n\",\n        \"np.random.seed(42)\\n\",\n        \"\\n\",\n        \"window_size = 64\\n\",\n        \"train_set = seq2seq_window_dataset(x_train, window_size,\\n\",\n        \"                                   batch_size=128)\\n\",\n        \"\\n\",\n        \"model = keras.models.Sequential()\\n\",\n        \"model.add(keras.layers.InputLayer(input_shape=[None, 1]))\\n\",\n        \"for dilation_rate in (1, 2, 4, 8, 16, 32):\\n\",\n        \"    model.add(\\n\",\n        \"      keras.layers.Conv1D(filters=32,\\n\",\n        \"                          kernel_size=2,\\n\",\n        \"                          strides=1,\\n\",\n        \"                          dilation_rate=dilation_rate,\\n\",\n        \"                          padding=\\\"causal\\\",\\n\",\n        \"                          activation=\\\"relu\\\")\\n\",\n        \"    )\\n\",\n        \"model.add(keras.layers.Conv1D(filters=1, kernel_size=1))\\n\",\n        \"lr_schedule = keras.callbacks.LearningRateScheduler(\\n\",\n        \"    lambda epoch: 1e-4 * 10**(epoch / 30))\\n\",\n        \"optimizer = keras.optimizers.Adam(lr=1e-4)\\n\",\n        \"model.compile(loss=keras.losses.Huber(),\\n\",\n        \"              optimizer=optimizer,\\n\",\n        \"              metrics=[\\\"mae\\\"])\\n\",\n        \"history = model.fit(train_set, epochs=100, callbacks=[lr_schedule])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"GfWVZ8k-8OJf\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.semilogx(history.history[\\\"lr\\\"], history.history[\\\"loss\\\"])\\n\",\n        \"plt.axis([1e-4, 1e-1, 0, 30])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"WVrxlzbk8OJg\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.backend.clear_session()\\n\",\n        \"tf.random.set_seed(42)\\n\",\n        \"np.random.seed(42)\\n\",\n        \"\\n\",\n        \"window_size = 64\\n\",\n        \"train_set = seq2seq_window_dataset(x_train, window_size,\\n\",\n        \"                                   batch_size=128)\\n\",\n        \"valid_set = seq2seq_window_dataset(x_valid, window_size,\\n\",\n        \"                                   batch_size=128)\\n\",\n        \"\\n\",\n        \"model = keras.models.Sequential()\\n\",\n        \"model.add(keras.layers.InputLayer(input_shape=[None, 1]))\\n\",\n        \"for dilation_rate in (1, 2, 4, 8, 16, 32):\\n\",\n        \"    model.add(\\n\",\n        \"      keras.layers.Conv1D(filters=32,\\n\",\n        \"                          kernel_size=2,\\n\",\n        \"                          strides=1,\\n\",\n        \"                          dilation_rate=dilation_rate,\\n\",\n        \"                          padding=\\\"causal\\\",\\n\",\n        \"                          activation=\\\"relu\\\")\\n\",\n        \"    )\\n\",\n        \"model.add(keras.layers.Conv1D(filters=1, kernel_size=1))\\n\",\n        \"optimizer = keras.optimizers.Adam(lr=3e-4)\\n\",\n        \"model.compile(loss=keras.losses.Huber(),\\n\",\n        \"              optimizer=optimizer,\\n\",\n        \"              metrics=[\\\"mae\\\"])\\n\",\n        \"\\n\",\n        \"model_checkpoint = keras.callbacks.ModelCheckpoint(\\n\",\n        \"    \\\"my_checkpoint.h5\\\", save_best_only=True)\\n\",\n        \"early_stopping = keras.callbacks.EarlyStopping(patience=50)\\n\",\n        \"history = model.fit(train_set, epochs=500,\\n\",\n        \"                    validation_data=valid_set,\\n\",\n        \"                    callbacks=[early_stopping, model_checkpoint])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"eNwWZB0d8OJh\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = keras.models.load_model(\\\"my_checkpoint.h5\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"PgYwn9VM8OJi\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"cnn_forecast = model_forecast(model, series[..., np.newaxis], window_size)\\n\",\n        \"cnn_forecast = cnn_forecast[split_time - window_size:-1, -1, 0]\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"MCgshNPx8OJi\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.figure(figsize=(10, 6))\\n\",\n        \"plot_series(time_valid, x_valid)\\n\",\n        \"plot_series(time_valid, cnn_forecast)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"epK1gFEN8OJj\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"keras.metrics.mean_absolute_error(x_valid, cnn_forecast).numpy()\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [\n        \"vidayERjaO5q\"\n      ],\n      \"name\": \"l08c09_forecasting_with_cnn.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l09c01_nlp_turn_words_into_tokens.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"punL79CN7Ox6\"\n      },\n      \"source\": [\n        \"##### Copyright 2020 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"_ckMIh7O7s6D\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"fFv-USWkhQKA\"\n      },\n      \"source\": [\n        \"# Tokenizing text and creating sequences for sentences\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"S5Uhzt6vVIB2\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l09c01_nlp_turn_words_into_tokens.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l09c01_nlp_turn_words_into_tokens.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"7hMGfCIDPnm8\"\n      },\n      \"source\": [\n        \"This colab shows you how to tokenize text and create sequences for sentences as the first stage of preparing text for use with TensorFlow models.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"4mGaRDFcSamt\"\n      },\n      \"source\": [\n        \"## Import the Tokenizer\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"EN1-FZodOuPl\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Import the Tokenizer\\n\",\n        \"from tensorflow.keras.preprocessing.text import Tokenizer\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"C5Qwn_7FSXW-\"\n      },\n      \"source\": [\n        \"## Write some sentences\\n\",\n        \"\\n\",\n        \"Feel free to change and add sentences as you like\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"RMiq8BpWVVRa\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"sentences = [\\n\",\n        \"    'My favorite food is ice cream',\\n\",\n        \"    'do you like ice cream too?',\\n\",\n        \"    'My dog likes ice cream!',\\n\",\n        \"    \\\"your favorite flavor of icecream is chocolate\\\",\\n\",\n        \"    \\\"chocolate isn't good for dogs\\\",\\n\",\n        \"    \\\"your dog, your cat, and your parrot prefer broccoli\\\"\\n\",\n        \"]\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"wz845OtfRBCM\"\n      },\n      \"source\": [\n        \"## Tokenize the words\\n\",\n        \"\\n\",\n        \"The first step to preparing text to be used in a machine learning model is to tokenize the text, in other words, to generate numbers for the words.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"ZHTK1DAlQ1zO\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Optionally set the max number of words to tokenize.\\n\",\n        \"# The out of vocabulary (OOV) token represents words that are not in the index.\\n\",\n        \"# Call fit_on_text() on the tokenizer to generate unique numbers for each word\\n\",\n        \"tokenizer = Tokenizer(num_words = 100, oov_token=\\\"<OOV>\\\")\\n\",\n        \"tokenizer.fit_on_texts(sentences)\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Mylv-WuiRzd0\"\n      },\n      \"source\": [\n        \"## View the word index\\n\",\n        \"After you tokenize the text, the tokenizer has a word index that contains key-value pairs for all the words and their numbers.\\n\",\n        \"\\n\",\n        \"The word is the key, and the number is the value.\\n\",\n        \"\\n\",\n        \"Notice that the OOV token is the first entry.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"kX4VvsLySC7Z\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Examine the word index\\n\",\n        \"word_index = tokenizer.word_index\\n\",\n        \"print(word_index)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"JXKrGxsIVtLo\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Get the number for a given word\\n\",\n        \"print(word_index['favorite'])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"kcN_yM8O1oSX\"\n      },\n      \"source\": [\n        \"# Create sequences for the sentences\\n\",\n        \"\\n\",\n        \"After you tokenize the words, the word index contains a unique number for each word. However, the numbers in the word index are not ordered. Words in a sentence have an order. So after tokenizing the words, the next step is to generate sequences for the sentences.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"QlUL6Ybf1sso\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"sequences = tokenizer.texts_to_sequences(sentences)\\n\",\n        \"print (sequences)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"AswZPbuW8f-f\"\n      },\n      \"source\": [\n        \"# Sequence sentences that contain words that are not in the word index\\n\",\n        \"\\n\",\n        \"Let's take a look at what happens if the sentence being sequenced contains words that are not in the word index.\\n\",\n        \"\\n\",\n        \"The Out of Vocabluary (OOV) token is the first entry in the word index. You will see it shows up in the sequences in place of any word that is not in the word index.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Fir7qd6X8eZc\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"sentences2 = [\\\"I like hot chocolate\\\", \\\"My dogs and my hedgehog like kibble but my squirrel prefers grapes and my chickens like ice cream, preferably vanilla\\\"]\\n\",\n        \"\\n\",\n        \"sequences2 = tokenizer.texts_to_sequences(sentences2)\\n\",\n        \"print(sequences2)\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l09c01_nlp_turn_words_into_tokens.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l09c02_nlp_padding.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"punL79CN7Ox6\"\n      },\n      \"source\": [\n        \"##### Copyright 2020 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"_ckMIh7O7s6D\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"RX9Yx50TUies\"\n      },\n      \"source\": [\n        \"# Preparing text to use with TensorFlow models\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"S5Uhzt6vVIB2\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l09c02_nlp_padding.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l09c02_nlp_padding.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"5_MCdtjT-bly\"\n      },\n      \"source\": [\n        \"The high level steps to prepare text to be used in a machine learning model are:\\n\",\n        \"\\n\",\n        \"1.   Tokenize the words to get numerical values for them\\n\",\n        \"2.   Create numerical sequences of the sentences\\n\",\n        \"3.   Adjust the sequences to all be the same length.\\n\",\n        \"\\n\",\n        \"In this colab, you learn how to use padding to make the sequences all be the same length.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"qJsd8KslUn7j\"\n      },\n      \"source\": [\n        \"## Import the classes you need\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"_ZxQf11OUtQI\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Import Tokenizer and pad_sequences\\n\",\n        \"from tensorflow.keras.preprocessing.text import Tokenizer\\n\",\n        \"from tensorflow.keras.preprocessing.sequence import pad_sequences\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"1MeEgRq4WX0v\"\n      },\n      \"source\": [\n        \"## Write some sentences\\n\",\n        \"\\n\",\n        \"Feel free to write your own sentences here.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"PwM7IP2lTr7T\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"sentences = [\\n\",\n        \"    'My favorite food is ice cream',\\n\",\n        \"    'do you like ice cream too?',\\n\",\n        \"    'My dog likes ice cream!',\\n\",\n        \"    \\\"your favorite flavor of icecream is chocolate\\\",\\n\",\n        \"    \\\"chocolate isn't good for dogs\\\",\\n\",\n        \"    \\\"your dog, your cat, and your parrot prefer broccoli\\\"\\n\",\n        \"]\\n\",\n        \"print(sentences)\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"jaRa9opNWmA7\"\n      },\n      \"source\": [\n        \"## Create the Tokenizer and define an out of vocabulary token\\n\",\n        \"When creating the Tokenizer, you can specify the max number of words in the dictionary. You can also specify a token to represent words that are out of the vocabulary (OOV), in other words, that are not in the dictionary. This OOV token will be used when you create sequences for sentences that contain words that are not in the word index.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"P7wuOJaBWiHZ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"tokenizer = Tokenizer(num_words = 100, oov_token=\\\"<OOV>\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"r7nILrhKXPge\"\n      },\n      \"source\": [\n        \"## Tokenize the words\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"YXooiuwrXROU\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"tokenizer.fit_on_texts(sentences)\\n\",\n        \"word_index = tokenizer.word_index\\n\",\n        \"print(word_index)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"0U-oe201Xm7T\"\n      },\n      \"source\": [\n        \"## Turn sentences into sequences \\n\",\n        \"\\n\",\n        \"Each word now has a unique number in the word index.  However, words in a sentence are in a specific order. You can't just randomly mix up words and have the outcome be a sentence.\\n\",\n        \"\\n\",\n        \"For example, although \\\"chocolate isn't good for dogs\\\" is a perfectly fine sentence, \\\"dogs isn't for chocolate good\\\" does not make sense as a sentence.\\n\",\n        \"\\n\",\n        \"So the next step to representing text in a way that can be meaningfully used by machine learning programs is to create numerical sequences that represent the sentences in the text.\\n\",\n        \"\\n\",\n        \"Each sentence will be converted into a sequence where each word is replaced by its number in the word index. \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"70l5x1XRXoV4\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"sequences = tokenizer.texts_to_sequences(sentences)\\n\",\n        \"print (sequences)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"tcFghvQ34cZK\"\n      },\n      \"source\": [\n        \"## Make the sequences all the same length\\n\",\n        \"\\n\",\n        \"Later, when you feed the sequences into a neural network to train a model, the sequences all need to be uniform in size. Currently the sequences have varied lengths, so the next step is to make them all be the same size, either by padding them with zeros and/or truncating them.\\n\",\n        \"\\n\",\n        \"Use f.keras.preprocessing.sequence.pad_sequences to add zeros to the sequences to make them all be the same length. By default, the padding goes at the start of the sequences, but you can specify to pad at the end.\\n\",\n        \"\\n\",\n        \"You can optionally specify the maximum length to pad the sequences to. Sequences that are longer than the specified max length will be truncated. By default, sequences are truncated from the beginning of the sequence, but you can specify to truncate from the end.\\n\",\n        \"\\n\",\n        \"If you don't provide the max length, then the sequences are padded to match the length of the longest sentence.\\n\",\n        \"\\n\",\n        \"For all the options when padding and truncating sequences, see https://www.tensorflow.org/api_docs/python/tf/keras/preprocessing/sequence/pad_sequences\\n\",\n        \"\\n\",\n        \" \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"r0m_nqHg4gqu\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"padded = pad_sequences(sequences)\\n\",\n        \"print(\\\"\\\\nWord Index = \\\" , word_index)\\n\",\n        \"print(\\\"\\\\nSequences = \\\" , sequences)\\n\",\n        \"print(\\\"\\\\nPadded Sequences:\\\")\\n\",\n        \"print(padded)\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"VzbGtYWQ6Ofo\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Specify a max length for the padded sequences\\n\",\n        \"padded = pad_sequences(sequences, maxlen=15)\\n\",\n        \"print(padded)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"HzkbHi0B64w8\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Put the padding at the end of the sequences\\n\",\n        \"padded = pad_sequences(sequences, maxlen=15, padding=\\\"post\\\")\\n\",\n        \"print(padded)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"OLHheI477okX\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Limit the length of the sequences, you will see some sequences get truncated\\n\",\n        \"padded = pad_sequences(sequences, maxlen=3)\\n\",\n        \"print(padded)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"OnRKDsR197-J\"\n      },\n      \"source\": [\n        \"## What happens if some of the sentences contain words that are not in the word index?\\n\",\n        \"\\n\",\n        \"Here's where the \\\"out of vocabulary\\\" token is used. Try generating sequences for some sentences that have words that are not in the word index.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"iqodOpn64c2U\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Try turning sentences that contain words that \\n\",\n        \"# aren't in the word index into sequences.\\n\",\n        \"# Add your own sentences to the test_data\\n\",\n        \"test_data = [\\n\",\n        \"    \\\"my best friend's favorite ice cream flavor is strawberry\\\",\\n\",\n        \"    \\\"my dog's best friend is a manatee\\\"\\n\",\n        \"]\\n\",\n        \"print (test_data)\\n\",\n        \"\\n\",\n        \"# Remind ourselves which number corresponds to the\\n\",\n        \"# out of vocabulary token in the word index\\n\",\n        \"print(\\\"<OOV> has the number\\\", word_index['<OOV>'], \\\"in the word index.\\\")\\n\",\n        \"\\n\",\n        \"# Convert the test sentences to sequences\\n\",\n        \"test_seq = tokenizer.texts_to_sequences(test_data)\\n\",\n        \"print(\\\"\\\\nTest Sequence = \\\", test_seq)\\n\",\n        \"\\n\",\n        \"# Pad the new sequences\\n\",\n        \"padded = pad_sequences(test_seq, maxlen=10)\\n\",\n        \"print(\\\"\\\\nPadded Test Sequence: \\\")\\n\",\n        \"\\n\",\n        \"# Notice that \\\"1\\\" appears in the sequence wherever there's a word \\n\",\n        \"# that's not in the word index\\n\",\n        \"print(padded)\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l09c02_nlp_padding.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l09c03_nlp_prepare_larger_text_corpus.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"punL79CN7Ox6\"\n      },\n      \"source\": [\n        \"##### Copyright 2020 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"_ckMIh7O7s6D\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"qzwilbae73N4\"\n      },\n      \"source\": [\n        \"# Tokenize and sequence a bigger corpus of text\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"S5Uhzt6vVIB2\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l09c03_nlp_prepare_larger_text_corpus.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l09c03_nlp_prepare_larger_text_corpus.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"VcB-N6WrAT9q\"\n      },\n      \"source\": [\n        \"So far, you have written some test sentences and generated a word index and then created sequences for the sentences. \\n\",\n        \"\\n\",\n        \"Now you will tokenize and sequence a larger body of text, specifically reviews from Amazon and Yelp. \\n\",\n        \"\\n\",\n        \"## About the dataset\\n\",\n        \"\\n\",\n        \"You will use a dataset containing Amazon and Yelp reviews of products and restaurants. This dataset was originally extracted from [Kaggle](https://www.kaggle.com/marklvl/sentiment-labelled-sentences-data-set).\\n\",\n        \"\\n\",\n        \"The dataset includes reviews, and each review is labelled as 0 (bad) or 1 (good). However, in this exercise, you will only work with the reviews, not the labels, to practice tokenizing and sequencing the text. \\n\",\n        \"\\n\",\n        \"### Example good reviews:\\n\",\n        \"\\n\",\n        \"*   This is hands down the best phone I've ever had.\\n\",\n        \"*   Four stars for the food & the guy in the blue shirt for his great vibe & still letting us in to eat !\\n\",\n        \"\\n\",\n        \"### Example bad reviews:  \\n\",\n        \"\\n\",\n        \"*   A lady at the table next to us found a live green caterpillar In her salad\\n\",\n        \"*   If you plan to use this in a car forget about it.\\n\",\n        \"\\n\",\n        \"### See more reviews\\n\",\n        \"Feel free to [download the dataset](https://drive.google.com/uc?id=13ySLC_ue6Umt9RJYSeM2t-V0kCv-4C-P) from a drive folder belonging to Udacity and open it on your local machine to see more reviews.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"wr21SvWhQhvN\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Import Tokenizer and pad_sequences\\n\",\n        \"import tensorflow as tf\\n\",\n        \"from tensorflow.keras.preprocessing.text import Tokenizer\\n\",\n        \"from tensorflow.keras.preprocessing.sequence import pad_sequences\\n\",\n        \"\\n\",\n        \"# Import numpy and pandas\\n\",\n        \"import numpy as np\\n\",\n        \"import pandas as pd\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"cJOCSbdERsdc\"\n      },\n      \"source\": [\n        \"# Get the corpus of text\\n\",\n        \"\\n\",\n        \"The combined dataset of reviews has been saved in a Google drive belonging to Udacity. You can download it from there.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"kBpFip-X69Hf\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"path = tf.keras.utils.get_file('reviews.csv', \\n\",\n        \"                               'https://drive.google.com/uc?id=13ySLC_ue6Umt9RJYSeM2t-V0kCv-4C-P')\\n\",\n        \"print (path)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ZCT57MVGTENX\"\n      },\n      \"source\": [\n        \"# Get the dataset\\n\",\n        \"\\n\",\n        \"Each row in the csv file is a separate review.\\n\",\n        \"\\n\",\n        \"The csv file has 2 columns:\\n\",\n        \"\\n\",\n        \"*   **text** (the review)\\n\",\n        \"*   **sentiment** (0 or 1 indicating a bad or good review)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"TlyreClyS7H3\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Read the csv file\\n\",\n        \"dataset = pd.read_csv(path)\\n\",\n        \"\\n\",\n        \"# Review the first few entries in the dataset\\n\",\n        \"dataset.head()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Fk5uzq4Oco7h\"\n      },\n      \"source\": [\n        \"# Get the reviews from the csv file\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"u7uCBlAqdEzK\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Get the reviews from the text column\\n\",\n        \"reviews = dataset['text'].tolist()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"OS0mg5yoVzQL\"\n      },\n      \"source\": [\n        \"# Tokenize the text\\n\",\n        \"Create the tokenizer, specify the OOV token, tokenize the text, then inspect the word index.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"atgLJzAiVwqB\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"tokenizer = Tokenizer(oov_token=\\\"<OOV>\\\")\\n\",\n        \"tokenizer.fit_on_texts(reviews)\\n\",\n        \"\\n\",\n        \"word_index = tokenizer.word_index\\n\",\n        \"print(len(word_index))\\n\",\n        \"print(word_index)\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Vfh0WGmKWyjI\"\n      },\n      \"source\": [\n        \"# Generate sequences for the reviews\\n\",\n        \"Generate a sequence for each review. Set the max length to match the longest review. Add the padding zeros at the end of the review for reviews that are not as long as the longest one.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"VwyqBS2nV53o\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"sequences = tokenizer.texts_to_sequences(reviews)\\n\",\n        \"padded_sequences = pad_sequences(sequences, padding='post')\\n\",\n        \"\\n\",\n        \"# What is the shape of the vector containing the padded sequences?\\n\",\n        \"# The shape shows the number of sequences and the length of each one.\\n\",\n        \"print(padded_sequences.shape)\\n\",\n        \"\\n\",\n        \"# What is the first review?\\n\",\n        \"print (reviews[0])\\n\",\n        \"\\n\",\n        \"# Show the sequence for the first review\\n\",\n        \"print(padded_sequences[0])\\n\",\n        \"\\n\",\n        \"# Try printing the review and padded sequence for other elements.\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"name\": \"l09c03_nlp_prepare_larger_text_corpus.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l09c04_nlp_embeddings_and_sentiment.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"punL79CN7Ox6\"\n      },\n      \"source\": [\n        \"##### Copyright 2020 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"_ckMIh7O7s6D\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"9uBA1i1BbiJn\"\n      },\n      \"source\": [\n        \"# Word Embeddings and Sentiment\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"S5Uhzt6vVIB2\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l09c04_nlp_embeddings_and_sentiment.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l09c04_nlp_embeddings_and_sentiment.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"1iLe4E0dB7tj\"\n      },\n      \"source\": [\n        \"In this colab, you'll work with word embeddings and train a basic neural network to predict text sentiment. At the end, you'll be able to visualize how the network sees the related sentiment of each word in the dataset.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"wqvz1jVgbwIN\"\n      },\n      \"source\": [\n        \"## Import TensorFlow and related functions\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"XIG52aKPdpux\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow as tf\\n\",\n        \"\\n\",\n        \"from tensorflow.keras.preprocessing.text import Tokenizer\\n\",\n        \"from tensorflow.keras.preprocessing.sequence import pad_sequences\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"pazU5OmxehIA\"\n      },\n      \"source\": [\n        \"## Get the dataset\\n\",\n        \"\\n\",\n        \"We're going to use a dataset containing Amazon and Yelp reviews, with their related sentiment (1 for positive, 0 for negative). This dataset was originally extracted from [here](https://www.kaggle.com/marklvl/sentiment-labelled-sentences-data-set).\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"qpwQT2E9ez5B\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!wget --no-check-certificate \\\\\\n\",\n        \"    -O /tmp/sentiment.csv https://drive.google.com/uc?id=13ySLC_ue6Umt9RJYSeM2t-V0kCv-4C-P\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"6Zvp9NScfMnw\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import numpy as np\\n\",\n        \"import pandas as pd\\n\",\n        \"\\n\",\n        \"dataset = pd.read_csv('/tmp/sentiment.csv')\\n\",\n        \"\\n\",\n        \"sentences = dataset['text'].tolist()\\n\",\n        \"labels = dataset['sentiment'].tolist()\\n\",\n        \"\\n\",\n        \"# Separate out the sentences and labels into training and test sets\\n\",\n        \"training_size = int(len(sentences) * 0.8)\\n\",\n        \"\\n\",\n        \"training_sentences = sentences[0:training_size]\\n\",\n        \"testing_sentences = sentences[training_size:]\\n\",\n        \"training_labels = labels[0:training_size]\\n\",\n        \"testing_labels = labels[training_size:]\\n\",\n        \"\\n\",\n        \"# Make labels into numpy arrays for use with the network later\\n\",\n        \"training_labels_final = np.array(training_labels)\\n\",\n        \"testing_labels_final = np.array(testing_labels)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"NHpvqaSigcST\"\n      },\n      \"source\": [\n        \"## Tokenize the dataset\\n\",\n        \"\\n\",\n        \"Tokenize the dataset, including padding and OOV\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"78icewYRgfxh\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"vocab_size = 1000\\n\",\n        \"embedding_dim = 16\\n\",\n        \"max_length = 100\\n\",\n        \"trunc_type='post'\\n\",\n        \"padding_type='post'\\n\",\n        \"oov_tok = \\\"<OOV>\\\"\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"from tensorflow.keras.preprocessing.text import Tokenizer\\n\",\n        \"from tensorflow.keras.preprocessing.sequence import pad_sequences\\n\",\n        \"\\n\",\n        \"tokenizer = Tokenizer(num_words = vocab_size, oov_token=oov_tok)\\n\",\n        \"tokenizer.fit_on_texts(training_sentences)\\n\",\n        \"word_index = tokenizer.word_index\\n\",\n        \"sequences = tokenizer.texts_to_sequences(training_sentences)\\n\",\n        \"padded = pad_sequences(sequences,maxlen=max_length, padding=padding_type, \\n\",\n        \"                       truncating=trunc_type)\\n\",\n        \"\\n\",\n        \"testing_sequences = tokenizer.texts_to_sequences(testing_sentences)\\n\",\n        \"testing_padded = pad_sequences(testing_sequences,maxlen=max_length, \\n\",\n        \"                               padding=padding_type, truncating=trunc_type)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"q4yIEk_8kszh\"\n      },\n      \"source\": [\n        \"## Review a Sequence\\n\",\n        \"\\n\",\n        \"Let's quickly take a look at one of the padded sequences to ensure everything above worked appropriately.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"JTU3FmVGk100\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])\\n\",\n        \"\\n\",\n        \"def decode_review(text):\\n\",\n        \"    return ' '.join([reverse_word_index.get(i, '?') for i in text])\\n\",\n        \"\\n\",\n        \"print(decode_review(padded[1]))\\n\",\n        \"print(training_sentences[1])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"RI91liJnlA92\"\n      },\n      \"source\": [\n        \"## Train a Basic Sentiment Model with Embeddings\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"bBMgzp-_lMTp\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Build a basic sentiment network\\n\",\n        \"# Note the embedding layer is first, \\n\",\n        \"# and the output is only 1 node as it is either 0 or 1 (negative or positive)\\n\",\n        \"model = tf.keras.Sequential([\\n\",\n        \"    tf.keras.layers.Embedding(vocab_size, embedding_dim, input_length=max_length),\\n\",\n        \"    tf.keras.layers.Flatten(),\\n\",\n        \"    tf.keras.layers.Dense(6, activation='relu'),\\n\",\n        \"    tf.keras.layers.Dense(1, activation='sigmoid')\\n\",\n        \"])\\n\",\n        \"model.compile(loss='binary_crossentropy',optimizer='adam',metrics=['accuracy'])\\n\",\n        \"model.summary()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Pfl1W-zVldpn\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"num_epochs = 10\\n\",\n        \"model.fit(padded, training_labels_final, epochs=num_epochs, validation_data=(testing_padded, testing_labels_final))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"GjMZ4ZFQl_48\"\n      },\n      \"source\": [\n        \"## Get files for visualizing the network\\n\",\n        \"\\n\",\n        \"The code below will download two files for visualizing how your network \\\"sees\\\" the sentiment related to each word. Head to http://projector.tensorflow.org/ and load these files, then click the \\\"Sphereize\\\" checkbox.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"S2lB46FirAVx\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# First get the weights of the embedding layer\\n\",\n        \"e = model.layers[0]\\n\",\n        \"weights = e.get_weights()[0]\\n\",\n        \"print(weights.shape) # shape: (vocab_size, embedding_dim)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Xcha0oGemHX2\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import io\\n\",\n        \"\\n\",\n        \"# Write out the embedding vectors and metadata\\n\",\n        \"out_v = io.open('vecs.tsv', 'w', encoding='utf-8')\\n\",\n        \"out_m = io.open('meta.tsv', 'w', encoding='utf-8')\\n\",\n        \"for word_num in range(1, vocab_size):\\n\",\n        \"  word = reverse_word_index[word_num]\\n\",\n        \"  embeddings = weights[word_num]\\n\",\n        \"  out_m.write(word + \\\"\\\\n\\\")\\n\",\n        \"  out_v.write('\\\\t'.join([str(x) for x in embeddings]) + \\\"\\\\n\\\")\\n\",\n        \"out_v.close()\\n\",\n        \"out_m.close()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"g-Q6ALywmWVz\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Download the files\\n\",\n        \"try:\\n\",\n        \"  from google.colab import files\\n\",\n        \"except ImportError:\\n\",\n        \"  pass\\n\",\n        \"else:\\n\",\n        \"  files.download('vecs.tsv')\\n\",\n        \"  files.download('meta.tsv')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"GNoxfY-i3Ir1\"\n      },\n      \"source\": [\n        \"## Predicting Sentiment in New Reviews\\n\",\n        \"\\n\",\n        \"Now that you've trained and visualized your network, take a look below at how we can predict sentiment in new reviews the network has never seen before.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"QXtfw-OY3WoZ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Use the model to predict a review   \\n\",\n        \"fake_reviews = ['I love this phone', 'I hate spaghetti', \\n\",\n        \"                'Everything was cold',\\n\",\n        \"                'Everything was hot exactly as I wanted', \\n\",\n        \"                'Everything was green', \\n\",\n        \"                'the host seated us immediately',\\n\",\n        \"                'they gave us free chocolate cake', \\n\",\n        \"                'not sure about the wilted flowers on the table',\\n\",\n        \"                'only works when I stand on tippy toes', \\n\",\n        \"                'does not work when I stand on my head']\\n\",\n        \"\\n\",\n        \"print(fake_reviews) \\n\",\n        \"\\n\",\n        \"# Create the sequences\\n\",\n        \"padding_type='post'\\n\",\n        \"sample_sequences = tokenizer.texts_to_sequences(fake_reviews)\\n\",\n        \"fakes_padded = pad_sequences(sample_sequences, padding=padding_type, maxlen=max_length)           \\n\",\n        \"\\n\",\n        \"print('\\\\nHOT OFF THE PRESS! HERE ARE SOME NEWLY MINTED, ABSOLUTELY GENUINE REVIEWS!\\\\n')              \\n\",\n        \"\\n\",\n        \"classes = model.predict(fakes_padded)\\n\",\n        \"\\n\",\n        \"# The closer the class is to 1, the more positive the review is deemed to be\\n\",\n        \"for x in range(len(fake_reviews)):\\n\",\n        \"  print(fake_reviews[x])\\n\",\n        \"  print(classes[x])\\n\",\n        \"  print('\\\\n')\\n\",\n        \"\\n\",\n        \"# Try adding reviews of your own\\n\",\n        \"# Add some negative words (such as \\\"not\\\") to the good reviews and see what happens\\n\",\n        \"# For example:\\n\",\n        \"# they gave us free chocolate cake and did not charge us\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l09c04_nlp_embeddings_and_sentiment.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l09c05_nlp_tweaking_the_model.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"punL79CN7Ox6\"\n      },\n      \"source\": [\n        \"##### Copyright 2020 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"_ckMIh7O7s6D\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"QrxSyyyhygUR\"\n      },\n      \"source\": [\n        \"# Tweaking the Model\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"S5Uhzt6vVIB2\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l09c05_nlp_tweaking_the_model.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l09c05_nlp_tweaking_the_model.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"xiWacy71Cu54\"\n      },\n      \"source\": [\n        \"In this colab, you'll investigate how various tweaks to data processing and the model itself can impact results. At the end, you'll once again be able to visualize how the network sees the related sentiment of each word in the dataset.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"hY-fjvwfy2P9\"\n      },\n      \"source\": [\n        \"## Import TensorFlow and related functions\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"drsUfVVXyxJl\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow as tf\\n\",\n        \"\\n\",\n        \"from tensorflow.keras.preprocessing.text import Tokenizer\\n\",\n        \"from tensorflow.keras.preprocessing.sequence import pad_sequences\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ZIf1N46jy6Ed\"\n      },\n      \"source\": [\n        \"## Get the dataset\\n\",\n        \"\\n\",\n        \"We'll once again use the dataset containing Amazon and Yelp reviews. This dataset was originally extracted from [here](https://www.kaggle.com/marklvl/sentiment-labelled-sentences-data-set).\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"m83g42sJzGO0\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!wget --no-check-certificate \\\\\\n\",\n        \"    https://drive.google.com/uc?id=13ySLC_ue6Umt9RJYSeM2t-V0kCv-4C-P \\\\\\n\",\n        \"    -O /tmp/sentiment.csv\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"y4e6GG2CzJUq\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import numpy as np\\n\",\n        \"import pandas as pd\\n\",\n        \"\\n\",\n        \"dataset = pd.read_csv('/tmp/sentiment.csv')\\n\",\n        \"\\n\",\n        \"sentences = dataset['text'].tolist()\\n\",\n        \"labels = dataset['sentiment'].tolist()\\n\",\n        \"\\n\",\n        \"# Separate out the sentences and labels into training and test sets\\n\",\n        \"training_size = int(len(sentences) * 0.8)\\n\",\n        \"\\n\",\n        \"training_sentences = sentences[0:training_size]\\n\",\n        \"testing_sentences = sentences[training_size:]\\n\",\n        \"training_labels = labels[0:training_size]\\n\",\n        \"testing_labels = labels[training_size:]\\n\",\n        \"\\n\",\n        \"# Make labels into numpy arrays for use with the network later\\n\",\n        \"training_labels_final = np.array(training_labels)\\n\",\n        \"testing_labels_final = np.array(testing_labels)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"drDkTFMuzW6N\"\n      },\n      \"source\": [\n        \"## Tokenize the dataset (with tweaks!)\\n\",\n        \"\\n\",\n        \"Now, we'll tokenize the dataset, but we can make some changes to this from before. Previously, we used: \\n\",\n        \"```\\n\",\n        \"vocab_size = 1000\\n\",\n        \"embedding_dim = 16\\n\",\n        \"max_length = 100\\n\",\n        \"trunc_type='post'\\n\",\n        \"padding_type='post'\\n\",\n        \"```\\n\",\n        \"\\n\",\n        \"How might changing the `vocab_size`, `embedding_dim` or `max_length` affect how the model performs?\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"hjPUJFhQzuee\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"vocab_size = 500\\n\",\n        \"embedding_dim = 16\\n\",\n        \"max_length = 50\\n\",\n        \"trunc_type='post'\\n\",\n        \"padding_type='post'\\n\",\n        \"oov_tok = \\\"<OOV>\\\"\\n\",\n        \"\\n\",\n        \"tokenizer = Tokenizer(num_words = vocab_size, oov_token=oov_tok)\\n\",\n        \"tokenizer.fit_on_texts(training_sentences)\\n\",\n        \"word_index = tokenizer.word_index\\n\",\n        \"training_sequences = tokenizer.texts_to_sequences(training_sentences)\\n\",\n        \"training_padded = pad_sequences(training_sequences, maxlen=max_length, padding=padding_type, truncating=trunc_type)\\n\",\n        \"\\n\",\n        \"testing_sequences = tokenizer.texts_to_sequences(testing_sentences)\\n\",\n        \"testing_padded = pad_sequences(testing_sequences, maxlen=max_length, padding=padding_type, truncating=trunc_type)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"FwFjO1kg0UUK\"\n      },\n      \"source\": [\n        \"## Train a Sentiment Model (with tweaks!)\\n\",\n        \"\\n\",\n        \"We'll use a slightly different model here, using `GlobalAveragePooling1D` instead of `Flatten()`.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"ectP92fl0dFO\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = tf.keras.Sequential([\\n\",\n        \"    tf.keras.layers.Embedding(vocab_size, embedding_dim, input_length=max_length),\\n\",\n        \"    tf.keras.layers.GlobalAveragePooling1D(),\\n\",\n        \"    tf.keras.layers.Dense(6, activation='relu'),\\n\",\n        \"    tf.keras.layers.Dense(1, activation='sigmoid')\\n\",\n        \"])\\n\",\n        \"model.compile(loss='binary_crossentropy',optimizer='adam',metrics=['accuracy'])\\n\",\n        \"model.summary()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"7TQIaGjs073w\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"num_epochs = 30\\n\",\n        \"history = model.fit(training_padded, training_labels_final, epochs=num_epochs, validation_data=(testing_padded, testing_labels_final))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"alAlYort7gWV\"\n      },\n      \"source\": [\n        \"## Visualize the training graph\\n\",\n        \"\\n\",\n        \"You can use the code below to visualize the training and validation accuracy while you try out different tweaks to the hyperparameters and model.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"o9l5vBeU71vH\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import matplotlib.pyplot as plt\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"def plot_graphs(history, string):\\n\",\n        \"  plt.plot(history.history[string])\\n\",\n        \"  plt.plot(history.history['val_'+string])\\n\",\n        \"  plt.xlabel(\\\"Epochs\\\")\\n\",\n        \"  plt.ylabel(string)\\n\",\n        \"  plt.legend([string, 'val_'+string])\\n\",\n        \"  plt.show()\\n\",\n        \"  \\n\",\n        \"plot_graphs(history, \\\"accuracy\\\")\\n\",\n        \"plot_graphs(history, \\\"loss\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"SZzXE-pT8K57\"\n      },\n      \"source\": [\n        \"## Get files for visualizing the network\\n\",\n        \"\\n\",\n        \"The code below will download two files for visualizing how your network \\\"sees\\\" the sentiment related to each word. Head to http://projector.tensorflow.org/ and load these files, then click the checkbox to \\\"sphereize\\\" the data.\\n\",\n        \"\\n\",\n        \"Note: You may run into errors with the projection if your `vocab_size` earlier was larger than the actual number of words in the vocabulary, in which case you'll need to decrease this variable and re-train in order to visualize.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"2Ex4o7Lc8Njl\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# First get the weights of the embedding layer\\n\",\n        \"e = model.layers[0]\\n\",\n        \"weights = e.get_weights()[0]\\n\",\n        \"print(weights.shape) # shape: (vocab_size, embedding_dim)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"bUL1zk5p8WIV\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import io\\n\",\n        \"\\n\",\n        \"# Create the reverse word index\\n\",\n        \"reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])\\n\",\n        \"\\n\",\n        \"# Write out the embedding vectors and metadata\\n\",\n        \"out_v = io.open('vecs.tsv', 'w', encoding='utf-8')\\n\",\n        \"out_m = io.open('meta.tsv', 'w', encoding='utf-8')\\n\",\n        \"for word_num in range(1, vocab_size):\\n\",\n        \"  word = reverse_word_index[word_num]\\n\",\n        \"  embeddings = weights[word_num]\\n\",\n        \"  out_m.write(word + \\\"\\\\n\\\")\\n\",\n        \"  out_v.write('\\\\t'.join([str(x) for x in embeddings]) + \\\"\\\\n\\\")\\n\",\n        \"out_v.close()\\n\",\n        \"out_m.close()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"lqyV8QYnD46U\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Download the files\\n\",\n        \"try:\\n\",\n        \"  from google.colab import files\\n\",\n        \"except ImportError:\\n\",\n        \"  pass\\n\",\n        \"else:\\n\",\n        \"  files.download('vecs.tsv')\\n\",\n        \"  files.download('meta.tsv')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"XUXAlNNk59gG\"\n      },\n      \"source\": [\n        \"## Predicting Sentiment in New Reviews\\n\",\n        \"\\n\",\n        \"Below, we've again included some example new reviews you can test your results on.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"JbFTTcaK6Dan\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Use the model to predict a review   \\n\",\n        \"fake_reviews = ['I love this phone', 'I hate spaghetti', \\n\",\n        \"                'Everything was cold',\\n\",\n        \"                'Everything was hot exactly as I wanted', \\n\",\n        \"                'Everything was green', \\n\",\n        \"                'the host seated us immediately',\\n\",\n        \"                'they gave us free chocolate cake', \\n\",\n        \"                'not sure about the wilted flowers on the table',\\n\",\n        \"                'only works when I stand on tippy toes', \\n\",\n        \"                'does not work when I stand on my head']\\n\",\n        \"\\n\",\n        \"print(fake_reviews) \\n\",\n        \"\\n\",\n        \"# Create the sequences\\n\",\n        \"padding_type='post'\\n\",\n        \"sample_sequences = tokenizer.texts_to_sequences(fake_reviews)\\n\",\n        \"fakes_padded = pad_sequences(sample_sequences, padding=padding_type, maxlen=max_length)           \\n\",\n        \"\\n\",\n        \"print('\\\\nHOT OFF THE PRESS! HERE ARE SOME NEWLY MINTED, ABSOLUTELY GENUINE REVIEWS!\\\\n')              \\n\",\n        \"\\n\",\n        \"classes = model.predict(fakes_padded)\\n\",\n        \"\\n\",\n        \"# The closer the class is to 1, the more positive the review is deemed to be\\n\",\n        \"for x in range(len(fake_reviews)):\\n\",\n        \"  print(fake_reviews[x])\\n\",\n        \"  print(classes[x])\\n\",\n        \"  print('\\\\n')\\n\",\n        \"\\n\",\n        \"# Try adding reviews of your own\\n\",\n        \"# Add some negative words (such as \\\"not\\\") to the good reviews and see what happens\\n\",\n        \"# For example:\\n\",\n        \"# they gave us free chocolate cake and did not charge us\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l09c05_nlp_tweaking_the_model.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l09c06_nlp_subwords.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"punL79CN7Ox6\"\n      },\n      \"source\": [\n        \"##### Copyright 2020 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"_ckMIh7O7s6D\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"CH5gnvxl-N3U\"\n      },\n      \"source\": [\n        \"# What's in a (sub)word?\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"S5Uhzt6vVIB2\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l09c06_nlp_subwords.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l09c06_nlp_subwords.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ykxAKKa1Dl0s\"\n      },\n      \"source\": [\n        \"In this colab, we'll work with subwords, or words made up of the pieces of larger words, and see how that impacts our network and related embeddings.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"QQCr_NAT-g5w\"\n      },\n      \"source\": [\n        \"## Import TensorFlow and related functions\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Q8Wa_ZlX-mPH\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow as tf\\n\",\n        \"\\n\",\n        \"from tensorflow.keras.preprocessing.sequence import pad_sequences\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"MRHk-4Te-yLJ\"\n      },\n      \"source\": [\n        \"## Get the original dataset\\n\",\n        \"\\n\",\n        \"We'll once again use the dataset containing Amazon and Yelp reviews. This dataset was originally extracted from [here](https://www.kaggle.com/marklvl/sentiment-labelled-sentences-data-set).\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"XJAxrOLi-02C\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!wget --no-check-certificate \\\\\\n\",\n        \"    https://drive.google.com/uc?id=13ySLC_ue6Umt9RJYSeM2t-V0kCv-4C-P \\\\\\n\",\n        \"    -O /tmp/sentiment.csv\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Dr-EDUKP_HBl\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import pandas as pd\\n\",\n        \"\\n\",\n        \"dataset = pd.read_csv('/tmp/sentiment.csv')\\n\",\n        \"\\n\",\n        \"# Just extract out sentences and labels first - we will create subwords here\\n\",\n        \"sentences = dataset['text'].tolist()\\n\",\n        \"labels = dataset['sentiment'].tolist()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"8zut9Wng_R3B\"\n      },\n      \"source\": [\n        \"## Create a subwords dataset\\n\",\n        \"\\n\",\n        \"We can use the existing Amazon and Yelp reviews dataset with `tensorflow_datasets`'s `SubwordTextEncoder` functionality. `SubwordTextEncoder.build_from_corpus()` will create a tokenizer for us. You could also use this functionality to get subwords from a much larger corpus of text as well, but we'll just use our existing dataset here.\\n\",\n        \"\\n\",\n        \"The Amazon and Yelp dataset we are using isn't super large, so we'll create a subword `vocab_size` of only the 1,000 most common words, as well as cutting off each subword to be at most 5 characters.\\n\",\n        \"\\n\",\n        \"Check out the related documentation [here](https://www.tensorflow.org/datasets/api_docs/python/tfds/features/text/SubwordTextEncoder#build_from_corpus).\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"aElsgxia_43g\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow_datasets as tfds\\n\",\n        \"\\n\",\n        \"vocab_size = 1000\\n\",\n        \"tokenizer = tfds.deprecated.text.SubwordTextEncoder.build_from_corpus(sentences, vocab_size, max_subword_length=5)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"0XNZWGKqBDc3\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Check that the tokenizer works appropriately\\n\",\n        \"num = 5\\n\",\n        \"print(sentences[num])\\n\",\n        \"encoded = tokenizer.encode(sentences[num])\\n\",\n        \"print(encoded)\\n\",\n        \"# Separately print out each subword, decoded\\n\",\n        \"for i in encoded:\\n\",\n        \"  print(tokenizer.decode([i]))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"gYnbqctXGKcC\"\n      },\n      \"source\": [\n        \"## Replace sentence data with encoded subwords\\n\",\n        \"\\n\",\n        \"Now, we'll re-create the dataset to be used for training by actually encoding each of the individual sentences. This is equivalent to `text_to_sequences` with the `Tokenizer` we used in earlier exercises.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"rAmql34aGfeV\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"for i, sentence in enumerate(sentences):\\n\",\n        \"  sentences[i] = tokenizer.encode(sentence)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"jNnee_csG5Iz\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Check the sentences are appropriately replaced\\n\",\n        \"print(sentences[1])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"zpIigjecHVkF\"\n      },\n      \"source\": [\n        \"## Final pre-processing\\n\",\n        \"\\n\",\n        \"Before training, we still need to pad the sequences, as well as split into training and test sets.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"INIFSAcEHool\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import numpy as np\\n\",\n        \"\\n\",\n        \"max_length = 50\\n\",\n        \"trunc_type='post'\\n\",\n        \"padding_type='post'\\n\",\n        \"\\n\",\n        \"# Pad all sentences\\n\",\n        \"sentences_padded = pad_sequences(sentences, maxlen=max_length, \\n\",\n        \"                                 padding=padding_type, truncating=trunc_type)\\n\",\n        \"\\n\",\n        \"# Separate out the sentences and labels into training and test sets\\n\",\n        \"training_size = int(len(sentences) * 0.8)\\n\",\n        \"\\n\",\n        \"training_sentences = sentences_padded[0:training_size]\\n\",\n        \"testing_sentences = sentences_padded[training_size:]\\n\",\n        \"training_labels = labels[0:training_size]\\n\",\n        \"testing_labels = labels[training_size:]\\n\",\n        \"\\n\",\n        \"# Make labels into numpy arrays for use with the network later\\n\",\n        \"training_labels_final = np.array(training_labels)\\n\",\n        \"testing_labels_final = np.array(testing_labels)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"QC9A-sTpPPiL\"\n      },\n      \"source\": [\n        \"## Train a Sentiment Model\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"eDKcL64IPcfy\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"embedding_dim = 16\\n\",\n        \"model = tf.keras.Sequential([\\n\",\n        \"    tf.keras.layers.Embedding(vocab_size, embedding_dim, input_length=max_length),\\n\",\n        \"    tf.keras.layers.GlobalAveragePooling1D(),\\n\",\n        \"    tf.keras.layers.Dense(6, activation='relu'),\\n\",\n        \"    tf.keras.layers.Dense(1, activation='sigmoid')\\n\",\n        \"])\\n\",\n        \"\\n\",\n        \"model.summary()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"VqkMNtIeP3oz\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"num_epochs = 30\\n\",\n        \"model.compile(loss='binary_crossentropy',optimizer='adam',metrics=['accuracy'])\\n\",\n        \"history = model.fit(training_sentences, training_labels_final, epochs=num_epochs, \\n\",\n        \"                    validation_data=(testing_sentences, testing_labels_final))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"sj18M42kQkCi\"\n      },\n      \"source\": [\n        \"## Visualize the Training Graph\\n\",\n        \"\\n\",\n        \"We can visualize the training graph below again. Does there appear to be a difference in how validation accuracy and loss is trending compared to with full words?\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"uy8KIMPIQlvH\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import matplotlib.pyplot as plt\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"def plot_graphs(history, string):\\n\",\n        \"  plt.plot(history.history[string])\\n\",\n        \"  plt.plot(history.history['val_'+string])\\n\",\n        \"  plt.xlabel(\\\"Epochs\\\")\\n\",\n        \"  plt.ylabel(string)\\n\",\n        \"  plt.legend([string, 'val_'+string])\\n\",\n        \"  plt.show()\\n\",\n        \"  \\n\",\n        \"plot_graphs(history, \\\"accuracy\\\")\\n\",\n        \"plot_graphs(history, \\\"loss\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"_m7QzouQQ1Rs\"\n      },\n      \"source\": [\n        \"## Get files for visualizing the network\\n\",\n        \"\\n\",\n        \"Once again, you can visualize the sentiment related to all of the subwords using the below code and by heading to http://projector.tensorflow.org/ to upload and view the data.\\n\",\n        \"\\n\",\n        \"Note that the below code does have a few small changes to handle the different way text is encoded in our dataset compared to before with the built in `Tokenizer`.\\n\",\n        \"\\n\",\n        \"You may get an error like \\\"Number of tensors (999) do not match the number of lines in metadata (992).\\\" As long as you load the vectors first without error and wait a few seconds after this pops up, you will be able to click outside the file load menu and still view the visualization.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"dezs4wE5RMQu\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# First get the weights of the embedding layer\\n\",\n        \"e = model.layers[0]\\n\",\n        \"weights = e.get_weights()[0]\\n\",\n        \"print(weights.shape) # shape: (vocab_size, embedding_dim)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"LXKqy9Z1RSmt\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import io\\n\",\n        \"\\n\",\n        \"# Write out the embedding vectors and metadata\\n\",\n        \"out_v = io.open('vecs.tsv', 'w', encoding='utf-8')\\n\",\n        \"out_m = io.open('meta.tsv', 'w', encoding='utf-8')\\n\",\n        \"for word_num in range(0, vocab_size - 1):\\n\",\n        \"  word = tokenizer.decode([word_num])\\n\",\n        \"  embeddings = weights[word_num]\\n\",\n        \"  out_m.write(word + \\\"\\\\n\\\")\\n\",\n        \"  out_v.write('\\\\t'.join([str(x) for x in embeddings]) + \\\"\\\\n\\\")\\n\",\n        \"out_v.close()\\n\",\n        \"out_m.close()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"v04wBMybRoGx\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Download the files\\n\",\n        \"try:\\n\",\n        \"  from google.colab import files\\n\",\n        \"except ImportError:\\n\",\n        \"  pass\\n\",\n        \"else:\\n\",\n        \"  files.download('vecs.tsv')\\n\",\n        \"  files.download('meta.tsv')\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l09c06_nlp_subwords.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l10c01_nlp_lstms_with_reviews_subwords_dataset.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"punL79CN7Ox6\"\n      },\n      \"source\": [\n        \"##### Copyright 2020 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"_ckMIh7O7s6D\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"hAclqSm3OOml\"\n      },\n      \"source\": [\n        \"# Using LSTMs with the subwords dataset\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"S5Uhzt6vVIB2\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l10c01_nlp_lstms_with_reviews_subwords_dataset.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l10c01_nlp_lstms_with_reviews_subwords_dataset.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"KTVx8__oGR9J\"\n      },\n      \"source\": [\n        \"In this colab, you'll compare the results of using a model with an Embedding layer and then adding bidirectional LSTM layers.\\n\",\n        \"\\n\",\n        \"You'll work with the dataset of subwords for the combined Yelp and Amazon reviews.\\n\",\n        \"\\n\",\n        \"You'll use your models to predict the sentiment of new reviews.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"L62G7LTwNzoD\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow as tf\\n\",\n        \"\\n\",\n        \"from tensorflow.keras.preprocessing.sequence import pad_sequences\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"hLcl0QHvDjTV\"\n      },\n      \"source\": [\n        \"# Get the dataset\\n\",\n        \"\\n\",\n        \"Start by getting the dataset containing Amazon and Yelp reviews, with their related sentiment (1 for positive, 0 for negative). This dataset was originally extracted from [here](https://www.kaggle.com/marklvl/sentiment-labelled-sentences-data-set).\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"nCOtiRJZbxCH\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!wget --no-check-certificate \\\\\\n\",\n        \"    https://drive.google.com/uc?id=13ySLC_ue6Umt9RJYSeM2t-V0kCv-4C-P -O /tmp/sentiment.csv\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"XuqER_KMD-xS\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import pandas as pd\\n\",\n        \"\\n\",\n        \"dataset = pd.read_csv('/tmp/sentiment.csv')\\n\",\n        \"\\n\",\n        \"# Extract out sentences and labels\\n\",\n        \"sentences = dataset['text'].tolist()\\n\",\n        \"labels = dataset['sentiment'].tolist()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Tbsx1T2CXPNO\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Print some example sentences and labels\\n\",\n        \"for x in range(2):\\n\",\n        \"  print(sentences[x])\\n\",\n        \"  print(labels[x])\\n\",\n        \"  print(\\\"\\\\n\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"33AthPiALFZK\"\n      },\n      \"source\": [\n        \"#Create a subwords dataset\\n\",\n        \"\\n\",\n        \"We will use the Amazon and Yelp reviews dataset with tensorflow_datasets's SubwordTextEncoder functionality. \\n\",\n        \"\\n\",\n        \"SubwordTextEncoder.build_from_corpus() will create a tokenizer for us. You could also use this functionality to get subwords from a much larger corpus of text as well, but we'll just use our existing dataset here.\\n\",\n        \"\\n\",\n        \"We'll create a subword vocab_size of only the 1,000 most common subwords, as well as cutting off each subword to be at most 5 characters.\\n\",\n        \"\\n\",\n        \"Check out the related documentation for the the subword text encoder [here](https://www.tensorflow.org/datasets/api_docs/python/tfds/features/text/SubwordTextEncoder#build_from_corpus).\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"6NaicNCcLYyf\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow_datasets as tfds\\n\",\n        \"\\n\",\n        \"vocab_size = 1000\\n\",\n        \"tokenizer = tfds.deprecated.text.SubwordTextEncoder.build_from_corpus(sentences, vocab_size, max_subword_length=5)\\n\",\n        \"\\n\",\n        \"# How big is the vocab size?\\n\",\n        \"print(\\\"Vocab size is \\\", tokenizer.vocab_size)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"xvRVoeIVLevh\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Check that the tokenizer works appropriately\\n\",\n        \"num = 5\\n\",\n        \"print(sentences[num])\\n\",\n        \"encoded = tokenizer.encode(sentences[num])\\n\",\n        \"print(encoded)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"G_vacTCifklV\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Separately print out each subword, decoded\\n\",\n        \"for i in encoded:\\n\",\n        \"  print(tokenizer.decode([i]))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"cT528cptLupl\"\n      },\n      \"source\": [\n        \"## Replace sentence data with encoded subwords\\n\",\n        \"\\n\",\n        \"Now, we'll create the sequences to be used for training by actually encoding each of the individual sentences. This is equivalent to `text_to_sequences` with the `Tokenizer` we used in earlier exercises.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"lkseMhxjL09F\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"for i, sentence in enumerate(sentences):\\n\",\n        \"  sentences[i] = tokenizer.encode(sentence)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"y21yRuzmL43U\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Check the sentences are appropriately replaced\\n\",\n        \"print(sentences[5])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"8HrcPHESMBMs\"\n      },\n      \"source\": [\n        \"## Final pre-processing\\n\",\n        \"\\n\",\n        \"Before training, we still need to pad the sequences, as well as split into training and test sets.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"50-hTsogLSL-\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import numpy as np\\n\",\n        \"\\n\",\n        \"max_length = 50\\n\",\n        \"trunc_type='post'\\n\",\n        \"padding_type='post'\\n\",\n        \"\\n\",\n        \"# Pad all sequences\\n\",\n        \"sequences_padded = pad_sequences(sentences, maxlen=max_length, \\n\",\n        \"                                 padding=padding_type, truncating=trunc_type)\\n\",\n        \"\\n\",\n        \"# Separate out the sentences and labels into training and test sets\\n\",\n        \"training_size = int(len(sentences) * 0.8)\\n\",\n        \"\\n\",\n        \"training_sequences = sequences_padded[0:training_size]\\n\",\n        \"testing_sequences = sequences_padded[training_size:]\\n\",\n        \"training_labels = labels[0:training_size]\\n\",\n        \"testing_labels = labels[training_size:]\\n\",\n        \"\\n\",\n        \"# Make labels into numpy arrays for use with the network later\\n\",\n        \"training_labels_final = np.array(training_labels)\\n\",\n        \"testing_labels_final = np.array(testing_labels)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"PahZm7YEQ8EI\"\n      },\n      \"source\": [\n        \"# Create the model using an Embedding\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"c_nyQeI0RCCv\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"embedding_dim = 16\\n\",\n        \"\\n\",\n        \"model = tf.keras.Sequential([\\n\",\n        \"    tf.keras.layers.Embedding(vocab_size, embedding_dim, input_length=max_length),\\n\",\n        \"    tf.keras.layers.GlobalAveragePooling1D(), \\n\",\n        \"    tf.keras.layers.Dense(6, activation='relu'),\\n\",\n        \"    tf.keras.layers.Dense(1, activation='sigmoid')\\n\",\n        \"])\\n\",\n        \"\\n\",\n        \"model.summary()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"3WRXrx8BRO2L\"\n      },\n      \"source\": [\n        \"# Train the model\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"oBKyVYvxRQ_9\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"num_epochs = 30\\n\",\n        \"model.compile(loss='binary_crossentropy',optimizer='adam',metrics=['accuracy'])\\n\",\n        \"history = model.fit(training_sequences, training_labels_final, epochs=num_epochs, validation_data=(testing_sequences, testing_labels_final))\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"HhLPbUl2AZ0y\"\n      },\n      \"source\": [\n        \"# Plot the accuracy and loss\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"jzBM1PpJAYfD\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import matplotlib.pyplot as plt\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"def plot_graphs(history, string):\\n\",\n        \"  plt.plot(history.history[string])\\n\",\n        \"  plt.plot(history.history['val_'+string])\\n\",\n        \"  plt.xlabel(\\\"Epochs\\\")\\n\",\n        \"  plt.ylabel(string)\\n\",\n        \"  plt.legend([string, 'val_'+string])\\n\",\n        \"  plt.show()\\n\",\n        \"  \\n\",\n        \"plot_graphs(history, \\\"accuracy\\\")\\n\",\n        \"plot_graphs(history, \\\"loss\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Fwr5inBiWffb\"\n      },\n      \"source\": [\n        \"# Define a function to predict the sentiment of reviews\\n\",\n        \"\\n\",\n        \"We'll be creating models with some differences and will use each model to predict the sentiment of some new reviews.\\n\",\n        \"\\n\",\n        \"To save time, create a function that will take in a model and some new reviews, and print out the sentiment of each reviews.\\n\",\n        \"\\n\",\n        \"The higher the sentiment value is to 1, the more positive the review is.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"aPNOYiiaha2y\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Define a function to take a series of reviews\\n\",\n        \"# and predict whether each one is a positive or negative review\\n\",\n        \"\\n\",\n        \"# max_length = 100 # previously defined\\n\",\n        \"\\n\",\n        \"def predict_review(model, new_sentences, maxlen=max_length, show_padded_sequence=True ):\\n\",\n        \"  # Keep the original sentences so that we can keep using them later\\n\",\n        \"  # Create an array to hold the encoded sequences\\n\",\n        \"  new_sequences = []\\n\",\n        \"\\n\",\n        \"  # Convert the new reviews to sequences\\n\",\n        \"  for i, frvw in enumerate(new_sentences):\\n\",\n        \"    new_sequences.append(tokenizer.encode(frvw))\\n\",\n        \"\\n\",\n        \"  trunc_type='post' \\n\",\n        \"  padding_type='post'\\n\",\n        \"\\n\",\n        \"  # Pad all sequences for the new reviews\\n\",\n        \"  new_reviews_padded = pad_sequences(new_sequences, maxlen=max_length, \\n\",\n        \"                                 padding=padding_type, truncating=trunc_type)             \\n\",\n        \"\\n\",\n        \"  classes = model.predict(new_reviews_padded)\\n\",\n        \"\\n\",\n        \"  # The closer the class is to 1, the more positive the review is\\n\",\n        \"  for x in range(len(new_sentences)):\\n\",\n        \"    \\n\",\n        \"    # We can see the padded sequence if desired\\n\",\n        \"    # Print the sequence\\n\",\n        \"    if (show_padded_sequence):\\n\",\n        \"      print(new_reviews_padded[x])\\n\",\n        \"    # Print the review as text\\n\",\n        \"    print(new_sentences[x])\\n\",\n        \"    # Print its predicted class\\n\",\n        \"    print(classes[x])\\n\",\n        \"    print(\\\"\\\\n\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Qg-maex27KPW\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Use the model to predict some reviews   \\n\",\n        \"fake_reviews = [\\\"I love this phone\\\", \\n\",\n        \"                \\\"Everything was cold\\\",\\n\",\n        \"                \\\"Everything was hot exactly as I wanted\\\", \\n\",\n        \"                \\\"Everything was green\\\", \\n\",\n        \"                \\\"the host seated us immediately\\\",\\n\",\n        \"                \\\"they gave us free chocolate cake\\\", \\n\",\n        \"                \\\"we couldn't hear each other talk because of the shouting in the kitchen\\\"\\n\",\n        \"              ]\\n\",\n        \"\\n\",\n        \"predict_review(model, fake_reviews)\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ycJKbMq3K4iy\"\n      },\n      \"source\": [\n        \"# Define a function to train and show the results of models with different layers\\n\",\n        \"\\n\",\n        \"In the rest of this colab, we will define models, and then see the results. \\n\",\n        \"\\n\",\n        \"Define a function that will take the model, compile it, train it, graph the accuracy and loss, and then predict some results.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"PevUcINXK3gn\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def fit_model_now (model, sentences) :\\n\",\n        \"  model.compile(loss='binary_crossentropy',optimizer='adam',metrics=['accuracy'])\\n\",\n        \"  model.summary()\\n\",\n        \"  history = model.fit(training_sequences, training_labels_final, epochs=num_epochs, \\n\",\n        \"                      validation_data=(testing_sequences, testing_labels_final))\\n\",\n        \"  return history\\n\",\n        \"\\n\",\n        \"def plot_results (history):\\n\",\n        \"  plot_graphs(history, \\\"accuracy\\\")\\n\",\n        \"  plot_graphs(history, \\\"loss\\\")\\n\",\n        \"\\n\",\n        \"def fit_model_and_show_results (model, sentences):\\n\",\n        \"  history = fit_model_now(model, sentences)\\n\",\n        \"  plot_results(history)\\n\",\n        \"  predict_review(model, sentences)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"U13JBiJUG1oq\"\n      },\n      \"source\": [\n        \"# Add a bidirectional LSTM\\n\",\n        \"\\n\",\n        \"Create a new model that uses a bidirectional LSTM.\\n\",\n        \"\\n\",\n        \"Then use the function we have already defined to compile the model, train it, graph the accuracy and loss, then predict some results.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"scTUsFPAG4zP\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Define the model\\n\",\n        \"model_bidi_lstm = tf.keras.Sequential([\\n\",\n        \"    tf.keras.layers.Embedding(vocab_size, embedding_dim, input_length=max_length),\\n\",\n        \"    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(embedding_dim)), \\n\",\n        \"    tf.keras.layers.Dense(6, activation='relu'), \\n\",\n        \"    tf.keras.layers.Dense(1, activation='sigmoid')\\n\",\n        \"])\\n\",\n        \"\\n\",\n        \"# Compile and train the model and then show the predictions for our extra sentences\\n\",\n        \"fit_model_and_show_results(model_bidi_lstm, fake_reviews)\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"QsxKPbCnPJTj\"\n      },\n      \"source\": [\n        \"# Use multiple bidirectional layers\\n\",\n        \"\\n\",\n        \"Now let's see if we get any improvements from adding another Bidirectional LSTM layer to the model.\\n\",\n        \"\\n\",\n        \"Notice that the first Bidirectionl LSTM layer returns a sequence.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"3N6Zul47PMED\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model_multiple_bidi_lstm = tf.keras.Sequential([\\n\",\n        \"    tf.keras.layers.Embedding(vocab_size, embedding_dim, input_length=max_length),\\n\",\n        \"    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(embedding_dim, \\n\",\n        \"                                                       return_sequences=True)), \\n\",\n        \"    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(embedding_dim)),\\n\",\n        \"    tf.keras.layers.Dense(6, activation='relu'),\\n\",\n        \"    tf.keras.layers.Dense(1, activation='sigmoid')\\n\",\n        \"])\\n\",\n        \"\\n\",\n        \"fit_model_and_show_results(model_multiple_bidi_lstm, fake_reviews)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ABVYYPwba8Hx\"\n      },\n      \"source\": [\n        \"# Compare predictions for all the models\\n\",\n        \"\\n\",\n        \"It can be hard to see which model gives a better prediction for different reviews when you examine each model separately. So for comparison purposes, here we define some more reviews and print out the predictions that each of the three models gives for each review:\\n\",\n        \"\\n\",\n        \"*   Embeddings and a Global Average Pooling layer\\n\",\n        \"*   Embeddings and a Bidirectional LSTM layer\\n\",\n        \"*   Embeddings and two Bidirectional LSTM layers\\n\",\n        \"\\n\",\n        \"The results are not always what you might expect. The input dataset is fairly small, it has less than 2000 reviews. Some of the reviews are fairly short, and some of the short ones are fairly repetitive which reduces their impact on improving the  model, such as these two reviews:\\n\",\n        \"\\n\",\n        \"*   Bad Quality.\\n\",\n        \"*   Low Quality.\\n\",\n        \"\\n\",\n        \"Feel free to add more reviews of your own, or change the reviews. The results will depend on the combination of words in the reviews, and how well they match to reviews in the training set. \\n\",\n        \"\\n\",\n        \"How do the different models handle things like \\\"wasn't good\\\" which contains a positive word (good) but is a poor review?\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"6XebrXt0jtOy\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"my_reviews =[\\\"lovely\\\", \\\"dreadful\\\", \\\"stay away\\\",\\n\",\n        \"             \\\"everything was hot exactly as I wanted\\\",\\n\",\n        \"             \\\"everything was not exactly as I wanted\\\",\\n\",\n        \"             \\\"they gave us free chocolate cake\\\",\\n\",\n        \"             \\\"I've never eaten anything so spicy in my life, my throat burned for hours\\\",\\n\",\n        \"             \\\"for a phone that is as expensive as this one I expect it to be much easier to use than this thing is\\\",\\n\",\n        \"             \\\"we left there very full for a low price so I'd say you just can't go wrong at this place\\\",\\n\",\n        \"             \\\"that place does not have quality meals and it isn't a good place to go for dinner\\\",\\n\",\n        \"             ]\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"tRWGjkJLkY2y\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"===================================\\\\n\\\",\\\"Embeddings only:\\\\n\\\", \\\"===================================\\\",)\\n\",\n        \"predict_review(model, my_reviews, show_padded_sequence=False)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"G2FJR3IVBt30\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"===================================\\\\n\\\", \\\"With a single bidirectional LSTM:\\\\n\\\", \\\"===================================\\\")\\n\",\n        \"predict_review(model_bidi_lstm, my_reviews, show_padded_sequence=False)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"81v1r3Y2BwvC\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"===================================\\\\n\\\",\\\"With two bidirectional LSTMs:\\\\n\\\", \\\"===================================\\\")\\n\",\n        \"predict_review(model_multiple_bidi_lstm, my_reviews, show_padded_sequence=False)\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"name\": \"l10c01_nlp_lstms_with_reviews_subwords_dataset.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l10c02_nlp_multiple_models_for_predicting_sentiment.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"punL79CN7Ox6\"\n      },\n      \"source\": [\n        \"##### Copyright 2020 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"_ckMIh7O7s6D\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"hAclqSm3OOml\"\n      },\n      \"source\": [\n        \"# Using LSTMs, CNNs, GRUs with a larger dataset\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"S5Uhzt6vVIB2\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l10c02_nlp_multiple_models_for_predicting_sentiment.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l10c02_nlp_multiple_models_for_predicting_sentiment.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"fentd-GnIj-j\"\n      },\n      \"source\": [\n        \"In this colab, you use different kinds of layers to see how they affect the model.\\n\",\n        \"\\n\",\n        \"You will use the glue/sst2 dataset, which is available through tensorflow_datasets. \\n\",\n        \"\\n\",\n        \"The General Language Understanding Evaluation (GLUE) benchmark (https://gluebenchmark.com/) is a collection of resources for training, evaluating, and analyzing natural language understanding systems.\\n\",\n        \"\\n\",\n        \"These resources include the Stanford Sentiment Treebank (SST) dataset that consists of sentences from movie reviews and human annotations of their sentiment. This colab uses version 2 of the SST dataset.\\n\",\n        \"\\n\",\n        \"The splits are:\\n\",\n        \"\\n\",\n        \"*   train\\t67,349\\n\",\n        \"*   validation\\t872\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"and the column headings are:\\n\",\n        \"\\n\",\n        \"*   sentence\\n\",\n        \"*   label\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"For more information about the dataset, see [https://www.tensorflow.org/datasets/catalog/glue#gluesst2](https://www.tensorflow.org/datasets/catalog/glue#gluesst2)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"L62G7LTwNzoD\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow as tf\\n\",\n        \"import tensorflow_datasets as tfds\\n\",\n        \"\\n\",\n        \"import numpy as np\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"o2P6xdtIJMyc\"\n      },\n      \"source\": [\n        \"# Get the dataset\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"nCOtiRJZbxCH\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Get the dataset.\\n\",\n        \"# It has 70000 items, so might take a while to download\\n\",\n        \"dataset, info = tfds.load('glue/sst2', with_info=True)\\n\",\n        \"print(info.features)\\n\",\n        \"print(info.features[\\\"label\\\"].num_classes)\\n\",\n        \"print(info.features[\\\"label\\\"].names)\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"yBMPhIdStAe2\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Get the training and validation datasets\\n\",\n        \"dataset_train, dataset_validation = dataset['train'], dataset['validation']\\n\",\n        \"dataset_train\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"jbZ-faiNWu1U\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Print some of the entries\\n\",\n        \"for example in dataset_train.take(2):  \\n\",\n        \"  review, label = example[\\\"sentence\\\"], example[\\\"label\\\"]\\n\",\n        \"  print(\\\"Review:\\\", review)\\n\",\n        \"  print(\\\"Label: %d \\\\n\\\" % label.numpy())\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"_fVZItTeZSbL\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Get the sentences and the labels\\n\",\n        \"# for both the training and the validation sets\\n\",\n        \"training_reviews = []\\n\",\n        \"training_labels = []\\n\",\n        \" \\n\",\n        \"validation_reviews = []\\n\",\n        \"validation_labels = []\\n\",\n        \"\\n\",\n        \"# The dataset has 67,000 training entries, but that's a lot to process here!\\n\",\n        \"\\n\",\n        \"# If you want to take the entire dataset: WARNING: takes longer!!\\n\",\n        \"# for item in dataset_train.take(-1):\\n\",\n        \"\\n\",\n        \"# Take 10,000 reviews\\n\",\n        \"for item in dataset_train.take(10000):\\n\",\n        \"  review, label = item[\\\"sentence\\\"], item[\\\"label\\\"]\\n\",\n        \"  training_reviews.append(str(review.numpy()))\\n\",\n        \"  training_labels.append(label.numpy())\\n\",\n        \"\\n\",\n        \"print (\\\"\\\\nNumber of training reviews is: \\\", len(training_reviews))\\n\",\n        \"\\n\",\n        \"# print some of the reviews and labels\\n\",\n        \"for i in range(0, 2):\\n\",\n        \"  print (training_reviews[i])\\n\",\n        \"  print (training_labels[i])\\n\",\n        \"\\n\",\n        \"# Get the validation data\\n\",\n        \"# there's only about 800 items, so take them all\\n\",\n        \"for item in dataset_validation.take(-1):  \\n\",\n        \"  review, label = item[\\\"sentence\\\"], item[\\\"label\\\"]\\n\",\n        \"  validation_reviews.append(str(review.numpy()))\\n\",\n        \"  validation_labels.append(label.numpy())\\n\",\n        \"\\n\",\n        \"print (\\\"\\\\nNumber of validation reviews is: \\\", len(validation_reviews))\\n\",\n        \"\\n\",\n        \"# Print some of the validation reviews and labels\\n\",\n        \"for i in range(0, 2):\\n\",\n        \"  print (validation_reviews[i])\\n\",\n        \"  print (validation_labels[i])\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"BY4ZoptJO55o\"\n      },\n      \"source\": [\n        \"# Tokenize the words and sequence the sentences\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"0TWLvXA1Oa_W\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# There's a total of 21224 words in the reviews\\n\",\n        \"# but many of them are irrelevant like with, it, of, on.\\n\",\n        \"# If we take a subset of the training data, then the vocab\\n\",\n        \"# will be smaller.\\n\",\n        \"\\n\",\n        \"# A reasonable review might have about 50 words or so,\\n\",\n        \"# so we can set max_length to 50 (but feel free to change it as you like)\\n\",\n        \"\\n\",\n        \"vocab_size = 4000\\n\",\n        \"embedding_dim = 16\\n\",\n        \"max_length = 50\\n\",\n        \"trunc_type='post'\\n\",\n        \"pad_type='post'\\n\",\n        \"oov_tok = \\\"<OOV>\\\"\\n\",\n        \"\\n\",\n        \"from tensorflow.keras.preprocessing.text import Tokenizer\\n\",\n        \"from tensorflow.keras.preprocessing.sequence import pad_sequences\\n\",\n        \"\\n\",\n        \"tokenizer = Tokenizer(num_words = vocab_size, oov_token=oov_tok)\\n\",\n        \"tokenizer.fit_on_texts(training_reviews)\\n\",\n        \"word_index = tokenizer.word_index\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"JV-Ff5N0ryWv\"\n      },\n      \"source\": [\n        \"# Pad the sequences\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"B-3scEznH2Va\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Pad the sequences so that they are all the same length\\n\",\n        \"training_sequences = tokenizer.texts_to_sequences(training_reviews)\\n\",\n        \"training_padded = pad_sequences(training_sequences,maxlen=max_length, \\n\",\n        \"                                truncating=trunc_type, padding=pad_type)\\n\",\n        \"\\n\",\n        \"validation_sequences = tokenizer.texts_to_sequences(validation_reviews)\\n\",\n        \"validation_padded = pad_sequences(validation_sequences,maxlen=max_length)\\n\",\n        \"\\n\",\n        \"training_labels_final = np.array(training_labels)\\n\",\n        \"validation_labels_final = np.array(validation_labels)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"PahZm7YEQ8EI\"\n      },\n      \"source\": [\n        \"# Create the model using an Embedding\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"c_nyQeI0RCCv\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = tf.keras.Sequential([\\n\",\n        \"    tf.keras.layers.Embedding(vocab_size, embedding_dim, input_length=max_length),\\n\",\n        \"    tf.keras.layers.GlobalAveragePooling1D(),  \\n\",\n        \"    tf.keras.layers.Dense(1, activation='sigmoid')\\n\",\n        \"])\\n\",\n        \"model.compile(loss='binary_crossentropy',optimizer='adam',metrics=['accuracy'])\\n\",\n        \"model.summary()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"3WRXrx8BRO2L\"\n      },\n      \"source\": [\n        \"# Train the model\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"oBKyVYvxRQ_9\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"num_epochs = 20\\n\",\n        \"history = model.fit(training_padded, training_labels_final, epochs=num_epochs, \\n\",\n        \"                    validation_data=(validation_padded, validation_labels_final))\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"HhLPbUl2AZ0y\"\n      },\n      \"source\": [\n        \"# Plot the accurracy and loss\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"jzBM1PpJAYfD\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import matplotlib.pyplot as plt\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"def plot_graphs(history, string):\\n\",\n        \"  plt.plot(history.history[string])\\n\",\n        \"  plt.plot(history.history['val_'+string])\\n\",\n        \"  plt.xlabel(\\\"Epochs\\\")\\n\",\n        \"  plt.ylabel(string)\\n\",\n        \"  plt.legend([string, 'val_'+string])\\n\",\n        \"  plt.show()\\n\",\n        \"  \\n\",\n        \"plot_graphs(history, \\\"accuracy\\\")\\n\",\n        \"plot_graphs(history, \\\"loss\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"HEbcMCVEKToB\"\n      },\n      \"source\": [\n        \"# Write a function to predict the sentiment of reviews\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"K0nKY9M4xzWE\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Write some new reviews \\n\",\n        \"\\n\",\n        \"review1 = \\\"\\\"\\\"I loved this movie\\\"\\\"\\\"\\n\",\n        \"\\n\",\n        \"review2 = \\\"\\\"\\\"that was the worst movie I've ever seen\\\"\\\"\\\"\\n\",\n        \"\\n\",\n        \"review3 = \\\"\\\"\\\"too much violence even for a Bond film\\\"\\\"\\\"\\n\",\n        \"\\n\",\n        \"review4 = \\\"\\\"\\\"a captivating recounting of a cherished myth\\\"\\\"\\\"\\n\",\n        \"\\n\",\n        \"new_reviews = [review1, review2, review3, review4]\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Qg-maex27KPW\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Define a function to prepare the new reviews for use with a model\\n\",\n        \"# and then use the model to predict the sentiment of the new reviews           \\n\",\n        \"\\n\",\n        \"def predict_review(model, reviews):\\n\",\n        \"  # Create the sequences\\n\",\n        \"  padding_type='post'\\n\",\n        \"  sample_sequences = tokenizer.texts_to_sequences(reviews)\\n\",\n        \"  reviews_padded = pad_sequences(sample_sequences, padding=padding_type, \\n\",\n        \"                                 maxlen=max_length) \\n\",\n        \"  classes = model.predict(reviews_padded)\\n\",\n        \"  for x in range(len(reviews_padded)):\\n\",\n        \"    print(reviews[x])\\n\",\n        \"    print(classes[x])\\n\",\n        \"    print('\\\\n')\\n\",\n        \"\\n\",\n        \"predict_review(model, new_reviews)\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ycJKbMq3K4iy\"\n      },\n      \"source\": [\n        \"# Define a function to train and show the results of models with different layers\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"PevUcINXK3gn\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def fit_model_and_show_results (model, reviews):\\n\",\n        \"  model.summary()\\n\",\n        \"  history = model.fit(training_padded, training_labels_final, epochs=num_epochs, \\n\",\n        \"                      validation_data=(validation_padded, validation_labels_final))\\n\",\n        \"  plot_graphs(history, \\\"accuracy\\\")\\n\",\n        \"  plot_graphs(history, \\\"loss\\\")\\n\",\n        \"  predict_review(model, reviews)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"W8jW-OLfTrDM\"\n      },\n      \"source\": [\n        \"# Use a CNN\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"merAu9T3TtmQ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"num_epochs = 30\\n\",\n        \"\\n\",\n        \"model_cnn = tf.keras.Sequential([\\n\",\n        \"    tf.keras.layers.Embedding(vocab_size, embedding_dim, input_length=max_length),\\n\",\n        \"    tf.keras.layers.Conv1D(16, 5, activation='relu'),\\n\",\n        \"    tf.keras.layers.GlobalMaxPooling1D(),\\n\",\n        \"    tf.keras.layers.Dense(1, activation='sigmoid')\\n\",\n        \"])\\n\",\n        \"\\n\",\n        \"# Default learning rate for the Adam optimizer is 0.001\\n\",\n        \"# Let's slow down the learning rate by 10.\\n\",\n        \"learning_rate = 0.0001\\n\",\n        \"model_cnn.compile(loss='binary_crossentropy',\\n\",\n        \"                  optimizer=tf.keras.optimizers.Adam(learning_rate), \\n\",\n        \"                  metrics=['accuracy'])\\n\",\n        \"\\n\",\n        \"fit_model_and_show_results(model_cnn, new_reviews)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"tXnoq0zITmSM\"\n      },\n      \"source\": [\n        \"# Use a GRU\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"6jP6KAzZTpQ6\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"num_epochs = 30\\n\",\n        \"\\n\",\n        \"model_gru = tf.keras.Sequential([\\n\",\n        \"    tf.keras.layers.Embedding(vocab_size, embedding_dim, input_length=max_length),\\n\",\n        \"    tf.keras.layers.Bidirectional(tf.keras.layers.GRU(32)),\\n\",\n        \"    tf.keras.layers.Dense(1, activation='sigmoid')\\n\",\n        \"])\\n\",\n        \"\\n\",\n        \"learning_rate = 0.00003 # slower than the default learning rate\\n\",\n        \"model_gru.compile(loss='binary_crossentropy',\\n\",\n        \"                  optimizer=tf.keras.optimizers.Adam(learning_rate),\\n\",\n        \"                  metrics=['accuracy'])\\n\",\n        \"\\n\",\n        \"fit_model_and_show_results(model_gru, new_reviews)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"U13JBiJUG1oq\"\n      },\n      \"source\": [\n        \"# Add a bidirectional LSTM\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"scTUsFPAG4zP\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"num_epochs = 30\\n\",\n        \"\\n\",\n        \"model_bidi_lstm = tf.keras.Sequential([\\n\",\n        \"    tf.keras.layers.Embedding(vocab_size, embedding_dim, input_length=max_length),\\n\",\n        \"    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(embedding_dim)), \\n\",\n        \"    tf.keras.layers.Dense(1, activation='sigmoid')\\n\",\n        \"])\\n\",\n        \"\\n\",\n        \"learning_rate = 0.00003\\n\",\n        \"model_bidi_lstm.compile(loss='binary_crossentropy',\\n\",\n        \"                        optimizer=tf.keras.optimizers.Adam(learning_rate),\\n\",\n        \"                        metrics=['accuracy'])\\n\",\n        \"fit_model_and_show_results(model_bidi_lstm, new_reviews)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"QsxKPbCnPJTj\"\n      },\n      \"source\": [\n        \"# Use multiple bidirectional LSTMs\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"3N6Zul47PMED\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"num_epochs = 30\\n\",\n        \"\\n\",\n        \"model_multiple_bidi_lstm = tf.keras.Sequential([\\n\",\n        \"    tf.keras.layers.Embedding(vocab_size, embedding_dim, input_length=max_length),\\n\",\n        \"    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(embedding_dim, \\n\",\n        \"                                                       return_sequences=True)),\\n\",\n        \"    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(embedding_dim)),\\n\",\n        \"    tf.keras.layers.Dense(1, activation='sigmoid')\\n\",\n        \"])\\n\",\n        \"\\n\",\n        \"learning_rate = 0.0003\\n\",\n        \"model_multiple_bidi_lstm.compile(loss='binary_crossentropy',\\n\",\n        \"                                 optimizer=tf.keras.optimizers.Adam(learning_rate),\\n\",\n        \"                                 metrics=['accuracy'])\\n\",\n        \"fit_model_and_show_results(model_multiple_bidi_lstm, new_reviews)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"gdN2tdW4YYJ1\"\n      },\n      \"source\": [\n        \"# Try some more reviews\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"e45UQxQl_QAI\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Write some new reviews \\n\",\n        \"\\n\",\n        \"review1 = \\\"\\\"\\\"I loved this movie\\\"\\\"\\\"\\n\",\n        \"\\n\",\n        \"review2 = \\\"\\\"\\\"that was the worst movie I've ever seen\\\"\\\"\\\"\\n\",\n        \"\\n\",\n        \"review3 = \\\"\\\"\\\"too much violence even for a Bond film\\\"\\\"\\\"\\n\",\n        \"\\n\",\n        \"review4 = \\\"\\\"\\\"a captivating recounting of a cherished myth\\\"\\\"\\\"\\n\",\n        \"\\n\",\n        \"review5 = \\\"\\\"\\\"I saw this movie yesterday and I was feeling low to start with,\\n\",\n        \" but it was such a wonderful movie that it lifted my spirits and brightened \\n\",\n        \" my day, you can\\\\'t go wrong with a movie with Whoopi Goldberg in it.\\\"\\\"\\\"\\n\",\n        \"\\n\",\n        \"review6 = \\\"\\\"\\\"I don\\\\'t understand why it received an oscar recommendation\\n\",\n        \" for best movie, it was long and boring\\\"\\\"\\\"\\n\",\n        \"\\n\",\n        \"review7 = \\\"\\\"\\\"the scenery was magnificent, the CGI of the dogs was so realistic I\\n\",\n        \" thought they were played by real dogs even though they talked!\\\"\\\"\\\"\\n\",\n        \"\\n\",\n        \"review8 = \\\"\\\"\\\"The ending was so sad and yet so uplifting at the same time. \\n\",\n        \" I'm looking for an excuse to see it again\\\"\\\"\\\"\\n\",\n        \"\\n\",\n        \"review9 = \\\"\\\"\\\"I had expected so much more from a movie made by the director \\n\",\n        \" who made my most favorite movie ever, I was very disappointed in the tedious \\n\",\n        \" story\\\"\\\"\\\"\\n\",\n        \"\\n\",\n        \"review10 = \\\"I wish I could watch this movie every day for the rest of my life\\\"\\n\",\n        \"\\n\",\n        \"more_reviews = [review1, review2, review3, review4, review5, review6, review7, \\n\",\n        \"               review8, review9, review10]\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"cQ4YZHOjYXQ2\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"============================\\\\n\\\",\\\"Embeddings only:\\\\n\\\", \\\"============================\\\")\\n\",\n        \"predict_review(model, more_reviews)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"NI04noTAHK5t\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"============================\\\\n\\\",\\\"With CNN\\\\n\\\", \\\"============================\\\")\\n\",\n        \"predict_review(model_cnn, more_reviews)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"vGJ32sRUHNu6\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"===========================\\\\n\\\",\\\"With bidirectional GRU\\\\n\\\", \\\"============================\\\")\\n\",\n        \"predict_review(model_gru, more_reviews)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"IFw9Q0iQHP2P\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"===========================\\\\n\\\", \\\"With a single bidirectional LSTM:\\\\n\\\", \\\"===========================\\\")\\n\",\n        \"predict_review(model_bidi_lstm, more_reviews)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"zaw7fEVQHSWK\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"===========================\\\\n\\\", \\\"With multiple bidirectional LSTM:\\\\n\\\", \\\"==========================\\\")\\n\",\n        \"predict_review(model_multiple_bidi_lstm, more_reviews)\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"name\": \"l10c02_nlp_multiple_models_for_predicting_sentiment.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l10c03_nlp_constructing_text_generation_model.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"punL79CN7Ox6\"\n      },\n      \"source\": [\n        \"##### Copyright 2020 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"_ckMIh7O7s6D\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Ph5eir3Pf-3z\"\n      },\n      \"source\": [\n        \"# Constructing a Text Generation Model\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"S5Uhzt6vVIB2\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l10c03_nlp_constructing_text_generation_model.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l10c03_nlp_constructing_text_generation_model.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"7GbGfr_oLCat\"\n      },\n      \"source\": [\n        \"Using most of the techniques you've already learned, it's now possible to generate new text by predicting the next word that follows a given seed word. To practice this method, we'll use the [Kaggle Song Lyrics Dataset](https://www.kaggle.com/mousehead/songlyrics).\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"4aHK2CYygXom\"\n      },\n      \"source\": [\n        \"## Import TensorFlow and related functions\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"2LmLTREBf5ng\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow as tf\\n\",\n        \"\\n\",\n        \"from tensorflow.keras.preprocessing.text import Tokenizer\\n\",\n        \"from tensorflow.keras.preprocessing.sequence import pad_sequences\\n\",\n        \"\\n\",\n        \"# Other imports for processing data\\n\",\n        \"import string\\n\",\n        \"import numpy as np\\n\",\n        \"import pandas as pd\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"GmLTO_dpgge9\"\n      },\n      \"source\": [\n        \"## Get the Dataset\\n\",\n        \"\\n\",\n        \"As noted above, we'll utilize the [Song Lyrics dataset](https://www.kaggle.com/mousehead/songlyrics) on Kaggle.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"4Bf5FVHfganK\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!wget --no-check-certificate \\\\\\n\",\n        \"    https://drive.google.com/uc?id=1LiJFZd41ofrWoBtW-pMYsfz1w8Ny0Bj8 \\\\\\n\",\n        \"    -O /tmp/songdata.csv\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"gu1BTzMIS1oy\"\n      },\n      \"source\": [\n        \"## **First 10 Songs**\\n\",\n        \"\\n\",\n        \"Let's first look at just 10 songs from the dataset, and see how things perform.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"fmb9rGaAUDO-\"\n      },\n      \"source\": [\n        \"### Preprocessing\\n\",\n        \"\\n\",\n        \"Let's perform some basic preprocessing to get rid of punctuation and make everything lowercase. We'll then split the lyrics up by line and tokenize the lyrics.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"2AVAvyF_Vuh5\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def tokenize_corpus(corpus, num_words=-1):\\n\",\n        \"  # Fit a Tokenizer on the corpus\\n\",\n        \"  if num_words > -1:\\n\",\n        \"    tokenizer = Tokenizer(num_words=num_words)\\n\",\n        \"  else:\\n\",\n        \"    tokenizer = Tokenizer()\\n\",\n        \"  tokenizer.fit_on_texts(corpus)\\n\",\n        \"  return tokenizer\\n\",\n        \"\\n\",\n        \"def create_lyrics_corpus(dataset, field):\\n\",\n        \"  # Remove all other punctuation\\n\",\n        \"  dataset[field] = dataset[field].str.replace('[{}]'.format(string.punctuation), '')\\n\",\n        \"  # Make it lowercase\\n\",\n        \"  dataset[field] = dataset[field].str.lower()\\n\",\n        \"  # Make it one long string to split by line\\n\",\n        \"  lyrics = dataset[field].str.cat()\\n\",\n        \"  corpus = lyrics.split('\\\\n')\\n\",\n        \"  # Remove any trailing whitespace\\n\",\n        \"  for l in range(len(corpus)):\\n\",\n        \"    corpus[l] = corpus[l].rstrip()\\n\",\n        \"  # Remove any empty lines\\n\",\n        \"  corpus = [l for l in corpus if l != '']\\n\",\n        \"\\n\",\n        \"  return corpus\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"apcEXp7WhVBs\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Read the dataset from csv - just first 10 songs for now\\n\",\n        \"dataset = pd.read_csv('/tmp/songdata.csv', dtype=str)[:10]\\n\",\n        \"# Create the corpus using the 'text' column containing lyrics\\n\",\n        \"corpus = create_lyrics_corpus(dataset, 'text')\\n\",\n        \"# Tokenize the corpus\\n\",\n        \"tokenizer = tokenize_corpus(corpus)\\n\",\n        \"\\n\",\n        \"total_words = len(tokenizer.word_index) + 1\\n\",\n        \"\\n\",\n        \"print(tokenizer.word_index)\\n\",\n        \"print(total_words)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"v9x68iN_X6FK\"\n      },\n      \"source\": [\n        \"### Create Sequences and Labels\\n\",\n        \"\\n\",\n        \"After preprocessing, we next need to create sequences and labels. Creating the sequences themselves is similar to before with `texts_to_sequences`, but also including the use of [N-Grams](https://towardsdatascience.com/introduction-to-language-models-n-gram-e323081503d9); creating the labels will now utilize those sequences as well as utilize one-hot encoding over all potential output words.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"QmlTsUqfikVO\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"sequences = []\\n\",\n        \"for line in corpus:\\n\",\n        \"\\ttoken_list = tokenizer.texts_to_sequences([line])[0]\\n\",\n        \"\\tfor i in range(1, len(token_list)):\\n\",\n        \"\\t\\tn_gram_sequence = token_list[:i+1]\\n\",\n        \"\\t\\tsequences.append(n_gram_sequence)\\n\",\n        \"\\n\",\n        \"# Pad sequences for equal input length \\n\",\n        \"max_sequence_len = max([len(seq) for seq in sequences])\\n\",\n        \"sequences = np.array(pad_sequences(sequences, maxlen=max_sequence_len, padding='pre'))\\n\",\n        \"\\n\",\n        \"# Split sequences between the \\\"input\\\" sequence and \\\"output\\\" predicted word\\n\",\n        \"input_sequences, labels = sequences[:,:-1], sequences[:,-1]\\n\",\n        \"# One-hot encode the labels\\n\",\n        \"one_hot_labels = tf.keras.utils.to_categorical(labels, num_classes=total_words)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Zsmu3aEId49i\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Check out how some of our data is being stored\\n\",\n        \"# The Tokenizer has just a single index per word\\n\",\n        \"print(tokenizer.word_index['know'])\\n\",\n        \"print(tokenizer.word_index['feeling'])\\n\",\n        \"# Input sequences will have multiple indexes\\n\",\n        \"print(input_sequences[5])\\n\",\n        \"print(input_sequences[6])\\n\",\n        \"# And the one hot labels will be as long as the full spread of tokenized words\\n\",\n        \"print(one_hot_labels[5])\\n\",\n        \"print(one_hot_labels[6])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"-1TAJMlmfO8r\"\n      },\n      \"source\": [\n        \"### Train a Text Generation Model\\n\",\n        \"\\n\",\n        \"Building an RNN to train our text generation model will be very similar to the sentiment models you've built previously. The only real change necessary is to make sure to use Categorical instead of Binary Cross Entropy as the loss function - we could use Binary before since the sentiment was only 0 or 1, but now there are hundreds of categories.\\n\",\n        \"\\n\",\n        \"From there, we should also consider using *more* epochs than before, as text generation can take a little longer to converge than sentiment analysis, *and* we aren't working with all that much data yet. I'll set it at 200 epochs here since we're only use part of the dataset, and training will tail off quite a bit over that many epochs.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"G1YXuxIqfygN\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"from tensorflow.keras.models import Sequential\\n\",\n        \"from tensorflow.keras.layers import Embedding, LSTM, Dense, Bidirectional\\n\",\n        \"\\n\",\n        \"model = Sequential()\\n\",\n        \"model.add(Embedding(total_words, 64, input_length=max_sequence_len-1))\\n\",\n        \"model.add(Bidirectional(LSTM(20)))\\n\",\n        \"model.add(Dense(total_words, activation='softmax'))\\n\",\n        \"model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])\\n\",\n        \"history = model.fit(input_sequences, one_hot_labels, epochs=200, verbose=1)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"AXVFpoREhV6Y\"\n      },\n      \"source\": [\n        \"### View the Training Graph\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"aeSNfS7uhch0\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import matplotlib.pyplot as plt\\n\",\n        \"\\n\",\n        \"def plot_graphs(history, string):\\n\",\n        \"  plt.plot(history.history[string])\\n\",\n        \"  plt.xlabel(\\\"Epochs\\\")\\n\",\n        \"  plt.ylabel(string)\\n\",\n        \"  plt.show()\\n\",\n        \"\\n\",\n        \"plot_graphs(history, 'accuracy')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"1rAgRpxYhjpB\"\n      },\n      \"source\": [\n        \"### Generate new lyrics!\\n\",\n        \"\\n\",\n        \"It's finally time to generate some new lyrics from the trained model, and see what we get. To do so, we'll provide some \\\"seed text\\\", or an input sequence for the model to start with. We'll also decide just how long of an output sequence we want - this could essentially be infinite, as the input plus the previous output will be continuously fed in for a new output word (at least up to our max sequence length).\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"DC7zfcgviDTp\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"seed_text = \\\"im feeling chills\\\"\\n\",\n        \"next_words = 100\\n\",\n        \"  \\n\",\n        \"for _ in range(next_words):\\n\",\n        \"\\ttoken_list = tokenizer.texts_to_sequences([seed_text])[0]\\n\",\n        \"\\ttoken_list = pad_sequences([token_list], maxlen=max_sequence_len-1, padding='pre')\\n\",\n        \"\\tpredicted = np.argmax(model.predict(token_list), axis=-1)\\n\",\n        \"\\toutput_word = \\\"\\\"\\n\",\n        \"\\tfor word, index in tokenizer.word_index.items():\\n\",\n        \"\\t\\tif index == predicted:\\n\",\n        \"\\t\\t\\toutput_word = word\\n\",\n        \"\\t\\t\\tbreak\\n\",\n        \"\\tseed_text += \\\" \\\" + output_word\\n\",\n        \"print(seed_text)\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l10c03_nlp_constructing_text_generation_model.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_for_deep_learning/l10c04_nlp_optimizing_the_text_generation_model.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"punL79CN7Ox6\"\n      },\n      \"source\": [\n        \"##### Copyright 2020 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"_ckMIh7O7s6D\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Ph5eir3Pf-3z\"\n      },\n      \"source\": [\n        \"# Optimizing the Text Generation Model\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"S5Uhzt6vVIB2\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l10c04_nlp_optimizing_the_text_generation_model.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l10c04_nlp_optimizing_the_text_generation_model.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"dCxhW3mtLmfb\"\n      },\n      \"source\": [\n        \"You've already done some amazing work with generating new songs, but so far we've seen some issues with repetition and a fair amount of incoherence. By using more data and further tweaking the model, you'll be able to get improved results. We'll once again use the [Kaggle Song Lyrics Dataset](https://www.kaggle.com/mousehead/songlyrics) here.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"4aHK2CYygXom\"\n      },\n      \"source\": [\n        \"## Import TensorFlow and related functions\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"2LmLTREBf5ng\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow as tf\\n\",\n        \"\\n\",\n        \"from tensorflow.keras.preprocessing.text import Tokenizer\\n\",\n        \"from tensorflow.keras.preprocessing.sequence import pad_sequences\\n\",\n        \"\\n\",\n        \"# Other imports for processing data\\n\",\n        \"import string\\n\",\n        \"import numpy as np\\n\",\n        \"import pandas as pd\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"GmLTO_dpgge9\"\n      },\n      \"source\": [\n        \"## Get the Dataset\\n\",\n        \"\\n\",\n        \"As noted above, we'll utilize the [Song Lyrics dataset](https://www.kaggle.com/mousehead/songlyrics) on Kaggle again.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"4Bf5FVHfganK\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!wget --no-check-certificate \\\\\\n\",\n        \"    https://drive.google.com/uc?id=1LiJFZd41ofrWoBtW-pMYsfz1w8Ny0Bj8 \\\\\\n\",\n        \"    -O /tmp/songdata.csv\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Jz9x-7dWihxx\"\n      },\n      \"source\": [\n        \"## 250 Songs\\n\",\n        \"\\n\",\n        \"Now we've seen a model trained on just a small sample of songs, and how this often leads to repetition as you get further along in trying to generate new text. Let's switch to using the 250 songs instead, and see if our output improves. This will actually be nearly 10K lines of lyrics, which should be sufficient.\\n\",\n        \"\\n\",\n        \"Note that we won't use the full dataset here as it will take up quite a bit of RAM and processing time, but you're welcome to try doing so on your own later. If interested, you'll likely want to use only some of the more common words for the Tokenizer, which will help shrink processing time and memory needed (or else you'd have an output array hundreds of thousands of words long).\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"nWbMN_19jfRT\"\n      },\n      \"source\": [\n        \"### Preprocessing\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"LRmPPJegovBe\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def tokenize_corpus(corpus, num_words=-1):\\n\",\n        \"  # Fit a Tokenizer on the corpus\\n\",\n        \"  if num_words > -1:\\n\",\n        \"    tokenizer = Tokenizer(num_words=num_words)\\n\",\n        \"  else:\\n\",\n        \"    tokenizer = Tokenizer()\\n\",\n        \"  tokenizer.fit_on_texts(corpus)\\n\",\n        \"  return tokenizer\\n\",\n        \"\\n\",\n        \"def create_lyrics_corpus(dataset, field):\\n\",\n        \"  # Remove all other punctuation\\n\",\n        \"  dataset[field] = dataset[field].str.replace('[{}]'.format(string.punctuation), '')\\n\",\n        \"  # Make it lowercase\\n\",\n        \"  dataset[field] = dataset[field].str.lower()\\n\",\n        \"  # Make it one long string to split by line\\n\",\n        \"  lyrics = dataset[field].str.cat()\\n\",\n        \"  corpus = lyrics.split('\\\\n')\\n\",\n        \"  # Remove any trailing whitespace\\n\",\n        \"  for l in range(len(corpus)):\\n\",\n        \"    corpus[l] = corpus[l].rstrip()\\n\",\n        \"  # Remove any empty lines\\n\",\n        \"  corpus = [l for l in corpus if l != '']\\n\",\n        \"\\n\",\n        \"  return corpus\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"kIGedF3XjHj4\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def tokenize_corpus(corpus, num_words=-1):\\n\",\n        \"  # Fit a Tokenizer on the corpus\\n\",\n        \"  if num_words > -1:\\n\",\n        \"    tokenizer = Tokenizer(num_words=num_words)\\n\",\n        \"  else:\\n\",\n        \"    tokenizer = Tokenizer()\\n\",\n        \"  tokenizer.fit_on_texts(corpus)\\n\",\n        \"  return tokenizer\\n\",\n        \"\\n\",\n        \"# Read the dataset from csv - this time with 250 songs\\n\",\n        \"dataset = pd.read_csv('/tmp/songdata.csv', dtype=str)[:250]\\n\",\n        \"# Create the corpus using the 'text' column containing lyrics\\n\",\n        \"corpus = create_lyrics_corpus(dataset, 'text')\\n\",\n        \"# Tokenize the corpus\\n\",\n        \"tokenizer = tokenize_corpus(corpus, num_words=2000)\\n\",\n        \"total_words = tokenizer.num_words\\n\",\n        \"\\n\",\n        \"# There should be a lot more words now\\n\",\n        \"print(total_words)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"quoDmw_FkNBA\"\n      },\n      \"source\": [\n        \"### Create Sequences and Labels\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"kkLAf3HmkPSo\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"sequences = []\\n\",\n        \"for line in corpus:\\n\",\n        \"\\ttoken_list = tokenizer.texts_to_sequences([line])[0]\\n\",\n        \"\\tfor i in range(1, len(token_list)):\\n\",\n        \"\\t\\tn_gram_sequence = token_list[:i+1]\\n\",\n        \"\\t\\tsequences.append(n_gram_sequence)\\n\",\n        \"\\n\",\n        \"# Pad sequences for equal input length \\n\",\n        \"max_sequence_len = max([len(seq) for seq in sequences])\\n\",\n        \"sequences = np.array(pad_sequences(sequences, maxlen=max_sequence_len, padding='pre'))\\n\",\n        \"\\n\",\n        \"# Split sequences between the \\\"input\\\" sequence and \\\"output\\\" predicted word\\n\",\n        \"input_sequences, labels = sequences[:,:-1], sequences[:,-1]\\n\",\n        \"# One-hot encode the labels\\n\",\n        \"one_hot_labels = tf.keras.utils.to_categorical(labels, num_classes=total_words)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"cECbqT-blMk-\"\n      },\n      \"source\": [\n        \"### Train a (Better) Text Generation Model\\n\",\n        \"\\n\",\n        \"With more data, we'll cut off after 100 epochs to avoid keeping you here all day. You'll also want to change your runtime type to GPU if you haven't already (you'll need to re-run the above cells if you change runtimes).\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"7nHOp6uWlP_P\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"from tensorflow.keras.models import Sequential\\n\",\n        \"from tensorflow.keras.layers import Embedding, LSTM, Dense, Bidirectional\\n\",\n        \"\\n\",\n        \"model = Sequential()\\n\",\n        \"model.add(Embedding(total_words, 64, input_length=max_sequence_len-1))\\n\",\n        \"model.add(Bidirectional(LSTM(20)))\\n\",\n        \"model.add(Dense(total_words, activation='softmax'))\\n\",\n        \"model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])\\n\",\n        \"history = model.fit(input_sequences, one_hot_labels, epochs=100, verbose=1)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"MgvIz20nlQcq\"\n      },\n      \"source\": [\n        \"### View the Training Graph\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"rOqmmarvlSLh\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import matplotlib.pyplot as plt\\n\",\n        \"\\n\",\n        \"def plot_graphs(history, string):\\n\",\n        \"  plt.plot(history.history[string])\\n\",\n        \"  plt.xlabel(\\\"Epochs\\\")\\n\",\n        \"  plt.ylabel(string)\\n\",\n        \"  plt.show()\\n\",\n        \"\\n\",\n        \"plot_graphs(history, 'accuracy')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ISLZZGlQlSxh\"\n      },\n      \"source\": [\n        \"### Generate better lyrics!\\n\",\n        \"\\n\",\n        \"This time around, we should be able to get a more interesting output with less repetition.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"P96oVMk3lU7y\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"seed_text = \\\"im feeling chills\\\"\\n\",\n        \"next_words = 100\\n\",\n        \"  \\n\",\n        \"for _ in range(next_words):\\n\",\n        \"\\ttoken_list = tokenizer.texts_to_sequences([seed_text])[0]\\n\",\n        \"\\ttoken_list = pad_sequences([token_list], maxlen=max_sequence_len-1, padding='pre')\\n\",\n        \"\\tpredicted = np.argmax(model.predict(token_list), axis=-1)\\n\",\n        \"\\toutput_word = \\\"\\\"\\n\",\n        \"\\tfor word, index in tokenizer.word_index.items():\\n\",\n        \"\\t\\tif index == predicted:\\n\",\n        \"\\t\\t\\toutput_word = word\\n\",\n        \"\\t\\t\\tbreak\\n\",\n        \"\\tseed_text += \\\" \\\" + output_word\\n\",\n        \"print(seed_text)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"upgJKV8_oRU9\"\n      },\n      \"source\": [\n        \"### Varying the Possible Outputs\\n\",\n        \"\\n\",\n        \"In running the above, you may notice that the same seed text will generate similar outputs. This is because the code is currently always choosing the top predicted class as the next word. What if you wanted more variance in the output? \\n\",\n        \"\\n\",\n        \"Switching from `model.predict_classes` to `model.predict_proba` will get us all of the class probabilities. We can combine this with `np.random.choice` to select a given predicted output based on a probability, thereby giving a bit more randomness to our outputs.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"lZe9gaJeoGVP\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Test the method with just the first word after the seed text\\n\",\n        \"seed_text = \\\"im feeling chills\\\"\\n\",\n        \"next_words = 100\\n\",\n        \"  \\n\",\n        \"token_list = tokenizer.texts_to_sequences([seed_text])[0]\\n\",\n        \"token_list = pad_sequences([token_list], maxlen=max_sequence_len-1, padding='pre')\\n\",\n        \"predicted_probs = model.predict(token_list)[0]\\n\",\n        \"predicted = np.random.choice([x for x in range(len(predicted_probs))], \\n\",\n        \"                             p=predicted_probs)\\n\",\n        \"# Running this cell multiple times should get you some variance in output\\n\",\n        \"print(predicted)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"ee7WKgRGrJy1\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Use this process for the full output generation\\n\",\n        \"seed_text = \\\"im feeling chills\\\"\\n\",\n        \"next_words = 100\\n\",\n        \"  \\n\",\n        \"for _ in range(next_words):\\n\",\n        \"  token_list = tokenizer.texts_to_sequences([seed_text])[0]\\n\",\n        \"  token_list = pad_sequences([token_list], maxlen=max_sequence_len-1, padding='pre')\\n\",\n        \"  predicted_probs = model.predict(token_list)[0]\\n\",\n        \"  predicted = np.random.choice([x for x in range(len(predicted_probs))],\\n\",\n        \"                               p=predicted_probs)\\n\",\n        \"  output_word = \\\"\\\"\\n\",\n        \"  for word, index in tokenizer.word_index.items():\\n\",\n        \"    if index == predicted:\\n\",\n        \"      output_word = word\\n\",\n        \"      break\\n\",\n        \"  seed_text += \\\" \\\" + output_word\\n\",\n        \"print(seed_text)\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"l10c04_nlp_optimizing_the_text_generation_model.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_lite/tflite_c01_linear_regression.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Za8-Nr5k11fh\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"Eq10uEbw0E4l\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"UysiGN3tGQHY\"\n      },\n      \"source\": [\n        \"# Running TFLite models\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"2hOrvdmswy5O\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_lite/tflite_c01_linear_regression.ipynb\\\">\\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />\\n\",\n        \"    Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_lite/tflite_c01_linear_regression.ipynb\\\">\\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />\\n\",\n        \"    View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"W-VhTkyTGcaQ\"\n      },\n      \"source\": [\n        \"## Setup\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"dy4BcTjBFTWx\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow as tf\\n\",\n        \"\\n\",\n        \"import pathlib\\n\",\n        \"import numpy as np\\n\",\n        \"import matplotlib.pyplot as plt\\n\",\n        \"\\n\",\n        \"from tensorflow.keras.models import Model\\n\",\n        \"from tensorflow.keras.layers import Input\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ceibQLDeGhI4\"\n      },\n      \"source\": [\n        \"## Create a basic model of the form y = mx + c\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"YIBCsjQNF46Z\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Create a simple Keras model.\\n\",\n        \"x = [-1, 0, 1, 2, 3, 4]\\n\",\n        \"y = [-3, -1, 1, 3, 5, 7]\\n\",\n        \"\\n\",\n        \"model = tf.keras.models.Sequential([\\n\",\n        \"    tf.keras.layers.Dense(units=1, input_shape=[1])\\n\",\n        \"])\\n\",\n        \"model.compile(optimizer='sgd', loss='mean_squared_error')\\n\",\n        \"model.fit(x, y, epochs=200, verbose=1)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"EjsB-QICGt6L\"\n      },\n      \"source\": [\n        \"## Generate a SavedModel\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"a9xcbK7QHOfm\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"export_dir = 'saved_model/1'\\n\",\n        \"tf.saved_model.save(model, export_dir)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"RRtsNwkiGxcO\"\n      },\n      \"source\": [\n        \"## Convert the SavedModel to TFLite\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"TtM8yKTVTpD3\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Convert the model.\\n\",\n        \"converter = tf.lite.TFLiteConverter.from_saved_model(export_dir)\\n\",\n        \"tflite_model = converter.convert()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"4idYulcNHTdO\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"tflite_model_file = pathlib.Path('model.tflite')\\n\",\n        \"tflite_model_file.write_bytes(tflite_model)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"HgGvp2yBG25Q\"\n      },\n      \"source\": [\n        \"## Initialize the TFLite interpreter to try it out\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"DOt94wIWF8m7\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Load TFLite model and allocate tensors.\\n\",\n        \"interpreter = tf.lite.Interpreter(model_content=tflite_model)\\n\",\n        \"interpreter.allocate_tensors()\\n\",\n        \"\\n\",\n        \"# Get input and output tensors.\\n\",\n        \"input_details = interpreter.get_input_details()\\n\",\n        \"output_details = interpreter.get_output_details()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"JGYkEK08F8qK\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Test the TensorFlow Lite model on random input data.\\n\",\n        \"input_shape = input_details[0]['shape']\\n\",\n        \"inputs, outputs = [], []\\n\",\n        \"for _ in range(100):\\n\",\n        \"  input_data = np.array(np.random.random_sample(input_shape), dtype=np.float32)\\n\",\n        \"  interpreter.set_tensor(input_details[0]['index'], input_data)\\n\",\n        \"\\n\",\n        \"  interpreter.invoke()\\n\",\n        \"  tflite_results = interpreter.get_tensor(output_details[0]['index'])\\n\",\n        \"\\n\",\n        \"  # Test the TensorFlow model on random input data.\\n\",\n        \"  tf_results = model(tf.constant(input_data))\\n\",\n        \"  output_data = np.array(tf_results)\\n\",\n        \"  \\n\",\n        \"  inputs.append(input_data[0][0])\\n\",\n        \"  outputs.append(output_data[0][0])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"t1gQGH1KWAgW\"\n      },\n      \"source\": [\n        \"## Visualize the model\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"ccvQ1mEJVrqo\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"plt.plot(inputs, outputs, 'r')\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"WbugMH6yKvtd\"\n      },\n      \"source\": [\n        \"## Download the TFLite model file\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"FOAIMETeJmkc\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"try:\\n\",\n        \"  from google.colab import files\\n\",\n        \"  files.download(tflite_model_file)\\n\",\n        \"except:\\n\",\n        \"  pass\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"tflite_c01_linear_regression.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_lite/tflite_c02_transfer_learning.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Za8-Nr5k11fh\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"Eq10uEbw0E4l\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"oYM61xrTsP5d\"\n      },\n      \"source\": [\n        \"# Transfer Learning with TensorFlow Hub for TFLite\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"aFNhz34Svuhe\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_lite/tflite_c02_transfer_learning.ipynb\\\">\\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />\\n\",\n        \"    Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_lite/tflite_c02_transfer_learning.ipynb\\\">\\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />\\n\",\n        \"    View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"bL54LWCHt5q5\"\n      },\n      \"source\": [\n        \"## Setup \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"dlauq-4FWGZM\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import os\\n\",\n        \"\\n\",\n        \"import matplotlib.pylab as plt\\n\",\n        \"import numpy as np\\n\",\n        \"\\n\",\n        \"import tensorflow as tf\\n\",\n        \"import tensorflow_hub as hub\\n\",\n        \"\\n\",\n        \"print(\\\"Version: \\\", tf.__version__)\\n\",\n        \"print(\\\"Eager mode: \\\", tf.executing_eagerly())\\n\",\n        \"print(\\\"Hub version: \\\", hub.__version__)\\n\",\n        \"print(\\\"GPU is\\\", \\\"available\\\" if tf.config.list_physical_devices('GPU') else \\\"NOT AVAILABLE\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"mmaHHH7Pvmth\"\n      },\n      \"source\": [\n        \"## Select the Hub/TF2 module to use\\n\",\n        \"\\n\",\n        \"Hub modules for TF 1.x won't work here, please use one of the selections provided.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"FlsEcKVeuCnf\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"module_selection = (\\\"mobilenet_v2\\\", 224, 1280) #@param [\\\"(\\\\\\\"mobilenet_v2\\\\\\\", 224, 1280)\\\", \\\"(\\\\\\\"inception_v3\\\\\\\", 299, 2048)\\\"] {type:\\\"raw\\\", allow-input: true}\\n\",\n        \"handle_base, pixels, FV_SIZE = module_selection\\n\",\n        \"MODULE_HANDLE =\\\"https://tfhub.dev/google/tf2-preview/{}/feature_vector/4\\\".format(handle_base)\\n\",\n        \"IMAGE_SIZE = (pixels, pixels)\\n\",\n        \"print(\\\"Using {} with input size {} and output dimension {}\\\".format(\\n\",\n        \"  MODULE_HANDLE, IMAGE_SIZE, FV_SIZE))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"sYUsgwCBv87A\"\n      },\n      \"source\": [\n        \"## Data preprocessing\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"8nqVX3KYwGPh\"\n      },\n      \"source\": [\n        \"Use [TensorFlow Datasets](http://tensorflow.org/datasets) to load the cats and dogs dataset.\\n\",\n        \"\\n\",\n        \"This `tfds` package is the easiest way to load pre-defined data. If you have your own data, and are interested in importing using it with TensorFlow see [loading image data](../load_data/images.ipynb)\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"jGvpkDj4wBup\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow_datasets as tfds\\n\",\n        \"tfds.disable_progress_bar()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"YkF4Boe5wN7N\"\n      },\n      \"source\": [\n        \"The `tfds.load` method downloads and caches the data, and returns a `tf.data.Dataset` object. These objects provide powerful, efficient methods for manipulating data and piping it into your model.\\n\",\n        \"\\n\",\n        \"Since `\\\"cats_vs_dogs\\\"` doesn't define standard splits, use the subsplit feature to divide it into (train, validation, test) with 80%, 10%, 10% of the data respectively.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"SQ9xK9F2wGD8\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"(train_examples, validation_examples, test_examples), info = tfds.load(\\n\",\n        \"    'cats_vs_dogs',\\n\",\n        \"    split=['train[80%:]', 'train[80%:90%]', 'train[90%:]'],\\n\",\n        \"    with_info=True, \\n\",\n        \"    as_supervised=True, \\n\",\n        \")\\n\",\n        \"\\n\",\n        \"num_examples = info.splits['train'].num_examples\\n\",\n        \"num_classes = info.features['label'].num_classes\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"pmXQYXNWwf19\"\n      },\n      \"source\": [\n        \"### Format the Data\\n\",\n        \"\\n\",\n        \"Use the `tf.image` module to format the images for the task.\\n\",\n        \"\\n\",\n        \"Resize the images to a fixes input size, and rescale the input channels\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"y7UyXblSwkUS\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def format_image(image, label):\\n\",\n        \"  image = tf.image.resize(image, IMAGE_SIZE) / 255.0\\n\",\n        \"  return  image, label\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"1nrDR8CnwrVk\"\n      },\n      \"source\": [\n        \"Now shuffle and batch the data\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"zAEUG7vawxLm\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"BATCH_SIZE = 32 #@param {type:\\\"integer\\\"}\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"fHEC9mbswxvM\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"train_batches = train_examples.shuffle(num_examples // 4).map(format_image).batch(BATCH_SIZE).prefetch(1)\\n\",\n        \"validation_batches = validation_examples.map(format_image).batch(BATCH_SIZE).prefetch(1)\\n\",\n        \"test_batches = test_examples.map(format_image).batch(1)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ghQhZjgEw1cK\"\n      },\n      \"source\": [\n        \"Inspect a batch\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"gz0xsMCjwx54\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"for image_batch, label_batch in train_batches.take(1):\\n\",\n        \"  pass\\n\",\n        \"\\n\",\n        \"image_batch.shape\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"FS_gVStowW3G\"\n      },\n      \"source\": [\n        \"## Defining the model\\n\",\n        \"\\n\",\n        \"All it takes is to put a linear classifier on top of the `feature_extractor_layer` with the Hub module.\\n\",\n        \"\\n\",\n        \"For speed, we start out with a non-trainable `feature_extractor_layer`, but you can also enable fine-tuning for greater accuracy.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"RaJW3XrPyFiF\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"do_fine_tuning = False #@param {type:\\\"boolean\\\"}\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"wd0KfstqaUmE\"\n      },\n      \"source\": [\n        \"Load TFHub Module\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"svvDrt3WUrrm\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"feature_extractor = hub.KerasLayer(MODULE_HANDLE,\\n\",\n        \"                                   input_shape=IMAGE_SIZE + (3,), \\n\",\n        \"                                   output_shape=[FV_SIZE],\\n\",\n        \"                                   trainable=do_fine_tuning)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"50FYNIb1dmJH\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"Building model with\\\", MODULE_HANDLE)\\n\",\n        \"model = tf.keras.Sequential([\\n\",\n        \"    feature_extractor,\\n\",\n        \"    tf.keras.layers.Dense(num_classes)\\n\",\n        \"])\\n\",\n        \"model.summary()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"1PzLoQK0Zadv\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title (Optional) Unfreeze some layers\\n\",\n        \"NUM_LAYERS = 7 #@param {type:\\\"slider\\\", min:1, max:50, step:1}\\n\",\n        \"      \\n\",\n        \"if do_fine_tuning:\\n\",\n        \"  feature_extractor.trainable = True\\n\",\n        \"  \\n\",\n        \"  for layer in model.layers[-NUM_LAYERS:]:\\n\",\n        \"    layer.trainable = True\\n\",\n        \"\\n\",\n        \"else:\\n\",\n        \"  feature_extractor.trainable = False\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"u2e5WupIw2N2\"\n      },\n      \"source\": [\n        \"## Training the model\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"9f3yBUvkd_VJ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"if do_fine_tuning:\\n\",\n        \"  model.compile(\\n\",\n        \"    optimizer=tf.keras.optimizers.SGD(lr=0.002, momentum=0.9), \\n\",\n        \"    loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\\n\",\n        \"    metrics=['accuracy'])\\n\",\n        \"else:\\n\",\n        \"  model.compile(\\n\",\n        \"    optimizer='adam', \\n\",\n        \"    loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\\n\",\n        \"    metrics=['accuracy'])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"w_YKX2Qnfg6x\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"EPOCHS = 5\\n\",\n        \"hist = model.fit(train_batches,\\n\",\n        \"                    epochs=EPOCHS,\\n\",\n        \"                    validation_data=validation_batches)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"u_psFoTeLpHU\"\n      },\n      \"source\": [\n        \"## Export the model\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"XaSb5nVzHcVv\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"CATS_VS_DOGS_SAVED_MODEL = \\\"exp_saved_model\\\"\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"fZqRAg1uz1Nu\"\n      },\n      \"source\": [\n        \"Export the SavedModel\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"yJMue5YgnwtN\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"tf.saved_model.save(model, CATS_VS_DOGS_SAVED_MODEL)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"SOQF4cOan0SY\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"%%bash -s $CATS_VS_DOGS_SAVED_MODEL\\n\",\n        \"saved_model_cli show --dir $1 --tag_set serve --signature_def serving_default\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"FY7QGBgBytwX\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"loaded = tf.saved_model.load(CATS_VS_DOGS_SAVED_MODEL)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"tIhPyMISz952\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(list(loaded.signatures.keys()))\\n\",\n        \"infer = loaded.signatures[\\\"serving_default\\\"]\\n\",\n        \"print(infer.structured_input_signature)\\n\",\n        \"print(infer.structured_outputs)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"XxLiLC8n0H16\"\n      },\n      \"source\": [\n        \"## Convert using TFLite's Converter\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"1aUYvCpfWmrQ\"\n      },\n      \"source\": [\n        \"Load the TFLiteConverter with the SavedModel\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"dqJRyIg8Wl1n\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"converter = tf.lite.TFLiteConverter.from_saved_model(CATS_VS_DOGS_SAVED_MODEL)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"AudcNjT0UtfF\"\n      },\n      \"source\": [\n        \"### Post-training quantization\\n\",\n        \"The simplest form of post-training quantization quantizes weights from floating point to 8-bits of precision. This technique is enabled as an option in the TensorFlow Lite converter. At inference, weights are converted from 8-bits of precision to floating point and computed using floating-point kernels. This conversion is done once and cached to reduce latency.\\n\",\n        \"\\n\",\n        \"To further improve latency, hybrid operators dynamically quantize activations to 8-bits and perform computations with 8-bit weights and activations. This optimization provides latencies close to fully fixed-point inference. However, the outputs are still stored using floating point, so that the speedup with hybrid ops is less than a full fixed-point computation.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"WmSr2-yZoUhz\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"converter.optimizations = [tf.lite.Optimize.DEFAULT]\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"YpCijI08UxP0\"\n      },\n      \"source\": [\n        \"### Post-training integer quantization\\n\",\n        \"We can get further latency improvements, reductions in peak memory usage, and access to integer only hardware accelerators by making sure all model math is quantized. To do this, we need to measure the dynamic range of activations and inputs with a representative data set. You can simply create an input data generator and provide it to our converter.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"clM_dTIkWdIa\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def representative_data_gen():\\n\",\n        \"  for input_value, _ in test_batches.take(100):\\n\",\n        \"    yield [input_value]\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"0oPkAxDvUias\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"converter.representative_dataset = representative_data_gen\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"IGUAVTqXVfnu\"\n      },\n      \"source\": [\n        \"The resulting model will be fully quantized but still take float input and output for convenience.\\n\",\n        \"\\n\",\n        \"Ops that do not have quantized implementations will automatically be left in floating point. This allows conversion to occur smoothly but may restrict deployment to accelerators that support float. \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"cPVdjaEJVkHy\"\n      },\n      \"source\": [\n        \"### Full integer quantization\\n\",\n        \"\\n\",\n        \"To require the converter to only output integer operations, one can specify:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"eQi1aO2cVhoL\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"snwssESbVtFw\"\n      },\n      \"source\": [\n        \"### Finally convert the model\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"tUEgr46WVsqd\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"tflite_model = converter.convert()\\n\",\n        \"tflite_model_file = 'converted_model.tflite'\\n\",\n        \"\\n\",\n        \"with open(tflite_model_file, \\\"wb\\\") as f:\\n\",\n        \"  f.write(tflite_model)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"BbTF6nd1KG2o\"\n      },\n      \"source\": [\n        \"##Test the TFLite model using the Python Interpreter\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"dg2NkVTmLUdJ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Load TFLite model and allocate tensors.\\n\",\n        \"  \\n\",\n        \"interpreter = tf.lite.Interpreter(model_path=tflite_model_file)\\n\",\n        \"interpreter.allocate_tensors()\\n\",\n        \"\\n\",\n        \"input_index = interpreter.get_input_details()[0][\\\"index\\\"]\\n\",\n        \"output_index = interpreter.get_output_details()[0][\\\"index\\\"]\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"snJQVs9JNglv\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"from tqdm import tqdm\\n\",\n        \"\\n\",\n        \"# Gather results for the randomly sampled test images\\n\",\n        \"predictions = []\\n\",\n        \"\\n\",\n        \"test_labels, test_imgs = [], []\\n\",\n        \"for img, label in tqdm(test_batches.take(10)):\\n\",\n        \"  interpreter.set_tensor(input_index, img)\\n\",\n        \"  interpreter.invoke()\\n\",\n        \"  predictions.append(interpreter.get_tensor(output_index))\\n\",\n        \"  \\n\",\n        \"  test_labels.append(label.numpy()[0])\\n\",\n        \"  test_imgs.append(img)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"YMTWNqPpNiAI\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Utility functions for plotting\\n\",\n        \"# Utilities for plotting\\n\",\n        \"\\n\",\n        \"class_names = ['cat', 'dog']\\n\",\n        \"\\n\",\n        \"def plot_image(i, predictions_array, true_label, img):\\n\",\n        \"  predictions_array, true_label, img = predictions_array[i], true_label[i], img[i]\\n\",\n        \"  plt.grid(False)\\n\",\n        \"  plt.xticks([])\\n\",\n        \"  plt.yticks([])\\n\",\n        \"    \\n\",\n        \"  img = np.squeeze(img)\\n\",\n        \"\\n\",\n        \"  plt.imshow(img, cmap=plt.cm.binary)\\n\",\n        \"\\n\",\n        \"  predicted_label = np.argmax(predictions_array)\\n\",\n        \"  if predicted_label == true_label:\\n\",\n        \"    color = 'green'\\n\",\n        \"  else:\\n\",\n        \"    color = 'red'\\n\",\n        \"  \\n\",\n        \"  plt.xlabel(\\\"{} {:2.0f}% ({})\\\".format(class_names[predicted_label],\\n\",\n        \"                                100*np.max(predictions_array),\\n\",\n        \"                                class_names[true_label]),\\n\",\n        \"                                color=color)\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"fK_CTyL3XQt1\"\n      },\n      \"source\": [\n        \"NOTE: Colab runs on server CPUs. At the time of writing this, TensorFlow Lite doesn't have super optimized server CPU kernels. For this reason post-training full-integer quantized models  may be slower here than the other kinds of optimized models. But for mobile CPUs, considerable speedup can be observed.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"1-lbnicPNkZs\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Visualize the outputs { run: \\\"auto\\\" }\\n\",\n        \"index = 0 #@param {type:\\\"slider\\\", min:0, max:9, step:1}\\n\",\n        \"plt.figure(figsize=(6,3))\\n\",\n        \"plt.subplot(1,2,1)\\n\",\n        \"plot_image(index, predictions, test_labels, test_imgs)\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"PmZRieHmKLY5\"\n      },\n      \"source\": [\n        \"Download the model.\\n\",\n        \"\\n\",\n        \"**NOTE: You might have to run to the cell below twice**\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"0jJAxrQB2VFw\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"labels = ['cat', 'dog']\\n\",\n        \"\\n\",\n        \"with open('labels.txt', 'w') as f:\\n\",\n        \"  f.write('\\\\n'.join(labels))\\n\",\n        \"\\n\",\n        \"try:\\n\",\n        \"  from google.colab import files\\n\",\n        \"  files.download('converted_model.tflite')\\n\",\n        \"  files.download('labels.txt')\\n\",\n        \"except:\\n\",\n        \"  pass\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"BDlmpjC6VnFZ\"\n      },\n      \"source\": [\n        \"# Prepare the test images for download (Optional)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"_1ja_WA0WZOH\"\n      },\n      \"source\": [\n        \"This part involves downloading additional test images for the Mobile Apps only in case you need to try out more samples\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"fzLKEBrfTREA\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!mkdir -p test_images\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Qn7ukNQCSewb\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"from PIL import Image\\n\",\n        \"\\n\",\n        \"for index, (image, label) in enumerate(test_batches.take(50)):\\n\",\n        \"  image = tf.cast(image * 255.0, tf.uint8)\\n\",\n        \"  image = tf.squeeze(image).numpy()\\n\",\n        \"  pil_image = Image.fromarray(image)\\n\",\n        \"  pil_image.save('test_images/{}_{}.jpg'.format(class_names[label[0]], index))\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"xVKKWUG8UMO5\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!ls test_images\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"l_w_-UdlS9Vi\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!zip -qq cats_vs_dogs_test_images.zip -r test_images/\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Giva6EHwWm6Y\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"try:\\n\",\n        \"  files.download('cats_vs_dogs_test_images.zip')\\n\",\n        \"except:\\n\",\n        \"  pass\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"tflite_c02_transfer_learning.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_lite/tflite_c03_exercise_convert_model_to_tflite.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Za8-Nr5k11fh\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"Eq10uEbw0E4l\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"KlUrRaN4w3ct\"\n      },\n      \"source\": [\n        \"# Train Your Own Model and Convert It to TFLite\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"H3UojxdNw8J1\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_lite/tflite_c03_exercise_convert_model_to_tflite.ipynb\\\">\\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />\\n\",\n        \"    Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_lite/tflite_c03_exercise_convert_model_to_tflite.ipynb\\\">\\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />\\n\",\n        \"    View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"pXX-pi1r6NfG\"\n      },\n      \"source\": [\n        \"This notebook uses the [Fashion MNIST](https://github.com/zalandoresearch/fashion-mnist) dataset which contains 70,000 grayscale images in 10 categories. The images show individual articles of clothing at low resolution (28 by 28 pixels), as seen here:\\n\",\n        \"\\n\",\n        \"<table>\\n\",\n        \"  <tr><td>\\n\",\n        \"    <img src=\\\"https://tensorflow.org/images/fashion-mnist-sprite.png\\\"\\n\",\n        \"         alt=\\\"Fashion MNIST sprite\\\"  width=\\\"600\\\">\\n\",\n        \"  </td></tr>\\n\",\n        \"  <tr><td align=\\\"center\\\">\\n\",\n        \"    <b>Figure 1.</b> <a href=\\\"https://github.com/zalandoresearch/fashion-mnist\\\">Fashion-MNIST samples</a> (by Zalando, MIT License).<br/>&nbsp;\\n\",\n        \"  </td></tr>\\n\",\n        \"</table>\\n\",\n        \"\\n\",\n        \"Fashion MNIST is intended as a drop-in replacement for the classic [MNIST](http://yann.lecun.com/exdb/mnist/) dataset—often used as the \\\"Hello, World\\\" of machine learning programs for computer vision. The MNIST dataset contains images of handwritten digits (0, 1, 2, etc.) in a format identical to that of the articles of clothing we'll use here.\\n\",\n        \"\\n\",\n        \"This uses Fashion MNIST for variety, and because it's a slightly more challenging problem than regular MNIST. Both datasets are relatively small and are used to verify that an algorithm works as expected. They're good starting points to test and debug code.\\n\",\n        \"\\n\",\n        \"We will use 60,000 images to train the network and 10,000 images to evaluate how accurately the network learned to classify images. You can access the Fashion MNIST directly from TensorFlow. Import and load the Fashion MNIST data directly from TensorFlow:\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"rjOAfhgd__Sp\"\n      },\n      \"source\": [\n        \"# Setup\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"pfyZKowNAQ4j\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# TensorFlow and tf.keras\\n\",\n        \"import tensorflow as tf\\n\",\n        \"from tensorflow import keras\\n\",\n        \"\\n\",\n        \"import tensorflow_datasets as tfds\\n\",\n        \"tfds.disable_progress_bar()\\n\",\n        \"\\n\",\n        \"# Helper libraries\\n\",\n        \"import numpy as np\\n\",\n        \"import matplotlib.pyplot as plt\\n\",\n        \"import pathlib\\n\",\n        \"\\n\",\n        \"print(tf.__version__)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"tadPBTEiAprt\"\n      },\n      \"source\": [\n        \"# Download Fashion MNIST Dataset\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Ds9gfZKzAnkX\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"splits = tfds.Split.ALL.subsplit(weighted=(80, 10, 10))\\n\",\n        \"\\n\",\n        \"splits, info = tfds.load('fashion_mnist', with_info=True, as_supervised=True, split=splits)\\n\",\n        \"\\n\",\n        \"(train_examples, validation_examples, test_examples) = splits\\n\",\n        \"\\n\",\n        \"num_examples = info.splits['train'].num_examples\\n\",\n        \"num_classes = info.features['label'].num_classes\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"-eAv71FRm4JE\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"class_names = ['T-shirt_top', 'Trouser', 'Pullover', 'Dress', 'Coat',\\n\",\n        \"               'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"hXe6jNokqX3_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"with open('labels.txt', 'w') as f:\\n\",\n        \"  f.write('\\\\n'.join(class_names))\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"q0RxpwTmQN-y\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"IMG_SIZE = 28\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ZAkuq0V0Aw2X\"\n      },\n      \"source\": [\n        \"# Preprocessing data\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"_5SIivkunKCC\"\n      },\n      \"source\": [\n        \"## Preprocess\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"nQMIkJf9AvJ4\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Write a function to normalize and resize the images\\n\",\n        \"\\n\",\n        \"def format_example(image, label):\\n\",\n        \"  # Cast image to float32\\n\",\n        \"  image = # YOUR CODE HERE\\n\",\n        \"  # Resize the image if necessary\\n\",\n        \"  image = # YOUR CODE HERE\\n\",\n        \"  # Normalize the image in the range [0, 1]\\n\",\n        \"  image = # YOUR CODE HERE\\n\",\n        \"  return image, label\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"oEQP743aMv4C\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Set the batch size to 32\\n\",\n        \"\\n\",\n        \"BATCH_SIZE = 32\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"JM4HfIJtnNEk\"\n      },\n      \"source\": [\n        \"## Create a Dataset from images and labels\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"zOL4gSUARFjM\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Prepare the examples by preprocessing the them and then batching them (and optionally prefetching them)\\n\",\n        \"\\n\",\n        \"# If you wish you can shuffle train set here\\n\",\n        \"train_batches = # YOUR CODE HERE\\n\",\n        \"\\n\",\n        \"validation_batches = # YOUR CODE HERE\\n\",\n        \"test_batches = # YOUR CODE HERE\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"M-topQaOm_LM\"\n      },\n      \"source\": [\n        \"# Building the model\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"4gsYqdIlEFVg\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"\\\"\\\"\\\"\\n\",\n        \"Model: \\\"sequential\\\"\\n\",\n        \"_________________________________________________________________\\n\",\n        \"Layer (type)                 Output Shape              Param #   \\n\",\n        \"=================================================================\\n\",\n        \"conv2d (Conv2D)              (None, 26, 26, 16)        160       \\n\",\n        \"_________________________________________________________________\\n\",\n        \"max_pooling2d (MaxPooling2D) (None, 13, 13, 16)        0         \\n\",\n        \"_________________________________________________________________\\n\",\n        \"conv2d_1 (Conv2D)            (None, 11, 11, 32)        4640      \\n\",\n        \"_________________________________________________________________\\n\",\n        \"flatten (Flatten)            (None, 3872)              0         \\n\",\n        \"_________________________________________________________________\\n\",\n        \"dense (Dense)                (None, 64)                247872    \\n\",\n        \"_________________________________________________________________\\n\",\n        \"dense_1 (Dense)              (None, 10)                650       \\n\",\n        \"=================================================================\\n\",\n        \"Total params: 253,322\\n\",\n        \"Trainable params: 253,322\\n\",\n        \"Non-trainable params: 0\\n\",\n        \"\\\"\\\"\\\"\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"kDqcwksFB1bh\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Build the model shown in the previous cell\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"model = tf.keras.Sequential([\\n\",\n        \"  # Set the input shape to (28, 28, 1), kernel size=3, filters=16 and use ReLU activation,  \\n\",\n        \"  tf.keras.layers.Conv2D(# YOUR CODE HERE),    \\n\",\n        \"  tf.keras.layers.MaxPooling2D(),\\n\",\n        \"  # Set the number of filters to 32, kernel size to 3 and use ReLU activation \\n\",\n        \"  tf.keras.layers.Conv2D(# YOUR CODE HERE),\\n\",\n        \"  # Flatten the output layer to 1 dimension\\n\",\n        \"  tf.keras.layers.Flatten(),\\n\",\n        \"  # Add a fully connected layer with 64 hidden units and ReLU activation\\n\",\n        \"  tf.keras.layers.Dense(# YOUR CODE HERE),\\n\",\n        \"  # Attach a final softmax classification head\\n\",\n        \"  tf.keras.layers.Dense(# YOUR CODE HERE)])\\n\",\n        \"\\n\",\n        \"# Set the loss and accuracy metrics\\n\",\n        \"model.compile(\\n\",\n        \"    optimizer='adam', \\n\",\n        \"    loss=# YOUR CODE HERE, \\n\",\n        \"    metrics=# YOUR CODE HERE)\\n\",\n        \"      \"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"zEMOz-LDnxgD\"\n      },\n      \"source\": [\n        \"## Train\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"JGlNoRtzCP4_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model.fit(train_batches, \\n\",\n        \"          epochs=10,\\n\",\n        \"          validation_data=validation_batches)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"TZT9-7w9n4YO\"\n      },\n      \"source\": [\n        \"# Exporting to TFLite\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"9dq78KBkCV2_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"export_dir = 'saved_model/1'\\n\",\n        \"\\n\",\n        \"# Use the tf.saved_model API to export the SavedModel\\n\",\n        \"\\n\",\n        \"# Your Code Here\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"EDGiYrBdE6fl\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"optimization = tf.lite.Optimize.DEFAULT\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"RbcS9C00CzGe\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Use the TFLiteConverter SavedModel API to initialize the converter\\n\",\n        \"converter = # YOUR CODE HERE\\n\",\n        \"\\n\",\n        \"# Set the optimzations\\n\",\n        \"converter.optimizations = # YOUR CODE HERE\\n\",\n        \"\\n\",\n        \"# Invoke the converter to finally generate the TFLite model\\n\",\n        \"tflite_model = # YOUR CODE HERE\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"q5PWCDsTC3El\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"tflite_model_file = 'model.tflite'\\n\",\n        \"\\n\",\n        \"with open(tflite_model_file, \\\"wb\\\") as f:\\n\",\n        \"  f.write(tflite_model)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"SR6wFcQ1Fglm\"\n      },\n      \"source\": [\n        \"# Test if your model is working\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"O3IFOcUEIzQx\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Load TFLite model and allocate tensors.\\n\",\n        \"interpreter = tf.lite.Interpreter(model_content=tflite_model)\\n\",\n        \"interpreter.allocate_tensors()\\n\",\n        \"\\n\",\n        \"input_index = interpreter.get_input_details()[0][\\\"index\\\"]\\n\",\n        \"output_index = interpreter.get_output_details()[0][\\\"index\\\"]\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"rKcToCBEC-Bu\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Gather results for the randomly sampled test images\\n\",\n        \"predictions = []\\n\",\n        \"test_labels = []\\n\",\n        \"test_images = []\\n\",\n        \"\\n\",\n        \"for img, label in test_batches.take(50):\\n\",\n        \"  interpreter.set_tensor(input_index, img)\\n\",\n        \"  interpreter.invoke()\\n\",\n        \"  predictions.append(interpreter.get_tensor(output_index))\\n\",\n        \"  test_labels.append(label[0])\\n\",\n        \"  test_images.append(np.array(img))\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"kSjTmi05Tyod\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Utility functions for plotting\\n\",\n        \"# Utilities for plotting\\n\",\n        \"\\n\",\n        \"def plot_image(i, predictions_array, true_label, img):\\n\",\n        \"  predictions_array, true_label, img = predictions_array[i], true_label[i], img[i]\\n\",\n        \"  plt.grid(False)\\n\",\n        \"  plt.xticks([])\\n\",\n        \"  plt.yticks([])\\n\",\n        \"  \\n\",\n        \"  img = np.squeeze(img)\\n\",\n        \"\\n\",\n        \"  plt.imshow(img, cmap=plt.cm.binary)\\n\",\n        \"\\n\",\n        \"  predicted_label = np.argmax(predictions_array)\\n\",\n        \"  if predicted_label == true_label.numpy():\\n\",\n        \"    color = 'green'\\n\",\n        \"  else:\\n\",\n        \"    color = 'red'\\n\",\n        \"    \\n\",\n        \"  plt.xlabel(\\\"{} {:2.0f}% ({})\\\".format(class_names[predicted_label],\\n\",\n        \"                                100*np.max(predictions_array),\\n\",\n        \"                                class_names[true_label]),\\n\",\n        \"                                color=color)\\n\",\n        \"\\n\",\n        \"def plot_value_array(i, predictions_array, true_label):\\n\",\n        \"  predictions_array, true_label = predictions_array[i], true_label[i]\\n\",\n        \"  plt.grid(False)\\n\",\n        \"  plt.xticks(list(range(10)), class_names, rotation='vertical')\\n\",\n        \"  plt.yticks([])\\n\",\n        \"  thisplot = plt.bar(range(10), predictions_array[0], color=\\\"#777777\\\")\\n\",\n        \"  plt.ylim([0, 1])\\n\",\n        \"  predicted_label = np.argmax(predictions_array[0])\\n\",\n        \"\\n\",\n        \"  thisplot[predicted_label].set_color('red')\\n\",\n        \"  thisplot[true_label].set_color('green')\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"ZZwg0wFaVXhZ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Visualize the outputs { run: \\\"auto\\\" }\\n\",\n        \"index = 49 #@param {type:\\\"slider\\\", min:1, max:50, step:1}\\n\",\n        \"plt.figure(figsize=(6,3))\\n\",\n        \"plt.subplot(1,2,1)\\n\",\n        \"plot_image(index, predictions, test_labels, test_images)\\n\",\n        \"plt.show()\\n\",\n        \"plot_value_array(index, predictions, test_labels)\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"076bo3FMpRDb\"\n      },\n      \"source\": [\n        \"# Download TFLite model and assets\\n\",\n        \"\\n\",\n        \"**NOTE: You might have to run to the cell below twice**\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"XsPXqPlgZPjE\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"try:\\n\",\n        \"  from google.colab import files\\n\",\n        \"  files.download(tflite_model_file)\\n\",\n        \"  files.download('labels.txt')\\n\",\n        \"except:\\n\",\n        \"  pass\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"VyBVNwAzH3Oe\"\n      },\n      \"source\": [\n        \"# Deploying TFLite model\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"pdfa5L6wH87u\"\n      },\n      \"source\": [\n        \"Now once you've the trained TFLite model downloaded, you can ahead and deploy this on an Android/iOS application by placing the model assets in the appropriate location.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"iLY6X8P90L0P\"\n      },\n      \"source\": [\n        \"# Prepare the test images for download (Optional)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"G3bjzLj10OJv\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!mkdir -p test_images\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"pVrBZv1-0Py-\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"from PIL import Image\\n\",\n        \"\\n\",\n        \"for index, (image, label) in enumerate(test_batches.take(50)):\\n\",\n        \"  image = tf.cast(image * 255.0, tf.uint8)\\n\",\n        \"  image = tf.squeeze(image).numpy()\\n\",\n        \"  pil_image = Image.fromarray(image)\\n\",\n        \"  pil_image.save('test_images/{}_{}.jpg'.format(class_names[label[0]].lower(), index))\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"nX0N0M8u0R2s\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!ls test_images\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"LvLht1QM0W8k\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!zip -qq fmnist_test_images.zip -r test_images/\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"FdOq-4sT0X95\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"try:\\n\",\n        \"  files.download('fmnist_test_images.zip')\\n\",\n        \"except:\\n\",\n        \"  pass\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"tflite_c03_exercise_convert_model_to_tflite.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_lite/tflite_c04_exercise_convert_model_to_tflite_solution.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Za8-Nr5k11fh\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"Eq10uEbw0E4l\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"06ndLauQxiQm\"\n      },\n      \"source\": [\n        \"# Train Your Own Model and Convert It to TFLite\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Dtav_aq2xh6n\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_lite/tflite_c04_exercise_convert_model_to_tflite_solution.ipynb\\\">\\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />\\n\",\n        \"    Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_lite/tflite_c04_exercise_convert_model_to_tflite_solution.ipynb\\\">\\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />\\n\",\n        \"    View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Ka96-ajYzxVU\"\n      },\n      \"source\": [\n        \"This notebook uses the [Fashion MNIST](https://github.com/zalandoresearch/fashion-mnist) dataset which contains 70,000 grayscale images in 10 categories. The images show individual articles of clothing at low resolution (28 by 28 pixels), as seen here:\\n\",\n        \"\\n\",\n        \"<table>\\n\",\n        \"  <tr><td>\\n\",\n        \"    <img src=\\\"https://tensorflow.org/images/fashion-mnist-sprite.png\\\"\\n\",\n        \"         alt=\\\"Fashion MNIST sprite\\\"  width=\\\"600\\\">\\n\",\n        \"  </td></tr>\\n\",\n        \"  <tr><td align=\\\"center\\\">\\n\",\n        \"    <b>Figure 1.</b> <a href=\\\"https://github.com/zalandoresearch/fashion-mnist\\\">Fashion-MNIST samples</a> (by Zalando, MIT License).<br/>&nbsp;\\n\",\n        \"  </td></tr>\\n\",\n        \"</table>\\n\",\n        \"\\n\",\n        \"Fashion MNIST is intended as a drop-in replacement for the classic [MNIST](http://yann.lecun.com/exdb/mnist/) dataset—often used as the \\\"Hello, World\\\" of machine learning programs for computer vision. The MNIST dataset contains images of handwritten digits (0, 1, 2, etc.) in a format identical to that of the articles of clothing we'll use here.\\n\",\n        \"\\n\",\n        \"This uses Fashion MNIST for variety, and because it's a slightly more challenging problem than regular MNIST. Both datasets are relatively small and are used to verify that an algorithm works as expected. They're good starting points to test and debug code.\\n\",\n        \"\\n\",\n        \"We will use 60,000 images to train the network and 10,000 images to evaluate how accurately the network learned to classify images. You can access the Fashion MNIST directly from TensorFlow. Import and load the Fashion MNIST data directly from TensorFlow:\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"rjOAfhgd__Sp\"\n      },\n      \"source\": [\n        \"# Setup\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"pfyZKowNAQ4j\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# TensorFlow and tf.keras\\n\",\n        \"import tensorflow as tf\\n\",\n        \"from tensorflow import keras\\n\",\n        \"\\n\",\n        \"# Helper libraries\\n\",\n        \"import numpy as np\\n\",\n        \"import matplotlib.pyplot as plt\\n\",\n        \"import pathlib\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"print(tf.__version__)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"tadPBTEiAprt\"\n      },\n      \"source\": [\n        \"# Download Fashion MNIST Dataset\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"jmSkLCyRKqKB\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow_datasets as tfds\\n\",\n        \"tfds.disable_progress_bar()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"XcNwi6nFKneZ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"splits, info = tfds.load('fashion_mnist', with_info=True, as_supervised=True, \\n\",\n        \"                         split=['train[:80%]', 'train[80%:90%]', 'train[90%:]'])\\n\",\n        \"\\n\",\n        \"(train_examples, validation_examples, test_examples) = splits\\n\",\n        \"\\n\",\n        \"num_examples = info.splits['train'].num_examples\\n\",\n        \"num_classes = info.features['label'].num_classes\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"-eAv71FRm4JE\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"class_names = ['T-shirt_top', 'Trouser', 'Pullover', 'Dress', 'Coat',\\n\",\n        \"               'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"hXe6jNokqX3_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"with open('labels.txt', 'w') as f:\\n\",\n        \"  f.write('\\\\n'.join(class_names))\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"iubWCThbdN8K\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"IMG_SIZE = 28\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ZAkuq0V0Aw2X\"\n      },\n      \"source\": [\n        \"# Preprocessing data\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"_5SIivkunKCC\"\n      },\n      \"source\": [\n        \"## Preprocess\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"BwyhsyGydHDl\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def format_example(image, label):\\n\",\n        \"  image = tf.cast(image, tf.float32)\\n\",\n        \"  image = tf.image.resize(image, (IMG_SIZE, IMG_SIZE))\\n\",\n        \"  image = image / 255.0\\n\",\n        \"  return image, label\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"HAlBlXOUMwqe\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"BATCH_SIZE = 32\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"JM4HfIJtnNEk\"\n      },\n      \"source\": [\n        \"## Create a Dataset from images and labels\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"uxe2I3oxLDhq\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"train_batches = train_examples.cache().shuffle(num_examples//4).batch(BATCH_SIZE).map(format_example).prefetch(1)\\n\",\n        \"validation_batches = validation_examples.cache().batch(BATCH_SIZE).map(format_example).prefetch(1)\\n\",\n        \"test_batches = test_examples.cache().batch(1).map(format_example)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"M-topQaOm_LM\"\n      },\n      \"source\": [\n        \"# Building the model\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"kDqcwksFB1bh\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = tf.keras.Sequential([\\n\",\n        \"  tf.keras.layers.Conv2D(16, 3, activation='relu', input_shape=(IMG_SIZE, IMG_SIZE, 1)),\\n\",\n        \"  tf.keras.layers.MaxPooling2D(),\\n\",\n        \"  tf.keras.layers.Conv2D(32, 3, activation='relu'),\\n\",\n        \"  tf.keras.layers.Flatten(),\\n\",\n        \"  tf.keras.layers.Dense(64, activation='relu'),\\n\",\n        \"  tf.keras.layers.Dense(10)\\n\",\n        \"])\\n\",\n        \"\\n\",\n        \"model.compile(\\n\",\n        \"    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\\n\",\n        \"    optimizer='adam',\\n\",\n        \"    metrics=['accuracy'])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"zEMOz-LDnxgD\"\n      },\n      \"source\": [\n        \"## Train\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"1fk2faPsjqfU\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"validation_batches\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"DGJe_CNvjnhT\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"train_batches, \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"JGlNoRtzCP4_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model.fit(train_batches, \\n\",\n        \"          epochs=10,\\n\",\n        \"          validation_data=validation_batches)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"TZT9-7w9n4YO\"\n      },\n      \"source\": [\n        \"# Exporting to TFLite\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"9dq78KBkCV2_\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"export_dir = 'saved_model/1'\\n\",\n        \"tf.saved_model.save(model, export_dir)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"EDGiYrBdE6fl\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"optimization = tf.lite.Optimize.DEFAULT\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"RbcS9C00CzGe\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Convert the model.\\n\",\n        \"converter = tf.lite.TFLiteConverter.from_saved_model(export_dir)\\n\",\n        \"converter.optimizations = [optimization]\\n\",\n        \"tflite_model = converter.convert()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"q5PWCDsTC3El\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"tflite_model_file = 'model.tflite'\\n\",\n        \"\\n\",\n        \"with open(tflite_model_file, \\\"wb\\\") as f:\\n\",\n        \"  f.write(tflite_model)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"SR6wFcQ1Fglm\"\n      },\n      \"source\": [\n        \"# Test the model with TFLite interpreter \"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"rKcToCBEC-Bu\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Load TFLite model and allocate tensors.\\n\",\n        \"interpreter = tf.lite.Interpreter(model_content=tflite_model)\\n\",\n        \"interpreter.allocate_tensors()\\n\",\n        \"\\n\",\n        \"input_index = interpreter.get_input_details()[0][\\\"index\\\"]\\n\",\n        \"output_index = interpreter.get_output_details()[0][\\\"index\\\"]\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"E8EpFpIBFkq8\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Gather results for the randomly sampled test images\\n\",\n        \"predictions = []\\n\",\n        \"test_labels = []\\n\",\n        \"test_images = []\\n\",\n        \"\\n\",\n        \"for img, label in test_batches.take(50):\\n\",\n        \"  interpreter.set_tensor(input_index, img)\\n\",\n        \"  interpreter.invoke()\\n\",\n        \"  predictions.append(interpreter.get_tensor(output_index))\\n\",\n        \"  test_labels.append(label[0])\\n\",\n        \"  test_images.append(np.array(img))\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"kSjTmi05Tyod\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Utility functions for plotting\\n\",\n        \"# Utilities for plotting\\n\",\n        \"\\n\",\n        \"def plot_image(i, predictions_array, true_label, img):\\n\",\n        \"  predictions_array, true_label, img = predictions_array[i], true_label[i], img[i]\\n\",\n        \"  plt.grid(False)\\n\",\n        \"  plt.xticks([])\\n\",\n        \"  plt.yticks([])\\n\",\n        \"  \\n\",\n        \"  img = np.squeeze(img)\\n\",\n        \"\\n\",\n        \"  plt.imshow(img, cmap=plt.cm.binary)\\n\",\n        \"\\n\",\n        \"  predicted_label = np.argmax(predictions_array)\\n\",\n        \"  if predicted_label == true_label.numpy():\\n\",\n        \"    color = 'green'\\n\",\n        \"  else:\\n\",\n        \"    color = 'red'\\n\",\n        \"    \\n\",\n        \"  plt.xlabel(\\\"{} {:2.0f}% ({})\\\".format(class_names[predicted_label],\\n\",\n        \"                                100*np.max(predictions_array),\\n\",\n        \"                                class_names[true_label]),\\n\",\n        \"                                color=color)\\n\",\n        \"\\n\",\n        \"def plot_value_array(i, predictions_array, true_label):\\n\",\n        \"  predictions_array, true_label = predictions_array[i], true_label[i]\\n\",\n        \"  plt.grid(False)\\n\",\n        \"  plt.xticks(list(range(10)), class_names, rotation='vertical')\\n\",\n        \"  plt.yticks([])\\n\",\n        \"  thisplot = plt.bar(range(10), predictions_array[0], color=\\\"#777777\\\")\\n\",\n        \"  plt.ylim([0, 1])\\n\",\n        \"  predicted_label = np.argmax(predictions_array[0])\\n\",\n        \"\\n\",\n        \"  thisplot[predicted_label].set_color('red')\\n\",\n        \"  thisplot[true_label].set_color('green')\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"ZZwg0wFaVXhZ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Visualize the outputs { run: \\\"auto\\\" }\\n\",\n        \"index = 12 #@param {type:\\\"slider\\\", min:1, max:50, step:1}\\n\",\n        \"plt.figure(figsize=(6,3))\\n\",\n        \"plt.subplot(1,2,1)\\n\",\n        \"plot_image(index, predictions, test_labels, test_images)\\n\",\n        \"plt.show()\\n\",\n        \"plot_value_array(index, predictions, test_labels)\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"076bo3FMpRDb\"\n      },\n      \"source\": [\n        \"# Download TFLite model and assets\\n\",\n        \"\\n\",\n        \"**NOTE: You might have to run to the cell below twice**\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"XsPXqPlgZPjE\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"try:\\n\",\n        \"  from google.colab import files\\n\",\n        \"\\n\",\n        \"  files.download(tflite_model_file)\\n\",\n        \"  files.download('labels.txt')\\n\",\n        \"except:\\n\",\n        \"  pass\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"H8t7_jRiz9Vw\"\n      },\n      \"source\": [\n        \"# Prepare the test images for download (Optional)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Fi09nIps0gBu\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!mkdir -p test_images\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"sF7EZ63J0hZs\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"from PIL import Image\\n\",\n        \"\\n\",\n        \"for index, (image, label) in enumerate(test_batches.take(50)):\\n\",\n        \"  image = tf.cast(image * 255.0, tf.uint8)\\n\",\n        \"  image = tf.squeeze(image).numpy()\\n\",\n        \"  pil_image = Image.fromarray(image)\\n\",\n        \"  pil_image.save('test_images/{}_{}.jpg'.format(class_names[label[0]].lower(), index))\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"uM35O-uv0iWS\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!ls test_images\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"aR20r4qW0jVm\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!zip -qq fmnist_test_images.zip -r test_images/\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"tjk4537X0kWN\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"try:\\n\",\n        \"  files.download('fmnist_test_images.zip')\\n\",\n        \"except:\\n\",\n        \"  pass\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"tflite_c04_exercise_convert_model_to_tflite_solution.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_lite/tflite_c05_exercise_rock_paper_scissors.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Za8-Nr5k11fh\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"Eq10uEbw0E4l\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"oYM61xrTsP5d\"\n      },\n      \"source\": [\n        \"# Rock, Paper & Scissors with TensorFlow Hub - TFLite\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"pAI9358VyAsE\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_lite/tflite_c05_exercise_rock_paper_scissors.ipynb\\\">\\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />\\n\",\n        \"    Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_lite/tflite_c05_exercise_rock_paper_scissors.ipynb\\\">\\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />\\n\",\n        \"    View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"bL54LWCHt5q5\"\n      },\n      \"source\": [\n        \"## Setup\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"dlauq-4FWGZM\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import os\\n\",\n        \"\\n\",\n        \"import matplotlib.pylab as plt\\n\",\n        \"import numpy as np\\n\",\n        \"\\n\",\n        \"import tensorflow as tf\\n\",\n        \"import tensorflow_hub as hub\\n\",\n        \"\\n\",\n        \"print(\\\"Version: \\\", tf.__version__)\\n\",\n        \"print(\\\"Eager mode: \\\", tf.executing_eagerly())\\n\",\n        \"print(\\\"Hub version: \\\", hub.__version__)\\n\",\n        \"print(\\\"GPU is\\\", \\\"available\\\" if tf.config.list_physical_devices('GPU') else \\\"NOT AVAILABLE\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"mmaHHH7Pvmth\"\n      },\n      \"source\": [\n        \"## Select the Hub/TF2 module to use\\n\",\n        \"\\n\",\n        \"Hub modules for TF 1.x won't work here, please use one of the selections provided.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"FlsEcKVeuCnf\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"module_selection = (\\\"mobilenet_v2\\\", 224, 1280) #@param [\\\"(\\\\\\\"mobilenet_v2\\\\\\\", 224, 1280)\\\", \\\"(\\\\\\\"inception_v3\\\\\\\", 299, 2048)\\\"] {type:\\\"raw\\\", allow-input: true}\\n\",\n        \"handle_base, pixels, FV_SIZE = module_selection\\n\",\n        \"MODULE_HANDLE =\\\"https://tfhub.dev/google/tf2-preview/{}/feature_vector/4\\\".format(handle_base)\\n\",\n        \"IMAGE_SIZE = (pixels, pixels)\\n\",\n        \"print(\\\"Using {} with input size {} and output dimension {}\\\".format(\\n\",\n        \"  MODULE_HANDLE, IMAGE_SIZE, FV_SIZE))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"sYUsgwCBv87A\"\n      },\n      \"source\": [\n        \"## Data preprocessing\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"8nqVX3KYwGPh\"\n      },\n      \"source\": [\n        \"Use [TensorFlow Datasets](http://tensorflow.org/datasets) to load the rock, paper and scissors dataset.\\n\",\n        \"\\n\",\n        \"This `tfds` package is the easiest way to load pre-defined data. If you have your own data, and are interested in importing using it with TensorFlow see [loading image data](../load_data/images.ipynb)\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"jGvpkDj4wBup\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow_datasets as tfds\\n\",\n        \"tfds.disable_progress_bar()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"YkF4Boe5wN7N\"\n      },\n      \"source\": [\n        \"The `tfds.load` method downloads and caches the data, and returns a `tf.data.Dataset` object. These objects provide powerful, efficient methods for manipulating data and piping it into your model.\\n\",\n        \"\\n\",\n        \"Since `\\\"rock_paper_scissors\\\"` doesn't define standard splits, use the subsplit feature to divide it into (train, validation, test) with 80%, 10%, 10% of the data respectively.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"SQ9xK9F2wGD8\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"splits = tfds.Split.ALL.subsplit(weighted=(80, 10, 10))\\n\",\n        \"\\n\",\n        \"# Go to the TensorFlow Dataset's website and search for the Rock, Paper, Scissors dataset and load it here\\n\",\n        \"splits, info = tfds.load( # YOUR CODE HERE )\\n\",\n        \"    \\n\",\n        \"# Save the dataset splits in a tuple\\n\",\n        \"(train_examples, validation_examples, test_examples) = splits\\n\",\n        \"\\n\",\n        \"num_examples = info.splits['train'].num_examples\\n\",\n        \"num_classes = info.features['label'].num_classes\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"pmXQYXNWwf19\"\n      },\n      \"source\": [\n        \"### Format the Data\\n\",\n        \"\\n\",\n        \"Use the `tf.image` module to format the images for the task.\\n\",\n        \"\\n\",\n        \"Resize the images to a fixes input size, and rescale the input channels\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"y7UyXblSwkUS\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def format_image(image, label):\\n\",\n        \"  image = tf.image.resize(image, IMAGE_SIZE) / 255.0\\n\",\n        \"  return  image, label\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"1nrDR8CnwrVk\"\n      },\n      \"source\": [\n        \"Now shuffle and batch the data\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"zAEUG7vawxLm\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"BATCH_SIZE = 32 #@param {type:\\\"integer\\\"}\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"fHEC9mbswxvM\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Prepare the examples by preprocessing them and then batching them (and optionally prefetching them)\\n\",\n        \"\\n\",\n        \"# If you wish you can shuffle train set here\\n\",\n        \"train_batches = # YOUR CODE HERE\\n\",\n        \"\\n\",\n        \"validation_batches = # YOUR CODE HERE\\n\",\n        \"test_batches = # YOUR CODE HERE\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ghQhZjgEw1cK\"\n      },\n      \"source\": [\n        \"Inspect a batch\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"gz0xsMCjwx54\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"for image_batch, label_batch in train_batches.take(1):\\n\",\n        \"  pass\\n\",\n        \"\\n\",\n        \"image_batch.shape\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"FS_gVStowW3G\"\n      },\n      \"source\": [\n        \"## Defining the model\\n\",\n        \"\\n\",\n        \"All it takes is to put a linear classifier on top of the `feature_extractor_layer` with the Hub module.\\n\",\n        \"\\n\",\n        \"For speed, we start out with a non-trainable `feature_extractor_layer`, but you can also enable fine-tuning for greater accuracy.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"RaJW3XrPyFiF\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"do_fine_tuning = False #@param {type:\\\"boolean\\\"}\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"50FYNIb1dmJH\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Build the model with a TFHub KerasLayer and attach a classification head to it\\n\",\n        \"print(\\\"Building model with\\\", MODULE_HANDLE)\\n\",\n        \"model = tf.keras.Sequential([\\n\",\n        \"    hub.KerasLayer(MODULE_HANDLE,\\n\",\n        \"                   input_shape=IMAGE_SIZE + (3, ), \\n\",\n        \"                   output_shape=[FV_SIZE],\\n\",\n        \"                   trainable=do_fine_tuning),\\n\",\n        \"    tf.keras.layers.Dense(num_classes)\\n\",\n        \"])\\n\",\n        \"model.summary()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"u2e5WupIw2N2\"\n      },\n      \"source\": [\n        \"## Training the model\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"9f3yBUvkd_VJ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"if do_fine_tuning:\\n\",\n        \"  model.compile(\\n\",\n        \"    optimizer=tf.keras.optimizers.SGD(lr=0.002, momentum=0.9), \\n\",\n        \"    loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\\n\",\n        \"    metrics=['accuracy'])\\n\",\n        \"else:\\n\",\n        \"  model.compile(\\n\",\n        \"    optimizer='adam', \\n\",\n        \"    loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\\n\",\n        \"    metrics=['accuracy'])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"w_YKX2Qnfg6x\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"EPOCHS = 3\\n\",\n        \"hist = model.fit(train_batches,\\n\",\n        \"                    epochs=EPOCHS,\\n\",\n        \"                    validation_data=validation_batches)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"u_psFoTeLpHU\"\n      },\n      \"source\": [\n        \"## Export the model\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"XaSb5nVzHcVv\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"RPS_SAVED_MODEL = \\\"exp_saved_model\\\"\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"fZqRAg1uz1Nu\"\n      },\n      \"source\": [\n        \"Export the SavedModel\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"yJMue5YgnwtN\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Use TensorFlow's SavedModel API to export the SavedModel from the trained Keras model\\n\",\n        \"# YOUR CODE HERE\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"uVrnAWjqCMxs\"\n      },\n      \"source\": [\n        \"Here you can verify the default signature of your exported SavedModel\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"SOQF4cOan0SY\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"%%bash -s $RPS_SAVED_MODEL\\n\",\n        \"saved_model_cli show --dir $1 --tag_set serve --signature_def serving_default\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"FY7QGBgBytwX\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"loaded = tf.saved_model.load(RPS_SAVED_MODEL)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"tIhPyMISz952\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(list(loaded.signatures.keys()))\\n\",\n        \"infer = loaded.signatures[\\\"serving_default\\\"]\\n\",\n        \"print(infer.structured_input_signature)\\n\",\n        \"print(infer.structured_outputs)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"XxLiLC8n0H16\"\n      },\n      \"source\": [\n        \"## Convert with TFLiteConverter\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"WmSr2-yZoUhz\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Intialize the TFLite converter to load the SavedModel\\n\",\n        \"converter = # YOUR CODE HERE\\n\",\n        \"# Set the optimization strategy for 'size' in the converter \\n\",\n        \"converter.optimizations = [# YOUR CODE HERE]\\n\",\n        \"\\n\",\n        \"# Use the tool to finally convert the model\\n\",\n        \"tflite_model = # YOUR CODE HERE\\n\",\n        \"    \\n\",\n        \"with open(\\\"converted_model.tflite\\\", \\\"wb\\\") as f:\\n\",\n        \"  f.write(tflite_model)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"BbTF6nd1KG2o\"\n      },\n      \"source\": [\n        \"Run the following cells to check whether your TFLite model is working using the Python Interpreter\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"dg2NkVTmLUdJ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Loading the converted TFLite model...\\n\",\n        \"# Load TFLite model and allocate tensors.\\n\",\n        \"tflite_model_file = 'converted_model.tflite'\\n\",\n        \"with open(tflite_model_file, 'rb') as fid:\\n\",\n        \"  tflite_model = fid.read()\\n\",\n        \"  \\n\",\n        \"interpreter = tf.lite.Interpreter(model_content=tflite_model)\\n\",\n        \"interpreter.allocate_tensors()\\n\",\n        \"\\n\",\n        \"input_index = interpreter.get_input_details()[0][\\\"index\\\"]\\n\",\n        \"output_index = interpreter.get_output_details()[0][\\\"index\\\"]\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"snJQVs9JNglv\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Testing on random test examples...\\n\",\n        \"from tqdm import tqdm\\n\",\n        \"\\n\",\n        \"# Gather results for the randomly sampled test images\\n\",\n        \"predictions = []\\n\",\n        \"\\n\",\n        \"test_labels, test_imgs = [], []\\n\",\n        \"for img, label in tqdm(test_batches.take(10)):\\n\",\n        \"  interpreter.set_tensor(input_index, img)\\n\",\n        \"  interpreter.invoke()\\n\",\n        \"  predictions.append(interpreter.get_tensor(output_index))\\n\",\n        \"  \\n\",\n        \"  test_labels.append(label.numpy()[0])\\n\",\n        \"  test_imgs.append(img)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"YMTWNqPpNiAI\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Utility functions for plotting\\n\",\n        \"# Utilities for plotting\\n\",\n        \"\\n\",\n        \"class_names = ['rock', 'paper', 'scissors']\\n\",\n        \"\\n\",\n        \"def plot_image(i, predictions_array, true_label, img):\\n\",\n        \"  predictions_array, true_label, img = predictions_array[i], true_label[i], img[i]\\n\",\n        \"  plt.grid(False)\\n\",\n        \"  plt.xticks([])\\n\",\n        \"  plt.yticks([])\\n\",\n        \"    \\n\",\n        \"  img = np.squeeze(img)\\n\",\n        \"\\n\",\n        \"  plt.imshow(img, cmap=plt.cm.binary)\\n\",\n        \"\\n\",\n        \"  predicted_label = np.argmax(predictions_array)\\n\",\n        \"  if predicted_label == true_label:\\n\",\n        \"    color = 'green'\\n\",\n        \"  else:\\n\",\n        \"    color = 'red'\\n\",\n        \"  \\n\",\n        \"  plt.xlabel(\\\"{} {:2.0f}% ({})\\\".format(class_names[predicted_label],\\n\",\n        \"                                100*np.max(predictions_array),\\n\",\n        \"                                class_names[true_label]),\\n\",\n        \"                                color=color)\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"1-lbnicPNkZs\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Visualize the outputs { run: \\\"auto\\\" }\\n\",\n        \"index = 9 #@param {type:\\\"slider\\\", min:0, max:9, step:1}\\n\",\n        \"plt.figure(figsize=(6,3))\\n\",\n        \"plt.subplot(1,2,1)\\n\",\n        \"plot_image(index, predictions, test_labels, test_imgs)\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"PmZRieHmKLY5\"\n      },\n      \"source\": [\n        \"Download the model\\n\",\n        \"\\n\",\n        \"**NOTE: You might have to run to the cell below twice**\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"0jJAxrQB2VFw\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"with open('labels.txt', 'w') as f:\\n\",\n        \"  f.write('\\\\n'.join(class_names))\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"try:\\n\",\n        \"  from google.colab import files\\n\",\n        \"  files.download('converted_model.tflite')  \\n\",\n        \"  files.download('labels.txt')\\n\",\n        \"except:\\n\",\n        \"  pass\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"BDlmpjC6VnFZ\"\n      },\n      \"source\": [\n        \"# Prepare the test images for download (Optional)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"_1ja_WA0WZOH\"\n      },\n      \"source\": [\n        \"This part involves downloading additional test images for the Mobile Apps only in case you need to try out more samples\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"fzLKEBrfTREA\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!mkdir -p test_images\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Qn7ukNQCSewb\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"from PIL import Image\\n\",\n        \"\\n\",\n        \"for index, (image, label) in enumerate(test_batches.take(50)):\\n\",\n        \"  image = tf.cast(image * 255.0, tf.uint8)\\n\",\n        \"  image = tf.squeeze(image).numpy()\\n\",\n        \"  pil_image = Image.fromarray(image)\\n\",\n        \"  pil_image.save('test_images/{}_{}.jpg'.format(class_names[label[0]], index))\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"xVKKWUG8UMO5\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!ls test_images\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"l_w_-UdlS9Vi\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!zip -qq rps_test_images.zip -r test_images/\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Giva6EHwWm6Y\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"try:\\n\",\n        \"  files.download('rps_test_images.zip')\\n\",\n        \"except:\\n\",\n        \"  pass\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"tflite_c05_exercise_rock_paper_scissors.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "courses/udacity_intro_to_tensorflow_lite/tflite_c06_exercise_rock_paper_scissors_solution.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Za8-Nr5k11fh\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"Eq10uEbw0E4l\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"oYM61xrTsP5d\"\n      },\n      \"source\": [\n        \"# Rock, Paper & Scissors with TensorFlow Hub - TFLite\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"xWFpUd1yy3gt\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_lite/tflite_c06_exercise_rock_paper_scissors_solution.ipynb\\\">\\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />\\n\",\n        \"    Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_lite/tflite_c06_exercise_rock_paper_scissors_solution.ipynb\\\">\\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />\\n\",\n        \"    View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a href=\\\"https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/4\\\"><img src=\\\"https://www.tensorflow.org/images/hub_logo_32px.png\\\" />See TF Hub model</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"bL54LWCHt5q5\"\n      },\n      \"source\": [\n        \"## Setup\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"dlauq-4FWGZM\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import os\\n\",\n        \"\\n\",\n        \"import matplotlib.pylab as plt\\n\",\n        \"import numpy as np\\n\",\n        \"\\n\",\n        \"import tensorflow as tf\\n\",\n        \"import tensorflow_hub as hub\\n\",\n        \"\\n\",\n        \"print(\\\"Version: \\\", tf.__version__)\\n\",\n        \"print(\\\"Eager mode: \\\", tf.executing_eagerly())\\n\",\n        \"print(\\\"Hub version: \\\", hub.__version__)\\n\",\n        \"print(\\\"GPU is\\\", \\\"available\\\" if tf.config.list_physical_devices('GPU') else \\\"NOT AVAILABLE\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"mmaHHH7Pvmth\"\n      },\n      \"source\": [\n        \"## Select the Hub/TF2 module to use\\n\",\n        \"\\n\",\n        \"Hub modules for TF 1.x won't work here, please use one of the selections provided.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"FlsEcKVeuCnf\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"module_selection = (\\\"mobilenet_v2\\\", 224, 1280) #@param [\\\"(\\\\\\\"mobilenet_v2\\\\\\\", 224, 1280)\\\", \\\"(\\\\\\\"inception_v3\\\\\\\", 299, 2048)\\\"] {type:\\\"raw\\\", allow-input: true}\\n\",\n        \"handle_base, pixels, FV_SIZE = module_selection\\n\",\n        \"MODULE_HANDLE = \\\"https://tfhub.dev/google/tf2-preview/{}/feature_vector/4\\\".format(handle_base)\\n\",\n        \"IMAGE_SIZE = (pixels, pixels)\\n\",\n        \"print(\\\"Using {} with input size {} and output dimension {}\\\".format(\\n\",\n        \"  MODULE_HANDLE, IMAGE_SIZE, FV_SIZE))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"sYUsgwCBv87A\"\n      },\n      \"source\": [\n        \"## Data preprocessing\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"8nqVX3KYwGPh\"\n      },\n      \"source\": [\n        \"Use [TensorFlow Datasets](http://tensorflow.org/datasets) to load the rock, paper and scissors dataset.\\n\",\n        \"\\n\",\n        \"This `tfds` package is the easiest way to load pre-defined data. If you have your own data, and are interested in importing using it with TensorFlow see [loading image data](../load_data/images.ipynb)\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"jGvpkDj4wBup\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow_datasets as tfds\\n\",\n        \"tfds.disable_progress_bar()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"YkF4Boe5wN7N\"\n      },\n      \"source\": [\n        \"The `tfds.load` method downloads and caches the data, and returns a `tf.data.Dataset` object. These objects provide powerful, efficient methods for manipulating data and piping it into your model.\\n\",\n        \"\\n\",\n        \"Since `\\\"rock_paper_scissors\\\"` doesn't define standard splits, use the subsplit feature to divide it into (train, validation, test) with 80%, 10%, 10% of the data respectively.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"SQ9xK9F2wGD8\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"splits = tfds.Split.ALL.subsplit(weighted=(80, 10, 10))\\n\",\n        \"\\n\",\n        \"splits, info = tfds.load('rock_paper_scissors', with_info=True, as_supervised=True, split = splits)\\n\",\n        \"\\n\",\n        \"(train_examples, validation_examples, test_examples) = splits\\n\",\n        \"\\n\",\n        \"num_examples = info.splits['train'].num_examples\\n\",\n        \"num_classes = info.features['label'].num_classes\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"pmXQYXNWwf19\"\n      },\n      \"source\": [\n        \"### Format the Data\\n\",\n        \"\\n\",\n        \"Use the `tf.image` module to format the images for the task.\\n\",\n        \"\\n\",\n        \"Resize the images to a fixes input size, and rescale the input channels\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"y7UyXblSwkUS\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def format_image(image, label):\\n\",\n        \"  image = tf.image.resize(image, IMAGE_SIZE) / 255.0\\n\",\n        \"  return  image, label\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"1nrDR8CnwrVk\"\n      },\n      \"source\": [\n        \"Now shuffle and batch the data\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"zAEUG7vawxLm\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"BATCH_SIZE = 32 #@param {type:\\\"integer\\\"}\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"fHEC9mbswxvM\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"train_batches = train_examples.shuffle(num_examples // 4).batch(BATCH_SIZE).map(format_image).prefetch(1)\\n\",\n        \"validation_batches = validation_examples.batch(BATCH_SIZE).map(format_image).prefetch(1)\\n\",\n        \"test_batches = test_examples.batch(1).map(format_image)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ghQhZjgEw1cK\"\n      },\n      \"source\": [\n        \"Inspect a batch\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"gz0xsMCjwx54\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"for image_batch, label_batch in train_batches.take(1):\\n\",\n        \"  pass\\n\",\n        \"\\n\",\n        \"image_batch.shape\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"FS_gVStowW3G\"\n      },\n      \"source\": [\n        \"## Defining the model\\n\",\n        \"\\n\",\n        \"All it takes is to put a linear classifier on top of the `feature_extractor_layer` with the Hub module.\\n\",\n        \"\\n\",\n        \"For speed, we start out with a non-trainable `feature_extractor_layer`, but you can also enable fine-tuning for greater accuracy.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"RaJW3XrPyFiF\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"do_fine_tuning = False #@param {type:\\\"boolean\\\"}\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"50FYNIb1dmJH\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(\\\"Building model with\\\", MODULE_HANDLE)\\n\",\n        \"model = tf.keras.Sequential([\\n\",\n        \"    hub.KerasLayer(MODULE_HANDLE,\\n\",\n        \"                   input_shape=IMAGE_SIZE + (3, ), \\n\",\n        \"                   output_shape=[FV_SIZE],\\n\",\n        \"                   trainable=do_fine_tuning),\\n\",\n        \"    tf.keras.layers.Dense(num_classes)\\n\",\n        \"])\\n\",\n        \"model.summary()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"u2e5WupIw2N2\"\n      },\n      \"source\": [\n        \"## Training the model\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"9f3yBUvkd_VJ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"if do_fine_tuning:\\n\",\n        \"  model.compile(\\n\",\n        \"    optimizer=tf.keras.optimizers.SGD(lr=0.002, momentum=0.9), \\n\",\n        \"    loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\\n\",\n        \"    metrics=['accuracy'])\\n\",\n        \"else:\\n\",\n        \"  model.compile(\\n\",\n        \"    optimizer='adam', \\n\",\n        \"    loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\\n\",\n        \"    metrics=['accuracy'])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"w_YKX2Qnfg6x\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"EPOCHS = 5\\n\",\n        \"hist = model.fit(train_batches,\\n\",\n        \"                    epochs=EPOCHS,\\n\",\n        \"                    validation_data=validation_batches)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"u_psFoTeLpHU\"\n      },\n      \"source\": [\n        \"## Export the model\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"XaSb5nVzHcVv\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"RPS_SAVED_MODEL = \\\"rps_saved_model\\\"\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"fZqRAg1uz1Nu\"\n      },\n      \"source\": [\n        \"Export the SavedModel\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"yJMue5YgnwtN\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"tf.saved_model.save(model, RPS_SAVED_MODEL)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"SOQF4cOan0SY\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"%%bash -s $RPS_SAVED_MODEL\\n\",\n        \"saved_model_cli show --dir $1 --tag_set serve --signature_def serving_default\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"FY7QGBgBytwX\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"loaded = tf.saved_model.load(RPS_SAVED_MODEL)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"tIhPyMISz952\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"print(list(loaded.signatures.keys()))\\n\",\n        \"infer = loaded.signatures[\\\"serving_default\\\"]\\n\",\n        \"print(infer.structured_input_signature)\\n\",\n        \"print(infer.structured_outputs)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"XxLiLC8n0H16\"\n      },\n      \"source\": [\n        \"## Convert with TFLiteConverter\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"WmSr2-yZoUhz\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"converter = tf.lite.TFLiteConverter.from_saved_model(RPS_SAVED_MODEL)\\n\",\n        \"converter.optimizations = [tf.lite.Optimize.DEFAULT]\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"tflite_model = converter.convert()\\n\",\n        \"with open(\\\"converted_model.tflite\\\", \\\"wb\\\") as f:\\n\",\n        \"  f.write(tflite_model)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"BbTF6nd1KG2o\"\n      },\n      \"source\": [\n        \"Test the TFLite model using the Python Interpreter\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"dg2NkVTmLUdJ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Load TFLite model and allocate tensors.\\n\",\n        \"tflite_model_file = 'converted_model.tflite'\\n\",\n        \"with open(tflite_model_file, 'rb') as fid:\\n\",\n        \"  tflite_model = fid.read()\\n\",\n        \"  \\n\",\n        \"interpreter = tf.lite.Interpreter(model_content=tflite_model)\\n\",\n        \"interpreter.allocate_tensors()\\n\",\n        \"\\n\",\n        \"input_index = interpreter.get_input_details()[0][\\\"index\\\"]\\n\",\n        \"output_index = interpreter.get_output_details()[0][\\\"index\\\"]\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"snJQVs9JNglv\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"from tqdm import tqdm\\n\",\n        \"\\n\",\n        \"# Gather results for the randomly sampled test images\\n\",\n        \"predictions = []\\n\",\n        \"\\n\",\n        \"test_labels, test_imgs = [], []\\n\",\n        \"for img, label in tqdm(test_batches.take(10)):\\n\",\n        \"  interpreter.set_tensor(input_index, img)\\n\",\n        \"  interpreter.invoke()\\n\",\n        \"  predictions.append(interpreter.get_tensor(output_index))\\n\",\n        \"  \\n\",\n        \"  test_labels.append(label.numpy()[0])\\n\",\n        \"  test_imgs.append(img)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"YMTWNqPpNiAI\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Utility functions for plotting\\n\",\n        \"# Utilities for plotting\\n\",\n        \"\\n\",\n        \"class_names = ['rock', 'paper', 'scissors']\\n\",\n        \"\\n\",\n        \"def plot_image(i, predictions_array, true_label, img):\\n\",\n        \"  predictions_array, true_label, img = predictions_array[i], true_label[i], img[i]\\n\",\n        \"  plt.grid(False)\\n\",\n        \"  plt.xticks([])\\n\",\n        \"  plt.yticks([])\\n\",\n        \"    \\n\",\n        \"  img = np.squeeze(img)\\n\",\n        \"\\n\",\n        \"  plt.imshow(img, cmap=plt.cm.binary)\\n\",\n        \"\\n\",\n        \"  predicted_label = np.argmax(predictions_array)\\n\",\n        \"  print(type(predicted_label), type(true_label))\\n\",\n        \"  if predicted_label == true_label:\\n\",\n        \"    color = 'green'\\n\",\n        \"  else:\\n\",\n        \"    color = 'red'\\n\",\n        \"  \\n\",\n        \"  plt.xlabel(\\\"{} {:2.0f}% ({})\\\".format(class_names[predicted_label],\\n\",\n        \"                                100*np.max(predictions_array),\\n\",\n        \"                                class_names[true_label]),\\n\",\n        \"                                color=color)\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"both\",\n        \"id\": \"1-lbnicPNkZs\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Visualize the outputs { run: \\\"auto\\\" }\\n\",\n        \"index = 0 #@param {type:\\\"slider\\\", min:0, max:9, step:1}\\n\",\n        \"plt.figure(figsize=(6,3))\\n\",\n        \"plt.subplot(1,2,1)\\n\",\n        \"plot_image(index, predictions, test_labels, test_imgs)\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"PmZRieHmKLY5\"\n      },\n      \"source\": [\n        \"Download the model\\n\",\n        \"\\n\",\n        \"**NOTE: You might have to run to the cell below twice**\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"0jJAxrQB2VFw\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"with open('labels.txt', 'w') as f:\\n\",\n        \"  f.write('\\\\n'.join(class_names))\\n\",\n        \"\\n\",\n        \"try:\\n\",\n        \"  from google.colab import files\\n\",\n        \"  files.download('converted_model.tflite')\\n\",\n        \"  files.download('labels.txt')\\n\",\n        \"except:\\n\",\n        \"  pass\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"BDlmpjC6VnFZ\"\n      },\n      \"source\": [\n        \"# Prepare the test images for download (Optional)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"_1ja_WA0WZOH\"\n      },\n      \"source\": [\n        \"This part involves downloading additional test images for the Mobile Apps only in case you need to try out more samples\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"fzLKEBrfTREA\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!mkdir -p test_images\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Qn7ukNQCSewb\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"from PIL import Image\\n\",\n        \"\\n\",\n        \"for index, (image, label) in enumerate(test_batches.take(50)):\\n\",\n        \"  image = tf.cast(image * 255.0, tf.uint8)\\n\",\n        \"  image = tf.squeeze(image).numpy()\\n\",\n        \"  pil_image = Image.fromarray(image)\\n\",\n        \"  pil_image.save('test_images/{}_{}.jpg'.format(class_names[label[0]], index))\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"xVKKWUG8UMO5\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!ls test_images\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"l_w_-UdlS9Vi\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!zip -qq rps_test_images.zip -r test_images/\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Giva6EHwWm6Y\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"try:\\n\",\n        \"  files.download('rps_test_images.zip')\\n\",\n        \"except:\\n\",\n        \"  pass\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"tflite_c06_exercise_rock_paper_scissors_solution.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "lite/.gitignore",
    "content": "# TensorFlow Lite model\n*.tflite\n\n# Swift\n\n## Build generated\nbuild/\nDerivedData/\n\n## Various settings\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!default.perspectivev3\nxcuserdata/\n\n## Other\n*.moved-aside\n*.xccheckout\n*.xcscmblueprint\n\n## Obj-C/Swift specific\n*.hmap\n*.ipa\n*.dSYM.zip\n*.dSYM\n\n# CocoaPods\nPods/\n*.xcworkspace\n\n# mkdocs ignore generated site\nsite/\n\n# Android\n\n# Built application files\n*.apk\n*.aar\n*.ap_\n*.aab\n\n# Files for the ART/Dalvik VM\n*.dex\n\n# Java class files\n*.class\n\n# Generated files\nbin/\ngen/\nout/\n#  Uncomment the following line in case you need and you don't have the release build type files in your app\n# release/\n\n# Gradle files\n.gradle/\nbuild/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Proguard folder generated by Eclipse\nproguard/\n\n# Log Files\n*.log\n\n# Android Studio Navigation editor temp files\n.navigation/\n\n# Android Studio captures folder\ncaptures/\n\n# IntelliJ\n*.iml\n.idea/workspace.xml\n.idea/tasks.xml\n.idea/gradle.xml\n.idea/assetWizardSettings.xml\n.idea/dictionaries\n.idea/libraries\n# Android Studio 3 in .gitignore file.\n.idea/caches\n.idea/modules.xml\n.idea/navEditor.xml\n\n# Keystore files\n# Uncomment the following lines if you do not want to check your keystore files in.\n#*.jks\n#*.keystore\n\n# External native build folder generated in Android Studio 2.2 and later\n.externalNativeBuild\n.cxx/\n\n# Google Services (e.g. APIs or Firebase)\n# google-services.json\n\n# Version control\nvcs.xml\n\n# lint\nlint/intermediates/\nlint/generated/\nlint/outputs/\nlint/tmp/\n# lint/reports/"
  },
  {
    "path": "lite/README.md",
    "content": "# TensorFlow Lite sample applications\n\n<!-- Note: These samples are being staged internally before being migrated to\n     the new TF examples repo. See also b/119060183. -->\n\nThe following samples demonstrate the use of TensorFlow Lite in mobile applications. Each sample is written for both Android and iOS.\n\n## Image classification\n\nThis app performs image classification on a live camera feed and displays the\ninference output in realtime on the screen.\n\n<!-- TODO(b/124116863): Add app screenshot and model details. -->\n\n### Samples\n\n[Android image classification](examples/image_classification/android/README.md)\n\n[iOS image classification](examples/image_classification/ios/README.md)\n\n[Raspberry Pi image classification](examples/image_classification/raspberry_pi/README.md)\n\n## Object detection\n\nThis app performs object detection on a live camera feed and displays the\nresults in realtime on the screen. The app displays the confidence scores,\nclasses and detected bounding boxes for multiple objects. A detected object is\nonly displayed if the confidence score is greater than a defined threshold.\n\n<!-- TODO(b/124116863): Add app screenshot and model details. -->\n\n### Samples\n\n[Android object detection](examples/object_detection/android/README.md)\n\n[iOS object detection](examples/object_detection/ios/README.md)\n\n[Raspberry Pi object detection](examples/object_detection/raspberry_pi/README.md)\n\n\n## Speech command recognition\n\nThis application recognizes a set of voice commands using the device's\nmicrophone input. When a command is spoken, the corresponding class in the app\nis highlighted.\n\n<!-- TODO(b/124116863): Add app screenshot and model details. -->\n\n### Samples\n\n[iOS speech commands](examples/speech_commands/ios/README.md)\n\n\n## Gesture classification\n\nThis app uses a model to classify and recognize different gestures. A model is trained on webcam data captured using a web interface. The model is then converted to a TensorFlow Lite model and used to classify gestures in a mobile application.\n\n![Gesture components](https://tensorflow.org/images/lite/screenshots/gesture_components.png)\n\n### Web app\n\nFirst, we use TensorFlow.js embedded in a web interface to collect the data required to train the model. We then use TensorFlow.js to train the model.\n\n[Web gesture classification](examples/gesture_classification/web/README.md)\n\n<!-- TODO(b/124116863): Add app screenshot and model details. -->\n\n### Conversion script\n\nThe model downloaded from the web interface is converted to a TensorFlow Lite model.\n\n[Conversion script (available as a Colab notebook)](examples/gesture_classification/ml/README.md).\n\n### Mobile apps\n\nOnce we have the TensorFlow Lite model, the implementation is very similar to the [Image classification](#image-classification) sample.\n\n<!-- TODO(b/124116863): Add app screenshot. -->\n\n#### Samples\n\n[Android gesture classification](examples/gesture_classification/android/README.md)\n\n[iOS gesture classification](examples/gesture_classification/ios/README.md)\n\n## Model personalization\n\nThis app performs model personalization on a live camera feed and displays the\nresults in realtime on the screen. The app displays the confidence scores,\nclasses and detected bounding boxes for multiple objects that were trained in\nrealtime.\n\n<!-- TODO(b/124116863): Add app screenshot and model details. -->\n\n### Samples\n\n[Android Model Personalization](examples/model_personalization/README.md)\n\n\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/README.md",
    "content": "# Codelabs for TensorFlow Lite\n\nThis repository contains the code for the a TensorFlow Lite codelab:\n\n* [Build a handwritten digit classifier app with TensorFlow Lite](https://codelabs.developers.google.com/codelabs/digit-classifier-tflite)\n\n## Introduction\n\nIn these codelabs, you will learn:\n\n* How to train a digit classifer model model with TensorFlow.\n* How to convert a TensorFlow model to a TensorFlow Lite format.\n* How to deploy a TensorFlow Lite model to a mobile app.\n\n## Pre-requisites\n\nNone.\n\n## Getting Started\n\nVisit the Google codelabs site to follow along the guided steps.\n\n## Screenshots\n\n<img src=\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/digit_classifier/screenshot.png\" alt=\"Screenshot\" width=\"300\"/>\n\n## Support\n\n- Stack Overflow: https://stackoverflow.com/questions/tagged/tensorflow-lite\n\nPatches are encouraged, and may be submitted by forking this project and\nsubmitting a pull request through GitHub.\n\n## License\n\nCopyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/finish/app/build.gradle",
    "content": "apply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\n\nandroid {\n    compileSdkVersion 30\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.codelabs.digitclassifier\"\n        minSdkVersion 21\n        targetSdkVersion 30\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    // TODO: Add an option to avoid compressing TF Lite model file\n    aaptOptions {\n        noCompress \"tflite\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\n// Sanity check if you have trained and downloaded TF Lite model.\npreBuild.doFirst {\n    assert file(\"./src/main/assets/mnist.tflite\").exists() :\n            \"mnist.tflite not found. Make sure you have trained and \" +\n                    \"downloaded your TensorFlow Lite model to assets/ folder\"\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version\"\n\n    // Support Libraries\n    implementation 'androidx.appcompat:appcompat:1.3.0'\n    implementation 'androidx.core:core-ktx:1.5.0'\n    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'\n\n    // AndroidDraw Library\n    implementation 'com.github.divyanshub024:AndroidDraw:v0.1'\n\n    // Task API\n    implementation \"com.google.android.gms:play-services-tasks:17.2.1\"\n\n    //TODO: Add TF Lite\n    implementation 'org.tensorflow:tensorflow-lite:2.5.0'\n\n    testImplementation 'junit:junit:4.13.2'\n    androidTestImplementation 'androidx.test:runner:1.3.0'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'\n}\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/finish/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/finish/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          xmlns:tools=\"http://schemas.android.com/tools\"\n          package=\"org.tensorflow.lite.codelabs.digitclassifier\">\n\n  <uses-sdk />\n\n  <application\n          android:allowBackup=\"true\"\n          android:icon=\"@mipmap/ic_launcher\"\n          android:label=\"@string/app_name\"\n          android:roundIcon=\"@mipmap/ic_launcher_round\"\n          android:supportsRtl=\"true\"\n          android:theme=\"@style/AppTheme\"\n          tools:ignore=\"GoogleAppIndexingWarning\">\n\n    <activity android:name=\".MainActivity\"\n              android:exported=\"true\">\n      <intent-filter>\n        <action android:name=\"android.intent.action.MAIN\"/>\n        <category android:name=\"android.intent.category.LAUNCHER\"/>\n      </intent-filter>\n    </activity>\n  </application>\n\n</manifest>\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/finish/app/src/main/assets/ADD_TFLITE_MODEL_HERE",
    "content": "Place the TensorFlow Lite model file (mnist.tflite) you generated from step 2: \"Train a machine learning model\" here."
  },
  {
    "path": "lite/codelabs/digit_classifier/android/finish/app/src/main/java/org/tensorflow/lite/codelabs/digitclassifier/DigitClassifier.kt",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.codelabs.digitclassifier\n\nimport android.content.Context\nimport android.content.res.AssetManager\nimport android.graphics.Bitmap\nimport android.util.Log\nimport com.google.android.gms.tasks.Task\nimport com.google.android.gms.tasks.TaskCompletionSource\nimport java.io.FileInputStream\nimport java.io.IOException\nimport java.nio.ByteBuffer\nimport java.nio.ByteOrder\nimport java.nio.channels.FileChannel\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Executors\nimport org.tensorflow.lite.Interpreter\n\nclass DigitClassifier(private val context: Context) {\n  // TODO: Add a TF Lite interpreter as a field.\n  private var interpreter: Interpreter? = null\n  var isInitialized = false\n    private set\n\n  /** Executor to run inference task in the background. */\n  private val executorService: ExecutorService = Executors.newCachedThreadPool()\n\n  private var inputImageWidth: Int = 0 // will be inferred from TF Lite model.\n  private var inputImageHeight: Int = 0 // will be inferred from TF Lite model.\n  private var modelInputSize: Int = 0 // will be inferred from TF Lite model.\n\n  fun initialize(): Task<Void?> {\n    val task = TaskCompletionSource<Void?>()\n    executorService.execute {\n      try {\n        initializeInterpreter()\n        task.setResult(null)\n      } catch (e: IOException) {\n        task.setException(e)\n      }\n    }\n    return task.task\n  }\n\n  @Throws(IOException::class)\n  private fun initializeInterpreter() {\n    // TODO: Load the TF Lite model from file and initialize an interpreter.\n\n    // Load the TF Lite model from asset folder and initialize TF Lite Interpreter with NNAPI enabled.\n    val assetManager = context.assets\n    val model = loadModelFile(assetManager, \"mnist.tflite\")\n    val interpreter = Interpreter(model)\n\n    // TODO: Read the model input shape from model file.\n\n    // Read input shape from model file.\n    val inputShape = interpreter.getInputTensor(0).shape()\n    inputImageWidth = inputShape[1]\n    inputImageHeight = inputShape[2]\n    modelInputSize = FLOAT_TYPE_SIZE * inputImageWidth *\n      inputImageHeight * PIXEL_SIZE\n\n    // Finish interpreter initialization.\n    this.interpreter = interpreter\n\n    isInitialized = true\n    Log.d(TAG, \"Initialized TFLite interpreter.\")\n  }\n\n  @Throws(IOException::class)\n  private fun loadModelFile(assetManager: AssetManager, filename: String): ByteBuffer {\n    val fileDescriptor = assetManager.openFd(filename)\n    val inputStream = FileInputStream(fileDescriptor.fileDescriptor)\n    val fileChannel = inputStream.channel\n    val startOffset = fileDescriptor.startOffset\n    val declaredLength = fileDescriptor.declaredLength\n    return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength)\n  }\n\n  private fun classify(bitmap: Bitmap): String {\n    check(isInitialized) { \"TF Lite Interpreter is not initialized yet.\" }\n\n    // TODO: Add code to run inference with TF Lite.\n    // Pre-processing: resize the input image to match the model input shape.\n    val resizedImage = Bitmap.createScaledBitmap(\n      bitmap,\n      inputImageWidth,\n      inputImageHeight,\n      true\n    )\n    val byteBuffer = convertBitmapToByteBuffer(resizedImage)\n\n    // Define an array to store the model output.\n    val output = Array(1) { FloatArray(OUTPUT_CLASSES_COUNT) }\n\n    // Run inference with the input data.\n    interpreter?.run(byteBuffer, output)\n\n    // Post-processing: find the digit that has the highest probability\n    // and return it a human-readable string.\n    val result = output[0]\n    val maxIndex = result.indices.maxByOrNull { result[it] } ?: -1\n    val resultString =\n      \"Prediction Result: %d\\nConfidence: %2f\"\n        .format(maxIndex, result[maxIndex])\n\n    return resultString\n  }\n\n  fun classifyAsync(bitmap: Bitmap): Task<String> {\n    val task = TaskCompletionSource<String>()\n    executorService.execute {\n      val result = classify(bitmap)\n      task.setResult(result)\n    }\n    return task.task\n  }\n\n  fun close() {\n    executorService.execute {\n      interpreter?.close()\n      Log.d(TAG, \"Closed TFLite interpreter.\")\n    }\n  }\n\n  private fun convertBitmapToByteBuffer(bitmap: Bitmap): ByteBuffer {\n    val byteBuffer = ByteBuffer.allocateDirect(modelInputSize)\n    byteBuffer.order(ByteOrder.nativeOrder())\n\n    val pixels = IntArray(inputImageWidth * inputImageHeight)\n    bitmap.getPixels(pixels, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)\n\n    for (pixelValue in pixels) {\n      val r = (pixelValue shr 16 and 0xFF)\n      val g = (pixelValue shr 8 and 0xFF)\n      val b = (pixelValue and 0xFF)\n\n      // Convert RGB to grayscale and normalize pixel value to [0..1].\n      val normalizedPixelValue = (r + g + b) / 3.0f / 255.0f\n      byteBuffer.putFloat(normalizedPixelValue)\n    }\n\n    return byteBuffer\n  }\n\n  companion object {\n    private const val TAG = \"DigitClassifier\"\n\n    private const val FLOAT_TYPE_SIZE = 4\n    private const val PIXEL_SIZE = 1\n\n    private const val OUTPUT_CLASSES_COUNT = 10\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/finish/app/src/main/java/org/tensorflow/lite/codelabs/digitclassifier/MainActivity.kt",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.codelabs.digitclassifier\n\nimport android.annotation.SuppressLint\nimport android.graphics.Color\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport android.util.Log\nimport android.view.MotionEvent\nimport android.widget.Button\nimport android.widget.TextView\nimport com.divyanshu.draw.widget.DrawView\n\nclass MainActivity : AppCompatActivity() {\n\n  private var drawView: DrawView? = null\n  private var clearButton: Button? = null\n  private var predictedTextView: TextView? = null\n  private var digitClassifier = DigitClassifier(this)\n\n  @SuppressLint(\"ClickableViewAccessibility\")\n  override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n    setContentView(R.layout.activity_main)\n\n    // Setup view instances.\n    drawView = findViewById(R.id.draw_view)\n    drawView?.setStrokeWidth(70.0f)\n    drawView?.setColor(Color.WHITE)\n    drawView?.setBackgroundColor(Color.BLACK)\n    clearButton = findViewById(R.id.clear_button)\n    predictedTextView = findViewById(R.id.predicted_text)\n\n    // Setup clear drawing button.\n    clearButton?.setOnClickListener {\n      drawView?.clearCanvas()\n      predictedTextView?.text = getString(R.string.prediction_text_placeholder)\n    }\n\n    // Setup classification trigger so that it classify after every stroke drew.\n    drawView?.setOnTouchListener { _, event ->\n      // As we have interrupted DrawView's touch event,\n      // we first need to pass touch events through to the instance for the drawing to show up.\n      drawView?.onTouchEvent(event)\n\n      // Then if user finished a touch event, run classification\n      if (event.action == MotionEvent.ACTION_UP) {\n        classifyDrawing()\n      }\n\n      true\n    }\n\n    // Setup digit classifier.\n    digitClassifier\n      .initialize()\n      .addOnFailureListener { e -> Log.e(TAG, \"Error to setting up digit classifier.\", e) }\n  }\n\n  override fun onDestroy() {\n    // Sync DigitClassifier instance lifecycle with MainActivity lifecycle,\n    // and free up resources (e.g. TF Lite instance) once the activity is destroyed.\n    digitClassifier.close()\n    super.onDestroy()\n  }\n\n  private fun classifyDrawing() {\n    val bitmap = drawView?.getBitmap()\n\n    if ((bitmap != null) && (digitClassifier.isInitialized)) {\n      digitClassifier\n        .classifyAsync(bitmap)\n        .addOnSuccessListener { resultText -> predictedTextView?.text = resultText }\n        .addOnFailureListener { e ->\n          predictedTextView?.text = getString(\n            R.string.classification_error_message,\n            e.localizedMessage\n          )\n          Log.e(TAG, \"Error classifying drawing.\", e)\n        }\n    }\n  }\n\n  companion object {\n    private const val TAG = \"MainActivity\"\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/finish/app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n  <path\n      android:fillColor=\"#008577\"\n      android:pathData=\"M0,0h108v108h-108z\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M9,0L9,108\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M19,0L19,108\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M29,0L29,108\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M39,0L39,108\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M49,0L49,108\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M59,0L59,108\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M69,0L69,108\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M79,0L79,108\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M89,0L89,108\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M99,0L99,108\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M0,9L108,9\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M0,19L108,19\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M0,29L108,29\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M0,39L108,39\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M0,49L108,49\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M0,59L108,59\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M0,69L108,69\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M0,79L108,79\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M0,89L108,89\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M0,99L108,99\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M19,29L89,29\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M19,39L89,39\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M19,49L89,49\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M19,59L89,59\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M19,69L89,69\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M19,79L89,79\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M29,19L29,89\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M39,19L39,89\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M49,19L49,89\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M59,19L59,89\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M69,19L69,89\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M79,19L79,89\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n</vector>\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/finish/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n  <path\n      android:fillType=\"evenOdd\"\n      android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\">\n    <aapt:attr name=\"android:fillColor\">\n      <gradient\n          android:endX=\"78.5885\"\n          android:endY=\"90.9159\"\n          android:startX=\"48.7653\"\n          android:startY=\"61.0927\"\n          android:type=\"linear\">\n        <item\n            android:color=\"#44000000\"\n            android:offset=\"0.0\"/>\n        <item\n            android:color=\"#00000000\"\n            android:offset=\"1.0\"/>\n      </gradient>\n    </aapt:attr>\n  </path>\n  <path\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"nonZero\"\n      android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\"/>\n</vector>\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/finish/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".MainActivity\">\n\n  <com.divyanshu.draw.widget.DrawView\n      android:id=\"@+id/draw_view\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"0dp\"\n      app:layout_constraintDimensionRatio=\"1:1\"\n      app:layout_constraintTop_toTopOf=\"parent\"/>\n\n  <TextView\n      android:id=\"@+id/predicted_text\"\n      android:textStyle=\"bold\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:text=\"@string/prediction_text_placeholder\"\n      android:textSize=\"20sp\"\n      app:layout_constraintBottom_toTopOf=\"@id/clear_button\"\n      app:layout_constraintLeft_toLeftOf=\"parent\"\n      app:layout_constraintRight_toRightOf=\"parent\"\n      app:layout_constraintTop_toBottomOf=\"@id/draw_view\"/>\n\n  <Button\n      android:id=\"@+id/clear_button\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:text=\"@string/clear_button_text\"\n      app:layout_constraintBottom_toBottomOf=\"parent\"\n      app:layout_constraintLeft_toLeftOf=\"parent\"\n      app:layout_constraintRight_toRightOf=\"parent\"/>\n\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/finish/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <background android:drawable=\"@drawable/ic_launcher_background\"/>\n  <foreground android:drawable=\"@drawable/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/finish/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <background android:drawable=\"@drawable/ic_launcher_background\"/>\n  <foreground android:drawable=\"@drawable/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/finish/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <color name=\"colorPrimary\">#008577</color>\n  <color name=\"colorPrimaryDark\">#00574B</color>\n  <color name=\"colorAccent\">#D81B60</color>\n</resources>\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/finish/app/src/main/res/values/strings.xml",
    "content": "<resources>\n  <string name=\"app_name\" translatable=\"false\">Digit Classifier</string>\n  <string name=\"clear_button_text\" translatable=\"false\">Clear</string>\n  <string name=\"prediction_text_placeholder\" translatable=\"false\">Please draw a digit</string>\n  <string name=\"classification_error_message\" translatable=\"false\">Error classifying drawing: %s</string>\n</resources>\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/finish/app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n  <!-- Base application theme. -->\n  <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n    <!-- Customize your theme here. -->\n    <item name=\"colorPrimary\">@color/colorPrimary</item>\n    <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n    <item name=\"colorAccent\">@color/colorAccent</item>\n  </style>\n\n</resources>\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/finish/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    ext.kotlin_version = '1.4.32'\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:4.1.3'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n        maven {\n            name 'ossrh-snapshot'\n            url 'https://oss.sonatype.org/content/repositories/snapshots'\n        }\n        maven { url 'https://jitpack.io' }\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/finish/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/finish/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app's APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Automatically convert third-party libraries to use AndroidX\nandroid.enableJetifier=true\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/finish/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/finish/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/finish/settings.gradle",
    "content": "rootProject.name = 'TFLite Digit Classifier Codelab (finish) App'\ninclude ':app'\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/start/app/build.gradle",
    "content": "apply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\n\nandroid {\n    compileSdkVersion 30\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.codelabs.digitclassifier\"\n        minSdkVersion 21\n        targetSdkVersion 30\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    // TODO: Add an option to avoid compressing TF Lite model file\n\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\n// Sanity check if you have trained and downloaded TF Lite model.\npreBuild.doFirst {\n    assert file(\"./src/main/assets/mnist.tflite\").exists() :\n            \"mnist.tflite not found. Make sure you have trained and \" +\n                    \"downloaded your TensorFlow Lite model to assets/ folder\"\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version\"\n\n    // Support Libraries\n    implementation 'androidx.appcompat:appcompat:1.3.0'\n    implementation 'androidx.core:core-ktx:1.5.0'\n    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'\n\n    // AndroidDraw Library\n    implementation 'com.github.divyanshub024:AndroidDraw:v0.1'\n\n    // Task API\n    implementation \"com.google.android.gms:play-services-tasks:17.2.1\"\n\n    //TODO: Add TF Lite\n\n\n    testImplementation 'junit:junit:4.13.2'\n    androidTestImplementation 'androidx.test:runner:1.3.0'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'\n}\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/start/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/start/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          xmlns:tools=\"http://schemas.android.com/tools\"\n          package=\"org.tensorflow.lite.codelabs.digitclassifier\">\n\n  <uses-sdk />\n\n  <application\n          android:allowBackup=\"true\"\n          android:icon=\"@mipmap/ic_launcher\"\n          android:label=\"@string/app_name\"\n          android:roundIcon=\"@mipmap/ic_launcher_round\"\n          android:supportsRtl=\"true\"\n          android:theme=\"@style/AppTheme\"\n          tools:ignore=\"GoogleAppIndexingWarning\">\n\n    <activity android:name=\".MainActivity\"\n              android:exported=\"true\">\n      <intent-filter>\n        <action android:name=\"android.intent.action.MAIN\"/>\n        <category android:name=\"android.intent.category.LAUNCHER\"/>\n      </intent-filter>\n    </activity>\n  </application>\n\n</manifest>\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/start/app/src/main/assets/ADD_TFLITE_MODEL_HERE",
    "content": "Place the TensorFlow Lite model file (mnist.tflite) you generated from step 2: \"Train a machine learning model\" here."
  },
  {
    "path": "lite/codelabs/digit_classifier/android/start/app/src/main/java/org/tensorflow/lite/codelabs/digitclassifier/DigitClassifier.kt",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.codelabs.digitclassifier\n\nimport android.content.Context\nimport android.content.res.AssetManager\nimport android.graphics.Bitmap\nimport android.util.Log\nimport com.google.android.gms.tasks.Task\nimport com.google.android.gms.tasks.TaskCompletionSource\nimport java.io.FileInputStream\nimport java.io.IOException\nimport java.nio.ByteBuffer\nimport java.nio.ByteOrder\nimport java.nio.channels.FileChannel\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Executors\n\nclass DigitClassifier(private val context: Context) {\n  // TODO: Add a TF Lite interpreter as a field.\n\n  var isInitialized = false\n    private set\n\n  /** Executor to run inference task in the background. */\n  private val executorService: ExecutorService = Executors.newCachedThreadPool()\n\n  private var inputImageWidth: Int = 0 // will be inferred from TF Lite model.\n  private var inputImageHeight: Int = 0 // will be inferred from TF Lite model.\n  private var modelInputSize: Int = 0 // will be inferred from TF Lite model.\n\n  fun initialize(): Task<Void?> {\n    val task = TaskCompletionSource<Void?>()\n    executorService.execute {\n      try {\n        initializeInterpreter()\n        task.setResult(null)\n      } catch (e: IOException) {\n        task.setException(e)\n      }\n    }\n    return task.task\n  }\n\n  @Throws(IOException::class)\n  private fun initializeInterpreter() {\n    // TODO: Load the TF Lite model from file and initialize an interpreter.\n\n    // TODO: Read the model input shape from model file.\n\n    isInitialized = true\n    Log.d(TAG, \"Initialized TFLite interpreter.\")\n  }\n\n  @Throws(IOException::class)\n  private fun loadModelFile(assetManager: AssetManager, filename: String): ByteBuffer {\n    val fileDescriptor = assetManager.openFd(filename)\n    val inputStream = FileInputStream(fileDescriptor.fileDescriptor)\n    val fileChannel = inputStream.channel\n    val startOffset = fileDescriptor.startOffset\n    val declaredLength = fileDescriptor.declaredLength\n    return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength)\n  }\n\n  private fun classify(bitmap: Bitmap): String {\n    check(isInitialized) { \"TF Lite Interpreter is not initialized yet.\" }\n\n    // TODO: Add code to run inference with TF Lite.\n\n    return \"Let's add TensorFlow Lite code!\"\n  }\n\n  fun classifyAsync(bitmap: Bitmap): Task<String> {\n    val task = TaskCompletionSource<String>()\n    executorService.execute {\n      val result = classify(bitmap)\n      task.setResult(result)\n    }\n    return task.task\n  }\n\n  fun close() {\n    executorService.execute {\n      // TODO: close the TF Lite interpreter here\n\n\n      Log.d(TAG, \"Closed TFLite interpreter.\")\n    }\n  }\n\n  private fun convertBitmapToByteBuffer(bitmap: Bitmap): ByteBuffer {\n    val byteBuffer = ByteBuffer.allocateDirect(modelInputSize)\n    byteBuffer.order(ByteOrder.nativeOrder())\n\n    val pixels = IntArray(inputImageWidth * inputImageHeight)\n    bitmap.getPixels(pixels, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)\n\n    for (pixelValue in pixels) {\n      val r = (pixelValue shr 16 and 0xFF)\n      val g = (pixelValue shr 8 and 0xFF)\n      val b = (pixelValue and 0xFF)\n\n      // Convert RGB to grayscale and normalize pixel value to [0..1].\n      val normalizedPixelValue = (r + g + b) / 3.0f / 255.0f\n      byteBuffer.putFloat(normalizedPixelValue)\n    }\n\n    return byteBuffer\n  }\n\n  companion object {\n    private const val TAG = \"DigitClassifier\"\n\n    private const val FLOAT_TYPE_SIZE = 4\n    private const val PIXEL_SIZE = 1\n\n    private const val OUTPUT_CLASSES_COUNT = 10\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/start/app/src/main/java/org/tensorflow/lite/codelabs/digitclassifier/MainActivity.kt",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.codelabs.digitclassifier\n\nimport android.annotation.SuppressLint\nimport android.graphics.Color\nimport android.os.Bundle\nimport android.util.Log\nimport android.view.MotionEvent\nimport android.widget.Button\nimport android.widget.TextView\nimport androidx.appcompat.app.AppCompatActivity\nimport com.divyanshu.draw.widget.DrawView\n\nclass MainActivity : AppCompatActivity() {\n\n  private var drawView: DrawView? = null\n  private var clearButton: Button? = null\n  private var predictedTextView: TextView? = null\n  private var digitClassifier = DigitClassifier(this)\n\n  @SuppressLint(\"ClickableViewAccessibility\")\n  override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n    setContentView(R.layout.activity_main)\n\n    // Setup view instances.\n    drawView = findViewById(R.id.draw_view)\n    drawView?.setStrokeWidth(70.0f)\n    drawView?.setColor(Color.WHITE)\n    drawView?.setBackgroundColor(Color.BLACK)\n    clearButton = findViewById(R.id.clear_button)\n    predictedTextView = findViewById(R.id.predicted_text)\n\n    // Setup clear drawing button.\n    clearButton?.setOnClickListener {\n      drawView?.clearCanvas()\n      predictedTextView?.text = getString(R.string.prediction_text_placeholder)\n    }\n\n    // Setup classification trigger so that it classify after every stroke drew.\n    drawView?.setOnTouchListener { _, event ->\n      // As we have interrupted DrawView's touch event,\n      // we first need to pass touch events through to the instance for the drawing to show up.\n      drawView?.onTouchEvent(event)\n\n      // Then if user finished a touch event, run classification\n      if (event.action == MotionEvent.ACTION_UP) {\n        classifyDrawing()\n      }\n\n      true\n    }\n\n    // Setup digit classifier.\n    digitClassifier\n      .initialize()\n      .addOnFailureListener { e -> Log.e(TAG, \"Error to setting up digit classifier.\", e) }\n  }\n\n  override fun onDestroy() {\n    // Sync DigitClassifier instance lifecycle with MainActivity lifecycle,\n    // and free up resources (e.g. TF Lite instance) once the activity is destroyed.\n    digitClassifier.close()\n    super.onDestroy()\n  }\n\n  private fun classifyDrawing() {\n    val bitmap = drawView?.getBitmap()\n\n    if ((bitmap != null) && (digitClassifier.isInitialized)) {\n      digitClassifier\n        .classifyAsync(bitmap)\n        .addOnSuccessListener { resultText -> predictedTextView?.text = resultText }\n        .addOnFailureListener { e ->\n          predictedTextView?.text = getString(\n            R.string.classification_error_message,\n            e.localizedMessage\n          )\n          Log.e(TAG, \"Error classifying drawing.\", e)\n        }\n    }\n  }\n\n  companion object {\n    private const val TAG = \"MainActivity\"\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/start/app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n  <path\n      android:fillColor=\"#008577\"\n      android:pathData=\"M0,0h108v108h-108z\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M9,0L9,108\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M19,0L19,108\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M29,0L29,108\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M39,0L39,108\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M49,0L49,108\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M59,0L59,108\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M69,0L69,108\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M79,0L79,108\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M89,0L89,108\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M99,0L99,108\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M0,9L108,9\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M0,19L108,19\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M0,29L108,29\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M0,39L108,39\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M0,49L108,49\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M0,59L108,59\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M0,69L108,69\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M0,79L108,79\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M0,89L108,89\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M0,99L108,99\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M19,29L89,29\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M19,39L89,39\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M19,49L89,49\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M19,59L89,59\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M19,69L89,69\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M19,79L89,79\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M29,19L29,89\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M39,19L39,89\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M49,19L49,89\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M59,19L59,89\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M69,19L69,89\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n  <path\n      android:fillColor=\"#00000000\"\n      android:pathData=\"M79,19L79,89\"\n      android:strokeColor=\"#33FFFFFF\"\n      android:strokeWidth=\"0.8\"/>\n</vector>\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/start/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n  <path\n      android:fillType=\"evenOdd\"\n      android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\">\n    <aapt:attr name=\"android:fillColor\">\n      <gradient\n          android:endX=\"78.5885\"\n          android:endY=\"90.9159\"\n          android:startX=\"48.7653\"\n          android:startY=\"61.0927\"\n          android:type=\"linear\">\n        <item\n            android:color=\"#44000000\"\n            android:offset=\"0.0\"/>\n        <item\n            android:color=\"#00000000\"\n            android:offset=\"1.0\"/>\n      </gradient>\n    </aapt:attr>\n  </path>\n  <path\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"nonZero\"\n      android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\"/>\n</vector>\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/start/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".MainActivity\">\n\n  <com.divyanshu.draw.widget.DrawView\n      android:id=\"@+id/draw_view\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"0dp\"\n      app:layout_constraintDimensionRatio=\"1:1\"\n      app:layout_constraintTop_toTopOf=\"parent\"/>\n\n  <TextView\n      android:id=\"@+id/predicted_text\"\n      android:textStyle=\"bold\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:text=\"@string/prediction_text_placeholder\"\n      android:textSize=\"20sp\"\n      app:layout_constraintBottom_toTopOf=\"@id/clear_button\"\n      app:layout_constraintLeft_toLeftOf=\"parent\"\n      app:layout_constraintRight_toRightOf=\"parent\"\n      app:layout_constraintTop_toBottomOf=\"@id/draw_view\"/>\n\n  <Button\n      android:id=\"@+id/clear_button\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:text=\"@string/clear_button_text\"\n      app:layout_constraintBottom_toBottomOf=\"parent\"\n      app:layout_constraintLeft_toLeftOf=\"parent\"\n      app:layout_constraintRight_toRightOf=\"parent\"/>\n\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/start/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <background android:drawable=\"@drawable/ic_launcher_background\"/>\n  <foreground android:drawable=\"@drawable/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/start/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <background android:drawable=\"@drawable/ic_launcher_background\"/>\n  <foreground android:drawable=\"@drawable/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/start/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <color name=\"colorPrimary\">#008577</color>\n  <color name=\"colorPrimaryDark\">#00574B</color>\n  <color name=\"colorAccent\">#D81B60</color>\n</resources>\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/start/app/src/main/res/values/strings.xml",
    "content": "<resources>\n  <string name=\"app_name\" translatable=\"false\">Digit Classifier</string>\n  <string name=\"clear_button_text\" translatable=\"false\">Clear</string>\n  <string name=\"prediction_text_placeholder\" translatable=\"false\">Please draw a digit</string>\n  <string name=\"classification_error_message\" translatable=\"false\">Error classifying drawing: %s</string>\n</resources>\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/start/app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n  <!-- Base application theme. -->\n  <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n    <!-- Customize your theme here. -->\n    <item name=\"colorPrimary\">@color/colorPrimary</item>\n    <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n    <item name=\"colorAccent\">@color/colorAccent</item>\n  </style>\n\n</resources>\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/start/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    ext.kotlin_version = '1.4.32'\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:4.1.3'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n        maven {\n            name 'ossrh-snapshot'\n            url 'https://oss.sonatype.org/content/repositories/snapshots'\n        }\n        maven { url 'https://jitpack.io' }\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/start/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/start/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app's APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Automatically convert third-party libraries to use AndroidX\nandroid.enableJetifier=true\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/start/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/start/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/android/start/settings.gradle",
    "content": "rootProject.name = 'TFLite Digit Classifier Codelab (start) App'\ninclude ':app'\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/ml/step2_train_ml_model.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"V2RPZQF05ngq\"\n      },\n      \"source\": [\n        \"##### Copyright 2019 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"PEig-M385xKS\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"9afYQkHI52mr\"\n      },\n      \"source\": [\n        \"# Step 2: Train a machine learning model\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"v8hkClAF58p6\"\n      },\n      \"source\": [\n        \"This is the notebook for step 2 of the codelab [**Build a handwritten digit classifier app with TensorFlow Lite**](https://codelabs.developers.google.com/codelabs/digit-classifier-tflite/).\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Yu1BOwfPzaIy\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/lite/codelabs/digit_classifier/ml/step2_train_ml_model.ipynb\\\">\\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />\\n\",\n        \"    Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/lite/codelabs/digit_classifier/ml/step2_train_ml_model.ipynb\\\">\\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />\\n\",\n        \"    View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"EXyJkL4WnqyS\"\n      },\n      \"source\": [\n        \"## Import dependencies\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"XXX8WpQI5U6_\"\n      },\n      \"source\": [\n        \"We start by importing TensorFlow and other supporting libraries that are used for data processing and visualization.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"kS_mq4yAlXHZ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# TensorFlow and tf.keras\\n\",\n        \"import tensorflow as tf\\n\",\n        \"from tensorflow import keras\\n\",\n        \"\\n\",\n        \"# Helper libraries\\n\",\n        \"import numpy as np\\n\",\n        \"import matplotlib.pyplot as plt\\n\",\n        \"import random\\n\",\n        \"\\n\",\n        \"print(tf.__version__)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"r0WlLrJcnwWp\"\n      },\n      \"source\": [\n        \"## Download and explore the MNIST dataset\\n\",\n        \"The MNIST database contains 60,000 training images and 10,000 testing images of handwritten digits. We will use the dataset to train our digit classification model.\\n\",\n        \"\\n\",\n        \"Each image in the MNIST dataset is a 28x28 grayscale image containing a digit from 0 to 9, and a label identifying which digit is in the image.\\n\",\n        \"![MNIST sample](https://github.com/khanhlvg/DigitClassifier/raw/master/images/mnist.png)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"B5REuMrblewK\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Keras provides a handy API to download the MNIST dataset, and split them into\\n\",\n        \"# \\\"train\\\" dataset and \\\"test\\\" dataset.\\n\",\n        \"mnist = keras.datasets.mnist\\n\",\n        \"(train_images, train_labels), (test_images, test_labels) = mnist.load_data()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"REY5lDDylpFh\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Normalize the input image so that each pixel value is between 0 to 1.\\n\",\n        \"train_images = train_images / 255.0\\n\",\n        \"test_images = test_images / 255.0\\n\",\n        \"print('Pixels are normalized')\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"SAOE84IplyWR\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Show the first 25 images in the training dataset.\\n\",\n        \"plt.figure(figsize=(10,10))\\n\",\n        \"for i in range(25):\\n\",\n        \"  plt.subplot(5,5,i+1)\\n\",\n        \"  plt.xticks([])\\n\",\n        \"  plt.yticks([])\\n\",\n        \"  plt.grid(False)\\n\",\n        \"  plt.imshow(train_images[i], cmap=plt.cm.gray)\\n\",\n        \"  plt.xlabel(train_labels[i])\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"9v-Wp3TxpLX6\"\n      },\n      \"source\": [\n        \"## Train a TensorFlow model to classify digit images\\n\",\n        \"\\n\",\n        \"Next, we use Keras API to build a TensorFlow model and train it on the MNIST \\\"train\\\" dataset. After training, our model will be able to classify the digit images.\\n\",\n        \"\\n\",\n        \"Our model takes **a 28px x 28px grayscale image** as an input, and outputs **a float array of length 10** representing the probability of the image being a digit from 0 to 9.\\n\",\n        \"\\n\",\n        \"Here we use a simple convolutional neural network, which is a common technique in computer vision. We will not go into details about model architecture in this codelab. If you want have a deeper understanding about different ML model architectures, please consider taking our free [TensorFlow training course](https://www.coursera.org/learn/introduction-tensorflow).\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"IWgBGmaplzcp\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Define the model architecture\\n\",\n        \"model = keras.Sequential([\\n\",\n        \"  keras.layers.InputLayer(input_shape=(28, 28)),\\n\",\n        \"  keras.layers.Reshape(target_shape=(28, 28, 1)),\\n\",\n        \"  keras.layers.Conv2D(filters=32, kernel_size=(3, 3), activation=tf.nn.relu),\\n\",\n        \"  keras.layers.Conv2D(filters=64, kernel_size=(3, 3), activation=tf.nn.relu),\\n\",\n        \"  keras.layers.MaxPooling2D(pool_size=(2, 2)),\\n\",\n        \"  keras.layers.Dropout(0.25),\\n\",\n        \"  keras.layers.Flatten(),\\n\",\n        \"  keras.layers.Dense(10)\\n\",\n        \"])\\n\",\n        \"\\n\",\n        \"# Define how to train the model\\n\",\n        \"model.compile(optimizer='adam',\\n\",\n        \"              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\\n\",\n        \"              metrics=['accuracy'])\\n\",\n        \"\\n\",\n        \"# Train the digit classification model\\n\",\n        \"model.fit(train_images, train_labels, epochs=5)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"WFHKkb7gcJei\"\n      },\n      \"source\": [\n        \"Let's take a closer look at our model structure.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"y7V6-UQqcuK-\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model.summary()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"n16JkSyNc5cf\"\n      },\n      \"source\": [\n        \"There is an extra dimension with **None** shape in every layer in our model, which is called the **batch dimension**. In machine learning, we usually process data in batches to improve throughput, so TensorFlow automatically add the dimension for you.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"za35DFJ5uFkX\"\n      },\n      \"source\": [\n        \"## Evaluate our model\\n\",\n        \"We run our digit classification model against our \\\"test\\\" dataset that the model has not seen during its training process to confirm that the model did not just remember the digits it saw but also generalize well to new images.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"sJI8nqFWmMwC\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Evaluate the model using all images in the test dataset.\\n\",\n        \"test_loss, test_acc = model.evaluate(test_images, test_labels)\\n\",\n        \"\\n\",\n        \"print('Test accuracy:', test_acc)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"7-qv9-9_cUb7\"\n      },\n      \"source\": [\n        \"Although our model is relatively simple, we were able to achieve good accuracy around 98% on new images that the model has never seen before. Let's visualize the result.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"PdlXEyUImeXC\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# A helper function that returns 'red'/'black' depending on if its two input\\n\",\n        \"# parameter matches or not.\\n\",\n        \"def get_label_color(val1, val2):\\n\",\n        \"  if val1 == val2:\\n\",\n        \"    return 'black'\\n\",\n        \"  else:\\n\",\n        \"    return 'red'\\n\",\n        \"\\n\",\n        \"# Predict the labels of digit images in our test dataset.\\n\",\n        \"predictions = model.predict(test_images)\\n\",\n        \"\\n\",\n        \"# As the model output 10 float representing the probability of the input image\\n\",\n        \"# being a digit from 0 to 9, we need to find the largest probability value\\n\",\n        \"# to find out which digit the model predicts to be most likely in the image.\\n\",\n        \"prediction_digits = np.argmax(predictions, axis=1)\\n\",\n        \"\\n\",\n        \"# Then plot 100 random test images and their predicted labels.\\n\",\n        \"# If a prediction result is different from the label provided label in \\\"test\\\"\\n\",\n        \"# dataset, we will highlight it in red color.\\n\",\n        \"plt.figure(figsize=(18, 18))\\n\",\n        \"for i in range(100):\\n\",\n        \"  ax = plt.subplot(10, 10, i+1)\\n\",\n        \"  plt.xticks([])\\n\",\n        \"  plt.yticks([])\\n\",\n        \"  plt.grid(False)\\n\",\n        \"  image_index = random.randint(0, len(prediction_digits))\\n\",\n        \"  plt.imshow(test_images[image_index], cmap=plt.cm.gray)\\n\",\n        \"  ax.xaxis.label.set_color(get_label_color(prediction_digits[image_index],\\\\\\n\",\n        \"                                           test_labels[image_index]))\\n\",\n        \"  plt.xlabel('Predicted: %d' % prediction_digits[image_index])\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"AWROBI4iv9fY\"\n      },\n      \"source\": [\n        \"## Convert the Keras model to TensorFlow Lite\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"bV99Izwykb-J\"\n      },\n      \"source\": [\n        \"Now as we have trained the digit classifer model, we will convert it to TensorFlow Lite format for mobile deployment.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"2fXStjR4mzkR\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Convert Keras model to TF Lite format.\\n\",\n        \"converter = tf.lite.TFLiteConverter.from_keras_model(model)\\n\",\n        \"tflite_float_model = converter.convert()\\n\",\n        \"\\n\",\n        \"# Show model size in KBs.\\n\",\n        \"float_model_size = len(tflite_float_model) / 1024\\n\",\n        \"print('Float model size = %dKBs.' % float_model_size)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"tfer6hI8ljEh\"\n      },\n      \"source\": [\n        \"As we will deploy our model to a mobile device, we want our model to be as small and as fast as possible. **Quantization** is a common technique often used in on-device machine learning to shrink ML models. Here we will use 8-bit number to approximate our 32-bit weights, which in turn shrinks the model size by a factor of 4.\\n\",\n        \"\\n\",\n        \"See [TensorFlow documentation](https://www.tensorflow.org/lite/performance/post_training_quantization) to learn more about other quantization techniques.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"yhY86SRTmtGC\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Re-convert the model to TF Lite using quantization.\\n\",\n        \"converter.optimizations = [tf.lite.Optimize.DEFAULT]\\n\",\n        \"tflite_quantized_model = converter.convert()\\n\",\n        \"\\n\",\n        \"# Show model size in KBs.\\n\",\n        \"quantized_model_size = len(tflite_quantized_model) / 1024\\n\",\n        \"print('Quantized model size = %dKBs,' % quantized_model_size)\\n\",\n        \"print('which is about %d%% of the float model size.'\\\\\\n\",\n        \"      % (quantized_model_size * 100 / float_model_size))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ahTP3T60nYJb\"\n      },\n      \"source\": [\n        \"## Evaluate the TensorFlow Lite model\\n\",\n        \"\\n\",\n        \"By using quantization, we often traded off a bit of accuracy for the benefit of having a significantly smaller model. Let's calculate the accuracy drop of our quantized model.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"YvszGa11ne6Q\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# A helper function to evaluate the TF Lite model using \\\"test\\\" dataset.\\n\",\n        \"def evaluate_tflite_model(tflite_model):\\n\",\n        \"  # Initialize TFLite interpreter using the model.\\n\",\n        \"  interpreter = tf.lite.Interpreter(model_content=tflite_model)\\n\",\n        \"  interpreter.allocate_tensors()\\n\",\n        \"  input_tensor_index = interpreter.get_input_details()[0][\\\"index\\\"]\\n\",\n        \"  output = interpreter.tensor(interpreter.get_output_details()[0][\\\"index\\\"])\\n\",\n        \"\\n\",\n        \"  # Run predictions on every image in the \\\"test\\\" dataset.\\n\",\n        \"  prediction_digits = []\\n\",\n        \"  for test_image in test_images:\\n\",\n        \"    # Pre-processing: add batch dimension and convert to float32 to match with\\n\",\n        \"    # the model's input data format.\\n\",\n        \"    test_image = np.expand_dims(test_image, axis=0).astype(np.float32)\\n\",\n        \"    interpreter.set_tensor(input_tensor_index, test_image)\\n\",\n        \"\\n\",\n        \"    # Run inference.\\n\",\n        \"    interpreter.invoke()\\n\",\n        \"\\n\",\n        \"    # Post-processing: remove batch dimension and find the digit with highest\\n\",\n        \"    # probability.\\n\",\n        \"    digit = np.argmax(output()[0])\\n\",\n        \"    prediction_digits.append(digit)\\n\",\n        \"\\n\",\n        \"  # Compare prediction results with ground truth labels to calculate accuracy.\\n\",\n        \"  accurate_count = 0\\n\",\n        \"  for index in range(len(prediction_digits)):\\n\",\n        \"    if prediction_digits[index] == test_labels[index]:\\n\",\n        \"      accurate_count += 1\\n\",\n        \"  accuracy = accurate_count * 1.0 / len(prediction_digits)\\n\",\n        \"\\n\",\n        \"  return accuracy\\n\",\n        \"\\n\",\n        \"# Evaluate the TF Lite float model. You'll find that its accurary is identical\\n\",\n        \"# to the original TF (Keras) model because they are essentially the same model\\n\",\n        \"# stored in different format.\\n\",\n        \"float_accuracy = evaluate_tflite_model(tflite_float_model)\\n\",\n        \"print('Float model accuracy = %.4f' % float_accuracy)\\n\",\n        \"\\n\",\n        \"# Evalualte the TF Lite quantized model.\\n\",\n        \"# Don't be surprised if you see quantized model accuracy is higher than\\n\",\n        \"# the original float model. It happens sometimes :)\\n\",\n        \"quantized_accuracy = evaluate_tflite_model(tflite_quantized_model)\\n\",\n        \"print('Quantized model accuracy = %.4f' % quantized_accuracy)\\n\",\n        \"print('Accuracy drop = %.4f' % (float_accuracy - quantized_accuracy))\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ItyEwAdCCVw6\"\n      },\n      \"source\": [\n        \"## Download the TensorFlow Lite model\\n\",\n        \"\\n\",\n        \"Let's get our model and integrate it into an Android app.\\n\",\n        \"\\n\",\n        \"If you see an error when downloading mnist.tflite from Colab, try running this cell again.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Q_Z5yLxrwbpI\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Save the quantized model to file to the Downloads directory\\n\",\n        \"f = open('mnist.tflite', \\\"wb\\\")\\n\",\n        \"f.write(tflite_quantized_model)\\n\",\n        \"f.close()\\n\",\n        \"\\n\",\n        \"# Download the digit classification model\\n\",\n        \"from google.colab import files\\n\",\n        \"files.download('mnist.tflite')\\n\",\n        \"\\n\",\n        \"print('`mnist.tflite` has been downloaded')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"C4ASalaLIbu2\"\n      },\n      \"source\": [\n        \"## Good job!\\n\",\n        \"This is the end of *Step 2: Train a machine learning model* in the codelab **Build a handwritten digit classifier app with TensorFlow Lite**. Let's go back to our codelab and proceed to the [next step](https://codelabs.developers.google.com/codelabs/digit-classifier-tflite/#2).\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"step2_train_ml_model.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "lite/codelabs/digit_classifier/ml/step7_improve_accuracy.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"5HEkgJW62Zhq\"\n      },\n      \"source\": [\n        \"Copyright 2020 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"ZvnzHC7lmzWB\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"mxPxpHKHMAkl\"\n      },\n      \"source\": [\n        \"# Step 7: Improve model accuracy with data augmentation\\n\",\n        \"\\n\",\n        \"This is the notebook for step 7 of the codelab [**Build a handwritten digit classifier app with TensorFlow Lite**](https://codelabs.developers.google.com/codelabs/digit-classifier-tflite/).\\n\",\n        \"\\n\",\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/lite/codelabs/digit_classifier/ml/step7_improve_accuracy.ipynb\\\">\\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />\\n\",\n        \"    Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/lite/codelabs/digit_classifier/ml/step7_improve_accuracy.ipynb\\\">\\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />\\n\",\n        \"    View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"w0qKcVsBNVyL\"\n      },\n      \"source\": [\n        \"In previous steps, we trained a model that could recognize handwritten digits using the MNIST dataset. We were able to achieve above 98% accuracy on our validation dataset. However, when you deploy the model in an Android app and test it, you probably noticed some accuracy issue. Although the app was able to recognize digits that you drew, the accuracy is probably way lower than 98%.\\n\",\n        \"\\n\",\n        \"In this notebook, we will explore the cause of the accuracy drop and use data augmentation to improve deployment accuracy.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"p8bO0hupMdZM\"\n      },\n      \"source\": [\n        \"## Preparation\\n\",\n        \"\\n\",\n        \"Let's start by importing TensorFlow and other supporting libraries that are used for data processing and visualization.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"nImr6z7TMBJQ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow as tf\\n\",\n        \"from tensorflow import keras\\n\",\n        \"\\n\",\n        \"import matplotlib.pyplot as plt\\n\",\n        \"import numpy as np\\n\",\n        \"import pandas as pd\\n\",\n        \"import random\\n\",\n        \"\\n\",\n        \"print(tf.__version__)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Rivc81WyRpXG\"\n      },\n      \"source\": [\n        \"Import MNIST dataset.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"ZGd5hkioMpcr\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"mnist = keras.datasets.mnist\\n\",\n        \"(train_images, train_labels), (test_images, test_labels) = mnist.load_data()\\n\",\n        \"\\n\",\n        \"# Normalize the input image so that each pixel value is between 0 to 1.\\n\",\n        \"train_images = train_images / 255.0\\n\",\n        \"test_images = test_images / 255.0\\n\",\n        \"\\n\",\n        \"# Add a color dimension to the images in \\\"train\\\" and \\\"validate\\\" dataset to\\n\",\n        \"# leverage Keras's data augmentation utilities later.\\n\",\n        \"train_images = np.expand_dims(train_images, axis=3)\\n\",\n        \"test_images = np.expand_dims(test_images, axis=3)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"0SbDbl3HSHh1\"\n      },\n      \"source\": [\n        \"Define an utility function so that we can create quickly create multiple models with the same model architecture for comparison.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"JjoUeI5WSE_H\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"def create_model():\\n\",\n        \"  model = keras.Sequential([\\n\",\n        \"    keras.layers.InputLayer(input_shape=(28, 28, 1)),\\n\",\n        \"    keras.layers.Conv2D(filters=32, kernel_size=(3, 3), activation=tf.nn.relu),\\n\",\n        \"    keras.layers.Conv2D(filters=64, kernel_size=(3, 3), activation=tf.nn.relu),\\n\",\n        \"    keras.layers.MaxPooling2D(pool_size=(2, 2)),\\n\",\n        \"    keras.layers.Dropout(0.25),\\n\",\n        \"    keras.layers.Flatten(),\\n\",\n        \"    keras.layers.Dense(10, activation=tf.nn.softmax)\\n\",\n        \"  ])\\n\",\n        \"  model.compile(optimizer='adam',\\n\",\n        \"                loss='sparse_categorical_crossentropy',\\n\",\n        \"                metrics=['accuracy'])\\n\",\n        \"  return model\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"QGgIJ4pYThkm\"\n      },\n      \"source\": [\n        \"Confirm that our model can achieve above 98% accuracy on MNIST dataset.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"W2cYWUbkTb8Q\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"base_model = create_model()\\n\",\n        \"base_model.fit(\\n\",\n        \"    train_images,\\n\",\n        \"    train_labels,\\n\",\n        \"    epochs=5,\\n\",\n        \"    validation_data=(test_images, test_labels)\\n\",\n        \")\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"lEtLCGS0Ufag\"\n      },\n      \"source\": [\n        \"# Troubleshoot the accuracy drop\\n\",\n        \"\\n\",\n        \"Let's see the digit images in MNIST again and guess the cause of the accuracy drop we experienced in deployment.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"xptCfTnBTgvm\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Show the first 25 images in the training dataset.\\n\",\n        \"plt.figure(figsize=(10,10))\\n\",\n        \"for i in range(25):\\n\",\n        \"  plt.subplot(5,5,i+1)\\n\",\n        \"  plt.xticks([])\\n\",\n        \"  plt.yticks([])\\n\",\n        \"  plt.grid(False)\\n\",\n        \"  plt.imshow(np.squeeze(train_images[i], axis=2), cmap=plt.cm.gray)\\n\",\n        \"  plt.xlabel(train_labels[i])\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"phB_hWqFWOQR\"\n      },\n      \"source\": [\n        \"We can see from the 25 images above that the digits are about the same size, and they are in the center of the images. Let's verify if this assumption is true across the MNIST dataset.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"2V0KsvSLVE6I\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# An utility function that returns where the digit is in the image.\\n\",\n        \"def digit_area(mnist_image):\\n\",\n        \"  # Remove the color axes\\n\",\n        \"  mnist_image = np.squeeze(mnist_image, axis=2)\\n\",\n        \"\\n\",\n        \"  # Extract the list of columns that contain at least 1 pixel from the digit\\n\",\n        \"  x_nonzero = np.nonzero(np.amax(mnist_image, 0))\\n\",\n        \"  x_min = np.min(x_nonzero)\\n\",\n        \"  x_max = np.max(x_nonzero)\\n\",\n        \"\\n\",\n        \"  # Extract the list of rows that contain at least 1 pixel from the digit\\n\",\n        \"  y_nonzero = np.nonzero(np.amax(mnist_image, 1))\\n\",\n        \"  y_min = np.min(y_nonzero)\\n\",\n        \"  y_max = np.max(y_nonzero)\\n\",\n        \"\\n\",\n        \"  return [x_min, x_max, y_min, y_max]\\n\",\n        \"\\n\",\n        \"# Calculate the area containing the digit across MNIST dataset\\n\",\n        \"digit_area_rows = []\\n\",\n        \"for image in train_images:\\n\",\n        \"  digit_area_row = digit_area(image)\\n\",\n        \"  digit_area_rows.append(digit_area_row)\\n\",\n        \"digit_area_df = pd.DataFrame(\\n\",\n        \"  digit_area_rows,\\n\",\n        \"  columns=['x_min', 'x_max', 'y_min', 'y_max']\\n\",\n        \")\\n\",\n        \"digit_area_df.hist()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"GulcBpSRc3CO\"\n      },\n      \"source\": [\n        \"Now from the histogram, you can confirm that the digit in MNIST images are fitted nicely in an certain area at the center of the images. \\n\",\n        \"\\n\",\n        \"![Mnist range](http://download.tensorflow.org/models/tflite/digit_classifier/mnist_range.png)\\n\",\n        \"\\n\",\n        \"However, when you wrote digits in your Android app, you probably did not pay attention to make sure your digit fit in the virtual area that the digits appear in MNIST dataset. The machine learning model have not seen such data before so it performed poorly, especially when you wrote a digit that was off the center of the drawing pad.\\n\",\n        \"\\n\",\n        \"Let's add some data augmentation to the MNIST dataset to verify if our assumption is true. We will distort our MNIST dataset by adding:\\n\",\n        \"* Rotation\\n\",\n        \"* Width and height shift\\n\",\n        \"* Shear\\n\",\n        \"* Zoom\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"AC5l3W7bY1td\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Define data augmentation\\n\",\n        \"datagen = keras.preprocessing.image.ImageDataGenerator(\\n\",\n        \"  rotation_range=30,\\n\",\n        \"  width_shift_range=0.25,\\n\",\n        \"  height_shift_range=0.25,\\n\",\n        \"  shear_range=0.25,\\n\",\n        \"  zoom_range=0.2\\n\",\n        \")\\n\",\n        \"\\n\",\n        \"# Generate augmented data from MNIST dataset\\n\",\n        \"train_generator = datagen.flow(train_images, train_labels)\\n\",\n        \"test_generator = datagen.flow(test_images, test_labels)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"wy1IfuRZjjec\"\n      },\n      \"source\": [\n        \"Let's see what our digit images look like after augmentation. You can see that we now clearly have much more variation on how the digits are placed in the images.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"1G-tWDc2aia1\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"augmented_images, augmented_labels = next(train_generator)\\n\",\n        \"plt.figure(figsize=(10,10))\\n\",\n        \"for i in range(25):\\n\",\n        \"    plt.subplot(5,5,i+1)\\n\",\n        \"    plt.xticks([])\\n\",\n        \"    plt.yticks([])\\n\",\n        \"    plt.grid(False)\\n\",\n        \"    plt.imshow(np.squeeze(augmented_images[i], axis=2), cmap=plt.cm.gray)\\n\",\n        \"    plt.xlabel('Label: %d' % augmented_labels[i])\\n\",\n        \"plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"G-zys-yNkL4b\"\n      },\n      \"source\": [\n        \"Let's evaluate the digit classifier model that we trained earlier on this augmented test dataset and see if it makes accuracy drop.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"IPnP4xPbjqWi\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"base_model.evaluate(test_generator)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"AgNlTPPVlDqX\"\n      },\n      \"source\": [\n        \"You can see that accuracy significantly dropped to below 40% in augmented test dataset.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Xls8oqBnlcZj\"\n      },\n      \"source\": [\n        \"# Improve accuracy with data augmentation\\n\",\n        \"\\n\",\n        \"Now let's train our model using augmented dataset to make it perform better in deployment.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"DX4XRgc9k-6s\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"improved_model = create_model()\\n\",\n        \"improved_model.fit(train_generator, epochs=5, validation_data=test_generator)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"3UlqZ1XnnJRu\"\n      },\n      \"source\": [\n        \"We can see that as the models saw more distorted digit images during training, its accuracy evaluated distorted test digit images were significantly improved to about 90%.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"fsI4mHtbpE_A\"\n      },\n      \"source\": [\n        \"# Convert to TensorFlow Lite\\n\",\n        \"\\n\",\n        \"Let's convert the improved model to TensorFlow Lite and redeploy to the Android app.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"a0l6HcBTmh7-\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Convert Keras model to TF Lite format and quantize.\\n\",\n        \"converter = tf.lite.TFLiteConverter.from_keras_model(improved_model)\\n\",\n        \"converter.optimizations = [tf.lite.Optimize.DEFAULT]\\n\",\n        \"tflite_quantized_model = converter.convert()\\n\",\n        \"\\n\",\n        \"# Save the quantized model to file to the Downloads directory\\n\",\n        \"f = open('mnist.tflite', \\\"wb\\\")\\n\",\n        \"f.write(tflite_quantized_model)\\n\",\n        \"f.close()\\n\",\n        \"\\n\",\n        \"# Download the digit classification model\\n\",\n        \"from google.colab import files\\n\",\n        \"files.download('mnist.tflite')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"CAp7mKIkrfjY\"\n      },\n      \"source\": [\n        \"## Good job!\\n\",\n        \"This is the end of *Improve model accuracy with data augmentation* in the codelab **Build a handwritten digit classifier app with TensorFlow Lite**. You can repeat [step 3](https://codelabs.developers.google.com/codelabs/digit-classifier-tflite/#2) to redeploy the improved model to your Android app and see if accuracy has been improved.\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"step7_improve_accuracy.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/README.md",
    "content": "# Codelabs for TensorFlow Lite\n\nThis folder contains the code for the TensorFlow Lite codelab:\n\n* [Recognize Flowers with TensorFlow on Android](https://codelabs.developers.google.com/codelabs/recognize-flowers-with-tensorflow-on-android/)\n\n## Introduction\n\nIn these codelabs, you will learn:\n\n*   How to convert your model using the TFLite converter.\n*   How to run it using the TFLite interpreter in an Android app.\n*   How to use TFLite Support Library to do preprocessing and postprocessing.\n*   How to use GPU on your phone to accelerate your model.\n\n## Pre-requisites\n\nNone.\n\n## Getting Started\n\nVisit the Google codelabs site to follow along the guided steps.\n\n## Support\n\n- Stack Overflow: https://stackoverflow.com/questions/tagged/tensorflow-lite\n\nPatches are encouraged, and may be submitted by forking this project and\nsubmitting a pull request through GitHub.\n\n## License\n\nCopyright 2020 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/.gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n\n/.gradle/\n/.idea/"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/.gitignore",
    "content": "/build\n\n/build/"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nproject.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'\n\nassert file(project.ext.ASSET_DIR + \"/model.tflite\").exists()\nassert file(project.ext.ASSET_DIR + \"/labels.txt\").exists()\n\nandroid {\n    compileSdkVersion 28\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.classification\"\n        minSdkVersion 21\n        targetSdkVersion 28\n        versionCode 1\n        versionName \"1.0\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n    // TODO: Add an option to avoid compressing TF Lite model file\n    aaptOptions {\n        noCompress \"tflite\"\n    }\n    compileOptions {\n        sourceCompatibility = '1.8'\n        targetCompatibility = '1.8'\n    }\n    lintOptions {\n        abortOnError false\n    }\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation 'androidx.appcompat:appcompat:1.0.0'\n    implementation 'androidx.coordinatorlayout:coordinatorlayout:1.0.0'\n    implementation 'com.google.android.material:material:1.0.0'\n\n    // Build off of nightly TensorFlow Lite\n    // TODO: Add TFLite dependencies\n    implementation 'org.tensorflow:tensorflow-lite:0.0.0-nightly-SNAPSHOT'\n    implementation 'org.tensorflow:tensorflow-lite-gpu:0.0.0-nightly-SNAPSHOT'\n\n    implementation 'org.tensorflow:tensorflow-lite-support:0.1.0'\n\n    // Use local TensorFlow library\n    // implementation 'org.tensorflow:tensorflow-lite-local:0.0.0'\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/androidTest/java/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.tensorflow.lite.examples.classification\">\n  <uses-sdk />\n</manifest>"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/androidTest/java/org/tensorflow/lite/examples/classification/ClassifierTest.java",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.classification;\n\nimport static com.google.common.truth.Truth.assertThat;\n\nimport android.content.res.AssetManager;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.util.Log;\nimport androidx.test.ext.junit.runners.AndroidJUnit4;\nimport androidx.test.platform.app.InstrumentationRegistry;\nimport androidx.test.rule.ActivityTestRule;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Scanner;\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier.Device;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier.Recognition;\n\n/** Golden test for Image Classification Reference app. */\n@RunWith(AndroidJUnit4.class)\npublic class ClassifierTest {\n\n  @Rule\n  public ActivityTestRule<ClassifierActivity> rule =\n      new ActivityTestRule<>(ClassifierActivity.class);\n\n  private static final String[] INPUTS = {\"fox.jpg\"};\n  private static final String[] GOLDEN_OUTPUTS = {\"fox-mobilenet_v1_1.0_224.txt\"};\n\n  @Test\n  public void classificationResultsShouldNotChange() throws IOException {\n    ClassifierActivity activity = rule.getActivity();\n    Classifier classifier = Classifier.create(activity, Device.CPU, 1);\n    for (int i = 0; i < INPUTS.length; i++) {\n      String imageFileName = INPUTS[i];\n      String goldenOutputFileName = GOLDEN_OUTPUTS[i];\n      Bitmap input = loadImage(imageFileName);\n      List<Recognition> goldenOutput = loadRecognitions(goldenOutputFileName);\n\n      List<Recognition> result = classifier.recognizeImage(input, 0);\n      Iterator<Recognition> goldenOutputIterator = goldenOutput.iterator();\n\n      for (Recognition actual : result) {\n        Assert.assertTrue(goldenOutputIterator.hasNext());\n        Recognition expected = goldenOutputIterator.next();\n        assertThat(actual.getTitle()).isEqualTo(expected.getTitle());\n        assertThat(actual.getConfidence()).isWithin(0.01f).of(expected.getConfidence());\n      }\n    }\n  }\n\n  private static Bitmap loadImage(String fileName) {\n    AssetManager assetManager =\n        InstrumentationRegistry.getInstrumentation().getContext().getAssets();\n    InputStream inputStream = null;\n    try {\n      inputStream = assetManager.open(fileName);\n    } catch (IOException e) {\n      Log.e(\"Test\", \"Cannot load image from assets\");\n    }\n    return BitmapFactory.decodeStream(inputStream);\n  }\n\n  private static List<Recognition> loadRecognitions(String fileName) {\n    AssetManager assetManager =\n        InstrumentationRegistry.getInstrumentation().getContext().getAssets();\n    InputStream inputStream = null;\n    try {\n      inputStream = assetManager.open(fileName);\n    } catch (IOException e) {\n      Log.e(\"Test\", \"Cannot load probability results from assets\");\n    }\n    Scanner scanner = new Scanner(inputStream);\n    List<Recognition> result = new ArrayList<>();\n    while (scanner.hasNext()) {\n      String category = scanner.next();\n      category = category.replace('_', ' ');\n      if (!scanner.hasNextFloat()) {\n        break;\n      }\n      float probability = scanner.nextFloat();\n      Recognition recognition = new Recognition(null, category, probability, null);\n      result.add(recognition);\n    }\n    return result;\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.tensorflow.lite.examples.classification\">\n\n    <uses-sdk />\n\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n\n    <uses-feature android:name=\"android.hardware.camera\" />\n    <uses-feature android:name=\"android.hardware.camera.autofocus\" />\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/tfe_ic_app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme.ImageClassification\">\n        <activity\n            android:name=\".ClassifierActivity\"\n            \n            android:screenOrientation=\"portrait\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n</manifest>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/assets/ADD_TFLITE_MODEL_HERE",
    "content": ""
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/assets/labels.txt",
    "content": "daisy\ndandelion\nroses\nsunflowers\ntulips"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/java/org/tensorflow/lite/examples/classification/CameraActivity.java",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.classification;\n\nimport android.Manifest;\nimport android.app.Fragment;\nimport android.content.Context;\nimport android.content.pm.PackageManager;\nimport android.hardware.Camera;\nimport android.hardware.camera2.CameraAccessException;\nimport android.hardware.camera2.CameraCharacteristics;\nimport android.hardware.camera2.CameraManager;\nimport android.hardware.camera2.params.StreamConfigurationMap;\nimport android.media.Image;\nimport android.media.Image.Plane;\nimport android.media.ImageReader;\nimport android.media.ImageReader.OnImageAvailableListener;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.HandlerThread;\nimport android.os.Trace;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.util.Size;\nimport android.view.Surface;\nimport android.view.View;\nimport android.view.ViewTreeObserver;\nimport android.view.WindowManager;\nimport android.widget.AdapterView;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.Spinner;\nimport android.widget.TextView;\nimport android.widget.Toast;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.UiThread;\nimport com.google.android.material.bottomsheet.BottomSheetBehavior;\nimport java.nio.ByteBuffer;\nimport java.util.List;\nimport org.tensorflow.lite.examples.classification.env.ImageUtils;\nimport org.tensorflow.lite.examples.classification.env.Logger;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier.Device;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier.Recognition;\n\npublic abstract class CameraActivity extends AppCompatActivity\n    implements OnImageAvailableListener,\n        Camera.PreviewCallback,\n        View.OnClickListener,\n        AdapterView.OnItemSelectedListener {\n  private static final Logger LOGGER = new Logger();\n\n  private static final int PERMISSIONS_REQUEST = 1;\n\n  private static final String PERMISSION_CAMERA = Manifest.permission.CAMERA;\n  protected int previewWidth = 0;\n  protected int previewHeight = 0;\n  private Handler handler;\n  private HandlerThread handlerThread;\n  private boolean useCamera2API;\n  private boolean isProcessingFrame = false;\n  private byte[][] yuvBytes = new byte[3][];\n  private int[] rgbBytes = null;\n  private int yRowStride;\n  private Runnable postInferenceCallback;\n  private Runnable imageConverter;\n  private LinearLayout bottomSheetLayout;\n  private LinearLayout gestureLayout;\n  private BottomSheetBehavior<LinearLayout> sheetBehavior;\n  protected TextView recognitionTextView,\n      recognition1TextView,\n      recognition2TextView,\n      recognitionValueTextView,\n      recognition1ValueTextView,\n      recognition2ValueTextView;\n  protected TextView frameValueTextView,\n      cropValueTextView,\n      cameraResolutionTextView,\n      rotationTextView,\n      inferenceTimeTextView;\n  protected ImageView bottomSheetArrowImageView;\n  private ImageView plusImageView, minusImageView;\n  private Spinner deviceSpinner;\n  private TextView threadsTextView;\n\n  private Device device = Device.CPU;\n  private int numThreads = -1;\n\n  @Override\n  protected void onCreate(final Bundle savedInstanceState) {\n    LOGGER.d(\"onCreate \" + this);\n    super.onCreate(null);\n    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);\n\n    setContentView(R.layout.tfe_ic_activity_camera);\n\n    if (hasPermission()) {\n      setFragment();\n    } else {\n      requestPermission();\n    }\n\n    threadsTextView = findViewById(R.id.threads);\n    plusImageView = findViewById(R.id.plus);\n    minusImageView = findViewById(R.id.minus);\n    deviceSpinner = findViewById(R.id.device_spinner);\n    bottomSheetLayout = findViewById(R.id.bottom_sheet_layout);\n    gestureLayout = findViewById(R.id.gesture_layout);\n    sheetBehavior = BottomSheetBehavior.from(bottomSheetLayout);\n    bottomSheetArrowImageView = findViewById(R.id.bottom_sheet_arrow);\n\n    ViewTreeObserver vto = gestureLayout.getViewTreeObserver();\n    vto.addOnGlobalLayoutListener(\n        new ViewTreeObserver.OnGlobalLayoutListener() {\n          @Override\n          public void onGlobalLayout() {\n            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n              gestureLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);\n            } else {\n              gestureLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);\n            }\n            //                int width = bottomSheetLayout.getMeasuredWidth();\n            int height = gestureLayout.getMeasuredHeight();\n\n            sheetBehavior.setPeekHeight(height);\n          }\n        });\n    sheetBehavior.setHideable(false);\n\n    sheetBehavior.setBottomSheetCallback(\n        new BottomSheetBehavior.BottomSheetCallback() {\n          @Override\n          public void onStateChanged(@NonNull View bottomSheet, int newState) {\n            switch (newState) {\n              case BottomSheetBehavior.STATE_HIDDEN:\n                break;\n              case BottomSheetBehavior.STATE_EXPANDED:\n                {\n                  bottomSheetArrowImageView.setImageResource(R.drawable.icn_chevron_down);\n                }\n                break;\n              case BottomSheetBehavior.STATE_COLLAPSED:\n                {\n                  bottomSheetArrowImageView.setImageResource(R.drawable.icn_chevron_up);\n                }\n                break;\n              case BottomSheetBehavior.STATE_DRAGGING:\n                break;\n              case BottomSheetBehavior.STATE_SETTLING:\n                bottomSheetArrowImageView.setImageResource(R.drawable.icn_chevron_up);\n                break;\n            }\n          }\n\n          @Override\n          public void onSlide(@NonNull View bottomSheet, float slideOffset) {}\n        });\n\n    recognitionTextView = findViewById(R.id.detected_item);\n    recognitionValueTextView = findViewById(R.id.detected_item_value);\n    recognition1TextView = findViewById(R.id.detected_item1);\n    recognition1ValueTextView = findViewById(R.id.detected_item1_value);\n    recognition2TextView = findViewById(R.id.detected_item2);\n    recognition2ValueTextView = findViewById(R.id.detected_item2_value);\n\n    frameValueTextView = findViewById(R.id.frame_info);\n    cropValueTextView = findViewById(R.id.crop_info);\n    cameraResolutionTextView = findViewById(R.id.view_info);\n    rotationTextView = findViewById(R.id.rotation_info);\n    inferenceTimeTextView = findViewById(R.id.inference_info);\n\n    deviceSpinner.setOnItemSelectedListener(this);\n\n    plusImageView.setOnClickListener(this);\n    minusImageView.setOnClickListener(this);\n\n    device = Device.valueOf(deviceSpinner.getSelectedItem().toString());\n    numThreads = Integer.parseInt(threadsTextView.getText().toString().trim());\n  }\n\n  protected int[] getRgbBytes() {\n    imageConverter.run();\n    return rgbBytes;\n  }\n\n  protected int getLuminanceStride() {\n    return yRowStride;\n  }\n\n  protected byte[] getLuminance() {\n    return yuvBytes[0];\n  }\n\n  /** Callback for android.hardware.Camera API */\n  @Override\n  public void onPreviewFrame(final byte[] bytes, final Camera camera) {\n    if (isProcessingFrame) {\n      LOGGER.w(\"Dropping frame!\");\n      return;\n    }\n\n    try {\n      // Initialize the storage bitmaps once when the resolution is known.\n      if (rgbBytes == null) {\n        Camera.Size previewSize = camera.getParameters().getPreviewSize();\n        previewHeight = previewSize.height;\n        previewWidth = previewSize.width;\n        rgbBytes = new int[previewWidth * previewHeight];\n        onPreviewSizeChosen(new Size(previewSize.width, previewSize.height), 90);\n      }\n    } catch (final Exception e) {\n      LOGGER.e(e, \"Exception!\");\n      return;\n    }\n\n    isProcessingFrame = true;\n    yuvBytes[0] = bytes;\n    yRowStride = previewWidth;\n\n    imageConverter =\n        new Runnable() {\n          @Override\n          public void run() {\n            ImageUtils.convertYUV420SPToARGB8888(bytes, previewWidth, previewHeight, rgbBytes);\n          }\n        };\n\n    postInferenceCallback =\n        new Runnable() {\n          @Override\n          public void run() {\n            camera.addCallbackBuffer(bytes);\n            isProcessingFrame = false;\n          }\n        };\n    processImage();\n  }\n\n  /** Callback for Camera2 API */\n  @Override\n  public void onImageAvailable(final ImageReader reader) {\n    // We need wait until we have some size from onPreviewSizeChosen\n    if (previewWidth == 0 || previewHeight == 0) {\n      return;\n    }\n    if (rgbBytes == null) {\n      rgbBytes = new int[previewWidth * previewHeight];\n    }\n    try {\n      final Image image = reader.acquireLatestImage();\n\n      if (image == null) {\n        return;\n      }\n\n      if (isProcessingFrame) {\n        image.close();\n        return;\n      }\n      isProcessingFrame = true;\n      Trace.beginSection(\"imageAvailable\");\n      final Plane[] planes = image.getPlanes();\n      fillBytes(planes, yuvBytes);\n      yRowStride = planes[0].getRowStride();\n      final int uvRowStride = planes[1].getRowStride();\n      final int uvPixelStride = planes[1].getPixelStride();\n\n      imageConverter =\n          new Runnable() {\n            @Override\n            public void run() {\n              ImageUtils.convertYUV420ToARGB8888(\n                  yuvBytes[0],\n                  yuvBytes[1],\n                  yuvBytes[2],\n                  previewWidth,\n                  previewHeight,\n                  yRowStride,\n                  uvRowStride,\n                  uvPixelStride,\n                  rgbBytes);\n            }\n          };\n\n      postInferenceCallback =\n          new Runnable() {\n            @Override\n            public void run() {\n              image.close();\n              isProcessingFrame = false;\n            }\n          };\n\n      processImage();\n    } catch (final Exception e) {\n      LOGGER.e(e, \"Exception!\");\n      Trace.endSection();\n      return;\n    }\n    Trace.endSection();\n  }\n\n  @Override\n  public synchronized void onStart() {\n    LOGGER.d(\"onStart \" + this);\n    super.onStart();\n  }\n\n  @Override\n  public synchronized void onResume() {\n    LOGGER.d(\"onResume \" + this);\n    super.onResume();\n\n    handlerThread = new HandlerThread(\"inference\");\n    handlerThread.start();\n    handler = new Handler(handlerThread.getLooper());\n  }\n\n  @Override\n  public synchronized void onPause() {\n    LOGGER.d(\"onPause \" + this);\n\n    handlerThread.quitSafely();\n    try {\n      handlerThread.join();\n      handlerThread = null;\n      handler = null;\n    } catch (final InterruptedException e) {\n      LOGGER.e(e, \"Exception!\");\n    }\n\n    super.onPause();\n  }\n\n  @Override\n  public synchronized void onStop() {\n    LOGGER.d(\"onStop \" + this);\n    super.onStop();\n  }\n\n  @Override\n  public synchronized void onDestroy() {\n    LOGGER.d(\"onDestroy \" + this);\n    super.onDestroy();\n  }\n\n  protected synchronized void runInBackground(final Runnable r) {\n    if (handler != null) {\n      handler.post(r);\n    }\n  }\n\n  @Override\n  public void onRequestPermissionsResult(\n      final int requestCode, final String[] permissions, final int[] grantResults) {\n    super.onRequestPermissionsResult(requestCode, permissions, grantResults);\n    if (requestCode == PERMISSIONS_REQUEST) {\n      if (allPermissionsGranted(grantResults)) {\n        setFragment();\n      } else {\n        requestPermission();\n      }\n    }\n  }\n\n  private static boolean allPermissionsGranted(final int[] grantResults) {\n    for (int result : grantResults) {\n      if (result != PackageManager.PERMISSION_GRANTED) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  private boolean hasPermission() {\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n      return checkSelfPermission(PERMISSION_CAMERA) == PackageManager.PERMISSION_GRANTED;\n    } else {\n      return true;\n    }\n  }\n\n  private void requestPermission() {\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n      if (shouldShowRequestPermissionRationale(PERMISSION_CAMERA)) {\n        Toast.makeText(\n                CameraActivity.this,\n                \"Camera permission is required for this demo\",\n                Toast.LENGTH_LONG)\n            .show();\n      }\n      requestPermissions(new String[] {PERMISSION_CAMERA}, PERMISSIONS_REQUEST);\n    }\n  }\n\n  // Returns true if the device supports the required hardware level, or better.\n  private boolean isHardwareLevelSupported(\n      CameraCharacteristics characteristics, int requiredLevel) {\n    int deviceLevel = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);\n    if (deviceLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {\n      return requiredLevel == deviceLevel;\n    }\n    // deviceLevel is not LEGACY, can use numerical sort\n    return requiredLevel <= deviceLevel;\n  }\n\n  private String chooseCamera() {\n    final CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);\n    try {\n      for (final String cameraId : manager.getCameraIdList()) {\n        final CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);\n\n        // We don't use a front facing camera in this sample.\n        final Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);\n        if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {\n          continue;\n        }\n\n        final StreamConfigurationMap map =\n            characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);\n\n        if (map == null) {\n          continue;\n        }\n\n        // Fallback to camera1 API for internal cameras that don't have full support.\n        // This should help with legacy situations where using the camera2 API causes\n        // distorted or otherwise broken previews.\n        useCamera2API =\n            (facing == CameraCharacteristics.LENS_FACING_EXTERNAL)\n                || isHardwareLevelSupported(\n                    characteristics, CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);\n        LOGGER.i(\"Camera API lv2?: %s\", useCamera2API);\n        return cameraId;\n      }\n    } catch (CameraAccessException e) {\n      LOGGER.e(e, \"Not allowed to access camera\");\n    }\n\n    return null;\n  }\n\n  protected void setFragment() {\n    String cameraId = chooseCamera();\n\n    Fragment fragment;\n    if (useCamera2API) {\n      CameraConnectionFragment camera2Fragment =\n          CameraConnectionFragment.newInstance(\n              new CameraConnectionFragment.ConnectionCallback() {\n                @Override\n                public void onPreviewSizeChosen(final Size size, final int rotation) {\n                  previewHeight = size.getHeight();\n                  previewWidth = size.getWidth();\n                  CameraActivity.this.onPreviewSizeChosen(size, rotation);\n                }\n              },\n              this,\n              getLayoutId(),\n              getDesiredPreviewFrameSize());\n\n      camera2Fragment.setCamera(cameraId);\n      fragment = camera2Fragment;\n    } else {\n      fragment =\n          new LegacyCameraConnectionFragment(this, getLayoutId(), getDesiredPreviewFrameSize());\n    }\n\n    getFragmentManager().beginTransaction().replace(R.id.container, fragment).commit();\n  }\n\n  protected void fillBytes(final Plane[] planes, final byte[][] yuvBytes) {\n    // Because of the variable row stride it's not possible to know in\n    // advance the actual necessary dimensions of the yuv planes.\n    for (int i = 0; i < planes.length; ++i) {\n      final ByteBuffer buffer = planes[i].getBuffer();\n      if (yuvBytes[i] == null) {\n        LOGGER.d(\"Initializing buffer %d at size %d\", i, buffer.capacity());\n        yuvBytes[i] = new byte[buffer.capacity()];\n      }\n      buffer.get(yuvBytes[i]);\n    }\n  }\n\n  protected void readyForNextImage() {\n    if (postInferenceCallback != null) {\n      postInferenceCallback.run();\n    }\n  }\n\n  protected int getScreenOrientation() {\n    switch (getWindowManager().getDefaultDisplay().getRotation()) {\n      case Surface.ROTATION_270:\n        return 270;\n      case Surface.ROTATION_180:\n        return 180;\n      case Surface.ROTATION_90:\n        return 90;\n      default:\n        return 0;\n    }\n  }\n\n  @UiThread\n  protected void showResultsInBottomSheet(List<Recognition> results) {\n    if (results != null && results.size() >= 3) {\n      Recognition recognition = results.get(0);\n      if (recognition != null) {\n        if (recognition.getTitle() != null) recognitionTextView.setText(recognition.getTitle());\n        if (recognition.getConfidence() != null)\n          recognitionValueTextView.setText(\n              String.format(\"%.2f\", (100 * recognition.getConfidence())) + \"%\");\n      }\n\n      Recognition recognition1 = results.get(1);\n      if (recognition1 != null) {\n        if (recognition1.getTitle() != null) recognition1TextView.setText(recognition1.getTitle());\n        if (recognition1.getConfidence() != null)\n          recognition1ValueTextView.setText(\n              String.format(\"%.2f\", (100 * recognition1.getConfidence())) + \"%\");\n      }\n\n      Recognition recognition2 = results.get(2);\n      if (recognition2 != null) {\n        if (recognition2.getTitle() != null) recognition2TextView.setText(recognition2.getTitle());\n        if (recognition2.getConfidence() != null)\n          recognition2ValueTextView.setText(\n              String.format(\"%.2f\", (100 * recognition2.getConfidence())) + \"%\");\n      }\n    }\n  }\n\n  protected void showFrameInfo(String frameInfo) {\n    frameValueTextView.setText(frameInfo);\n  }\n\n  protected void showCropInfo(String cropInfo) {\n    cropValueTextView.setText(cropInfo);\n  }\n\n  protected void showCameraResolution(String cameraInfo) {\n    cameraResolutionTextView.setText(cameraInfo);\n  }\n\n  protected void showRotationInfo(String rotation) {\n    rotationTextView.setText(rotation);\n  }\n\n  protected void showInference(String inferenceTime) {\n    inferenceTimeTextView.setText(inferenceTime);\n  }\n\n  protected Device getDevice() {\n    return device;\n  }\n\n  private void setDevice(Device device) {\n    if (this.device != device) {\n      LOGGER.d(\"Updating  device: \" + device);\n      this.device = device;\n      final boolean threadsEnabled = device == Device.CPU;\n      plusImageView.setEnabled(threadsEnabled);\n      minusImageView.setEnabled(threadsEnabled);\n      threadsTextView.setText(threadsEnabled ? String.valueOf(numThreads) : \"N/A\");\n      onInferenceConfigurationChanged();\n    }\n  }\n\n  protected int getNumThreads() {\n    return numThreads;\n  }\n\n  private void setNumThreads(int numThreads) {\n    if (this.numThreads != numThreads) {\n      LOGGER.d(\"Updating  numThreads: \" + numThreads);\n      this.numThreads = numThreads;\n      onInferenceConfigurationChanged();\n    }\n  }\n\n  protected abstract void processImage();\n\n  protected abstract void onPreviewSizeChosen(final Size size, final int rotation);\n\n  protected abstract int getLayoutId();\n\n  protected abstract Size getDesiredPreviewFrameSize();\n\n  protected abstract void onInferenceConfigurationChanged();\n\n  @Override\n  public void onClick(View v) {\n    if (v.getId() == R.id.plus) {\n      String threads = threadsTextView.getText().toString().trim();\n      int numThreads = Integer.parseInt(threads);\n      if (numThreads >= 9) return;\n      setNumThreads(++numThreads);\n      threadsTextView.setText(String.valueOf(numThreads));\n    } else if (v.getId() == R.id.minus) {\n      String threads = threadsTextView.getText().toString().trim();\n      int numThreads = Integer.parseInt(threads);\n      if (numThreads == 1) {\n        return;\n      }\n      setNumThreads(--numThreads);\n      threadsTextView.setText(String.valueOf(numThreads));\n    }\n  }\n\n  @Override\n  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {\n    if (parent == deviceSpinner) {\n      setDevice(Device.valueOf(parent.getItemAtPosition(pos).toString()));\n    }\n  }\n\n  @Override\n  public void onNothingSelected(AdapterView<?> parent) {\n    // Do nothing.\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/java/org/tensorflow/lite/examples/classification/CameraConnectionFragment.java",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.classification;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.app.AlertDialog;\nimport android.app.Dialog;\nimport android.app.DialogFragment;\nimport android.app.Fragment;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.content.res.Configuration;\nimport android.graphics.ImageFormat;\nimport android.graphics.Matrix;\nimport android.graphics.RectF;\nimport android.graphics.SurfaceTexture;\nimport android.hardware.camera2.CameraAccessException;\nimport android.hardware.camera2.CameraCaptureSession;\nimport android.hardware.camera2.CameraCharacteristics;\nimport android.hardware.camera2.CameraDevice;\nimport android.hardware.camera2.CameraManager;\nimport android.hardware.camera2.CaptureRequest;\nimport android.hardware.camera2.CaptureResult;\nimport android.hardware.camera2.TotalCaptureResult;\nimport android.hardware.camera2.params.StreamConfigurationMap;\nimport android.media.ImageReader;\nimport android.media.ImageReader.OnImageAvailableListener;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.HandlerThread;\nimport android.text.TextUtils;\nimport android.util.Size;\nimport android.util.SparseIntArray;\nimport android.view.LayoutInflater;\nimport android.view.Surface;\nimport android.view.TextureView;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.concurrent.Semaphore;\nimport java.util.concurrent.TimeUnit;\nimport org.tensorflow.lite.examples.classification.customview.AutoFitTextureView;\nimport org.tensorflow.lite.examples.classification.env.Logger;\n\n/**\n * Camera Connection Fragment that captures images from camera.\n *\n * <p>Instantiated by newInstance.</p>\n */\n@SuppressWarnings(\"FragmentNotInstantiable\")\npublic class CameraConnectionFragment extends Fragment {\n  private static final Logger LOGGER = new Logger();\n\n  /**\n   * The camera preview size will be chosen to be the smallest frame by pixel size capable of\n   * containing a DESIRED_SIZE x DESIRED_SIZE square.\n   */\n  private static final int MINIMUM_PREVIEW_SIZE = 320;\n\n  /** Conversion from screen rotation to JPEG orientation. */\n  private static final SparseIntArray ORIENTATIONS = new SparseIntArray();\n\n  private static final String FRAGMENT_DIALOG = \"dialog\";\n\n  static {\n    ORIENTATIONS.append(Surface.ROTATION_0, 90);\n    ORIENTATIONS.append(Surface.ROTATION_90, 0);\n    ORIENTATIONS.append(Surface.ROTATION_180, 270);\n    ORIENTATIONS.append(Surface.ROTATION_270, 180);\n  }\n\n  /** A {@link Semaphore} to prevent the app from exiting before closing the camera. */\n  private final Semaphore cameraOpenCloseLock = new Semaphore(1);\n  /** A {@link OnImageAvailableListener} to receive frames as they are available. */\n  private final OnImageAvailableListener imageListener;\n  /** The input size in pixels desired by TensorFlow (width and height of a square bitmap). */\n  private final Size inputSize;\n  /** The layout identifier to inflate for this Fragment. */\n  private final int layout;\n\n  private final ConnectionCallback cameraConnectionCallback;\n  private final CameraCaptureSession.CaptureCallback captureCallback =\n      new CameraCaptureSession.CaptureCallback() {\n        @Override\n        public void onCaptureProgressed(\n            final CameraCaptureSession session,\n            final CaptureRequest request,\n            final CaptureResult partialResult) {}\n\n        @Override\n        public void onCaptureCompleted(\n            final CameraCaptureSession session,\n            final CaptureRequest request,\n            final TotalCaptureResult result) {}\n      };\n  /** ID of the current {@link CameraDevice}. */\n  private String cameraId;\n  /** An {@link AutoFitTextureView} for camera preview. */\n  private AutoFitTextureView textureView;\n  /** A {@link CameraCaptureSession } for camera preview. */\n  private CameraCaptureSession captureSession;\n  /** A reference to the opened {@link CameraDevice}. */\n  private CameraDevice cameraDevice;\n  /** The rotation in degrees of the camera sensor from the display. */\n  private Integer sensorOrientation;\n  /** The {@link Size} of camera preview. */\n  private Size previewSize;\n  /** An additional thread for running tasks that shouldn't block the UI. */\n  private HandlerThread backgroundThread;\n  /** A {@link Handler} for running tasks in the background. */\n  private Handler backgroundHandler;\n  /**\n   * {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a {@link\n   * TextureView}.\n   */\n  private final TextureView.SurfaceTextureListener surfaceTextureListener =\n      new TextureView.SurfaceTextureListener() {\n        @Override\n        public void onSurfaceTextureAvailable(\n            final SurfaceTexture texture, final int width, final int height) {\n          openCamera(width, height);\n        }\n\n        @Override\n        public void onSurfaceTextureSizeChanged(\n            final SurfaceTexture texture, final int width, final int height) {\n          configureTransform(width, height);\n        }\n\n        @Override\n        public boolean onSurfaceTextureDestroyed(final SurfaceTexture texture) {\n          return true;\n        }\n\n        @Override\n        public void onSurfaceTextureUpdated(final SurfaceTexture texture) {}\n      };\n  /** An {@link ImageReader} that handles preview frame capture. */\n  private ImageReader previewReader;\n  /** {@link CaptureRequest.Builder} for the camera preview */\n  private CaptureRequest.Builder previewRequestBuilder;\n  /** {@link CaptureRequest} generated by {@link #previewRequestBuilder} */\n  private CaptureRequest previewRequest;\n  /** {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its state. */\n  private final CameraDevice.StateCallback stateCallback =\n      new CameraDevice.StateCallback() {\n        @Override\n        public void onOpened(final CameraDevice cd) {\n          // This method is called when the camera is opened.  We start camera preview here.\n          cameraOpenCloseLock.release();\n          cameraDevice = cd;\n          createCameraPreviewSession();\n        }\n\n        @Override\n        public void onDisconnected(final CameraDevice cd) {\n          cameraOpenCloseLock.release();\n          cd.close();\n          cameraDevice = null;\n        }\n\n        @Override\n        public void onError(final CameraDevice cd, final int error) {\n          cameraOpenCloseLock.release();\n          cd.close();\n          cameraDevice = null;\n          final Activity activity = getActivity();\n          if (null != activity) {\n            activity.finish();\n          }\n        }\n      };\n\n  @SuppressLint(\"ValidFragment\")\n  private CameraConnectionFragment(\n      final ConnectionCallback connectionCallback,\n      final OnImageAvailableListener imageListener,\n      final int layout,\n      final Size inputSize) {\n    this.cameraConnectionCallback = connectionCallback;\n    this.imageListener = imageListener;\n    this.layout = layout;\n    this.inputSize = inputSize;\n  }\n\n  /**\n   * Given {@code choices} of {@code Size}s supported by a camera, chooses the smallest one whose\n   * width and height are at least as large as the minimum of both, or an exact match if possible.\n   *\n   * @param choices The list of sizes that the camera supports for the intended output class\n   * @param width The minimum desired width\n   * @param height The minimum desired height\n   * @return The optimal {@code Size}, or an arbitrary one if none were big enough\n   */\n  protected static Size chooseOptimalSize(final Size[] choices, final int width, final int height) {\n    final int minSize = Math.max(Math.min(width, height), MINIMUM_PREVIEW_SIZE);\n    final Size desiredSize = new Size(width, height);\n\n    // Collect the supported resolutions that are at least as big as the preview Surface\n    boolean exactSizeFound = false;\n    final List<Size> bigEnough = new ArrayList<Size>();\n    final List<Size> tooSmall = new ArrayList<Size>();\n    for (final Size option : choices) {\n      if (option.equals(desiredSize)) {\n        // Set the size but don't return yet so that remaining sizes will still be logged.\n        exactSizeFound = true;\n      }\n\n      if (option.getHeight() >= minSize && option.getWidth() >= minSize) {\n        bigEnough.add(option);\n      } else {\n        tooSmall.add(option);\n      }\n    }\n\n    LOGGER.i(\"Desired size: \" + desiredSize + \", min size: \" + minSize + \"x\" + minSize);\n    LOGGER.i(\"Valid preview sizes: [\" + TextUtils.join(\", \", bigEnough) + \"]\");\n    LOGGER.i(\"Rejected preview sizes: [\" + TextUtils.join(\", \", tooSmall) + \"]\");\n\n    if (exactSizeFound) {\n      LOGGER.i(\"Exact size match found.\");\n      return desiredSize;\n    }\n\n    // Pick the smallest of those, assuming we found any\n    if (bigEnough.size() > 0) {\n      final Size chosenSize = Collections.min(bigEnough, new CompareSizesByArea());\n      LOGGER.i(\"Chosen size: \" + chosenSize.getWidth() + \"x\" + chosenSize.getHeight());\n      return chosenSize;\n    } else {\n      LOGGER.e(\"Couldn't find any suitable preview size\");\n      return choices[0];\n    }\n  }\n\n  public static CameraConnectionFragment newInstance(\n      final ConnectionCallback callback,\n      final OnImageAvailableListener imageListener,\n      final int layout,\n      final Size inputSize) {\n    return new CameraConnectionFragment(callback, imageListener, layout, inputSize);\n  }\n\n  /**\n   * Shows a {@link Toast} on the UI thread.\n   *\n   * @param text The message to show\n   */\n  private void showToast(final String text) {\n    final Activity activity = getActivity();\n    if (activity != null) {\n      activity.runOnUiThread(\n          new Runnable() {\n            @Override\n            public void run() {\n              Toast.makeText(activity, text, Toast.LENGTH_SHORT).show();\n            }\n          });\n    }\n  }\n\n  @Override\n  public View onCreateView(\n      final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {\n    return inflater.inflate(layout, container, false);\n  }\n\n  @Override\n  public void onViewCreated(final View view, final Bundle savedInstanceState) {\n    textureView = (AutoFitTextureView) view.findViewById(R.id.texture);\n  }\n\n  @Override\n  public void onActivityCreated(final Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n  }\n\n  @Override\n  public void onResume() {\n    super.onResume();\n    startBackgroundThread();\n\n    // When the screen is turned off and turned back on, the SurfaceTexture is already\n    // available, and \"onSurfaceTextureAvailable\" will not be called. In that case, we can open\n    // a camera and start preview from here (otherwise, we wait until the surface is ready in\n    // the SurfaceTextureListener).\n    if (textureView.isAvailable()) {\n      openCamera(textureView.getWidth(), textureView.getHeight());\n    } else {\n      textureView.setSurfaceTextureListener(surfaceTextureListener);\n    }\n  }\n\n  @Override\n  public void onPause() {\n    closeCamera();\n    stopBackgroundThread();\n    super.onPause();\n  }\n\n  public void setCamera(String cameraId) {\n    this.cameraId = cameraId;\n  }\n\n  /** Sets up member variables related to camera. */\n  private void setUpCameraOutputs() {\n    final Activity activity = getActivity();\n    final CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);\n    try {\n      final CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);\n\n      final StreamConfigurationMap map =\n          characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);\n\n      sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);\n\n      // Danger, W.R.! Attempting to use too large a preview size could  exceed the camera\n      // bus' bandwidth limitation, resulting in gorgeous previews but the storage of\n      // garbage capture data.\n      previewSize =\n          chooseOptimalSize(\n              map.getOutputSizes(SurfaceTexture.class),\n              inputSize.getWidth(),\n              inputSize.getHeight());\n\n      // We fit the aspect ratio of TextureView to the size of preview we picked.\n      final int orientation = getResources().getConfiguration().orientation;\n      if (orientation == Configuration.ORIENTATION_LANDSCAPE) {\n        textureView.setAspectRatio(previewSize.getWidth(), previewSize.getHeight());\n      } else {\n        textureView.setAspectRatio(previewSize.getHeight(), previewSize.getWidth());\n      }\n    } catch (final CameraAccessException e) {\n      LOGGER.e(e, \"Exception!\");\n    } catch (final NullPointerException e) {\n      // Currently an NPE is thrown when the Camera2API is used but not supported on the\n      // device this code runs.\n      ErrorDialog.newInstance(getString(R.string.tfe_ic_camera_error))\n          .show(getChildFragmentManager(), FRAGMENT_DIALOG);\n      throw new IllegalStateException(getString(R.string.tfe_ic_camera_error));\n    }\n\n    cameraConnectionCallback.onPreviewSizeChosen(previewSize, sensorOrientation);\n  }\n\n  /** Opens the camera specified by {@link CameraConnectionFragment#cameraId}. */\n  private void openCamera(final int width, final int height) {\n    setUpCameraOutputs();\n    configureTransform(width, height);\n    final Activity activity = getActivity();\n    final CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);\n    try {\n      if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {\n        throw new RuntimeException(\"Time out waiting to lock camera opening.\");\n      }\n      manager.openCamera(cameraId, stateCallback, backgroundHandler);\n    } catch (final CameraAccessException e) {\n      LOGGER.e(e, \"Exception!\");\n    } catch (final InterruptedException e) {\n      throw new RuntimeException(\"Interrupted while trying to lock camera opening.\", e);\n    }\n  }\n\n  /** Closes the current {@link CameraDevice}. */\n  private void closeCamera() {\n    try {\n      cameraOpenCloseLock.acquire();\n      if (null != captureSession) {\n        captureSession.close();\n        captureSession = null;\n      }\n      if (null != cameraDevice) {\n        cameraDevice.close();\n        cameraDevice = null;\n      }\n      if (null != previewReader) {\n        previewReader.close();\n        previewReader = null;\n      }\n    } catch (final InterruptedException e) {\n      throw new RuntimeException(\"Interrupted while trying to lock camera closing.\", e);\n    } finally {\n      cameraOpenCloseLock.release();\n    }\n  }\n\n  /** Starts a background thread and its {@link Handler}. */\n  private void startBackgroundThread() {\n    backgroundThread = new HandlerThread(\"ImageListener\");\n    backgroundThread.start();\n    backgroundHandler = new Handler(backgroundThread.getLooper());\n  }\n\n  /** Stops the background thread and its {@link Handler}. */\n  private void stopBackgroundThread() {\n    backgroundThread.quitSafely();\n    try {\n      backgroundThread.join();\n      backgroundThread = null;\n      backgroundHandler = null;\n    } catch (final InterruptedException e) {\n      LOGGER.e(e, \"Exception!\");\n    }\n  }\n\n  /** Creates a new {@link CameraCaptureSession} for camera preview. */\n  private void createCameraPreviewSession() {\n    try {\n      final SurfaceTexture texture = textureView.getSurfaceTexture();\n      assert texture != null;\n\n      // We configure the size of default buffer to be the size of camera preview we want.\n      texture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());\n\n      // This is the output Surface we need to start preview.\n      final Surface surface = new Surface(texture);\n\n      // We set up a CaptureRequest.Builder with the output Surface.\n      previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);\n      previewRequestBuilder.addTarget(surface);\n\n      LOGGER.i(\"Opening camera preview: \" + previewSize.getWidth() + \"x\" + previewSize.getHeight());\n\n      // Create the reader for the preview frames.\n      previewReader =\n          ImageReader.newInstance(\n              previewSize.getWidth(), previewSize.getHeight(), ImageFormat.YUV_420_888, 2);\n\n      previewReader.setOnImageAvailableListener(imageListener, backgroundHandler);\n      previewRequestBuilder.addTarget(previewReader.getSurface());\n\n      // Here, we create a CameraCaptureSession for camera preview.\n      cameraDevice.createCaptureSession(\n          Arrays.asList(surface, previewReader.getSurface()),\n          new CameraCaptureSession.StateCallback() {\n\n            @Override\n            public void onConfigured(final CameraCaptureSession cameraCaptureSession) {\n              // The camera is already closed\n              if (null == cameraDevice) {\n                return;\n              }\n\n              // When the session is ready, we start displaying the preview.\n              captureSession = cameraCaptureSession;\n              try {\n                // Auto focus should be continuous for camera preview.\n                previewRequestBuilder.set(\n                    CaptureRequest.CONTROL_AF_MODE,\n                    CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);\n                // Flash is automatically enabled when necessary.\n                previewRequestBuilder.set(\n                    CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);\n\n                // Finally, we start displaying the camera preview.\n                previewRequest = previewRequestBuilder.build();\n                captureSession.setRepeatingRequest(\n                    previewRequest, captureCallback, backgroundHandler);\n              } catch (final CameraAccessException e) {\n                LOGGER.e(e, \"Exception!\");\n              }\n            }\n\n            @Override\n            public void onConfigureFailed(final CameraCaptureSession cameraCaptureSession) {\n              showToast(\"Failed\");\n            }\n          },\n          null);\n    } catch (final CameraAccessException e) {\n      LOGGER.e(e, \"Exception!\");\n    }\n  }\n\n  /**\n   * Configures the necessary {@link Matrix} transformation to `mTextureView`. This method should be\n   * called after the camera preview size is determined in setUpCameraOutputs and also the size of\n   * `mTextureView` is fixed.\n   *\n   * @param viewWidth The width of `mTextureView`\n   * @param viewHeight The height of `mTextureView`\n   */\n  private void configureTransform(final int viewWidth, final int viewHeight) {\n    final Activity activity = getActivity();\n    if (null == textureView || null == previewSize || null == activity) {\n      return;\n    }\n    final int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();\n    final Matrix matrix = new Matrix();\n    final RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);\n    final RectF bufferRect = new RectF(0, 0, previewSize.getHeight(), previewSize.getWidth());\n    final float centerX = viewRect.centerX();\n    final float centerY = viewRect.centerY();\n    if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {\n      bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());\n      matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);\n      final float scale =\n          Math.max(\n              (float) viewHeight / previewSize.getHeight(),\n              (float) viewWidth / previewSize.getWidth());\n      matrix.postScale(scale, scale, centerX, centerY);\n      matrix.postRotate(90 * (rotation - 2), centerX, centerY);\n    } else if (Surface.ROTATION_180 == rotation) {\n      matrix.postRotate(180, centerX, centerY);\n    }\n    textureView.setTransform(matrix);\n  }\n\n  /**\n   * Callback for Activities to use to initialize their data once the selected preview size is\n   * known.\n   */\n  public interface ConnectionCallback {\n    void onPreviewSizeChosen(Size size, int cameraRotation);\n  }\n\n  /** Compares two {@code Size}s based on their areas. */\n  static class CompareSizesByArea implements Comparator<Size> {\n    @Override\n    public int compare(final Size lhs, final Size rhs) {\n      // We cast here to ensure the multiplications won't overflow\n      return Long.signum(\n          (long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight());\n    }\n  }\n\n  /** Shows an error message dialog. */\n  public static class ErrorDialog extends DialogFragment {\n    private static final String ARG_MESSAGE = \"message\";\n\n    public static ErrorDialog newInstance(final String message) {\n      final ErrorDialog dialog = new ErrorDialog();\n      final Bundle args = new Bundle();\n      args.putString(ARG_MESSAGE, message);\n      dialog.setArguments(args);\n      return dialog;\n    }\n\n    @Override\n    public Dialog onCreateDialog(final Bundle savedInstanceState) {\n      final Activity activity = getActivity();\n      return new AlertDialog.Builder(activity)\n          .setMessage(getArguments().getString(ARG_MESSAGE))\n          .setPositiveButton(\n              android.R.string.ok,\n              new DialogInterface.OnClickListener() {\n                @Override\n                public void onClick(final DialogInterface dialogInterface, final int i) {\n                  activity.finish();\n                }\n              })\n          .create();\n    }\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/java/org/tensorflow/lite/examples/classification/ClassifierActivity.java",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.classification;\n\nimport android.graphics.Bitmap;\nimport android.graphics.Bitmap.Config;\nimport android.graphics.Typeface;\nimport android.media.ImageReader.OnImageAvailableListener;\nimport android.os.SystemClock;\nimport android.util.Size;\nimport android.util.TypedValue;\nimport java.io.IOException;\nimport java.util.List;\nimport org.tensorflow.lite.examples.classification.env.BorderedText;\nimport org.tensorflow.lite.examples.classification.env.Logger;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier.Device;\n\npublic class ClassifierActivity extends CameraActivity implements OnImageAvailableListener {\n  private static final Logger LOGGER = new Logger();\n  private static final Size DESIRED_PREVIEW_SIZE = new Size(640, 480);\n  private static final float TEXT_SIZE_DIP = 10;\n  private Bitmap rgbFrameBitmap = null;\n  private long lastProcessingTimeMs;\n  private Integer sensorOrientation;\n  private Classifier classifier;\n  private BorderedText borderedText;\n  /** Input image size of the model along x axis. */\n  private int imageSizeX;\n  /** Input image size of the model along y axis. */\n  private int imageSizeY;\n\n  @Override\n  protected int getLayoutId() {\n    return R.layout.tfe_ic_camera_connection_fragment;\n  }\n\n  @Override\n  protected Size getDesiredPreviewFrameSize() {\n    return DESIRED_PREVIEW_SIZE;\n  }\n\n  @Override\n  public void onPreviewSizeChosen(final Size size, final int rotation) {\n    final float textSizePx =\n        TypedValue.applyDimension(\n            TypedValue.COMPLEX_UNIT_DIP, TEXT_SIZE_DIP, getResources().getDisplayMetrics());\n    borderedText = new BorderedText(textSizePx);\n    borderedText.setTypeface(Typeface.MONOSPACE);\n\n    recreateClassifier(getDevice(), getNumThreads());\n    if (classifier == null) {\n      LOGGER.e(\"No classifier on preview!\");\n      return;\n    }\n\n    previewWidth = size.getWidth();\n    previewHeight = size.getHeight();\n\n    sensorOrientation = rotation - getScreenOrientation();\n    LOGGER.i(\"Camera orientation relative to screen canvas: %d\", sensorOrientation);\n\n    LOGGER.i(\"Initializing at size %dx%d\", previewWidth, previewHeight);\n    rgbFrameBitmap = Bitmap.createBitmap(previewWidth, previewHeight, Config.ARGB_8888);\n  }\n\n  @Override\n  protected void processImage() {\n    rgbFrameBitmap.setPixels(getRgbBytes(), 0, previewWidth, 0, 0, previewWidth, previewHeight);\n    final int cropSize = Math.min(previewWidth, previewHeight);\n\n    runInBackground(\n        new Runnable() {\n          @Override\n          public void run() {\n            if (classifier != null) {\n              final long startTime = SystemClock.uptimeMillis();\n              final List<Classifier.Recognition> results =\n                  classifier.recognizeImage(rgbFrameBitmap, sensorOrientation);\n              lastProcessingTimeMs = SystemClock.uptimeMillis() - startTime;\n              LOGGER.v(\"Detect: %s\", results);\n\n              runOnUiThread(\n                  new Runnable() {\n                    @Override\n                    public void run() {\n                      showResultsInBottomSheet(results);\n                      showFrameInfo(previewWidth + \"x\" + previewHeight);\n                      showCropInfo(imageSizeX + \"x\" + imageSizeY);\n                      showCameraResolution(cropSize + \"x\" + cropSize);\n                      showRotationInfo(String.valueOf(sensorOrientation));\n                      showInference(lastProcessingTimeMs + \"ms\");\n                    }\n                  });\n            }\n            readyForNextImage();\n          }\n        });\n  }\n\n  @Override\n  protected void onInferenceConfigurationChanged() {\n    if (rgbFrameBitmap == null) {\n      // Defer creation until we're getting camera frames.\n      return;\n    }\n    final Device device = getDevice();\n    final int numThreads = getNumThreads();\n    runInBackground(() -> recreateClassifier(device, numThreads));\n  }\n\n  private void recreateClassifier(Device device, int numThreads) {\n    if (classifier != null) {\n      LOGGER.d(\"Closing classifier.\");\n      classifier.close();\n      classifier = null;\n    }\n    try {\n      LOGGER.d(\n          \"Creating classifier (device=%s, numThreads=%d)\", device, numThreads);\n      classifier = Classifier.create(this, device, numThreads);\n    } catch (IOException e) {\n      LOGGER.e(e, \"Failed to create classifier.\");\n    }\n\n    // Updates the input image size.\n    imageSizeX = classifier.getImageSizeX();\n    imageSizeY = classifier.getImageSizeY();\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/java/org/tensorflow/lite/examples/classification/LegacyCameraConnectionFragment.java",
    "content": "package org.tensorflow.lite.examples.classification;\n\n/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport android.annotation.SuppressLint;\nimport android.app.Fragment;\nimport android.graphics.SurfaceTexture;\nimport android.hardware.Camera;\nimport android.hardware.Camera.CameraInfo;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.HandlerThread;\nimport android.util.Size;\nimport android.util.SparseIntArray;\nimport android.view.LayoutInflater;\nimport android.view.Surface;\nimport android.view.TextureView;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport java.io.IOException;\nimport java.util.List;\nimport org.tensorflow.lite.examples.classification.customview.AutoFitTextureView;\nimport org.tensorflow.lite.examples.classification.env.ImageUtils;\nimport org.tensorflow.lite.examples.classification.env.Logger;\n\npublic class LegacyCameraConnectionFragment extends Fragment {\n  private static final Logger LOGGER = new Logger();\n  /** Conversion from screen rotation to JPEG orientation. */\n  private static final SparseIntArray ORIENTATIONS = new SparseIntArray();\n\n  static {\n    ORIENTATIONS.append(Surface.ROTATION_0, 90);\n    ORIENTATIONS.append(Surface.ROTATION_90, 0);\n    ORIENTATIONS.append(Surface.ROTATION_180, 270);\n    ORIENTATIONS.append(Surface.ROTATION_270, 180);\n  }\n\n  private Camera camera;\n  private Camera.PreviewCallback imageListener;\n  private Size desiredSize;\n  /** The layout identifier to inflate for this Fragment. */\n  private int layout;\n  /** An {@link AutoFitTextureView} for camera preview. */\n  private AutoFitTextureView textureView;\n  /**\n   * {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a {@link\n   * TextureView}.\n   */\n  private final TextureView.SurfaceTextureListener surfaceTextureListener =\n      new TextureView.SurfaceTextureListener() {\n        @Override\n        public void onSurfaceTextureAvailable(\n            final SurfaceTexture texture, final int width, final int height) {\n\n          int index = getCameraId();\n          camera = Camera.open(index);\n\n          try {\n            Camera.Parameters parameters = camera.getParameters();\n            List<String> focusModes = parameters.getSupportedFocusModes();\n            if (focusModes != null\n                && focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {\n              parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);\n            }\n            List<Camera.Size> cameraSizes = parameters.getSupportedPreviewSizes();\n            Size[] sizes = new Size[cameraSizes.size()];\n            int i = 0;\n            for (Camera.Size size : cameraSizes) {\n              sizes[i++] = new Size(size.width, size.height);\n            }\n            Size previewSize =\n                CameraConnectionFragment.chooseOptimalSize(\n                    sizes, desiredSize.getWidth(), desiredSize.getHeight());\n            parameters.setPreviewSize(previewSize.getWidth(), previewSize.getHeight());\n            camera.setDisplayOrientation(90);\n            camera.setParameters(parameters);\n            camera.setPreviewTexture(texture);\n          } catch (IOException exception) {\n            camera.release();\n          }\n\n          camera.setPreviewCallbackWithBuffer(imageListener);\n          Camera.Size s = camera.getParameters().getPreviewSize();\n          camera.addCallbackBuffer(\n              new byte[ImageUtils.getYUVByteSize(/* width= */ s.height, /* height= */ s.width)]);\n\n          textureView.setAspectRatio(/* width= */ s.height, /* height= */ s.width);\n\n          camera.startPreview();\n        }\n\n        @Override\n        public void onSurfaceTextureSizeChanged(\n            final SurfaceTexture texture, final int width, final int height) {}\n\n        @Override\n        public boolean onSurfaceTextureDestroyed(final SurfaceTexture texture) {\n          return true;\n        }\n\n        @Override\n        public void onSurfaceTextureUpdated(final SurfaceTexture texture) {}\n      };\n  /** An additional thread for running tasks that shouldn't block the UI. */\n  private HandlerThread backgroundThread;\n\n  @SuppressLint(\"ValidFragment\")\n  public LegacyCameraConnectionFragment(\n      final Camera.PreviewCallback imageListener, final int layout, final Size desiredSize) {\n    this.imageListener = imageListener;\n    this.layout = layout;\n    this.desiredSize = desiredSize;\n  }\n\n  public LegacyCameraConnectionFragment() {\n  }\n\n  @Override\n  public View onCreateView(\n      final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {\n    return inflater.inflate(layout, container, false);\n  }\n\n  @Override\n  public void onViewCreated(final View view, final Bundle savedInstanceState) {\n    textureView = (AutoFitTextureView) view.findViewById(R.id.texture);\n  }\n\n  @Override\n  public void onActivityCreated(final Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n  }\n\n  @Override\n  public void onResume() {\n    super.onResume();\n    startBackgroundThread();\n    // When the screen is turned off and turned back on, the SurfaceTexture is already\n    // available, and \"onSurfaceTextureAvailable\" will not be called. In that case, we can open\n    // a camera and start preview from here (otherwise, we wait until the surface is ready in\n    // the SurfaceTextureListener).\n\n    if (textureView.isAvailable()) {\n      if (camera != null) {\n        camera.startPreview();\n      }\n    } else {\n      textureView.setSurfaceTextureListener(surfaceTextureListener);\n    }\n  }\n\n  @Override\n  public void onPause() {\n    stopCamera();\n    stopBackgroundThread();\n    super.onPause();\n  }\n\n  /** Starts a background thread and its {@link Handler}. */\n  private void startBackgroundThread() {\n    backgroundThread = new HandlerThread(\"CameraBackground\");\n    backgroundThread.start();\n  }\n\n  /** Stops the background thread and its {@link Handler}. */\n  private void stopBackgroundThread() {\n    backgroundThread.quitSafely();\n    try {\n      backgroundThread.join();\n      backgroundThread = null;\n    } catch (final InterruptedException e) {\n      LOGGER.e(e, \"Exception!\");\n    }\n  }\n\n  protected void stopCamera() {\n    if (camera != null) {\n      camera.stopPreview();\n      camera.setPreviewCallback(null);\n      camera.release();\n      camera = null;\n    }\n  }\n\n  private int getCameraId() {\n    CameraInfo ci = new CameraInfo();\n    for (int i = 0; i < Camera.getNumberOfCameras(); i++) {\n      Camera.getCameraInfo(i, ci);\n      if (ci.facing == CameraInfo.CAMERA_FACING_BACK) return i;\n    }\n    return -1; // No camera found\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/java/org/tensorflow/lite/examples/classification/customview/AutoFitTextureView.java",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.classification.customview;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.TextureView;\n\n/** A {@link TextureView} that can be adjusted to a specified aspect ratio. */\npublic class AutoFitTextureView extends TextureView {\n  private int ratioWidth = 0;\n  private int ratioHeight = 0;\n\n  public AutoFitTextureView(final Context context) {\n    this(context, null);\n  }\n\n  public AutoFitTextureView(final Context context, final AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public AutoFitTextureView(final Context context, final AttributeSet attrs, final int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  /**\n   * Sets the aspect ratio for this view. The size of the view will be measured based on the ratio\n   * calculated from the parameters. Note that the actual sizes of parameters don't matter, that is,\n   * calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result.\n   *\n   * @param width Relative horizontal size\n   * @param height Relative vertical size\n   */\n  public void setAspectRatio(final int width, final int height) {\n    if (width < 0 || height < 0) {\n      throw new IllegalArgumentException(\"Size cannot be negative.\");\n    }\n    ratioWidth = width;\n    ratioHeight = height;\n    requestLayout();\n  }\n\n  @Override\n  protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {\n    super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n    final int width = MeasureSpec.getSize(widthMeasureSpec);\n    final int height = MeasureSpec.getSize(heightMeasureSpec);\n    if (0 == ratioWidth || 0 == ratioHeight) {\n      setMeasuredDimension(width, height);\n    } else {\n      if (width < height * ratioWidth / ratioHeight) {\n        setMeasuredDimension(width, width * ratioHeight / ratioWidth);\n      } else {\n        setMeasuredDimension(height * ratioWidth / ratioHeight, height);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/java/org/tensorflow/lite/examples/classification/customview/OverlayView.java",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.examples.classification.customview;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport java.util.LinkedList;\nimport java.util.List;\n\n/** A simple View providing a render callback to other classes. */\npublic class OverlayView extends View {\n  private final List<DrawCallback> callbacks = new LinkedList<DrawCallback>();\n\n  public OverlayView(final Context context, final AttributeSet attrs) {\n    super(context, attrs);\n  }\n\n  public void addCallback(final DrawCallback callback) {\n    callbacks.add(callback);\n  }\n\n  @Override\n  public synchronized void draw(final Canvas canvas) {\n    for (final DrawCallback callback : callbacks) {\n      callback.drawCallback(canvas);\n    }\n  }\n\n  /** Interface defining the callback for client classes. */\n  public interface DrawCallback {\n    public void drawCallback(final Canvas canvas);\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/java/org/tensorflow/lite/examples/classification/customview/RecognitionScoreView.java",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.examples.classification.customview;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.util.AttributeSet;\nimport android.util.TypedValue;\nimport android.view.View;\nimport java.util.List;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier.Recognition;\n\npublic class RecognitionScoreView extends View implements ResultsView {\n  private static final float TEXT_SIZE_DIP = 16;\n  private final float textSizePx;\n  private final Paint fgPaint;\n  private final Paint bgPaint;\n  private List<Recognition> results;\n\n  public RecognitionScoreView(final Context context, final AttributeSet set) {\n    super(context, set);\n\n    textSizePx =\n        TypedValue.applyDimension(\n            TypedValue.COMPLEX_UNIT_DIP, TEXT_SIZE_DIP, getResources().getDisplayMetrics());\n    fgPaint = new Paint();\n    fgPaint.setTextSize(textSizePx);\n\n    bgPaint = new Paint();\n    bgPaint.setColor(0xcc4285f4);\n  }\n\n  @Override\n  public void setResults(final List<Recognition> results) {\n    this.results = results;\n    postInvalidate();\n  }\n\n  @Override\n  public void onDraw(final Canvas canvas) {\n    final int x = 10;\n    int y = (int) (fgPaint.getTextSize() * 1.5f);\n\n    canvas.drawPaint(bgPaint);\n\n    if (results != null) {\n      for (final Recognition recog : results) {\n        canvas.drawText(recog.getTitle() + \": \" + recog.getConfidence(), x, y, fgPaint);\n        y += (int) (fgPaint.getTextSize() * 1.5f);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/java/org/tensorflow/lite/examples/classification/customview/ResultsView.java",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.examples.classification.customview;\n\nimport java.util.List;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier.Recognition;\n\npublic interface ResultsView {\n  public void setResults(final List<Recognition> results);\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/java/org/tensorflow/lite/examples/classification/env/BorderedText.java",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.examples.classification.env;\n\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.Paint.Align;\nimport android.graphics.Paint.Style;\nimport android.graphics.Rect;\nimport android.graphics.Typeface;\nimport java.util.Vector;\n\n/** A class that encapsulates the tedious bits of rendering legible, bordered text onto a canvas. */\npublic class BorderedText {\n  private final Paint interiorPaint;\n  private final Paint exteriorPaint;\n\n  private final float textSize;\n\n  /**\n   * Creates a left-aligned bordered text object with a white interior, and a black exterior with\n   * the specified text size.\n   *\n   * @param textSize text size in pixels\n   */\n  public BorderedText(final float textSize) {\n    this(Color.WHITE, Color.BLACK, textSize);\n  }\n\n  /**\n   * Create a bordered text object with the specified interior and exterior colors, text size and\n   * alignment.\n   *\n   * @param interiorColor the interior text color\n   * @param exteriorColor the exterior text color\n   * @param textSize text size in pixels\n   */\n  public BorderedText(final int interiorColor, final int exteriorColor, final float textSize) {\n    interiorPaint = new Paint();\n    interiorPaint.setTextSize(textSize);\n    interiorPaint.setColor(interiorColor);\n    interiorPaint.setStyle(Style.FILL);\n    interiorPaint.setAntiAlias(false);\n    interiorPaint.setAlpha(255);\n\n    exteriorPaint = new Paint();\n    exteriorPaint.setTextSize(textSize);\n    exteriorPaint.setColor(exteriorColor);\n    exteriorPaint.setStyle(Style.FILL_AND_STROKE);\n    exteriorPaint.setStrokeWidth(textSize / 8);\n    exteriorPaint.setAntiAlias(false);\n    exteriorPaint.setAlpha(255);\n\n    this.textSize = textSize;\n  }\n\n  public void setTypeface(Typeface typeface) {\n    interiorPaint.setTypeface(typeface);\n    exteriorPaint.setTypeface(typeface);\n  }\n\n  public void drawText(final Canvas canvas, final float posX, final float posY, final String text) {\n    canvas.drawText(text, posX, posY, exteriorPaint);\n    canvas.drawText(text, posX, posY, interiorPaint);\n  }\n\n  public void drawLines(Canvas canvas, final float posX, final float posY, Vector<String> lines) {\n    int lineNum = 0;\n    for (final String line : lines) {\n      drawText(canvas, posX, posY - getTextSize() * (lines.size() - lineNum - 1), line);\n      ++lineNum;\n    }\n  }\n\n  public void setInteriorColor(final int color) {\n    interiorPaint.setColor(color);\n  }\n\n  public void setExteriorColor(final int color) {\n    exteriorPaint.setColor(color);\n  }\n\n  public float getTextSize() {\n    return textSize;\n  }\n\n  public void setAlpha(final int alpha) {\n    interiorPaint.setAlpha(alpha);\n    exteriorPaint.setAlpha(alpha);\n  }\n\n  public void getTextBounds(\n      final String line, final int index, final int count, final Rect lineBounds) {\n    interiorPaint.getTextBounds(line, index, count, lineBounds);\n  }\n\n  public void setTextAlign(final Align align) {\n    interiorPaint.setTextAlign(align);\n    exteriorPaint.setTextAlign(align);\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/java/org/tensorflow/lite/examples/classification/env/ImageUtils.java",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.examples.classification.env;\n\nimport android.graphics.Bitmap;\nimport android.os.Environment;\nimport java.io.File;\nimport java.io.FileOutputStream;\n\n/** Utility class for manipulating images. */\npublic class ImageUtils {\n  // This value is 2 ^ 18 - 1, and is used to clamp the RGB values before their ranges\n  // are normalized to eight bits.\n  static final int kMaxChannelValue = 262143;\n\n  @SuppressWarnings(\"unused\")\n  private static final Logger LOGGER = new Logger();\n\n  /**\n   * Utility method to compute the allocated size in bytes of a YUV420SP image of the given\n   * dimensions.\n   */\n  public static int getYUVByteSize(final int width, final int height) {\n    // The luminance plane requires 1 byte per pixel.\n    final int ySize = width * height;\n\n    // The UV plane works on 2x2 blocks, so dimensions with odd size must be rounded up.\n    // Each 2x2 block takes 2 bytes to encode, one each for U and V.\n    final int uvSize = ((width + 1) / 2) * ((height + 1) / 2) * 2;\n\n    return ySize + uvSize;\n  }\n\n  /**\n   * Saves a Bitmap object to disk for analysis.\n   *\n   * @param bitmap The bitmap to save.\n   */\n  public static void saveBitmap(final Bitmap bitmap) {\n    saveBitmap(bitmap, \"preview.png\");\n  }\n\n  /**\n   * Saves a Bitmap object to disk for analysis.\n   *\n   * @param bitmap The bitmap to save.\n   * @param filename The location to save the bitmap to.\n   */\n  public static void saveBitmap(final Bitmap bitmap, final String filename) {\n    final String root =\n        Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + \"tensorflow\";\n    LOGGER.i(\"Saving %dx%d bitmap to %s.\", bitmap.getWidth(), bitmap.getHeight(), root);\n    final File myDir = new File(root);\n\n    if (!myDir.mkdirs()) {\n      LOGGER.i(\"Make dir failed\");\n    }\n\n    final String fname = filename;\n    final File file = new File(myDir, fname);\n    if (file.exists()) {\n      file.delete();\n    }\n    try {\n      final FileOutputStream out = new FileOutputStream(file);\n      bitmap.compress(Bitmap.CompressFormat.PNG, 99, out);\n      out.flush();\n      out.close();\n    } catch (final Exception e) {\n      LOGGER.e(e, \"Exception!\");\n    }\n  }\n\n  public static void convertYUV420SPToARGB8888(byte[] input, int width, int height, int[] output) {\n    final int frameSize = width * height;\n    for (int j = 0, yp = 0; j < height; j++) {\n      int uvp = frameSize + (j >> 1) * width;\n      int u = 0;\n      int v = 0;\n\n      for (int i = 0; i < width; i++, yp++) {\n        int y = 0xff & input[yp];\n        if ((i & 1) == 0) {\n          v = 0xff & input[uvp++];\n          u = 0xff & input[uvp++];\n        }\n\n        output[yp] = YUV2RGB(y, u, v);\n      }\n    }\n  }\n\n  private static int YUV2RGB(int y, int u, int v) {\n    // Adjust and check YUV values\n    y = (y - 16) < 0 ? 0 : (y - 16);\n    u -= 128;\n    v -= 128;\n\n    // This is the floating point equivalent. We do the conversion in integer\n    // because some Android devices do not have floating point in hardware.\n    // nR = (int)(1.164 * nY + 2.018 * nU);\n    // nG = (int)(1.164 * nY - 0.813 * nV - 0.391 * nU);\n    // nB = (int)(1.164 * nY + 1.596 * nV);\n    int y1192 = 1192 * y;\n    int r = (y1192 + 1634 * v);\n    int g = (y1192 - 833 * v - 400 * u);\n    int b = (y1192 + 2066 * u);\n\n    // Clipping RGB values to be inside boundaries [ 0 , kMaxChannelValue ]\n    r = r > kMaxChannelValue ? kMaxChannelValue : (r < 0 ? 0 : r);\n    g = g > kMaxChannelValue ? kMaxChannelValue : (g < 0 ? 0 : g);\n    b = b > kMaxChannelValue ? kMaxChannelValue : (b < 0 ? 0 : b);\n\n    return 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);\n  }\n\n  public static void convertYUV420ToARGB8888(\n      byte[] yData,\n      byte[] uData,\n      byte[] vData,\n      int width,\n      int height,\n      int yRowStride,\n      int uvRowStride,\n      int uvPixelStride,\n      int[] out) {\n    int yp = 0;\n    for (int j = 0; j < height; j++) {\n      int pY = yRowStride * j;\n      int pUV = uvRowStride * (j >> 1);\n\n      for (int i = 0; i < width; i++) {\n        int uv_offset = pUV + (i >> 1) * uvPixelStride;\n\n        out[yp++] = YUV2RGB(0xff & yData[pY + i], 0xff & uData[uv_offset], 0xff & vData[uv_offset]);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/java/org/tensorflow/lite/examples/classification/env/Logger.java",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.examples.classification.env;\n\nimport android.util.Log;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/** Wrapper for the platform log function, allows convenient message prefixing and log disabling. */\npublic final class Logger {\n  private static final String DEFAULT_TAG = \"tensorflow\";\n  private static final int DEFAULT_MIN_LOG_LEVEL = Log.DEBUG;\n\n  // Classes to be ignored when examining the stack trace\n  private static final Set<String> IGNORED_CLASS_NAMES;\n\n  static {\n    IGNORED_CLASS_NAMES = new HashSet<String>(3);\n    IGNORED_CLASS_NAMES.add(\"dalvik.system.VMStack\");\n    IGNORED_CLASS_NAMES.add(\"java.lang.Thread\");\n    IGNORED_CLASS_NAMES.add(Logger.class.getCanonicalName());\n  }\n\n  private final String tag;\n  private final String messagePrefix;\n  private int minLogLevel = DEFAULT_MIN_LOG_LEVEL;\n\n  /**\n   * Creates a Logger using the class name as the message prefix.\n   *\n   * @param clazz the simple name of this class is used as the message prefix.\n   */\n  public Logger(final Class<?> clazz) {\n    this(clazz.getSimpleName());\n  }\n\n  /**\n   * Creates a Logger using the specified message prefix.\n   *\n   * @param messagePrefix is prepended to the text of every message.\n   */\n  public Logger(final String messagePrefix) {\n    this(DEFAULT_TAG, messagePrefix);\n  }\n\n  /**\n   * Creates a Logger with a custom tag and a custom message prefix. If the message prefix is set to\n   *\n   * <pre>null</pre>\n   *\n   * , the caller's class name is used as the prefix.\n   *\n   * @param tag identifies the source of a log message.\n   * @param messagePrefix prepended to every message if non-null. If null, the name of the caller is\n   *     being used\n   */\n  public Logger(final String tag, final String messagePrefix) {\n    this.tag = tag;\n    final String prefix = messagePrefix == null ? getCallerSimpleName() : messagePrefix;\n    this.messagePrefix = (prefix.length() > 0) ? prefix + \": \" : prefix;\n  }\n\n  /** Creates a Logger using the caller's class name as the message prefix. */\n  public Logger() {\n    this(DEFAULT_TAG, null);\n  }\n\n  /** Creates a Logger using the caller's class name as the message prefix. */\n  public Logger(final int minLogLevel) {\n    this(DEFAULT_TAG, null);\n    this.minLogLevel = minLogLevel;\n  }\n\n  /**\n   * Return caller's simple name.\n   *\n   * <p>Android getStackTrace() returns an array that looks like this: stackTrace[0]:\n   * dalvik.system.VMStack stackTrace[1]: java.lang.Thread stackTrace[2]:\n   * com.google.android.apps.unveil.env.UnveilLogger stackTrace[3]:\n   * com.google.android.apps.unveil.BaseApplication\n   *\n   * <p>This function returns the simple version of the first non-filtered name.\n   *\n   * @return caller's simple name\n   */\n  private static String getCallerSimpleName() {\n    // Get the current callstack so we can pull the class of the caller off of it.\n    final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();\n\n    for (final StackTraceElement elem : stackTrace) {\n      final String className = elem.getClassName();\n      if (!IGNORED_CLASS_NAMES.contains(className)) {\n        // We're only interested in the simple name of the class, not the complete package.\n        final String[] classParts = className.split(\"\\\\.\");\n        return classParts[classParts.length - 1];\n      }\n    }\n\n    return Logger.class.getSimpleName();\n  }\n\n  public void setMinLogLevel(final int minLogLevel) {\n    this.minLogLevel = minLogLevel;\n  }\n\n  public boolean isLoggable(final int logLevel) {\n    return logLevel >= minLogLevel || Log.isLoggable(tag, logLevel);\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  private String toMessage(final String format, final Object... args) {\n    return messagePrefix + (args.length > 0 ? String.format(format, args) : format);\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  public void v(final String format, final Object... args) {\n    if (isLoggable(Log.VERBOSE)) {\n      Log.v(tag, toMessage(format, args));\n    }\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  public void v(final Throwable t, final String format, final Object... args) {\n    if (isLoggable(Log.VERBOSE)) {\n      Log.v(tag, toMessage(format, args), t);\n    }\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  public void d(final String format, final Object... args) {\n    if (isLoggable(Log.DEBUG)) {\n      Log.d(tag, toMessage(format, args));\n    }\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  public void d(final Throwable t, final String format, final Object... args) {\n    if (isLoggable(Log.DEBUG)) {\n      Log.d(tag, toMessage(format, args), t);\n    }\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  public void i(final String format, final Object... args) {\n    if (isLoggable(Log.INFO)) {\n      Log.i(tag, toMessage(format, args));\n    }\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  public void i(final Throwable t, final String format, final Object... args) {\n    if (isLoggable(Log.INFO)) {\n      Log.i(tag, toMessage(format, args), t);\n    }\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  public void w(final String format, final Object... args) {\n    if (isLoggable(Log.WARN)) {\n      Log.w(tag, toMessage(format, args));\n    }\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  public void w(final Throwable t, final String format, final Object... args) {\n    if (isLoggable(Log.WARN)) {\n      Log.w(tag, toMessage(format, args), t);\n    }\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  public void e(final String format, final Object... args) {\n    if (isLoggable(Log.ERROR)) {\n      Log.e(tag, toMessage(format, args));\n    }\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  public void e(final Throwable t, final String format, final Object... args) {\n    if (isLoggable(Log.ERROR)) {\n      Log.e(tag, toMessage(format, args), t);\n    }\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/java/org/tensorflow/lite/examples/classification/tflite/Classifier.java",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.examples.classification.tflite;\n\nimport android.app.Activity;\nimport android.graphics.Bitmap;\nimport android.graphics.RectF;\nimport android.os.SystemClock;\nimport android.os.Trace;\nimport java.io.IOException;\nimport java.nio.MappedByteBuffer;\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.PriorityQueue;\nimport org.tensorflow.lite.DataType;\nimport org.tensorflow.lite.Interpreter;\nimport org.tensorflow.lite.examples.classification.env.Logger;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier.Device;\nimport org.tensorflow.lite.gpu.GpuDelegate;\nimport org.tensorflow.lite.support.common.FileUtil;\nimport org.tensorflow.lite.support.common.TensorOperator;\nimport org.tensorflow.lite.support.common.TensorProcessor;\nimport org.tensorflow.lite.support.image.ImageProcessor;\nimport org.tensorflow.lite.support.image.TensorImage;\nimport org.tensorflow.lite.support.image.ops.ResizeOp;\nimport org.tensorflow.lite.support.image.ops.ResizeOp.ResizeMethod;\nimport org.tensorflow.lite.support.image.ops.ResizeWithCropOrPadOp;\nimport org.tensorflow.lite.support.image.ops.Rot90Op;\nimport org.tensorflow.lite.support.label.TensorLabel;\nimport org.tensorflow.lite.support.tensorbuffer.TensorBuffer;\n\n/** A classifier specialized to label images using TensorFlow Lite. */\npublic abstract class Classifier {\n  private static final Logger LOGGER = new Logger();\n\n  /** The runtime device type used for executing classification. */\n  public enum Device {\n    CPU,\n    GPU\n  }\n\n  /** Number of results to show in the UI. */\n  private static final int MAX_RESULTS = 3;\n\n  /** The loaded TensorFlow Lite model. */\n  private MappedByteBuffer tfliteModel;\n\n  /** Image size along the x axis. */\n  private final int imageSizeX;\n\n  /** Image size along the y axis. */\n  private final int imageSizeY;\n\n  /** Optional GPU delegate for acceleration. */\n  // TODO: Declare a GPU delegate\n  private GpuDelegate gpuDelegate = null;\n\n  /** An instance of the driver class to run model inference with Tensorflow Lite. */\n  // TODO: Declare a TFLite interpreter\n  protected Interpreter tflite;\n\n  /** Options for configuring the Interpreter. */\n  private final Interpreter.Options tfliteOptions = new Interpreter.Options();\n\n  /** Labels corresponding to the output of the vision model. */\n  private List<String> labels;\n\n  /** Input image TensorBuffer. */\n  private TensorImage inputImageBuffer;\n\n  /** Output probability TensorBuffer. */\n  private final TensorBuffer outputProbabilityBuffer;\n\n  /** Processer to apply post processing of the output probability. */\n  private final TensorProcessor probabilityProcessor;\n\n  /**\n   * Creates a classifier with the provided configuration.\n   *\n   * @param activity The current Activity.\n   * @param device The device to use for classification.\n   * @param numThreads The number of threads to use for classification.\n   * @return A classifier with the desired configuration.\n   */\n  public static Classifier create(Activity activity, Device device, int numThreads)\n      throws IOException {\n\n    return new ClassifierFloatMobileNet(activity, device, numThreads);\n  }\n\n  /** An immutable result returned by a Classifier describing what was recognized. */\n  public static class Recognition {\n    /**\n     * A unique identifier for what has been recognized. Specific to the class, not the instance of\n     * the object.\n     */\n    private final String id;\n\n    /** Display name for the recognition. */\n    private final String title;\n\n    /**\n     * A sortable score for how good the recognition is relative to others. Higher should be better.\n     */\n    private final Float confidence;\n\n    /** Optional location within the source image for the location of the recognized object. */\n    private RectF location;\n\n    public Recognition(\n        final String id, final String title, final Float confidence, final RectF location) {\n      this.id = id;\n      this.title = title;\n      this.confidence = confidence;\n      this.location = location;\n    }\n\n    public String getId() {\n      return id;\n    }\n\n    public String getTitle() {\n      return title;\n    }\n\n    public Float getConfidence() {\n      return confidence;\n    }\n\n    public RectF getLocation() {\n      return new RectF(location);\n    }\n\n    public void setLocation(RectF location) {\n      this.location = location;\n    }\n\n    @Override\n    public String toString() {\n      String resultString = \"\";\n      if (id != null) {\n        resultString += \"[\" + id + \"] \";\n      }\n\n      if (title != null) {\n        resultString += title + \" \";\n      }\n\n      if (confidence != null) {\n        resultString += String.format(\"(%.1f%%) \", confidence * 100.0f);\n      }\n\n      if (location != null) {\n        resultString += location + \" \";\n      }\n\n      return resultString.trim();\n    }\n  }\n\n  /** Initializes a {@code Classifier}. */\n  protected Classifier(Activity activity, Device device, int numThreads) throws IOException {\n    tfliteModel = FileUtil.loadMappedFile(activity, getModelPath());\n    switch (device) {\n      case GPU:\n        // TODO: Create a GPU delegate instance and add it to the interpreter options\n        gpuDelegate = new GpuDelegate();\n        tfliteOptions.addDelegate(gpuDelegate);\n        break;\n      case CPU:\n        break;\n    }\n    tfliteOptions.setNumThreads(numThreads);\n    // TODO: Create a TFLite interpreter instance\n    tflite = new Interpreter(tfliteModel, tfliteOptions);\n\n    // Loads labels out from the label file.\n    labels = FileUtil.loadLabels(activity, getLabelPath());\n\n    // Reads type and shape of input and output tensors, respectively.\n    int imageTensorIndex = 0;\n    int[] imageShape = tflite.getInputTensor(imageTensorIndex).shape(); // {1, height, width, 3}\n    imageSizeY = imageShape[1];\n    imageSizeX = imageShape[2];\n    DataType imageDataType = tflite.getInputTensor(imageTensorIndex).dataType();\n    int probabilityTensorIndex = 0;\n    int[] probabilityShape =\n        tflite.getOutputTensor(probabilityTensorIndex).shape(); // {1, NUM_CLASSES}\n    DataType probabilityDataType = tflite.getOutputTensor(probabilityTensorIndex).dataType();\n\n    // Creates the input tensor.\n    inputImageBuffer = new TensorImage(imageDataType);\n\n    // Creates the output tensor and its processor.\n    outputProbabilityBuffer = TensorBuffer.createFixedSize(probabilityShape, probabilityDataType);\n\n    // Creates the post processor for the output probability.\n    probabilityProcessor = new TensorProcessor.Builder().add(getPostprocessNormalizeOp()).build();\n\n    LOGGER.d(\"Created a Tensorflow Lite Image Classifier.\");\n  }\n\n  /** Runs inference and returns the classification results. */\n  public List<Recognition> recognizeImage(final Bitmap bitmap, int sensorOrientation) {\n    // Logs this method so that it can be analyzed with systrace.\n    Trace.beginSection(\"recognizeImage\");\n\n    Trace.beginSection(\"loadImage\");\n    long startTimeForLoadImage = SystemClock.uptimeMillis();\n    inputImageBuffer = loadImage(bitmap, sensorOrientation);\n    long endTimeForLoadImage = SystemClock.uptimeMillis();\n    Trace.endSection();\n    LOGGER.v(\"Timecost to load the image: \" + (endTimeForLoadImage - startTimeForLoadImage));\n\n    // Runs the inference call.\n    Trace.beginSection(\"runInference\");\n    long startTimeForReference = SystemClock.uptimeMillis();\n    // TODO: Run TFLite inference\n    tflite.run(inputImageBuffer.getBuffer(), outputProbabilityBuffer.getBuffer().rewind());\n    long endTimeForReference = SystemClock.uptimeMillis();\n    Trace.endSection();\n    LOGGER.v(\"Timecost to run model inference: \" + (endTimeForReference - startTimeForReference));\n\n    // Gets the map of label and probability.\n    // TODO: Use TensorLabel from TFLite Support Library to associate the probabilities\n    //       with category labels\n    Map<String, Float> labeledProbability =\n        new TensorLabel(labels, probabilityProcessor.process(outputProbabilityBuffer))\n            .getMapWithFloatValue();\n    Trace.endSection();\n\n    // Gets top-k results.\n    return getTopKProbability(labeledProbability);\n  }\n\n  /** Closes the interpreter and model to release resources. */\n  public void close() {\n    if (tflite != null) {\n      // TODO: Close the interpreter\n      tflite.close();\n      tflite = null;\n    }\n    // TODO: Close the GPU delegate\n    if (gpuDelegate != null) {\n      gpuDelegate.close();\n      gpuDelegate = null;\n    }\n\n    tfliteModel = null;\n  }\n\n  /** Get the image size along the x axis. */\n  public int getImageSizeX() {\n    return imageSizeX;\n  }\n\n  /** Get the image size along the y axis. */\n  public int getImageSizeY() {\n    return imageSizeY;\n  }\n\n  /** Loads input image, and applies preprocessing. */\n  private TensorImage loadImage(final Bitmap bitmap, int sensorOrientation) {\n    // Loads bitmap into a TensorImage.\n    inputImageBuffer.load(bitmap);\n\n    // Creates processor for the TensorImage.\n    int cropSize = Math.min(bitmap.getWidth(), bitmap.getHeight());\n    int numRoration = sensorOrientation / 90;\n    // TODO(b/143564309): Fuse ops inside ImageProcessor.\n    // TODO: Define an ImageProcessor from TFLite Support Library to do preprocessing\n    ImageProcessor imageProcessor =\n        new ImageProcessor.Builder()\n            .add(new ResizeWithCropOrPadOp(cropSize, cropSize))\n            .add(new ResizeOp(imageSizeX, imageSizeY, ResizeMethod.NEAREST_NEIGHBOR))\n            .add(new Rot90Op(numRoration))\n            .add(getPreprocessNormalizeOp())\n            .build();\n    return imageProcessor.process(inputImageBuffer);\n  }\n\n  /** Gets the top-k results. */\n  private static List<Recognition> getTopKProbability(Map<String, Float> labelProb) {\n    // Find the best classifications.\n    PriorityQueue<Recognition> pq =\n        new PriorityQueue<>(\n            MAX_RESULTS,\n            new Comparator<Recognition>() {\n              @Override\n              public int compare(Recognition lhs, Recognition rhs) {\n                // Intentionally reversed to put high confidence at the head of the queue.\n                return Float.compare(rhs.getConfidence(), lhs.getConfidence());\n              }\n            });\n\n    for (Map.Entry<String, Float> entry : labelProb.entrySet()) {\n      pq.add(new Recognition(\"\" + entry.getKey(), entry.getKey(), entry.getValue(), null));\n    }\n\n    final ArrayList<Recognition> recognitions = new ArrayList<>();\n    int recognitionsSize = Math.min(pq.size(), MAX_RESULTS);\n    for (int i = 0; i < recognitionsSize; ++i) {\n      recognitions.add(pq.poll());\n    }\n    return recognitions;\n  }\n\n  /** Gets the name of the model file stored in Assets. */\n  protected abstract String getModelPath();\n\n  /** Gets the name of the label file stored in Assets. */\n  protected abstract String getLabelPath();\n\n  /** Gets the TensorOperator to nomalize the input image in preprocessing. */\n  protected abstract TensorOperator getPreprocessNormalizeOp();\n\n  /**\n   * Gets the TensorOperator to dequantize the output probability in post processing.\n   *\n   * <p>For quantized model, we need de-quantize the prediction with NormalizeOp (as they are all\n   * essentially linear transformation). For float model, de-quantize is not required. But to\n   * uniform the API, de-quantize is added to float model too. Mean and std are set to 0.0f and\n   * 1.0f, respectively.\n   */\n  protected abstract TensorOperator getPostprocessNormalizeOp();\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/java/org/tensorflow/lite/examples/classification/tflite/ClassifierFloatMobileNet.java",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.examples.classification.tflite;\n\nimport android.app.Activity;\nimport java.io.IOException;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier.Device;\nimport org.tensorflow.lite.support.common.TensorOperator;\nimport org.tensorflow.lite.support.common.ops.NormalizeOp;\n\n/** This TensorFlowLite classifier works with the float MobileNet model. */\npublic class ClassifierFloatMobileNet extends Classifier {\n\n  /** Float MobileNet requires additional normalization of the used input. */\n  private static final float IMAGE_MEAN = 0f;\n\n  private static final float IMAGE_STD = 255f;\n\n  /**\n   * Float model does not need dequantization in the post-processing. Setting mean and std as 0.0f\n   * and 1.0f, repectively, to bypass the normalization.\n   */\n  private static final float PROBABILITY_MEAN = 0.0f;\n\n  private static final float PROBABILITY_STD = 1.0f;\n\n  /**\n   * Initializes a {@code ClassifierFloatMobileNet}.\n   *\n   * @param activity\n   */\n  public ClassifierFloatMobileNet(Activity activity, Device device, int numThreads)\n      throws IOException {\n    super(activity, device, numThreads);\n  }\n\n  // TODO: Specify model.tflite as the model file and labels.txt as the label file\n  @Override\n  protected String getModelPath() {\n    return \"model.tflite\";\n  }\n\n  @Override\n  protected String getLabelPath() {\n    return \"labels.txt\";\n  }\n\n  @Override\n  protected TensorOperator getPreprocessNormalizeOp() {\n    return new NormalizeOp(IMAGE_MEAN, IMAGE_STD);\n  }\n\n  @Override\n  protected TensorOperator getPostprocessNormalizeOp() {\n    return new NormalizeOp(PROBABILITY_MEAN, PROBABILITY_STD);\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/res/drawable/bottom_sheet_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <corners\n        android:topLeftRadius=\"@dimen/tfe_bottom_sheet_corner_radius\"\n        android:topRightRadius=\"@dimen/tfe_bottom_sheet_corner_radius\" />\n    <padding android:top=\"@dimen/tfe_bottom_sheet_top_padding\" />\n    <solid android:color=\"@android:color/white\" />\n</shape>"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/res/drawable/ic_baseline_add.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/res/drawable/ic_baseline_remove.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13H5v-2h14v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillColor=\"#26A69A\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n</vector>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/res/drawable/rectangle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/listview_background_shape\">\n    <stroke\n        android:width=\"1dp\"\n        android:color=\"@android:color/darker_gray\" />\n    <padding\n        android:bottom=\"2dp\"\n        android:left=\"2dp\"\n        android:right=\"2dp\"\n        android:top=\"2dp\" />\n    <solid android:color=\"#ffffffff\" />\n</shape>"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n  <path\n      android:fillType=\"evenOdd\"\n      android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\">\n    <aapt:attr name=\"android:fillColor\">\n      <gradient\n          android:endX=\"78.5885\"\n          android:endY=\"90.9159\"\n          android:startX=\"48.7653\"\n          android:startY=\"61.0927\"\n          android:type=\"linear\">\n        <item\n            android:color=\"#44000000\"\n            android:offset=\"0.0\"/>\n        <item\n            android:color=\"#00000000\"\n            android:offset=\"1.0\"/>\n      </gradient>\n    </aapt:attr>\n  </path>\n  <path\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"nonZero\"\n      android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\"/>\n</vector>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/res/layout/tfe_ic_activity_camera.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"#00000000\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"#00000000\"\n        android:orientation=\"vertical\">\n\n\n        <FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n            xmlns:tools=\"http://schemas.android.com/tools\"\n            android:id=\"@+id/container\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:background=\"@android:color/black\"\n            tools:context=\"org.tensorflow.lite.examples.classification.CameraActivity\" />\n\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:layout_alignParentTop=\"true\"\n            android:background=\"@color/tfe_semi_transparent\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/tfl2_logo\" />\n        </androidx.appcompat.widget.Toolbar>\n\n    </RelativeLayout>\n\n    <include\n        android:id=\"@+id/bottom_sheet_layout\"\n        layout=\"@layout/tfe_ic_layout_bottom_sheet\" />\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/res/layout/tfe_ic_camera_connection_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <org.tensorflow.lite.examples.classification.customview.AutoFitTextureView\n        android:id=\"@+id/texture\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentTop=\"true\" />\n\n\n</RelativeLayout>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/res/layout/tfe_ic_layout_bottom_sheet.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/bottom_sheet_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_gravity=\"center_horizontal\"\n    android:background=\"@drawable/bottom_sheet_bg\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:padding=\"8dp\"\n    app:behavior_hideable=\"true\"\n    app:layout_behavior=\"com.google.android.material.bottomsheet.BottomSheetBehavior\">\n\n    <LinearLayout\n        android:id=\"@+id/gesture_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:paddingTop=\"10dp\"\n        android:paddingBottom=\"10dp\">\n\n        <ImageView\n            android:id=\"@+id/bottom_sheet_arrow\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:src=\"@drawable/icn_chevron_up\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/detected_item\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"@android:color/black\"\n                android:textSize=\"16sp\" />\n\n            <TextView\n                android:id=\"@+id/detected_item_value\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:textColor=\"@android:color/black\"\n                android:textSize=\"16sp\" />\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/detected_item1\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"@android:color/darker_gray\"\n                android:textSize=\"12sp\" />\n\n            <TextView\n                android:id=\"@+id/detected_item1_value\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:textColor=\"@android:color/darker_gray\"\n                android:textSize=\"12sp\" />\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/detected_item2\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"@android:color/darker_gray\"\n                android:textSize=\"12sp\" />\n\n            <TextView\n                android:id=\"@+id/detected_item2_value\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:textColor=\"@android:color/darker_gray\"\n                android:textSize=\"12sp\" />\n\n        </LinearLayout>\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"1px\"\n            android:layout_marginTop=\"8dp\"\n            android:background=\"@android:color/darker_gray\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:id=\"@+id/frame\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:text=\"Frame\"\n            android:textColor=\"@android:color/black\" />\n\n        <TextView\n            android:id=\"@+id/frame_info\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:gravity=\"right\"\n            android:text=\"640*480\"\n            android:textColor=\"@android:color/black\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:id=\"@+id/crop\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:text=\"Crop\"\n            android:textColor=\"@android:color/black\" />\n\n        <TextView\n            android:id=\"@+id/crop_info\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:gravity=\"right\"\n            android:text=\"640*480\"\n            android:textColor=\"@android:color/black\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:id=\"@+id/view\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:text=\"View\"\n            android:textColor=\"@android:color/black\" />\n\n        <TextView\n            android:id=\"@+id/view_info\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:gravity=\"right\"\n            android:text=\"640*480\"\n            android:textColor=\"@android:color/black\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:id=\"@+id/rotation\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:text=\"Rotation\"\n            android:textColor=\"@android:color/black\" />\n\n        <TextView\n            android:id=\"@+id/rotation_info\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:gravity=\"right\"\n            android:text=\"640*480\"\n            android:textColor=\"@android:color/black\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:id=\"@+id/inference\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:text=\"Inference Time\"\n            android:textColor=\"@android:color/black\" />\n\n        <TextView\n            android:id=\"@+id/inference_info\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:gravity=\"right\"\n            android:text=\"640*480\"\n            android:textColor=\"@android:color/black\" />\n    </LinearLayout>\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1px\"\n        android:layout_marginTop=\"10dp\"\n        android:background=\"@android:color/darker_gray\" />\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"10dp\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:text=\"Threads\"\n            android:textColor=\"@android:color/black\" />\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentRight=\"true\"\n            android:background=\"@drawable/rectangle\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\"\n            android:padding=\"4dp\">\n\n            <ImageView\n                android:id=\"@+id/minus\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/ic_baseline_remove\" />\n\n            <TextView\n                android:id=\"@+id/threads\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"10dp\"\n                android:layout_marginRight=\"10dp\"\n                android:text=\"1\"\n                android:textColor=\"@android:color/black\"\n                android:textSize=\"14sp\" />\n            <ImageView\n                android:id=\"@+id/plus\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/ic_baseline_add\" />\n\n        </LinearLayout>\n    </RelativeLayout>\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1px\"\n        android:layout_marginTop=\"10dp\"\n        android:background=\"@android:color/darker_gray\" />\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:text=\"@string/tfe_ic_device\"\n            android:textColor=\"@android:color/black\" />\n\n        <Spinner\n            android:id=\"@+id/device_spinner\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:layout_alignParentRight=\"true\"\n            android:entries=\"@array/tfe_ic_devices\"\n            android:prompt=\"@string/tfe_ic_device\" />\n    </RelativeLayout>\n</LinearLayout>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"tfe_color_primary\">#ffa800</color>\n    <color name=\"tfe_color_primary_dark\">#ff6f00</color>\n    <color name=\"tfe_color_accent\">#425066</color>\n\n    <color name=\"tfe_semi_transparent\">#66000000</color>\n</resources>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <dimen name=\"tfe_bottom_sheet_corner_radius\">15dp</dimen>\n    <dimen name=\"tfe_bottom_sheet_top_padding\">8dp</dimen>\n</resources>"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"tfe_ic_app_name\" translation_description=\"Image Classification demo app [CHAR_LIMIT=40]\">TFL Classify</string>\n    <string name=\"tfe_ic_camera_error\" translation_description=\"Error regarding camera support[CHAR_LIMIT=40]\">This device doesn\\'t support Camera2 API.</string>\n    <string name=\"tfe_ic_gpu_quant_error\" translation_description=\"Error regarding GPU support for Quant models[CHAR_LIMIT=60]\">GPU does not yet supported quantized models.</string>\n    <string name=\"tfe_ic_model\" translatable=\"false\">Model:</string>\n    <string-array name=\"tfe_ic_models\" translatable=\"false\">\n        <item>Quantized_EfficientNet</item>\n        <item>Float_EfficientNet</item>\n        <item>Quantized_MobileNet</item>\n        <item>Float_MobileNet</item>\n    </string-array>\n\n    <string name=\"tfe_ic_device\" translatable=\"false\">Device:</string>\n    <string-array name=\"tfe_ic_devices\" translatable=\"false\">\n        <item>CPU</item>\n        <!-- TODO: Add GPU  -->\n        <item>GPU</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme.ImageClassification\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/tfe_color_primary</item>\n        <item name=\"colorPrimaryDark\">@color/tfe_color_primary_dark</item>\n        <item name=\"colorAccent\">@color/tfe_color_accent</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:4.0.0'\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n        maven {\n            name 'ossrh-snapshot'\n            url 'https://oss.sonatype.org/content/repositories/snapshots'\n        }\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\nandroid.useAndroidX=true\nandroid.enableJetifier=true\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/finish/settings.gradle",
    "content": "rootProject.name = 'TFLite Flower Classification Codelab (finish) App'\ninclude ':app'\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/.gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n\n/.gradle/\n/.idea/"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/.gitignore",
    "content": "/build\n\n/build/"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nproject.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'\n\nassert file(project.ext.ASSET_DIR + \"/model.tflite\").exists()\nassert file(project.ext.ASSET_DIR + \"/labels.txt\").exists()\n\nandroid {\n    compileSdkVersion 28\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.classification\"\n        minSdkVersion 21\n        targetSdkVersion 28\n        versionCode 1\n        versionName \"1.0\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n    // TODO: Add an option to avoid compressing TF Lite model file\n\n    compileOptions {\n        sourceCompatibility = '1.8'\n        targetCompatibility = '1.8'\n    }\n    lintOptions {\n        abortOnError false\n    }\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation 'androidx.appcompat:appcompat:1.0.0'\n    implementation 'androidx.coordinatorlayout:coordinatorlayout:1.0.0'\n    implementation 'com.google.android.material:material:1.0.0'\n\n    // Build off of nightly TensorFlow Lite\n    // TODO: Add TFLite dependencies\n\n    // Use local TensorFlow library\n    // implementation 'org.tensorflow:tensorflow-lite-local:0.0.0'\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/androidTest/java/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.tensorflow.lite.examples.classification\">\n  <uses-sdk />\n</manifest>"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/androidTest/java/org/tensorflow/lite/examples/classification/ClassifierTest.java",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.classification;\n\nimport static com.google.common.truth.Truth.assertThat;\n\nimport android.content.res.AssetManager;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.util.Log;\nimport androidx.test.ext.junit.runners.AndroidJUnit4;\nimport androidx.test.platform.app.InstrumentationRegistry;\nimport androidx.test.rule.ActivityTestRule;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Scanner;\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier.Device;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier.Model;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier.Recognition;\n\n/** Golden test for Image Classification Reference app. */\n@RunWith(AndroidJUnit4.class)\npublic class ClassifierTest {\n\n  @Rule\n  public ActivityTestRule<ClassifierActivity> rule =\n      new ActivityTestRule<>(ClassifierActivity.class);\n\n  private static final String[] INPUTS = {\"fox.jpg\"};\n  private static final String[] GOLDEN_OUTPUTS = {\"fox-mobilenet_v1_1.0_224.txt\"};\n\n  @Test\n  public void classificationResultsShouldNotChange() throws IOException {\n    ClassifierActivity activity = rule.getActivity();\n    Classifier classifier = Classifier.create(activity, Model.FLOAT_MOBILENET, Device.CPU, 1);\n    for (int i = 0; i < INPUTS.length; i++) {\n      String imageFileName = INPUTS[i];\n      String goldenOutputFileName = GOLDEN_OUTPUTS[i];\n      Bitmap input = loadImage(imageFileName);\n      List<Recognition> goldenOutput = loadRecognitions(goldenOutputFileName);\n\n      List<Recognition> result = classifier.recognizeImage(input, 0);\n      Iterator<Recognition> goldenOutputIterator = goldenOutput.iterator();\n\n      for (Recognition actual : result) {\n        Assert.assertTrue(goldenOutputIterator.hasNext());\n        Recognition expected = goldenOutputIterator.next();\n        assertThat(actual.getTitle()).isEqualTo(expected.getTitle());\n        assertThat(actual.getConfidence()).isWithin(0.01f).of(expected.getConfidence());\n      }\n    }\n  }\n\n  private static Bitmap loadImage(String fileName) {\n    AssetManager assetManager =\n        InstrumentationRegistry.getInstrumentation().getContext().getAssets();\n    InputStream inputStream = null;\n    try {\n      inputStream = assetManager.open(fileName);\n    } catch (IOException e) {\n      Log.e(\"Test\", \"Cannot load image from assets\");\n    }\n    return BitmapFactory.decodeStream(inputStream);\n  }\n\n  private static List<Recognition> loadRecognitions(String fileName) {\n    AssetManager assetManager =\n        InstrumentationRegistry.getInstrumentation().getContext().getAssets();\n    InputStream inputStream = null;\n    try {\n      inputStream = assetManager.open(fileName);\n    } catch (IOException e) {\n      Log.e(\"Test\", \"Cannot load probability results from assets\");\n    }\n    Scanner scanner = new Scanner(inputStream);\n    List<Recognition> result = new ArrayList<>();\n    while (scanner.hasNext()) {\n      String category = scanner.next();\n      category = category.replace('_', ' ');\n      if (!scanner.hasNextFloat()) {\n        break;\n      }\n      float probability = scanner.nextFloat();\n      Recognition recognition = new Recognition(null, category, probability, null);\n      result.add(recognition);\n    }\n    return result;\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.tensorflow.lite.examples.classification\">\n\n    <uses-sdk />\n\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n\n    <uses-feature android:name=\"android.hardware.camera\" />\n    <uses-feature android:name=\"android.hardware.camera.autofocus\" />\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/tfe_ic_app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme.ImageClassification\">\n        <activity\n            android:name=\".ClassifierActivity\"\n            android:label=\"@string/tfe_ic_app_name\"\n            android:screenOrientation=\"portrait\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n</manifest>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/assets/ADD_TFLITE_MODEL_HERE",
    "content": ""
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/java/org/tensorflow/lite/examples/classification/CameraActivity.java",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.classification;\n\nimport android.Manifest;\nimport android.app.Fragment;\nimport android.content.Context;\nimport android.content.pm.PackageManager;\nimport android.hardware.Camera;\nimport android.hardware.camera2.CameraAccessException;\nimport android.hardware.camera2.CameraCharacteristics;\nimport android.hardware.camera2.CameraManager;\nimport android.hardware.camera2.params.StreamConfigurationMap;\nimport android.media.Image;\nimport android.media.Image.Plane;\nimport android.media.ImageReader;\nimport android.media.ImageReader.OnImageAvailableListener;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.HandlerThread;\nimport android.os.Trace;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.util.Size;\nimport android.view.Surface;\nimport android.view.View;\nimport android.view.ViewTreeObserver;\nimport android.view.WindowManager;\nimport android.widget.AdapterView;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.Spinner;\nimport android.widget.TextView;\nimport android.widget.Toast;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.UiThread;\nimport com.google.android.material.bottomsheet.BottomSheetBehavior;\nimport java.nio.ByteBuffer;\nimport java.util.List;\nimport org.tensorflow.lite.examples.classification.env.ImageUtils;\nimport org.tensorflow.lite.examples.classification.env.Logger;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier.Device;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier.Recognition;\n\npublic abstract class CameraActivity extends AppCompatActivity\n    implements OnImageAvailableListener,\n        Camera.PreviewCallback,\n        View.OnClickListener,\n        AdapterView.OnItemSelectedListener {\n  private static final Logger LOGGER = new Logger();\n\n  private static final int PERMISSIONS_REQUEST = 1;\n\n  private static final String PERMISSION_CAMERA = Manifest.permission.CAMERA;\n  protected int previewWidth = 0;\n  protected int previewHeight = 0;\n  private Handler handler;\n  private HandlerThread handlerThread;\n  private boolean useCamera2API;\n  private boolean isProcessingFrame = false;\n  private byte[][] yuvBytes = new byte[3][];\n  private int[] rgbBytes = null;\n  private int yRowStride;\n  private Runnable postInferenceCallback;\n  private Runnable imageConverter;\n  private LinearLayout bottomSheetLayout;\n  private LinearLayout gestureLayout;\n  private BottomSheetBehavior<LinearLayout> sheetBehavior;\n  protected TextView recognitionTextView,\n      recognition1TextView,\n      recognition2TextView,\n      recognitionValueTextView,\n      recognition1ValueTextView,\n      recognition2ValueTextView;\n  protected TextView frameValueTextView,\n      cropValueTextView,\n      cameraResolutionTextView,\n      rotationTextView,\n      inferenceTimeTextView;\n  protected ImageView bottomSheetArrowImageView;\n  private ImageView plusImageView, minusImageView;\n  private Spinner deviceSpinner;\n  private TextView threadsTextView;\n\n  private Device device = Device.CPU;\n  private int numThreads = -1;\n\n  @Override\n  protected void onCreate(final Bundle savedInstanceState) {\n    LOGGER.d(\"onCreate \" + this);\n    super.onCreate(null);\n    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);\n\n    setContentView(R.layout.tfe_ic_activity_camera);\n\n    if (hasPermission()) {\n      setFragment();\n    } else {\n      requestPermission();\n    }\n\n    threadsTextView = findViewById(R.id.threads);\n    plusImageView = findViewById(R.id.plus);\n    minusImageView = findViewById(R.id.minus);\n    deviceSpinner = findViewById(R.id.device_spinner);\n    bottomSheetLayout = findViewById(R.id.bottom_sheet_layout);\n    gestureLayout = findViewById(R.id.gesture_layout);\n    sheetBehavior = BottomSheetBehavior.from(bottomSheetLayout);\n    bottomSheetArrowImageView = findViewById(R.id.bottom_sheet_arrow);\n\n    ViewTreeObserver vto = gestureLayout.getViewTreeObserver();\n    vto.addOnGlobalLayoutListener(\n        new ViewTreeObserver.OnGlobalLayoutListener() {\n          @Override\n          public void onGlobalLayout() {\n            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n              gestureLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);\n            } else {\n              gestureLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);\n            }\n            //                int width = bottomSheetLayout.getMeasuredWidth();\n            int height = gestureLayout.getMeasuredHeight();\n\n            sheetBehavior.setPeekHeight(height);\n          }\n        });\n    sheetBehavior.setHideable(false);\n\n    sheetBehavior.setBottomSheetCallback(\n        new BottomSheetBehavior.BottomSheetCallback() {\n          @Override\n          public void onStateChanged(@NonNull View bottomSheet, int newState) {\n            switch (newState) {\n              case BottomSheetBehavior.STATE_HIDDEN:\n                break;\n              case BottomSheetBehavior.STATE_EXPANDED:\n                {\n                  bottomSheetArrowImageView.setImageResource(R.drawable.icn_chevron_down);\n                }\n                break;\n              case BottomSheetBehavior.STATE_COLLAPSED:\n                {\n                  bottomSheetArrowImageView.setImageResource(R.drawable.icn_chevron_up);\n                }\n                break;\n              case BottomSheetBehavior.STATE_DRAGGING:\n                break;\n              case BottomSheetBehavior.STATE_SETTLING:\n                bottomSheetArrowImageView.setImageResource(R.drawable.icn_chevron_up);\n                break;\n            }\n          }\n\n          @Override\n          public void onSlide(@NonNull View bottomSheet, float slideOffset) {}\n        });\n\n    recognitionTextView = findViewById(R.id.detected_item);\n    recognitionValueTextView = findViewById(R.id.detected_item_value);\n    recognition1TextView = findViewById(R.id.detected_item1);\n    recognition1ValueTextView = findViewById(R.id.detected_item1_value);\n    recognition2TextView = findViewById(R.id.detected_item2);\n    recognition2ValueTextView = findViewById(R.id.detected_item2_value);\n\n    frameValueTextView = findViewById(R.id.frame_info);\n    cropValueTextView = findViewById(R.id.crop_info);\n    cameraResolutionTextView = findViewById(R.id.view_info);\n    rotationTextView = findViewById(R.id.rotation_info);\n    inferenceTimeTextView = findViewById(R.id.inference_info);\n\n    deviceSpinner.setOnItemSelectedListener(this);\n\n    plusImageView.setOnClickListener(this);\n    minusImageView.setOnClickListener(this);\n\n    device = Device.valueOf(deviceSpinner.getSelectedItem().toString());\n    numThreads = Integer.parseInt(threadsTextView.getText().toString().trim());\n  }\n\n  protected int[] getRgbBytes() {\n    imageConverter.run();\n    return rgbBytes;\n  }\n\n  protected int getLuminanceStride() {\n    return yRowStride;\n  }\n\n  protected byte[] getLuminance() {\n    return yuvBytes[0];\n  }\n\n  /** Callback for android.hardware.Camera API */\n  @Override\n  public void onPreviewFrame(final byte[] bytes, final Camera camera) {\n    if (isProcessingFrame) {\n      LOGGER.w(\"Dropping frame!\");\n      return;\n    }\n\n    try {\n      // Initialize the storage bitmaps once when the resolution is known.\n      if (rgbBytes == null) {\n        Camera.Size previewSize = camera.getParameters().getPreviewSize();\n        previewHeight = previewSize.height;\n        previewWidth = previewSize.width;\n        rgbBytes = new int[previewWidth * previewHeight];\n        onPreviewSizeChosen(new Size(previewSize.width, previewSize.height), 90);\n      }\n    } catch (final Exception e) {\n      LOGGER.e(e, \"Exception!\");\n      return;\n    }\n\n    isProcessingFrame = true;\n    yuvBytes[0] = bytes;\n    yRowStride = previewWidth;\n\n    imageConverter =\n        new Runnable() {\n          @Override\n          public void run() {\n            ImageUtils.convertYUV420SPToARGB8888(bytes, previewWidth, previewHeight, rgbBytes);\n          }\n        };\n\n    postInferenceCallback =\n        new Runnable() {\n          @Override\n          public void run() {\n            camera.addCallbackBuffer(bytes);\n            isProcessingFrame = false;\n          }\n        };\n    processImage();\n  }\n\n  /** Callback for Camera2 API */\n  @Override\n  public void onImageAvailable(final ImageReader reader) {\n    // We need wait until we have some size from onPreviewSizeChosen\n    if (previewWidth == 0 || previewHeight == 0) {\n      return;\n    }\n    if (rgbBytes == null) {\n      rgbBytes = new int[previewWidth * previewHeight];\n    }\n    try {\n      final Image image = reader.acquireLatestImage();\n\n      if (image == null) {\n        return;\n      }\n\n      if (isProcessingFrame) {\n        image.close();\n        return;\n      }\n      isProcessingFrame = true;\n      Trace.beginSection(\"imageAvailable\");\n      final Plane[] planes = image.getPlanes();\n      fillBytes(planes, yuvBytes);\n      yRowStride = planes[0].getRowStride();\n      final int uvRowStride = planes[1].getRowStride();\n      final int uvPixelStride = planes[1].getPixelStride();\n\n      imageConverter =\n          new Runnable() {\n            @Override\n            public void run() {\n              ImageUtils.convertYUV420ToARGB8888(\n                  yuvBytes[0],\n                  yuvBytes[1],\n                  yuvBytes[2],\n                  previewWidth,\n                  previewHeight,\n                  yRowStride,\n                  uvRowStride,\n                  uvPixelStride,\n                  rgbBytes);\n            }\n          };\n\n      postInferenceCallback =\n          new Runnable() {\n            @Override\n            public void run() {\n              image.close();\n              isProcessingFrame = false;\n            }\n          };\n\n      processImage();\n    } catch (final Exception e) {\n      LOGGER.e(e, \"Exception!\");\n      Trace.endSection();\n      return;\n    }\n    Trace.endSection();\n  }\n\n  @Override\n  public synchronized void onStart() {\n    LOGGER.d(\"onStart \" + this);\n    super.onStart();\n  }\n\n  @Override\n  public synchronized void onResume() {\n    LOGGER.d(\"onResume \" + this);\n    super.onResume();\n\n    handlerThread = new HandlerThread(\"inference\");\n    handlerThread.start();\n    handler = new Handler(handlerThread.getLooper());\n  }\n\n  @Override\n  public synchronized void onPause() {\n    LOGGER.d(\"onPause \" + this);\n\n    handlerThread.quitSafely();\n    try {\n      handlerThread.join();\n      handlerThread = null;\n      handler = null;\n    } catch (final InterruptedException e) {\n      LOGGER.e(e, \"Exception!\");\n    }\n\n    super.onPause();\n  }\n\n  @Override\n  public synchronized void onStop() {\n    LOGGER.d(\"onStop \" + this);\n    super.onStop();\n  }\n\n  @Override\n  public synchronized void onDestroy() {\n    LOGGER.d(\"onDestroy \" + this);\n    super.onDestroy();\n  }\n\n  protected synchronized void runInBackground(final Runnable r) {\n    if (handler != null) {\n      handler.post(r);\n    }\n  }\n\n  @Override\n  public void onRequestPermissionsResult(\n      final int requestCode, final String[] permissions, final int[] grantResults) {\n    if (requestCode == PERMISSIONS_REQUEST) {\n      if (allPermissionsGranted(grantResults)) {\n        setFragment();\n      } else {\n        requestPermission();\n      }\n    }\n  }\n\n  private static boolean allPermissionsGranted(final int[] grantResults) {\n    for (int result : grantResults) {\n      if (result != PackageManager.PERMISSION_GRANTED) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  private boolean hasPermission() {\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n      return checkSelfPermission(PERMISSION_CAMERA) == PackageManager.PERMISSION_GRANTED;\n    } else {\n      return true;\n    }\n  }\n\n  private void requestPermission() {\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n      if (shouldShowRequestPermissionRationale(PERMISSION_CAMERA)) {\n        Toast.makeText(\n                CameraActivity.this,\n                \"Camera permission is required for this demo\",\n                Toast.LENGTH_LONG)\n            .show();\n      }\n      requestPermissions(new String[] {PERMISSION_CAMERA}, PERMISSIONS_REQUEST);\n    }\n  }\n\n  // Returns true if the device supports the required hardware level, or better.\n  private boolean isHardwareLevelSupported(\n      CameraCharacteristics characteristics, int requiredLevel) {\n    int deviceLevel = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);\n    if (deviceLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {\n      return requiredLevel == deviceLevel;\n    }\n    // deviceLevel is not LEGACY, can use numerical sort\n    return requiredLevel <= deviceLevel;\n  }\n\n  private String chooseCamera() {\n    final CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);\n    try {\n      for (final String cameraId : manager.getCameraIdList()) {\n        final CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);\n\n        // We don't use a front facing camera in this sample.\n        final Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);\n        if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {\n          continue;\n        }\n\n        final StreamConfigurationMap map =\n            characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);\n\n        if (map == null) {\n          continue;\n        }\n\n        // Fallback to camera1 API for internal cameras that don't have full support.\n        // This should help with legacy situations where using the camera2 API causes\n        // distorted or otherwise broken previews.\n        useCamera2API =\n            (facing == CameraCharacteristics.LENS_FACING_EXTERNAL)\n                || isHardwareLevelSupported(\n                    characteristics, CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);\n        LOGGER.i(\"Camera API lv2?: %s\", useCamera2API);\n        return cameraId;\n      }\n    } catch (CameraAccessException e) {\n      LOGGER.e(e, \"Not allowed to access camera\");\n    }\n\n    return null;\n  }\n\n  protected void setFragment() {\n    String cameraId = chooseCamera();\n\n    Fragment fragment;\n    if (useCamera2API) {\n      CameraConnectionFragment camera2Fragment =\n          CameraConnectionFragment.newInstance(\n              new CameraConnectionFragment.ConnectionCallback() {\n                @Override\n                public void onPreviewSizeChosen(final Size size, final int rotation) {\n                  previewHeight = size.getHeight();\n                  previewWidth = size.getWidth();\n                  CameraActivity.this.onPreviewSizeChosen(size, rotation);\n                }\n              },\n              this,\n              getLayoutId(),\n              getDesiredPreviewFrameSize());\n\n      camera2Fragment.setCamera(cameraId);\n      fragment = camera2Fragment;\n    } else {\n      fragment =\n          new LegacyCameraConnectionFragment(this, getLayoutId(), getDesiredPreviewFrameSize());\n    }\n\n    getFragmentManager().beginTransaction().replace(R.id.container, fragment).commit();\n  }\n\n  protected void fillBytes(final Plane[] planes, final byte[][] yuvBytes) {\n    // Because of the variable row stride it's not possible to know in\n    // advance the actual necessary dimensions of the yuv planes.\n    for (int i = 0; i < planes.length; ++i) {\n      final ByteBuffer buffer = planes[i].getBuffer();\n      if (yuvBytes[i] == null) {\n        LOGGER.d(\"Initializing buffer %d at size %d\", i, buffer.capacity());\n        yuvBytes[i] = new byte[buffer.capacity()];\n      }\n      buffer.get(yuvBytes[i]);\n    }\n  }\n\n  protected void readyForNextImage() {\n    if (postInferenceCallback != null) {\n      postInferenceCallback.run();\n    }\n  }\n\n  protected int getScreenOrientation() {\n    switch (getWindowManager().getDefaultDisplay().getRotation()) {\n      case Surface.ROTATION_270:\n        return 270;\n      case Surface.ROTATION_180:\n        return 180;\n      case Surface.ROTATION_90:\n        return 90;\n      default:\n        return 0;\n    }\n  }\n\n  @UiThread\n  protected void showResultsInBottomSheet(List<Recognition> results) {\n    if (results != null && results.size() >= 3) {\n      Recognition recognition = results.get(0);\n      if (recognition != null) {\n        if (recognition.getTitle() != null) recognitionTextView.setText(recognition.getTitle());\n        if (recognition.getConfidence() != null)\n          recognitionValueTextView.setText(\n              String.format(\"%.2f\", (100 * recognition.getConfidence())) + \"%\");\n      }\n\n      Recognition recognition1 = results.get(1);\n      if (recognition1 != null) {\n        if (recognition1.getTitle() != null) recognition1TextView.setText(recognition1.getTitle());\n        if (recognition1.getConfidence() != null)\n          recognition1ValueTextView.setText(\n              String.format(\"%.2f\", (100 * recognition1.getConfidence())) + \"%\");\n      }\n\n      Recognition recognition2 = results.get(2);\n      if (recognition2 != null) {\n        if (recognition2.getTitle() != null) recognition2TextView.setText(recognition2.getTitle());\n        if (recognition2.getConfidence() != null)\n          recognition2ValueTextView.setText(\n              String.format(\"%.2f\", (100 * recognition2.getConfidence())) + \"%\");\n      }\n    }\n  }\n\n  protected void showFrameInfo(String frameInfo) {\n    frameValueTextView.setText(frameInfo);\n  }\n\n  protected void showCropInfo(String cropInfo) {\n    cropValueTextView.setText(cropInfo);\n  }\n\n  protected void showCameraResolution(String cameraInfo) {\n    cameraResolutionTextView.setText(cameraInfo);\n  }\n\n  protected void showRotationInfo(String rotation) {\n    rotationTextView.setText(rotation);\n  }\n\n  protected void showInference(String inferenceTime) {\n    inferenceTimeTextView.setText(inferenceTime);\n  }\n\n  protected Device getDevice() {\n    return device;\n  }\n\n  private void setDevice(Device device) {\n    if (this.device != device) {\n      LOGGER.d(\"Updating  device: \" + device);\n      this.device = device;\n      final boolean threadsEnabled = device == Device.CPU;\n      plusImageView.setEnabled(threadsEnabled);\n      minusImageView.setEnabled(threadsEnabled);\n      threadsTextView.setText(threadsEnabled ? String.valueOf(numThreads) : \"N/A\");\n      onInferenceConfigurationChanged();\n    }\n  }\n\n  protected int getNumThreads() {\n    return numThreads;\n  }\n\n  private void setNumThreads(int numThreads) {\n    if (this.numThreads != numThreads) {\n      LOGGER.d(\"Updating  numThreads: \" + numThreads);\n      this.numThreads = numThreads;\n      onInferenceConfigurationChanged();\n    }\n  }\n\n  protected abstract void processImage();\n\n  protected abstract void onPreviewSizeChosen(final Size size, final int rotation);\n\n  protected abstract int getLayoutId();\n\n  protected abstract Size getDesiredPreviewFrameSize();\n\n  protected abstract void onInferenceConfigurationChanged();\n\n  @Override\n  public void onClick(View v) {\n    if (v.getId() == R.id.plus) {\n      String threads = threadsTextView.getText().toString().trim();\n      int numThreads = Integer.parseInt(threads);\n      if (numThreads >= 9) return;\n      setNumThreads(++numThreads);\n      threadsTextView.setText(String.valueOf(numThreads));\n    } else if (v.getId() == R.id.minus) {\n      String threads = threadsTextView.getText().toString().trim();\n      int numThreads = Integer.parseInt(threads);\n      if (numThreads == 1) {\n        return;\n      }\n      setNumThreads(--numThreads);\n      threadsTextView.setText(String.valueOf(numThreads));\n    }\n  }\n\n  @Override\n  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {\n    if (parent == deviceSpinner) {\n      setDevice(Device.valueOf(parent.getItemAtPosition(pos).toString()));\n    }\n  }\n\n  @Override\n  public void onNothingSelected(AdapterView<?> parent) {\n    // Do nothing.\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/java/org/tensorflow/lite/examples/classification/CameraConnectionFragment.java",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.classification;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.app.AlertDialog;\nimport android.app.Dialog;\nimport android.app.DialogFragment;\nimport android.app.Fragment;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.content.res.Configuration;\nimport android.graphics.ImageFormat;\nimport android.graphics.Matrix;\nimport android.graphics.RectF;\nimport android.graphics.SurfaceTexture;\nimport android.hardware.camera2.CameraAccessException;\nimport android.hardware.camera2.CameraCaptureSession;\nimport android.hardware.camera2.CameraCharacteristics;\nimport android.hardware.camera2.CameraDevice;\nimport android.hardware.camera2.CameraManager;\nimport android.hardware.camera2.CaptureRequest;\nimport android.hardware.camera2.CaptureResult;\nimport android.hardware.camera2.TotalCaptureResult;\nimport android.hardware.camera2.params.StreamConfigurationMap;\nimport android.media.ImageReader;\nimport android.media.ImageReader.OnImageAvailableListener;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.HandlerThread;\nimport android.text.TextUtils;\nimport android.util.Size;\nimport android.util.SparseIntArray;\nimport android.view.LayoutInflater;\nimport android.view.Surface;\nimport android.view.TextureView;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.concurrent.Semaphore;\nimport java.util.concurrent.TimeUnit;\nimport org.tensorflow.lite.examples.classification.customview.AutoFitTextureView;\nimport org.tensorflow.lite.examples.classification.env.Logger;\n\n/**\n * Camera Connection Fragment that captures images from camera.\n *\n * <p>Instantiated by newInstance.</p>\n */\n@SuppressWarnings(\"FragmentNotInstantiable\")\npublic class CameraConnectionFragment extends Fragment {\n  private static final Logger LOGGER = new Logger();\n\n  /**\n   * The camera preview size will be chosen to be the smallest frame by pixel size capable of\n   * containing a DESIRED_SIZE x DESIRED_SIZE square.\n   */\n  private static final int MINIMUM_PREVIEW_SIZE = 320;\n\n  /** Conversion from screen rotation to JPEG orientation. */\n  private static final SparseIntArray ORIENTATIONS = new SparseIntArray();\n\n  private static final String FRAGMENT_DIALOG = \"dialog\";\n\n  static {\n    ORIENTATIONS.append(Surface.ROTATION_0, 90);\n    ORIENTATIONS.append(Surface.ROTATION_90, 0);\n    ORIENTATIONS.append(Surface.ROTATION_180, 270);\n    ORIENTATIONS.append(Surface.ROTATION_270, 180);\n  }\n\n  /** A {@link Semaphore} to prevent the app from exiting before closing the camera. */\n  private final Semaphore cameraOpenCloseLock = new Semaphore(1);\n  /** A {@link OnImageAvailableListener} to receive frames as they are available. */\n  private final OnImageAvailableListener imageListener;\n  /** The input size in pixels desired by TensorFlow (width and height of a square bitmap). */\n  private final Size inputSize;\n  /** The layout identifier to inflate for this Fragment. */\n  private final int layout;\n\n  private final ConnectionCallback cameraConnectionCallback;\n  private final CameraCaptureSession.CaptureCallback captureCallback =\n      new CameraCaptureSession.CaptureCallback() {\n        @Override\n        public void onCaptureProgressed(\n            final CameraCaptureSession session,\n            final CaptureRequest request,\n            final CaptureResult partialResult) {}\n\n        @Override\n        public void onCaptureCompleted(\n            final CameraCaptureSession session,\n            final CaptureRequest request,\n            final TotalCaptureResult result) {}\n      };\n  /** ID of the current {@link CameraDevice}. */\n  private String cameraId;\n  /** An {@link AutoFitTextureView} for camera preview. */\n  private AutoFitTextureView textureView;\n  /** A {@link CameraCaptureSession } for camera preview. */\n  private CameraCaptureSession captureSession;\n  /** A reference to the opened {@link CameraDevice}. */\n  private CameraDevice cameraDevice;\n  /** The rotation in degrees of the camera sensor from the display. */\n  private Integer sensorOrientation;\n  /** The {@link Size} of camera preview. */\n  private Size previewSize;\n  /** An additional thread for running tasks that shouldn't block the UI. */\n  private HandlerThread backgroundThread;\n  /** A {@link Handler} for running tasks in the background. */\n  private Handler backgroundHandler;\n  /**\n   * {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a {@link\n   * TextureView}.\n   */\n  private final TextureView.SurfaceTextureListener surfaceTextureListener =\n      new TextureView.SurfaceTextureListener() {\n        @Override\n        public void onSurfaceTextureAvailable(\n            final SurfaceTexture texture, final int width, final int height) {\n          openCamera(width, height);\n        }\n\n        @Override\n        public void onSurfaceTextureSizeChanged(\n            final SurfaceTexture texture, final int width, final int height) {\n          configureTransform(width, height);\n        }\n\n        @Override\n        public boolean onSurfaceTextureDestroyed(final SurfaceTexture texture) {\n          return true;\n        }\n\n        @Override\n        public void onSurfaceTextureUpdated(final SurfaceTexture texture) {}\n      };\n  /** An {@link ImageReader} that handles preview frame capture. */\n  private ImageReader previewReader;\n  /** {@link CaptureRequest.Builder} for the camera preview */\n  private CaptureRequest.Builder previewRequestBuilder;\n  /** {@link CaptureRequest} generated by {@link #previewRequestBuilder} */\n  private CaptureRequest previewRequest;\n  /** {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its state. */\n  private final CameraDevice.StateCallback stateCallback =\n      new CameraDevice.StateCallback() {\n        @Override\n        public void onOpened(final CameraDevice cd) {\n          // This method is called when the camera is opened.  We start camera preview here.\n          cameraOpenCloseLock.release();\n          cameraDevice = cd;\n          createCameraPreviewSession();\n        }\n\n        @Override\n        public void onDisconnected(final CameraDevice cd) {\n          cameraOpenCloseLock.release();\n          cd.close();\n          cameraDevice = null;\n        }\n\n        @Override\n        public void onError(final CameraDevice cd, final int error) {\n          cameraOpenCloseLock.release();\n          cd.close();\n          cameraDevice = null;\n          final Activity activity = getActivity();\n          if (null != activity) {\n            activity.finish();\n          }\n        }\n      };\n\n  @SuppressLint(\"ValidFragment\")\n  private CameraConnectionFragment(\n      final ConnectionCallback connectionCallback,\n      final OnImageAvailableListener imageListener,\n      final int layout,\n      final Size inputSize) {\n    this.cameraConnectionCallback = connectionCallback;\n    this.imageListener = imageListener;\n    this.layout = layout;\n    this.inputSize = inputSize;\n  }\n\n  /**\n   * Given {@code choices} of {@code Size}s supported by a camera, chooses the smallest one whose\n   * width and height are at least as large as the minimum of both, or an exact match if possible.\n   *\n   * @param choices The list of sizes that the camera supports for the intended output class\n   * @param width The minimum desired width\n   * @param height The minimum desired height\n   * @return The optimal {@code Size}, or an arbitrary one if none were big enough\n   */\n  protected static Size chooseOptimalSize(final Size[] choices, final int width, final int height) {\n    final int minSize = Math.max(Math.min(width, height), MINIMUM_PREVIEW_SIZE);\n    final Size desiredSize = new Size(width, height);\n\n    // Collect the supported resolutions that are at least as big as the preview Surface\n    boolean exactSizeFound = false;\n    final List<Size> bigEnough = new ArrayList<Size>();\n    final List<Size> tooSmall = new ArrayList<Size>();\n    for (final Size option : choices) {\n      if (option.equals(desiredSize)) {\n        // Set the size but don't return yet so that remaining sizes will still be logged.\n        exactSizeFound = true;\n      }\n\n      if (option.getHeight() >= minSize && option.getWidth() >= minSize) {\n        bigEnough.add(option);\n      } else {\n        tooSmall.add(option);\n      }\n    }\n\n    LOGGER.i(\"Desired size: \" + desiredSize + \", min size: \" + minSize + \"x\" + minSize);\n    LOGGER.i(\"Valid preview sizes: [\" + TextUtils.join(\", \", bigEnough) + \"]\");\n    LOGGER.i(\"Rejected preview sizes: [\" + TextUtils.join(\", \", tooSmall) + \"]\");\n\n    if (exactSizeFound) {\n      LOGGER.i(\"Exact size match found.\");\n      return desiredSize;\n    }\n\n    // Pick the smallest of those, assuming we found any\n    if (bigEnough.size() > 0) {\n      final Size chosenSize = Collections.min(bigEnough, new CompareSizesByArea());\n      LOGGER.i(\"Chosen size: \" + chosenSize.getWidth() + \"x\" + chosenSize.getHeight());\n      return chosenSize;\n    } else {\n      LOGGER.e(\"Couldn't find any suitable preview size\");\n      return choices[0];\n    }\n  }\n\n  public static CameraConnectionFragment newInstance(\n      final ConnectionCallback callback,\n      final OnImageAvailableListener imageListener,\n      final int layout,\n      final Size inputSize) {\n    return new CameraConnectionFragment(callback, imageListener, layout, inputSize);\n  }\n\n  /**\n   * Shows a {@link Toast} on the UI thread.\n   *\n   * @param text The message to show\n   */\n  private void showToast(final String text) {\n    final Activity activity = getActivity();\n    if (activity != null) {\n      activity.runOnUiThread(\n          new Runnable() {\n            @Override\n            public void run() {\n              Toast.makeText(activity, text, Toast.LENGTH_SHORT).show();\n            }\n          });\n    }\n  }\n\n  @Override\n  public View onCreateView(\n      final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {\n    return inflater.inflate(layout, container, false);\n  }\n\n  @Override\n  public void onViewCreated(final View view, final Bundle savedInstanceState) {\n    textureView = (AutoFitTextureView) view.findViewById(R.id.texture);\n  }\n\n  @Override\n  public void onActivityCreated(final Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n  }\n\n  @Override\n  public void onResume() {\n    super.onResume();\n    startBackgroundThread();\n\n    // When the screen is turned off and turned back on, the SurfaceTexture is already\n    // available, and \"onSurfaceTextureAvailable\" will not be called. In that case, we can open\n    // a camera and start preview from here (otherwise, we wait until the surface is ready in\n    // the SurfaceTextureListener).\n    if (textureView.isAvailable()) {\n      openCamera(textureView.getWidth(), textureView.getHeight());\n    } else {\n      textureView.setSurfaceTextureListener(surfaceTextureListener);\n    }\n  }\n\n  @Override\n  public void onPause() {\n    closeCamera();\n    stopBackgroundThread();\n    super.onPause();\n  }\n\n  public void setCamera(String cameraId) {\n    this.cameraId = cameraId;\n  }\n\n  /** Sets up member variables related to camera. */\n  private void setUpCameraOutputs() {\n    final Activity activity = getActivity();\n    final CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);\n    try {\n      final CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);\n\n      final StreamConfigurationMap map =\n          characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);\n\n      sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);\n\n      // Danger, W.R.! Attempting to use too large a preview size could  exceed the camera\n      // bus' bandwidth limitation, resulting in gorgeous previews but the storage of\n      // garbage capture data.\n      previewSize =\n          chooseOptimalSize(\n              map.getOutputSizes(SurfaceTexture.class),\n              inputSize.getWidth(),\n              inputSize.getHeight());\n\n      // We fit the aspect ratio of TextureView to the size of preview we picked.\n      final int orientation = getResources().getConfiguration().orientation;\n      if (orientation == Configuration.ORIENTATION_LANDSCAPE) {\n        textureView.setAspectRatio(previewSize.getWidth(), previewSize.getHeight());\n      } else {\n        textureView.setAspectRatio(previewSize.getHeight(), previewSize.getWidth());\n      }\n    } catch (final CameraAccessException e) {\n      LOGGER.e(e, \"Exception!\");\n    } catch (final NullPointerException e) {\n      // Currently an NPE is thrown when the Camera2API is used but not supported on the\n      // device this code runs.\n      ErrorDialog.newInstance(getString(R.string.tfe_ic_camera_error))\n          .show(getChildFragmentManager(), FRAGMENT_DIALOG);\n      throw new IllegalStateException(getString(R.string.tfe_ic_camera_error));\n    }\n\n    cameraConnectionCallback.onPreviewSizeChosen(previewSize, sensorOrientation);\n  }\n\n  /** Opens the camera specified by {@link CameraConnectionFragment#cameraId}. */\n  private void openCamera(final int width, final int height) {\n    setUpCameraOutputs();\n    configureTransform(width, height);\n    final Activity activity = getActivity();\n    final CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);\n    try {\n      if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {\n        throw new RuntimeException(\"Time out waiting to lock camera opening.\");\n      }\n      manager.openCamera(cameraId, stateCallback, backgroundHandler);\n    } catch (final CameraAccessException e) {\n      LOGGER.e(e, \"Exception!\");\n    } catch (final InterruptedException e) {\n      throw new RuntimeException(\"Interrupted while trying to lock camera opening.\", e);\n    }\n  }\n\n  /** Closes the current {@link CameraDevice}. */\n  private void closeCamera() {\n    try {\n      cameraOpenCloseLock.acquire();\n      if (null != captureSession) {\n        captureSession.close();\n        captureSession = null;\n      }\n      if (null != cameraDevice) {\n        cameraDevice.close();\n        cameraDevice = null;\n      }\n      if (null != previewReader) {\n        previewReader.close();\n        previewReader = null;\n      }\n    } catch (final InterruptedException e) {\n      throw new RuntimeException(\"Interrupted while trying to lock camera closing.\", e);\n    } finally {\n      cameraOpenCloseLock.release();\n    }\n  }\n\n  /** Starts a background thread and its {@link Handler}. */\n  private void startBackgroundThread() {\n    backgroundThread = new HandlerThread(\"ImageListener\");\n    backgroundThread.start();\n    backgroundHandler = new Handler(backgroundThread.getLooper());\n  }\n\n  /** Stops the background thread and its {@link Handler}. */\n  private void stopBackgroundThread() {\n    backgroundThread.quitSafely();\n    try {\n      backgroundThread.join();\n      backgroundThread = null;\n      backgroundHandler = null;\n    } catch (final InterruptedException e) {\n      LOGGER.e(e, \"Exception!\");\n    }\n  }\n\n  /** Creates a new {@link CameraCaptureSession} for camera preview. */\n  private void createCameraPreviewSession() {\n    try {\n      final SurfaceTexture texture = textureView.getSurfaceTexture();\n      assert texture != null;\n\n      // We configure the size of default buffer to be the size of camera preview we want.\n      texture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());\n\n      // This is the output Surface we need to start preview.\n      final Surface surface = new Surface(texture);\n\n      // We set up a CaptureRequest.Builder with the output Surface.\n      previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);\n      previewRequestBuilder.addTarget(surface);\n\n      LOGGER.i(\"Opening camera preview: \" + previewSize.getWidth() + \"x\" + previewSize.getHeight());\n\n      // Create the reader for the preview frames.\n      previewReader =\n          ImageReader.newInstance(\n              previewSize.getWidth(), previewSize.getHeight(), ImageFormat.YUV_420_888, 2);\n\n      previewReader.setOnImageAvailableListener(imageListener, backgroundHandler);\n      previewRequestBuilder.addTarget(previewReader.getSurface());\n\n      // Here, we create a CameraCaptureSession for camera preview.\n      cameraDevice.createCaptureSession(\n          Arrays.asList(surface, previewReader.getSurface()),\n          new CameraCaptureSession.StateCallback() {\n\n            @Override\n            public void onConfigured(final CameraCaptureSession cameraCaptureSession) {\n              // The camera is already closed\n              if (null == cameraDevice) {\n                return;\n              }\n\n              // When the session is ready, we start displaying the preview.\n              captureSession = cameraCaptureSession;\n              try {\n                // Auto focus should be continuous for camera preview.\n                previewRequestBuilder.set(\n                    CaptureRequest.CONTROL_AF_MODE,\n                    CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);\n                // Flash is automatically enabled when necessary.\n                previewRequestBuilder.set(\n                    CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);\n\n                // Finally, we start displaying the camera preview.\n                previewRequest = previewRequestBuilder.build();\n                captureSession.setRepeatingRequest(\n                    previewRequest, captureCallback, backgroundHandler);\n              } catch (final CameraAccessException e) {\n                LOGGER.e(e, \"Exception!\");\n              }\n            }\n\n            @Override\n            public void onConfigureFailed(final CameraCaptureSession cameraCaptureSession) {\n              showToast(\"Failed\");\n            }\n          },\n          null);\n    } catch (final CameraAccessException e) {\n      LOGGER.e(e, \"Exception!\");\n    }\n  }\n\n  /**\n   * Configures the necessary {@link Matrix} transformation to `mTextureView`. This method should be\n   * called after the camera preview size is determined in setUpCameraOutputs and also the size of\n   * `mTextureView` is fixed.\n   *\n   * @param viewWidth The width of `mTextureView`\n   * @param viewHeight The height of `mTextureView`\n   */\n  private void configureTransform(final int viewWidth, final int viewHeight) {\n    final Activity activity = getActivity();\n    if (null == textureView || null == previewSize || null == activity) {\n      return;\n    }\n    final int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();\n    final Matrix matrix = new Matrix();\n    final RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);\n    final RectF bufferRect = new RectF(0, 0, previewSize.getHeight(), previewSize.getWidth());\n    final float centerX = viewRect.centerX();\n    final float centerY = viewRect.centerY();\n    if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {\n      bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());\n      matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);\n      final float scale =\n          Math.max(\n              (float) viewHeight / previewSize.getHeight(),\n              (float) viewWidth / previewSize.getWidth());\n      matrix.postScale(scale, scale, centerX, centerY);\n      matrix.postRotate(90 * (rotation - 2), centerX, centerY);\n    } else if (Surface.ROTATION_180 == rotation) {\n      matrix.postRotate(180, centerX, centerY);\n    }\n    textureView.setTransform(matrix);\n  }\n\n  /**\n   * Callback for Activities to use to initialize their data once the selected preview size is\n   * known.\n   */\n  public interface ConnectionCallback {\n    void onPreviewSizeChosen(Size size, int cameraRotation);\n  }\n\n  /** Compares two {@code Size}s based on their areas. */\n  static class CompareSizesByArea implements Comparator<Size> {\n    @Override\n    public int compare(final Size lhs, final Size rhs) {\n      // We cast here to ensure the multiplications won't overflow\n      return Long.signum(\n          (long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight());\n    }\n  }\n\n  /** Shows an error message dialog. */\n  public static class ErrorDialog extends DialogFragment {\n    private static final String ARG_MESSAGE = \"message\";\n\n    public static ErrorDialog newInstance(final String message) {\n      final ErrorDialog dialog = new ErrorDialog();\n      final Bundle args = new Bundle();\n      args.putString(ARG_MESSAGE, message);\n      dialog.setArguments(args);\n      return dialog;\n    }\n\n    @Override\n    public Dialog onCreateDialog(final Bundle savedInstanceState) {\n      final Activity activity = getActivity();\n      return new AlertDialog.Builder(activity)\n          .setMessage(getArguments().getString(ARG_MESSAGE))\n          .setPositiveButton(\n              android.R.string.ok,\n              new DialogInterface.OnClickListener() {\n                @Override\n                public void onClick(final DialogInterface dialogInterface, final int i) {\n                  activity.finish();\n                }\n              })\n          .create();\n    }\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/java/org/tensorflow/lite/examples/classification/ClassifierActivity.java",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.classification;\n\nimport android.graphics.Bitmap;\nimport android.graphics.Bitmap.Config;\nimport android.graphics.Typeface;\nimport android.media.ImageReader.OnImageAvailableListener;\nimport android.os.SystemClock;\nimport android.util.Size;\nimport android.util.TypedValue;\nimport java.io.IOException;\nimport java.util.List;\nimport org.tensorflow.lite.examples.classification.env.BorderedText;\nimport org.tensorflow.lite.examples.classification.env.Logger;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier.Device;\n\npublic class ClassifierActivity extends CameraActivity implements OnImageAvailableListener {\n  private static final Logger LOGGER = new Logger();\n  private static final Size DESIRED_PREVIEW_SIZE = new Size(640, 480);\n  private static final float TEXT_SIZE_DIP = 10;\n  private Bitmap rgbFrameBitmap = null;\n  private long lastProcessingTimeMs;\n  private Integer sensorOrientation;\n  private Classifier classifier;\n  private BorderedText borderedText;\n  /** Input image size of the model along x axis. */\n  private int imageSizeX;\n  /** Input image size of the model along y axis. */\n  private int imageSizeY;\n\n  @Override\n  protected int getLayoutId() {\n    return R.layout.tfe_ic_camera_connection_fragment;\n  }\n\n  @Override\n  protected Size getDesiredPreviewFrameSize() {\n    return DESIRED_PREVIEW_SIZE;\n  }\n\n  @Override\n  public void onPreviewSizeChosen(final Size size, final int rotation) {\n    final float textSizePx =\n        TypedValue.applyDimension(\n            TypedValue.COMPLEX_UNIT_DIP, TEXT_SIZE_DIP, getResources().getDisplayMetrics());\n    borderedText = new BorderedText(textSizePx);\n    borderedText.setTypeface(Typeface.MONOSPACE);\n\n    recreateClassifier(getDevice(), getNumThreads());\n    if (classifier == null) {\n      LOGGER.e(\"No classifier on preview!\");\n      return;\n    }\n\n    previewWidth = size.getWidth();\n    previewHeight = size.getHeight();\n\n    sensorOrientation = rotation - getScreenOrientation();\n    LOGGER.i(\"Camera orientation relative to screen canvas: %d\", sensorOrientation);\n\n    LOGGER.i(\"Initializing at size %dx%d\", previewWidth, previewHeight);\n    rgbFrameBitmap = Bitmap.createBitmap(previewWidth, previewHeight, Config.ARGB_8888);\n  }\n\n  @Override\n  protected void processImage() {\n    rgbFrameBitmap.setPixels(getRgbBytes(), 0, previewWidth, 0, 0, previewWidth, previewHeight);\n    final int cropSize = Math.min(previewWidth, previewHeight);\n\n    runInBackground(\n        new Runnable() {\n          @Override\n          public void run() {\n            if (classifier != null) {\n              final long startTime = SystemClock.uptimeMillis();\n              final List<Classifier.Recognition> results =\n                  classifier.recognizeImage(rgbFrameBitmap, sensorOrientation);\n              lastProcessingTimeMs = SystemClock.uptimeMillis() - startTime;\n              LOGGER.v(\"Detect: %s\", results);\n\n              runOnUiThread(\n                  new Runnable() {\n                    @Override\n                    public void run() {\n                      showResultsInBottomSheet(results);\n                      showFrameInfo(previewWidth + \"x\" + previewHeight);\n                      showCropInfo(imageSizeX + \"x\" + imageSizeY);\n                      showCameraResolution(cropSize + \"x\" + cropSize);\n                      showRotationInfo(String.valueOf(sensorOrientation));\n                      showInference(lastProcessingTimeMs + \"ms\");\n                    }\n                  });\n            }\n            readyForNextImage();\n          }\n        });\n  }\n\n  @Override\n  protected void onInferenceConfigurationChanged() {\n    if (rgbFrameBitmap == null) {\n      // Defer creation until we're getting camera frames.\n      return;\n    }\n    final Device device = getDevice();\n    final int numThreads = getNumThreads();\n    runInBackground(() -> recreateClassifier(device, numThreads));\n  }\n\n  private void recreateClassifier(Device device, int numThreads) {\n    if (classifier != null) {\n      LOGGER.d(\"Closing classifier.\");\n      classifier.close();\n      classifier = null;\n    }\n    try {\n      LOGGER.d(\n          \"Creating classifier (device=%s, numThreads=%d)\", device, numThreads);\n      classifier = Classifier.create(this, device, numThreads);\n    } catch (IOException e) {\n      LOGGER.e(e, \"Failed to create classifier.\");\n    }\n\n    // Updates the input image size.\n    imageSizeX = classifier.getImageSizeX();\n    imageSizeY = classifier.getImageSizeY();\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/java/org/tensorflow/lite/examples/classification/LegacyCameraConnectionFragment.java",
    "content": "package org.tensorflow.lite.examples.classification;\n\n/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport android.annotation.SuppressLint;\nimport android.app.Fragment;\nimport android.graphics.SurfaceTexture;\nimport android.hardware.Camera;\nimport android.hardware.Camera.CameraInfo;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.HandlerThread;\nimport android.util.Size;\nimport android.util.SparseIntArray;\nimport android.view.LayoutInflater;\nimport android.view.Surface;\nimport android.view.TextureView;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport java.io.IOException;\nimport java.util.List;\nimport org.tensorflow.lite.examples.classification.customview.AutoFitTextureView;\nimport org.tensorflow.lite.examples.classification.env.ImageUtils;\nimport org.tensorflow.lite.examples.classification.env.Logger;\n\npublic class LegacyCameraConnectionFragment extends Fragment {\n  private static final Logger LOGGER = new Logger();\n  /** Conversion from screen rotation to JPEG orientation. */\n  private static final SparseIntArray ORIENTATIONS = new SparseIntArray();\n\n  static {\n    ORIENTATIONS.append(Surface.ROTATION_0, 90);\n    ORIENTATIONS.append(Surface.ROTATION_90, 0);\n    ORIENTATIONS.append(Surface.ROTATION_180, 270);\n    ORIENTATIONS.append(Surface.ROTATION_270, 180);\n  }\n\n  private Camera camera;\n  private Camera.PreviewCallback imageListener;\n  private Size desiredSize;\n  /** The layout identifier to inflate for this Fragment. */\n  private int layout;\n  /** An {@link AutoFitTextureView} for camera preview. */\n  private AutoFitTextureView textureView;\n  /**\n   * {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a {@link\n   * TextureView}.\n   */\n  private final TextureView.SurfaceTextureListener surfaceTextureListener =\n      new TextureView.SurfaceTextureListener() {\n        @Override\n        public void onSurfaceTextureAvailable(\n            final SurfaceTexture texture, final int width, final int height) {\n\n          int index = getCameraId();\n          camera = Camera.open(index);\n\n          try {\n            Camera.Parameters parameters = camera.getParameters();\n            List<String> focusModes = parameters.getSupportedFocusModes();\n            if (focusModes != null\n                && focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {\n              parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);\n            }\n            List<Camera.Size> cameraSizes = parameters.getSupportedPreviewSizes();\n            Size[] sizes = new Size[cameraSizes.size()];\n            int i = 0;\n            for (Camera.Size size : cameraSizes) {\n              sizes[i++] = new Size(size.width, size.height);\n            }\n            Size previewSize =\n                CameraConnectionFragment.chooseOptimalSize(\n                    sizes, desiredSize.getWidth(), desiredSize.getHeight());\n            parameters.setPreviewSize(previewSize.getWidth(), previewSize.getHeight());\n            camera.setDisplayOrientation(90);\n            camera.setParameters(parameters);\n            camera.setPreviewTexture(texture);\n          } catch (IOException exception) {\n            camera.release();\n          }\n\n          camera.setPreviewCallbackWithBuffer(imageListener);\n          Camera.Size s = camera.getParameters().getPreviewSize();\n          camera.addCallbackBuffer(\n              new byte[ImageUtils.getYUVByteSize(/* width= */ s.height, /* height= */ s.width)]);\n\n          textureView.setAspectRatio(/* width= */ s.height, /* height= */ s.width);\n\n          camera.startPreview();\n        }\n\n        @Override\n        public void onSurfaceTextureSizeChanged(\n            final SurfaceTexture texture, final int width, final int height) {}\n\n        @Override\n        public boolean onSurfaceTextureDestroyed(final SurfaceTexture texture) {\n          return true;\n        }\n\n        @Override\n        public void onSurfaceTextureUpdated(final SurfaceTexture texture) {}\n      };\n  /** An additional thread for running tasks that shouldn't block the UI. */\n  private HandlerThread backgroundThread;\n\n  @SuppressLint(\"ValidFragment\")\n  public LegacyCameraConnectionFragment(\n      final Camera.PreviewCallback imageListener, final int layout, final Size desiredSize) {\n    this.imageListener = imageListener;\n    this.layout = layout;\n    this.desiredSize = desiredSize;\n  }\n\n  public LegacyCameraConnectionFragment() {\n  }\n\n  @Override\n  public View onCreateView(\n      final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {\n    return inflater.inflate(layout, container, false);\n  }\n\n  @Override\n  public void onViewCreated(final View view, final Bundle savedInstanceState) {\n    textureView = (AutoFitTextureView) view.findViewById(R.id.texture);\n  }\n\n  @Override\n  public void onActivityCreated(final Bundle savedInstanceState) {\n    super.onActivityCreated(savedInstanceState);\n  }\n\n  @Override\n  public void onResume() {\n    super.onResume();\n    startBackgroundThread();\n    // When the screen is turned off and turned back on, the SurfaceTexture is already\n    // available, and \"onSurfaceTextureAvailable\" will not be called. In that case, we can open\n    // a camera and start preview from here (otherwise, we wait until the surface is ready in\n    // the SurfaceTextureListener).\n\n    if (textureView.isAvailable()) {\n      if (camera != null) {\n        camera.startPreview();\n      }\n    } else {\n      textureView.setSurfaceTextureListener(surfaceTextureListener);\n    }\n  }\n\n  @Override\n  public void onPause() {\n    stopCamera();\n    stopBackgroundThread();\n    super.onPause();\n  }\n\n  /** Starts a background thread and its {@link Handler}. */\n  private void startBackgroundThread() {\n    backgroundThread = new HandlerThread(\"CameraBackground\");\n    backgroundThread.start();\n  }\n\n  /** Stops the background thread and its {@link Handler}. */\n  private void stopBackgroundThread() {\n    backgroundThread.quitSafely();\n    try {\n      backgroundThread.join();\n      backgroundThread = null;\n    } catch (final InterruptedException e) {\n      LOGGER.e(e, \"Exception!\");\n    }\n  }\n\n  protected void stopCamera() {\n    if (camera != null) {\n      camera.stopPreview();\n      camera.setPreviewCallback(null);\n      camera.release();\n      camera = null;\n    }\n  }\n\n  private int getCameraId() {\n    CameraInfo ci = new CameraInfo();\n    for (int i = 0; i < Camera.getNumberOfCameras(); i++) {\n      Camera.getCameraInfo(i, ci);\n      if (ci.facing == CameraInfo.CAMERA_FACING_BACK) return i;\n    }\n    return -1; // No camera found\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/java/org/tensorflow/lite/examples/classification/customview/AutoFitTextureView.java",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.classification.customview;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.TextureView;\n\n/** A {@link TextureView} that can be adjusted to a specified aspect ratio. */\npublic class AutoFitTextureView extends TextureView {\n  private int ratioWidth = 0;\n  private int ratioHeight = 0;\n\n  public AutoFitTextureView(final Context context) {\n    this(context, null);\n  }\n\n  public AutoFitTextureView(final Context context, final AttributeSet attrs) {\n    this(context, attrs, 0);\n  }\n\n  public AutoFitTextureView(final Context context, final AttributeSet attrs, final int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  /**\n   * Sets the aspect ratio for this view. The size of the view will be measured based on the ratio\n   * calculated from the parameters. Note that the actual sizes of parameters don't matter, that is,\n   * calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result.\n   *\n   * @param width Relative horizontal size\n   * @param height Relative vertical size\n   */\n  public void setAspectRatio(final int width, final int height) {\n    if (width < 0 || height < 0) {\n      throw new IllegalArgumentException(\"Size cannot be negative.\");\n    }\n    ratioWidth = width;\n    ratioHeight = height;\n    requestLayout();\n  }\n\n  @Override\n  protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {\n    super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n    final int width = MeasureSpec.getSize(widthMeasureSpec);\n    final int height = MeasureSpec.getSize(heightMeasureSpec);\n    if (0 == ratioWidth || 0 == ratioHeight) {\n      setMeasuredDimension(width, height);\n    } else {\n      if (width < height * ratioWidth / ratioHeight) {\n        setMeasuredDimension(width, width * ratioHeight / ratioWidth);\n      } else {\n        setMeasuredDimension(height * ratioWidth / ratioHeight, height);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/java/org/tensorflow/lite/examples/classification/customview/OverlayView.java",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.examples.classification.customview;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport java.util.LinkedList;\nimport java.util.List;\n\n/** A simple View providing a render callback to other classes. */\npublic class OverlayView extends View {\n  private final List<DrawCallback> callbacks = new LinkedList<DrawCallback>();\n\n  public OverlayView(final Context context, final AttributeSet attrs) {\n    super(context, attrs);\n  }\n\n  public void addCallback(final DrawCallback callback) {\n    callbacks.add(callback);\n  }\n\n  @Override\n  public synchronized void draw(final Canvas canvas) {\n    for (final DrawCallback callback : callbacks) {\n      callback.drawCallback(canvas);\n    }\n  }\n\n  /** Interface defining the callback for client classes. */\n  public interface DrawCallback {\n    public void drawCallback(final Canvas canvas);\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/java/org/tensorflow/lite/examples/classification/customview/RecognitionScoreView.java",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.examples.classification.customview;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.util.AttributeSet;\nimport android.util.TypedValue;\nimport android.view.View;\nimport java.util.List;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier.Recognition;\n\npublic class RecognitionScoreView extends View implements ResultsView {\n  private static final float TEXT_SIZE_DIP = 16;\n  private final float textSizePx;\n  private final Paint fgPaint;\n  private final Paint bgPaint;\n  private List<Recognition> results;\n\n  public RecognitionScoreView(final Context context, final AttributeSet set) {\n    super(context, set);\n\n    textSizePx =\n        TypedValue.applyDimension(\n            TypedValue.COMPLEX_UNIT_DIP, TEXT_SIZE_DIP, getResources().getDisplayMetrics());\n    fgPaint = new Paint();\n    fgPaint.setTextSize(textSizePx);\n\n    bgPaint = new Paint();\n    bgPaint.setColor(0xcc4285f4);\n  }\n\n  @Override\n  public void setResults(final List<Recognition> results) {\n    this.results = results;\n    postInvalidate();\n  }\n\n  @Override\n  public void onDraw(final Canvas canvas) {\n    final int x = 10;\n    int y = (int) (fgPaint.getTextSize() * 1.5f);\n\n    canvas.drawPaint(bgPaint);\n\n    if (results != null) {\n      for (final Recognition recog : results) {\n        canvas.drawText(recog.getTitle() + \": \" + recog.getConfidence(), x, y, fgPaint);\n        y += (int) (fgPaint.getTextSize() * 1.5f);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/java/org/tensorflow/lite/examples/classification/customview/ResultsView.java",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.examples.classification.customview;\n\nimport java.util.List;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier.Recognition;\n\npublic interface ResultsView {\n  public void setResults(final List<Recognition> results);\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/java/org/tensorflow/lite/examples/classification/env/BorderedText.java",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.examples.classification.env;\n\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.Paint.Align;\nimport android.graphics.Paint.Style;\nimport android.graphics.Rect;\nimport android.graphics.Typeface;\nimport java.util.Vector;\n\n/** A class that encapsulates the tedious bits of rendering legible, bordered text onto a canvas. */\npublic class BorderedText {\n  private final Paint interiorPaint;\n  private final Paint exteriorPaint;\n\n  private final float textSize;\n\n  /**\n   * Creates a left-aligned bordered text object with a white interior, and a black exterior with\n   * the specified text size.\n   *\n   * @param textSize text size in pixels\n   */\n  public BorderedText(final float textSize) {\n    this(Color.WHITE, Color.BLACK, textSize);\n  }\n\n  /**\n   * Create a bordered text object with the specified interior and exterior colors, text size and\n   * alignment.\n   *\n   * @param interiorColor the interior text color\n   * @param exteriorColor the exterior text color\n   * @param textSize text size in pixels\n   */\n  public BorderedText(final int interiorColor, final int exteriorColor, final float textSize) {\n    interiorPaint = new Paint();\n    interiorPaint.setTextSize(textSize);\n    interiorPaint.setColor(interiorColor);\n    interiorPaint.setStyle(Style.FILL);\n    interiorPaint.setAntiAlias(false);\n    interiorPaint.setAlpha(255);\n\n    exteriorPaint = new Paint();\n    exteriorPaint.setTextSize(textSize);\n    exteriorPaint.setColor(exteriorColor);\n    exteriorPaint.setStyle(Style.FILL_AND_STROKE);\n    exteriorPaint.setStrokeWidth(textSize / 8);\n    exteriorPaint.setAntiAlias(false);\n    exteriorPaint.setAlpha(255);\n\n    this.textSize = textSize;\n  }\n\n  public void setTypeface(Typeface typeface) {\n    interiorPaint.setTypeface(typeface);\n    exteriorPaint.setTypeface(typeface);\n  }\n\n  public void drawText(final Canvas canvas, final float posX, final float posY, final String text) {\n    canvas.drawText(text, posX, posY, exteriorPaint);\n    canvas.drawText(text, posX, posY, interiorPaint);\n  }\n\n  public void drawLines(Canvas canvas, final float posX, final float posY, Vector<String> lines) {\n    int lineNum = 0;\n    for (final String line : lines) {\n      drawText(canvas, posX, posY - getTextSize() * (lines.size() - lineNum - 1), line);\n      ++lineNum;\n    }\n  }\n\n  public void setInteriorColor(final int color) {\n    interiorPaint.setColor(color);\n  }\n\n  public void setExteriorColor(final int color) {\n    exteriorPaint.setColor(color);\n  }\n\n  public float getTextSize() {\n    return textSize;\n  }\n\n  public void setAlpha(final int alpha) {\n    interiorPaint.setAlpha(alpha);\n    exteriorPaint.setAlpha(alpha);\n  }\n\n  public void getTextBounds(\n      final String line, final int index, final int count, final Rect lineBounds) {\n    interiorPaint.getTextBounds(line, index, count, lineBounds);\n  }\n\n  public void setTextAlign(final Align align) {\n    interiorPaint.setTextAlign(align);\n    exteriorPaint.setTextAlign(align);\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/java/org/tensorflow/lite/examples/classification/env/ImageUtils.java",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.examples.classification.env;\n\nimport android.graphics.Bitmap;\nimport android.os.Environment;\nimport java.io.File;\nimport java.io.FileOutputStream;\n\n/** Utility class for manipulating images. */\npublic class ImageUtils {\n  // This value is 2 ^ 18 - 1, and is used to clamp the RGB values before their ranges\n  // are normalized to eight bits.\n  static final int kMaxChannelValue = 262143;\n\n  @SuppressWarnings(\"unused\")\n  private static final Logger LOGGER = new Logger();\n\n  /**\n   * Utility method to compute the allocated size in bytes of a YUV420SP image of the given\n   * dimensions.\n   */\n  public static int getYUVByteSize(final int width, final int height) {\n    // The luminance plane requires 1 byte per pixel.\n    final int ySize = width * height;\n\n    // The UV plane works on 2x2 blocks, so dimensions with odd size must be rounded up.\n    // Each 2x2 block takes 2 bytes to encode, one each for U and V.\n    final int uvSize = ((width + 1) / 2) * ((height + 1) / 2) * 2;\n\n    return ySize + uvSize;\n  }\n\n  /**\n   * Saves a Bitmap object to disk for analysis.\n   *\n   * @param bitmap The bitmap to save.\n   */\n  public static void saveBitmap(final Bitmap bitmap) {\n    saveBitmap(bitmap, \"preview.png\");\n  }\n\n  /**\n   * Saves a Bitmap object to disk for analysis.\n   *\n   * @param bitmap The bitmap to save.\n   * @param filename The location to save the bitmap to.\n   */\n  public static void saveBitmap(final Bitmap bitmap, final String filename) {\n    final String root =\n        Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + \"tensorflow\";\n    LOGGER.i(\"Saving %dx%d bitmap to %s.\", bitmap.getWidth(), bitmap.getHeight(), root);\n    final File myDir = new File(root);\n\n    if (!myDir.mkdirs()) {\n      LOGGER.i(\"Make dir failed\");\n    }\n\n    final String fname = filename;\n    final File file = new File(myDir, fname);\n    if (file.exists()) {\n      file.delete();\n    }\n    try {\n      final FileOutputStream out = new FileOutputStream(file);\n      bitmap.compress(Bitmap.CompressFormat.PNG, 99, out);\n      out.flush();\n      out.close();\n    } catch (final Exception e) {\n      LOGGER.e(e, \"Exception!\");\n    }\n  }\n\n  public static void convertYUV420SPToARGB8888(byte[] input, int width, int height, int[] output) {\n    final int frameSize = width * height;\n    for (int j = 0, yp = 0; j < height; j++) {\n      int uvp = frameSize + (j >> 1) * width;\n      int u = 0;\n      int v = 0;\n\n      for (int i = 0; i < width; i++, yp++) {\n        int y = 0xff & input[yp];\n        if ((i & 1) == 0) {\n          v = 0xff & input[uvp++];\n          u = 0xff & input[uvp++];\n        }\n\n        output[yp] = YUV2RGB(y, u, v);\n      }\n    }\n  }\n\n  private static int YUV2RGB(int y, int u, int v) {\n    // Adjust and check YUV values\n    y = (y - 16) < 0 ? 0 : (y - 16);\n    u -= 128;\n    v -= 128;\n\n    // This is the floating point equivalent. We do the conversion in integer\n    // because some Android devices do not have floating point in hardware.\n    // nR = (int)(1.164 * nY + 2.018 * nU);\n    // nG = (int)(1.164 * nY - 0.813 * nV - 0.391 * nU);\n    // nB = (int)(1.164 * nY + 1.596 * nV);\n    int y1192 = 1192 * y;\n    int r = (y1192 + 1634 * v);\n    int g = (y1192 - 833 * v - 400 * u);\n    int b = (y1192 + 2066 * u);\n\n    // Clipping RGB values to be inside boundaries [ 0 , kMaxChannelValue ]\n    r = r > kMaxChannelValue ? kMaxChannelValue : (r < 0 ? 0 : r);\n    g = g > kMaxChannelValue ? kMaxChannelValue : (g < 0 ? 0 : g);\n    b = b > kMaxChannelValue ? kMaxChannelValue : (b < 0 ? 0 : b);\n\n    return 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);\n  }\n\n  public static void convertYUV420ToARGB8888(\n      byte[] yData,\n      byte[] uData,\n      byte[] vData,\n      int width,\n      int height,\n      int yRowStride,\n      int uvRowStride,\n      int uvPixelStride,\n      int[] out) {\n    int yp = 0;\n    for (int j = 0; j < height; j++) {\n      int pY = yRowStride * j;\n      int pUV = uvRowStride * (j >> 1);\n\n      for (int i = 0; i < width; i++) {\n        int uv_offset = pUV + (i >> 1) * uvPixelStride;\n\n        out[yp++] = YUV2RGB(0xff & yData[pY + i], 0xff & uData[uv_offset], 0xff & vData[uv_offset]);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/java/org/tensorflow/lite/examples/classification/env/Logger.java",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.examples.classification.env;\n\nimport android.util.Log;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/** Wrapper for the platform log function, allows convenient message prefixing and log disabling. */\npublic final class Logger {\n  private static final String DEFAULT_TAG = \"tensorflow\";\n  private static final int DEFAULT_MIN_LOG_LEVEL = Log.DEBUG;\n\n  // Classes to be ignored when examining the stack trace\n  private static final Set<String> IGNORED_CLASS_NAMES;\n\n  static {\n    IGNORED_CLASS_NAMES = new HashSet<String>(3);\n    IGNORED_CLASS_NAMES.add(\"dalvik.system.VMStack\");\n    IGNORED_CLASS_NAMES.add(\"java.lang.Thread\");\n    IGNORED_CLASS_NAMES.add(Logger.class.getCanonicalName());\n  }\n\n  private final String tag;\n  private final String messagePrefix;\n  private int minLogLevel = DEFAULT_MIN_LOG_LEVEL;\n\n  /**\n   * Creates a Logger using the class name as the message prefix.\n   *\n   * @param clazz the simple name of this class is used as the message prefix.\n   */\n  public Logger(final Class<?> clazz) {\n    this(clazz.getSimpleName());\n  }\n\n  /**\n   * Creates a Logger using the specified message prefix.\n   *\n   * @param messagePrefix is prepended to the text of every message.\n   */\n  public Logger(final String messagePrefix) {\n    this(DEFAULT_TAG, messagePrefix);\n  }\n\n  /**\n   * Creates a Logger with a custom tag and a custom message prefix. If the message prefix is set to\n   *\n   * <pre>null</pre>\n   *\n   * , the caller's class name is used as the prefix.\n   *\n   * @param tag identifies the source of a log message.\n   * @param messagePrefix prepended to every message if non-null. If null, the name of the caller is\n   *     being used\n   */\n  public Logger(final String tag, final String messagePrefix) {\n    this.tag = tag;\n    final String prefix = messagePrefix == null ? getCallerSimpleName() : messagePrefix;\n    this.messagePrefix = (prefix.length() > 0) ? prefix + \": \" : prefix;\n  }\n\n  /** Creates a Logger using the caller's class name as the message prefix. */\n  public Logger() {\n    this(DEFAULT_TAG, null);\n  }\n\n  /** Creates a Logger using the caller's class name as the message prefix. */\n  public Logger(final int minLogLevel) {\n    this(DEFAULT_TAG, null);\n    this.minLogLevel = minLogLevel;\n  }\n\n  /**\n   * Return caller's simple name.\n   *\n   * <p>Android getStackTrace() returns an array that looks like this: stackTrace[0]:\n   * dalvik.system.VMStack stackTrace[1]: java.lang.Thread stackTrace[2]:\n   * com.google.android.apps.unveil.env.UnveilLogger stackTrace[3]:\n   * com.google.android.apps.unveil.BaseApplication\n   *\n   * <p>This function returns the simple version of the first non-filtered name.\n   *\n   * @return caller's simple name\n   */\n  private static String getCallerSimpleName() {\n    // Get the current callstack so we can pull the class of the caller off of it.\n    final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();\n\n    for (final StackTraceElement elem : stackTrace) {\n      final String className = elem.getClassName();\n      if (!IGNORED_CLASS_NAMES.contains(className)) {\n        // We're only interested in the simple name of the class, not the complete package.\n        final String[] classParts = className.split(\"\\\\.\");\n        return classParts[classParts.length - 1];\n      }\n    }\n\n    return Logger.class.getSimpleName();\n  }\n\n  public void setMinLogLevel(final int minLogLevel) {\n    this.minLogLevel = minLogLevel;\n  }\n\n  public boolean isLoggable(final int logLevel) {\n    return logLevel >= minLogLevel || Log.isLoggable(tag, logLevel);\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  private String toMessage(final String format, final Object... args) {\n    return messagePrefix + (args.length > 0 ? String.format(format, args) : format);\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  public void v(final String format, final Object... args) {\n    if (isLoggable(Log.VERBOSE)) {\n      Log.v(tag, toMessage(format, args));\n    }\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  public void v(final Throwable t, final String format, final Object... args) {\n    if (isLoggable(Log.VERBOSE)) {\n      Log.v(tag, toMessage(format, args), t);\n    }\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  public void d(final String format, final Object... args) {\n    if (isLoggable(Log.DEBUG)) {\n      Log.d(tag, toMessage(format, args));\n    }\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  public void d(final Throwable t, final String format, final Object... args) {\n    if (isLoggable(Log.DEBUG)) {\n      Log.d(tag, toMessage(format, args), t);\n    }\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  public void i(final String format, final Object... args) {\n    if (isLoggable(Log.INFO)) {\n      Log.i(tag, toMessage(format, args));\n    }\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  public void i(final Throwable t, final String format, final Object... args) {\n    if (isLoggable(Log.INFO)) {\n      Log.i(tag, toMessage(format, args), t);\n    }\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  public void w(final String format, final Object... args) {\n    if (isLoggable(Log.WARN)) {\n      Log.w(tag, toMessage(format, args));\n    }\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  public void w(final Throwable t, final String format, final Object... args) {\n    if (isLoggable(Log.WARN)) {\n      Log.w(tag, toMessage(format, args), t);\n    }\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  public void e(final String format, final Object... args) {\n    if (isLoggable(Log.ERROR)) {\n      Log.e(tag, toMessage(format, args));\n    }\n  }\n\n  @SuppressWarnings(\"AnnotateFormatMethod\")\n  public void e(final Throwable t, final String format, final Object... args) {\n    if (isLoggable(Log.ERROR)) {\n      Log.e(tag, toMessage(format, args), t);\n    }\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/java/org/tensorflow/lite/examples/classification/tflite/Classifier.java",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.examples.classification.tflite;\n\nimport android.app.Activity;\nimport android.graphics.Bitmap;\nimport android.graphics.RectF;\nimport android.os.SystemClock;\nimport android.os.Trace;\nimport java.io.IOException;\nimport java.nio.MappedByteBuffer;\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.PriorityQueue;\nimport org.tensorflow.lite.DataType;\nimport org.tensorflow.lite.Interpreter;\nimport org.tensorflow.lite.examples.classification.env.Logger;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier.Device;\nimport org.tensorflow.lite.gpu.GpuDelegate;\nimport org.tensorflow.lite.support.common.FileUtil;\nimport org.tensorflow.lite.support.common.TensorOperator;\nimport org.tensorflow.lite.support.common.TensorProcessor;\nimport org.tensorflow.lite.support.image.ImageProcessor;\nimport org.tensorflow.lite.support.image.TensorImage;\nimport org.tensorflow.lite.support.image.ops.ResizeOp;\nimport org.tensorflow.lite.support.image.ops.ResizeOp.ResizeMethod;\nimport org.tensorflow.lite.support.image.ops.ResizeWithCropOrPadOp;\nimport org.tensorflow.lite.support.image.ops.Rot90Op;\nimport org.tensorflow.lite.support.label.TensorLabel;\nimport org.tensorflow.lite.support.tensorbuffer.TensorBuffer;\n\n/** A classifier specialized to label images using TensorFlow Lite. */\npublic abstract class Classifier {\n  private static final Logger LOGGER = new Logger();\n\n  /** The runtime device type used for executing classification. */\n  public enum Device {\n    CPU,\n    GPU\n  }\n\n  /** Number of results to show in the UI. */\n  private static final int MAX_RESULTS = 3;\n\n  /** The loaded TensorFlow Lite model. */\n  private MappedByteBuffer tfliteModel;\n\n  /** Image size along the x axis. */\n  private final int imageSizeX;\n\n  /** Image size along the y axis. */\n  private final int imageSizeY;\n\n  /** Optional GPU delegate for acceleration. */\n  // TODO: Declare a GPU delegate\n\n\n  /** An instance of the driver class to run model inference with Tensorflow Lite. */\n  // TODO: Declare a TFLite interpreter\n\n\n  /** Options for configuring the Interpreter. */\n  private final Interpreter.Options tfliteOptions = new Interpreter.Options();\n\n  /** Labels corresponding to the output of the vision model. */\n  private List<String> labels;\n\n  /** Input image TensorBuffer. */\n  private TensorImage inputImageBuffer;\n\n  /** Output probability TensorBuffer. */\n  private final TensorBuffer outputProbabilityBuffer;\n\n  /** Processer to apply post processing of the output probability. */\n  private final TensorProcessor probabilityProcessor;\n\n  /**\n   * Creates a classifier with the provided configuration.\n   *\n   * @param activity The current Activity.\n   * @param device The device to use for classification.\n   * @param numThreads The number of threads to use for classification.\n   * @return A classifier with the desired configuration.\n   */\n  public static Classifier create(Activity activity, Device device, int numThreads)\n      throws IOException {\n\n    return new ClassifierFloatMobileNet(activity, device, numThreads);\n  }\n\n  /** An immutable result returned by a Classifier describing what was recognized. */\n  public static class Recognition {\n    /**\n     * A unique identifier for what has been recognized. Specific to the class, not the instance of\n     * the object.\n     */\n    private final String id;\n\n    /** Display name for the recognition. */\n    private final String title;\n\n    /**\n     * A sortable score for how good the recognition is relative to others. Higher should be better.\n     */\n    private final Float confidence;\n\n    /** Optional location within the source image for the location of the recognized object. */\n    private RectF location;\n\n    public Recognition(\n        final String id, final String title, final Float confidence, final RectF location) {\n      this.id = id;\n      this.title = title;\n      this.confidence = confidence;\n      this.location = location;\n    }\n\n    public String getId() {\n      return id;\n    }\n\n    public String getTitle() {\n      return title;\n    }\n\n    public Float getConfidence() {\n      return confidence;\n    }\n\n    public RectF getLocation() {\n      return new RectF(location);\n    }\n\n    public void setLocation(RectF location) {\n      this.location = location;\n    }\n\n    @Override\n    public String toString() {\n      String resultString = \"\";\n      if (id != null) {\n        resultString += \"[\" + id + \"] \";\n      }\n\n      if (title != null) {\n        resultString += title + \" \";\n      }\n\n      if (confidence != null) {\n        resultString += String.format(\"(%.1f%%) \", confidence * 100.0f);\n      }\n\n      if (location != null) {\n        resultString += location + \" \";\n      }\n\n      return resultString.trim();\n    }\n  }\n\n  /** Initializes a {@code Classifier}. */\n  protected Classifier(Activity activity, Device device, int numThreads) throws IOException {\n    tfliteModel = FileUtil.loadMappedFile(activity, getModelPath());\n    switch (device) {\n      case GPU:\n        // TODO: Create a GPU delegate instance and add it to the interpreter options\n\n        break;\n      case CPU:\n        break;\n    }\n    tfliteOptions.setNumThreads(numThreads);\n\n    // TODO: Create a TFLite interpreter instance\n\n\n    // Loads labels out from the label file.\n    labels = FileUtil.loadLabels(activity, getLabelPath());\n\n    // Reads type and shape of input and output tensors, respectively.\n    int imageTensorIndex = 0;\n    int[] imageShape = tflite.getInputTensor(imageTensorIndex).shape(); // {1, height, width, 3}\n    imageSizeY = imageShape[1];\n    imageSizeX = imageShape[2];\n    DataType imageDataType = tflite.getInputTensor(imageTensorIndex).dataType();\n    int probabilityTensorIndex = 0;\n    int[] probabilityShape =\n        tflite.getOutputTensor(probabilityTensorIndex).shape(); // {1, NUM_CLASSES}\n    DataType probabilityDataType = tflite.getOutputTensor(probabilityTensorIndex).dataType();\n\n    // Creates the input tensor.\n    inputImageBuffer = new TensorImage(imageDataType);\n\n    // Creates the output tensor and its processor.\n    outputProbabilityBuffer = TensorBuffer.createFixedSize(probabilityShape, probabilityDataType);\n\n    // Creates the post processor for the output probability.\n    probabilityProcessor = new TensorProcessor.Builder().add(getPostprocessNormalizeOp()).build();\n\n    LOGGER.d(\"Created a Tensorflow Lite Image Classifier.\");\n  }\n\n  /** Runs inference and returns the classification results. */\n  public List<Recognition> recognizeImage(final Bitmap bitmap, int sensorOrientation) {\n    // Logs this method so that it can be analyzed with systrace.\n    Trace.beginSection(\"recognizeImage\");\n\n    Trace.beginSection(\"loadImage\");\n    long startTimeForLoadImage = SystemClock.uptimeMillis();\n    inputImageBuffer = loadImage(bitmap, sensorOrientation);\n    long endTimeForLoadImage = SystemClock.uptimeMillis();\n    Trace.endSection();\n    LOGGER.v(\"Timecost to load the image: \" + (endTimeForLoadImage - startTimeForLoadImage));\n\n    // Runs the inference call.\n    Trace.beginSection(\"runInference\");\n    long startTimeForReference = SystemClock.uptimeMillis();\n    // TODO: Run TFLite inference\n\n    long endTimeForReference = SystemClock.uptimeMillis();\n    Trace.endSection();\n    LOGGER.v(\"Timecost to run model inference: \" + (endTimeForReference - startTimeForReference));\n\n    // Gets the map of label and probability.\n    // TODO: Use TensorLabel from TFLite Support Library to associate the probabilities\n    //       with category labels\n\n    Trace.endSection();\n\n    // Gets top-k results.\n    return getTopKProbability(labeledProbability);\n  }\n\n  /** Closes the interpreter and model to release resources. */\n  public void close() {\n    if (tflite != null) {\n      // TODO: Close the interpreter\n\n    }\n    // TODO: Close the GPU delegate\n\n\n    tfliteModel = null;\n  }\n\n  /** Get the image size along the x axis. */\n  public int getImageSizeX() {\n    return imageSizeX;\n  }\n\n  /** Get the image size along the y axis. */\n  public int getImageSizeY() {\n    return imageSizeY;\n  }\n\n  /** Loads input image, and applies preprocessing. */\n  private TensorImage loadImage(final Bitmap bitmap, int sensorOrientation) {\n    // Loads bitmap into a TensorImage.\n    inputImageBuffer.load(bitmap);\n\n    // Creates processor for the TensorImage.\n    int cropSize = Math.min(bitmap.getWidth(), bitmap.getHeight());\n    int numRoration = sensorOrientation / 90;\n    // TODO: Define an ImageProcessor from TFLite Support Library to do preprocessing\n    ImageProcessor imageProcessor =\n            new ImageProcessor.Builder()\n\n\n\n\n                .build();\n    return imageProcessor.process(inputImageBuffer);\n  }\n\n  /** Gets the top-k results. */\n  private static List<Recognition> getTopKProbability(Map<String, Float> labelProb) {\n    // Find the best classifications.\n    PriorityQueue<Recognition> pq =\n        new PriorityQueue<>(\n            MAX_RESULTS,\n            new Comparator<Recognition>() {\n              @Override\n              public int compare(Recognition lhs, Recognition rhs) {\n                // Intentionally reversed to put high confidence at the head of the queue.\n                return Float.compare(rhs.getConfidence(), lhs.getConfidence());\n              }\n            });\n\n    for (Map.Entry<String, Float> entry : labelProb.entrySet()) {\n      pq.add(new Recognition(\"\" + entry.getKey(), entry.getKey(), entry.getValue(), null));\n    }\n\n    final ArrayList<Recognition> recognitions = new ArrayList<>();\n    int recognitionsSize = Math.min(pq.size(), MAX_RESULTS);\n    for (int i = 0; i < recognitionsSize; ++i) {\n      recognitions.add(pq.poll());\n    }\n    return recognitions;\n  }\n\n  /** Gets the name of the model file stored in Assets. */\n  protected abstract String getModelPath();\n\n  /** Gets the name of the label file stored in Assets. */\n  protected abstract String getLabelPath();\n\n  /** Gets the TensorOperator to nomalize the input image in preprocessing. */\n  protected abstract TensorOperator getPreprocessNormalizeOp();\n\n  /**\n   * Gets the TensorOperator to dequantize the output probability in post processing.\n   *\n   * <p>For quantized model, we need de-quantize the prediction with NormalizeOp (as they are all\n   * essentially linear transformation). For float model, de-quantize is not required. But to\n   * uniform the API, de-quantize is added to float model too. Mean and std are set to 0.0f and\n   * 1.0f, respectively.\n   */\n  protected abstract TensorOperator getPostprocessNormalizeOp();\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/java/org/tensorflow/lite/examples/classification/tflite/ClassifierFloatMobileNet.java",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.examples.classification.tflite;\n\nimport android.app.Activity;\nimport java.io.IOException;\nimport org.tensorflow.lite.examples.classification.tflite.Classifier.Device;\nimport org.tensorflow.lite.support.common.TensorOperator;\nimport org.tensorflow.lite.support.common.ops.NormalizeOp;\n\n/** This TensorFlowLite classifier works with the float MobileNet model. */\npublic class ClassifierFloatMobileNet extends Classifier {\n\n  /** Float MobileNet requires additional normalization of the used input. */\n  private static final float IMAGE_MEAN = 0f;\n\n  private static final float IMAGE_STD = 255f;\n\n  /**\n   * Float model does not need dequantization in the post-processing. Setting mean and std as 0.0f\n   * and 1.0f, repectively, to bypass the normalization.\n   */\n  private static final float PROBABILITY_MEAN = 0.0f;\n\n  private static final float PROBABILITY_STD = 1.0f;\n\n  /**\n   * Initializes a {@code ClassifierFloatMobileNet}.\n   *\n   * @param activity\n   */\n  public ClassifierFloatMobileNet(Activity activity, Device device, int numThreads)\n      throws IOException {\n    super(activity, device, numThreads);\n  }\n\n  // TODO: Specify model.tflite as the model file and labels.txt as the label file\n  @Override\n  protected String getModelPath() {\n\n  }\n\n  @Override\n  protected String getLabelPath() {\n\n  }\n\n  @Override\n  protected TensorOperator getPreprocessNormalizeOp() {\n    return new NormalizeOp(IMAGE_MEAN, IMAGE_STD);\n  }\n\n  @Override\n  protected TensorOperator getPostprocessNormalizeOp() {\n    return new NormalizeOp(PROBABILITY_MEAN, PROBABILITY_STD);\n  }\n}\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/res/drawable/bottom_sheet_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <corners\n        android:topLeftRadius=\"@dimen/tfe_bottom_sheet_corner_radius\"\n        android:topRightRadius=\"@dimen/tfe_bottom_sheet_corner_radius\" />\n    <padding android:top=\"@dimen/tfe_bottom_sheet_top_padding\" />\n    <solid android:color=\"@android:color/white\" />\n</shape>"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/res/drawable/ic_baseline_add.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/res/drawable/ic_baseline_remove.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13H5v-2h14v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillColor=\"#26A69A\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n</vector>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/res/drawable/rectangle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/listview_background_shape\">\n    <stroke\n        android:width=\"1dp\"\n        android:color=\"@android:color/darker_gray\" />\n    <padding\n        android:bottom=\"2dp\"\n        android:left=\"2dp\"\n        android:right=\"2dp\"\n        android:top=\"2dp\" />\n    <solid android:color=\"#ffffffff\" />\n</shape>"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n  <path\n      android:fillType=\"evenOdd\"\n      android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\">\n    <aapt:attr name=\"android:fillColor\">\n      <gradient\n          android:endX=\"78.5885\"\n          android:endY=\"90.9159\"\n          android:startX=\"48.7653\"\n          android:startY=\"61.0927\"\n          android:type=\"linear\">\n        <item\n            android:color=\"#44000000\"\n            android:offset=\"0.0\"/>\n        <item\n            android:color=\"#00000000\"\n            android:offset=\"1.0\"/>\n      </gradient>\n    </aapt:attr>\n  </path>\n  <path\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"nonZero\"\n      android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\"/>\n</vector>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/res/layout/tfe_ic_activity_camera.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"#00000000\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"#00000000\"\n        android:orientation=\"vertical\">\n\n\n        <FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n            xmlns:tools=\"http://schemas.android.com/tools\"\n            android:id=\"@+id/container\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:background=\"@android:color/black\"\n            tools:context=\"org.tensorflow.lite.examples.classification.CameraActivity\" />\n\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:layout_alignParentTop=\"true\"\n            android:background=\"@color/tfe_semi_transparent\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/tfl2_logo\" />\n        </androidx.appcompat.widget.Toolbar>\n\n    </RelativeLayout>\n\n    <include\n        android:id=\"@+id/bottom_sheet_layout\"\n        layout=\"@layout/tfe_ic_layout_bottom_sheet\" />\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/res/layout/tfe_ic_camera_connection_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <org.tensorflow.lite.examples.classification.customview.AutoFitTextureView\n        android:id=\"@+id/texture\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentTop=\"true\" />\n\n\n</RelativeLayout>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/res/layout/tfe_ic_layout_bottom_sheet.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/bottom_sheet_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_gravity=\"center_horizontal\"\n    android:background=\"@drawable/bottom_sheet_bg\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:padding=\"8dp\"\n    app:behavior_hideable=\"true\"\n    app:layout_behavior=\"com.google.android.material.bottomsheet.BottomSheetBehavior\">\n\n    <LinearLayout\n        android:id=\"@+id/gesture_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:paddingTop=\"10dp\"\n        android:paddingBottom=\"10dp\">\n\n        <ImageView\n            android:id=\"@+id/bottom_sheet_arrow\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:src=\"@drawable/icn_chevron_up\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/detected_item\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"@android:color/black\"\n                android:textSize=\"16sp\" />\n\n            <TextView\n                android:id=\"@+id/detected_item_value\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:textColor=\"@android:color/black\"\n                android:textSize=\"16sp\" />\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/detected_item1\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"@android:color/darker_gray\"\n                android:textSize=\"12sp\" />\n\n            <TextView\n                android:id=\"@+id/detected_item1_value\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:textColor=\"@android:color/darker_gray\"\n                android:textSize=\"12sp\" />\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/detected_item2\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"@android:color/darker_gray\"\n                android:textSize=\"12sp\" />\n\n            <TextView\n                android:id=\"@+id/detected_item2_value\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:textColor=\"@android:color/darker_gray\"\n                android:textSize=\"12sp\" />\n\n        </LinearLayout>\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"1px\"\n            android:layout_marginTop=\"8dp\"\n            android:background=\"@android:color/darker_gray\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:id=\"@+id/frame\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:text=\"Frame\"\n            android:textColor=\"@android:color/black\" />\n\n        <TextView\n            android:id=\"@+id/frame_info\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:gravity=\"right\"\n            android:text=\"640*480\"\n            android:textColor=\"@android:color/black\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:id=\"@+id/crop\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:text=\"Crop\"\n            android:textColor=\"@android:color/black\" />\n\n        <TextView\n            android:id=\"@+id/crop_info\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:gravity=\"right\"\n            android:text=\"640*480\"\n            android:textColor=\"@android:color/black\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:id=\"@+id/view\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:text=\"View\"\n            android:textColor=\"@android:color/black\" />\n\n        <TextView\n            android:id=\"@+id/view_info\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:gravity=\"right\"\n            android:text=\"640*480\"\n            android:textColor=\"@android:color/black\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:id=\"@+id/rotation\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:text=\"Rotation\"\n            android:textColor=\"@android:color/black\" />\n\n        <TextView\n            android:id=\"@+id/rotation_info\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:gravity=\"right\"\n            android:text=\"640*480\"\n            android:textColor=\"@android:color/black\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:id=\"@+id/inference\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:text=\"Inference Time\"\n            android:textColor=\"@android:color/black\" />\n\n        <TextView\n            android:id=\"@+id/inference_info\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:gravity=\"right\"\n            android:text=\"640*480\"\n            android:textColor=\"@android:color/black\" />\n    </LinearLayout>\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1px\"\n        android:layout_marginTop=\"10dp\"\n        android:background=\"@android:color/darker_gray\" />\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"10dp\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:text=\"Threads\"\n            android:textColor=\"@android:color/black\" />\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentRight=\"true\"\n            android:background=\"@drawable/rectangle\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\"\n            android:padding=\"4dp\">\n\n            <ImageView\n                android:id=\"@+id/minus\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/ic_baseline_remove\" />\n\n            <TextView\n                android:id=\"@+id/threads\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"10dp\"\n                android:layout_marginRight=\"10dp\"\n                android:text=\"1\"\n                android:textColor=\"@android:color/black\"\n                android:textSize=\"14sp\" />\n            <ImageView\n                android:id=\"@+id/plus\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/ic_baseline_add\" />\n\n        </LinearLayout>\n    </RelativeLayout>\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1px\"\n        android:layout_marginTop=\"10dp\"\n        android:background=\"@android:color/darker_gray\" />\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:text=\"@string/tfe_ic_device\"\n            android:textColor=\"@android:color/black\" />\n\n        <Spinner\n            android:id=\"@+id/device_spinner\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:layout_alignParentRight=\"true\"\n            android:entries=\"@array/tfe_ic_devices\"\n            android:prompt=\"@string/tfe_ic_device\" />\n    </RelativeLayout>\n</LinearLayout>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"tfe_color_primary\">#ffa800</color>\n    <color name=\"tfe_color_primary_dark\">#ff6f00</color>\n    <color name=\"tfe_color_accent\">#425066</color>\n\n    <color name=\"tfe_semi_transparent\">#66000000</color>\n</resources>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <dimen name=\"tfe_bottom_sheet_corner_radius\">15dp</dimen>\n    <dimen name=\"tfe_bottom_sheet_top_padding\">8dp</dimen>\n</resources>"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"tfe_ic_app_name\" translation_description=\"Image Classification demo app [CHAR_LIMIT=40]\">TFL Classify</string>\n    <string name=\"tfe_ic_camera_error\" translation_description=\"Error regarding camera support[CHAR_LIMIT=40]\">This device doesn\\'t support Camera2 API.</string>\n    <string name=\"tfe_ic_gpu_quant_error\" translation_description=\"Error regarding GPU support for Quant models[CHAR_LIMIT=60]\">GPU does not yet supported quantized models.</string>\n    <string name=\"tfe_ic_model\" translatable=\"false\">Model:</string>\n    <string-array name=\"tfe_ic_models\" translatable=\"false\">\n        <item>Quantized_EfficientNet</item>\n        <item>Float_EfficientNet</item>\n        <item>Quantized_MobileNet</item>\n        <item>Float_MobileNet</item>\n    </string-array>\n\n    <string name=\"tfe_ic_device\" translatable=\"false\">Device:</string>\n    <string-array name=\"tfe_ic_devices\" translatable=\"false\">\n        <item>CPU</item>\n        <!-- TODO: Add GPU  -->\n        \n    </string-array>\n</resources>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme.ImageClassification\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/tfe_color_primary</item>\n        <item name=\"colorPrimaryDark\">@color/tfe_color_primary_dark</item>\n        <item name=\"colorAccent\">@color/tfe_color_accent</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:4.0.0'\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n        maven {\n            name 'ossrh-snapshot'\n            url 'https://oss.sonatype.org/content/repositories/snapshots'\n        }\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\nandroid.useAndroidX=true\nandroid.enableJetifier=true\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/codelabs/flower_classification/android/start/settings.gradle",
    "content": "rootProject.name = 'TFLite Flower Classification Codelab (start) App'\ninclude ':app'\n"
  },
  {
    "path": "lite/codelabs/flower_classification/ml/Flower_Classification_with_TFLite_Model_Maker.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"h2q27gKz1H20\"\n      },\n      \"source\": [\n        \"##### Copyright 2020 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"TUfAcER1oUS6\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Gb7qyhNL1yWt\"\n      },\n      \"source\": [\n        \"# Flower classification with TensorFlow Lite Model Maker with TensorFlow 2.0\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"nDABAblytltI\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/lite/codelabs/flower_classification/ml/Flower_Classification_with_TFLite_Model_Maker.ipynb\\\">      \\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />\\n\",\n        \"    Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/lite/codelabs/flower_classification/ml/Flower_Classification_with_TFLite_Model_Maker.ipynb\\\">\\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />\\n\",\n        \"    View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"m86-Nh4pMHqY\"\n      },\n      \"source\": [\n        \"Model Maker library simplifies the process of adapting and converting a TensorFlow neural-network model to particular input data when deploying this model for on-device ML applications.\\n\",\n        \"\\n\",\n        \"This notebook shows an end-to-end example that utilizes this Model Maker library to illustrate the adaption and conversion of a commonly-used image classification model to classify flowers on a mobile device.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"bcLF2PKkSbV3\"\n      },\n      \"source\": [\n        \"## Prerequisites\\n\",\n        \"\\n\",\n        \"To run this example, we first need to install serveral required packages, including Model Maker package that in github [repo](https://github.com/tensorflow/examples/tree/master/tensorflow_examples/lite/model_maker).\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"6cv3K3oaksJv\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!pip install tflite-model-maker\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Gx1HGRoFQ54j\"\n      },\n      \"source\": [\n        \"Import the required packages.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"XtxiUeZEiXpt\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import numpy as np\\n\",\n        \"\\n\",\n        \"import tensorflow as tf\\n\",\n        \"assert tf.__version__.startswith('2')\\n\",\n        \"\\n\",\n        \"from tflite_model_maker import model_spec\\n\",\n        \"from tflite_model_maker import image_classifier\\n\",\n        \"from tflite_model_maker.image_classifier import DataLoader\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"KKRaYHABpob5\"\n      },\n      \"source\": [\n        \"## Simple End-to-End Example\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"SiZZ5DHXotaW\"\n      },\n      \"source\": [\n        \"### Get the data path\\n\",\n        \"\\n\",\n        \"Let's get some images to play with this simple end-to-end example. Hundreds of images is a good start for Model Maker while more data could achieve better accuracy.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"3jz5x0JoskPv\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"image_path = tf.keras.utils.get_file(\\n\",\n        \"      'flower_photos',\\n\",\n        \"      'https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',\\n\",\n        \"      untar=True)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"a55MR6i6nuDm\"\n      },\n      \"source\": [\n        \"You could replace `image_path` with your own image folders. As for uploading data to colab, you could find the upload button in the left sidebar shown in the image below with the red rectangle. Just have a try to upload a zip file and unzip it. The root file path is the current path.\\n\",\n        \"\\n\",\n        \"<img src=\\\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/screenshots/model_maker_image_classification.png\\\" alt=\\\"Upload File\\\" width=\\\"800\\\" hspace=\\\"100\\\">\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"NNRNv_mloS89\"\n      },\n      \"source\": [\n        \"If you prefer not to upload your images to the cloud, you could try to run the library locally following the [guide](https://github.com/tensorflow/examples/tree/master/tensorflow_examples/lite/model_maker) in github.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"w-VDriAdsowu\"\n      },\n      \"source\": [\n        \"### Run the example\\n\",\n        \"The example just consists of 4 lines of code as shown below, each of which representing one step of the overall process.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"6ahtcO86tZBL\"\n      },\n      \"source\": [\n        \"1.   Load input data specific to an on-device ML app. Split it to training data and testing data.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"lANoNS_gtdH1\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"data = DataLoader.from_folder(image_path)\\n\",\n        \"train_data, test_data = data.split(0.9)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Y_9IWyIztuRF\"\n      },\n      \"source\": [\n        \"2. Customize the TensorFlow model.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"yRXMZbrwtyRD\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = image_classifier.create(train_data)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"oxU2fDr-t2Ya\"\n      },\n      \"source\": [\n        \"3. Evaluate the model.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"wQr02VxJt6Cs\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"loss, accuracy = model.evaluate(test_data)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"eVZw9zU8t84y\"\n      },\n      \"source\": [\n        \"4.  Export to TensorFlow Lite model.\\n\",\n        \"You could download it in the left sidebar same as the uploading part for your own use.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Zb-eIzfluCoa\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model.export(export_dir='.', with_metadata=False)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"pyju1qc_v-wy\"\n      },\n      \"source\": [\n        \"After this simple 4 steps, we can now download the model and label files, and continue to the next step in the [codelab](https://codelabs.developers.google.com/codelabs/recognize-flowers-with-tensorflow-on-android/#4).\\n\",\n        \"\\n\",\n        \"For a more comprehensive guide to TFLite Model Maker, please refer to this [notebook](https://colab.sandbox.google.com/github/tensorflow/examples/blob/master/tensorflow_examples/lite/model_maker/demo/image_classification.ipynb) and its [documentation](https://github.com/tensorflow/examples/tree/master/tensorflow_examples/lite/model_maker).\\n\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"Flower_Classification_with_TFLite_Model_Maker.ipynb\",\n      \"provenance\": [],\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/app/build.gradle",
    "content": "apply plugin: 'com.android.application'\napply plugin: 'com.google.android.gms.strict-version-matcher-plugin'\n\n\nandroid {\n    compileSdk 31\n\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.accelerationservice\"\n        minSdk 21\n        targetSdk 31\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        all {\n            proguardFiles 'proguard-rules.pro'\n        }\n        release {\n            minifyEnabled true\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n\n    aaptOptions {\n        noCompress \"bin\"\n        noCompress \"tflite\"\n    }\n}\n\n\ndependencies {\n    implementation \"com.google.android.gms:play-services-tflite-acceleration-service:16.4.0-beta01\"\n\n    implementation \"com.google.android.gms:play-services-tflite-java:16.4.0\"\n    implementation \"com.google.android.gms:play-services-tflite-support:16.4.0\"\n    implementation \"com.google.android.gms:play-services-tflite-gpu:16.4.0\"\n\n    implementation \"com.google.android.gms:play-services-tasks:18.0.2\"\n    implementation \"androidx.appcompat:appcompat:1.4.1\"\n\n    // https://mvnrepository.com/artifact/com.google.errorprone/error_prone_annotations\n    implementation group: 'com.google.errorprone', name: 'error_prone_annotations', version: '2.18.0'\n\n    androidTestImplementation \"androidx.test:rules:1.1.0\"\n    androidTestImplementation \"androidx.test:runner:1.1.0\"\n    androidTestImplementation 'androidx.test.ext:junit:1.1.5'\n    androidTestImplementation \"com.google.truth:truth:1.1.3\"\n}\n\n// Download default models; if you wish to use your own models then\n// place them in the \"assets\" directory and comment out this line.\nproject.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'\napply from:'download.gradle'\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/app/download.gradle",
    "content": "def modelFloatDownloadUrl = \"https://storage.googleapis.com/download.tensorflow.org/models/mobilenet_v1_2018_02_22/mobilenet_v1_1.0_224.tgz\"\ndef localCacheFloat = \"build/intermediates/mobilenet_v1_1.0_224.tgz\"\ndef targetFolder = \"src/main/assets\"\ndef addDownloadUrl = \"https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/acceleration_service/add.tflite\"\ndef addTargetFile = \"src/main/assets/add.tflite\"\n\ntask downloadAddModel(type: DownloadUrlTask) {\n    doFirst {\n        println \"Downloading ${addDownloadUrl}\"\n    }\n    sourceUrl = \"${addDownloadUrl}\"\n    target = file(\"${addTargetFile}\")\n}\n\n\ntask downloadModelFloat(type: DownloadUrlTask) {\n    doFirst {\n        println \"Downloading ${modelFloatDownloadUrl}\"\n    }\n    sourceUrl = \"${modelFloatDownloadUrl}\"\n    target = file(\"${localCacheFloat}\")\n}\n\ntask unzipModelFloat(type: Copy, dependsOn: 'downloadModelFloat') {\n    doFirst {\n        println \"Unzipping ${localCacheFloat}\"\n    }\n    from tarTree(\"${localCacheFloat}\")\n    into \"${targetFolder}\"\n}\n\ntask cleanUnusedFiles(type: Delete, dependsOn: ['unzipModelFloat']) {\n    delete fileTree(\"${targetFolder}\").matching {\n        include \"*.pb\"\n        include \"*.ckpt.*\"\n        include \"*.pbtxt.*\"\n        include \"*.meta\"\n    }\n}\n\n\n// Ensure the model file is downloaded and extracted before every build\npreBuild.dependsOn downloadAddModel\npreBuild.dependsOn cleanUnusedFiles\n\nclass DownloadUrlTask extends DefaultTask {\n    @Input\n    String sourceUrl\n\n    @OutputFile\n    File target\n\n    @TaskAction\n    void download() {\n        ant.get(src: sourceUrl, dest: target)\n    }\n}\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/app/proguard-rules.pro",
    "content": "\n-keepclasseswithmembers class android.support.** { *; }\n-keepclasseswithmembers class androidx.** { *; }\n-keepclasseswithmembers class com.google.android.gms.tasks.** { *; }\n-keepclasseswithmembers class com.google.android.gms.tflite.acceleration.** { *; }\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/app/src/androidTest/java/org/tensorflow/lite/examples/accelerationservice/CustomValidationTest.java",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.accelerationservice;\n\nimport static com.google.common.truth.Truth.assertThat;\n\nimport android.content.Context;\nimport androidx.test.core.app.ApplicationProvider;\nimport androidx.test.ext.junit.rules.ActivityScenarioRule;\nimport androidx.test.ext.junit.runners.AndroidJUnit4;\nimport com.google.android.gms.tasks.Task;\nimport com.google.android.gms.tasks.TaskCompletionSource;\nimport com.google.android.gms.tasks.Tasks;\nimport com.google.android.gms.tflite.acceleration.AccelerationConfig;\nimport com.google.android.gms.tflite.acceleration.CpuAccelerationConfig;\nimport com.google.android.gms.tflite.acceleration.CustomValidationConfig;\nimport com.google.android.gms.tflite.acceleration.CustomValidationConfig.AccuracyValidator;\nimport com.google.android.gms.tflite.acceleration.ValidationConfig;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.Executors;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.tensorflow.lite.examples.accelerationservice.logger.Logger;\nimport org.tensorflow.lite.examples.accelerationservice.model.AssetModel;\nimport org.tensorflow.lite.examples.accelerationservice.model.AssetModelFactory;\nimport org.tensorflow.lite.examples.accelerationservice.model.AssetModelFactory.ModelType;\nimport org.tensorflow.lite.examples.accelerationservice.validator.MeanSquaredErrorValidator;\n\n/**\n * Instrumented test, which will execute on an Android device. The test will run the interpreter\n * using CPU config, and then check if the correct inference output is produced.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class CustomValidationTest {\n\n  private final AccelerationConfig accelerationConfig = new CpuAccelerationConfig.Builder().build();\n  private final Executor executor = Executors.newSingleThreadExecutor();\n\n  private AssetModelFactory assetModelFactory;\n  private AccuracyValidator validator;\n\n  @Rule\n  public ActivityScenarioRule<MainActivity> scenarioRule =\n      new ActivityScenarioRule<>(MainActivity.class);\n\n  @Before\n  public void setUp() throws ExecutionException, InterruptedException {\n    Context context = ApplicationProvider.getApplicationContext();\n    Logger logger = new NoopLogger();\n    validator = new MeanSquaredErrorValidator(logger, MainActivity.MSE_THRESHOLD);\n    assetModelFactory =\n        Tasks.await(\n            getMainActivity()\n                .onSuccessTask(\n                    activity -> Tasks.forResult(new AssetModelFactory(context, executor, logger))));\n  }\n\n  @Test\n  public void cpuCustomValidationOnPlainAdditionModel_succeeds()\n      throws ExecutionException, InterruptedException {\n    AssetModel assetModel = Tasks.await(assetModelFactory.load(ModelType.PLAIN_ADDITION));\n    assertThat(assetModel.getModel()).isNotNull();\n    assertThat(Tasks.await(runScenario(assetModel))).isTrue();\n  }\n\n  @Test\n  public void cpuCustomValidationOnMobileNetV1Model_succeeds()\n      throws ExecutionException, InterruptedException {\n    AssetModel assetModel = Tasks.await(assetModelFactory.load(ModelType.MOBILENET_V1));\n    assertThat(assetModel.getModel()).isNotNull();\n    assertThat(Tasks.await(runScenario(assetModel))).isTrue();\n  }\n\n  private Task<Boolean> runScenario(AssetModel assetModel) {\n    return getMainActivity()\n        .onSuccessTask(\n            activity -> {\n              ValidationConfig validationConfig =\n                  new CustomValidationConfig.Builder()\n                      .setGoldenInputs(assetModel.getInputs())\n                      .setAccuracyValidator(validator)\n                      .setBatchSize(assetModel.getBatchSize())\n                      .build();\n              return activity.runScenario(\n                  executor, assetModel, accelerationConfig, validationConfig);\n            });\n  }\n\n  private Task<MainActivity> getMainActivity() {\n    TaskCompletionSource<MainActivity> taskCompletionSource = new TaskCompletionSource<>();\n    scenarioRule.getScenario().onActivity(taskCompletionSource::setResult);\n    return taskCompletionSource.getTask();\n  }\n}\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/app/src/androidTest/java/org/tensorflow/lite/examples/accelerationservice/NoopLogger.java",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.accelerationservice;\n\nimport org.tensorflow.lite.examples.accelerationservice.logger.Logger;\n\n/** Logs no output. Used in instrumentation tests. */\nclass NoopLogger implements Logger {\n\n  @Override\n  public void error(String msg, Exception e) {}\n\n  @Override\n  public void info(String msg) {}\n\n  @Override\n  public void clear() {}\n}\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.tensorflow.lite.examples.accelerationservice\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\"\n        android:taskAffinity=\"\">\n        <activity android:name=\"org.tensorflow.lite.examples.accelerationservice.MainActivity\" android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n    <queries>\n        <package android:name=\"com.google.android.gms.policy_tflite_dynamite_dynamite\" />\n    </queries>\n</manifest>\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/app/src/main/java/org/tensorflow/lite/examples/accelerationservice/MainActivity.java",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.accelerationservice;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.text.method.ScrollingMovementMethod;\nimport android.widget.TextView;\nimport androidx.annotation.WorkerThread;\nimport androidx.appcompat.app.AppCompatActivity;\nimport com.google.android.gms.tasks.Task;\nimport com.google.android.gms.tasks.Tasks;\nimport com.google.android.gms.tflite.acceleration.AccelerationConfig;\nimport com.google.android.gms.tflite.acceleration.AccelerationService;\nimport com.google.android.gms.tflite.acceleration.BenchmarkResult.BenchmarkMetric;\nimport com.google.android.gms.tflite.acceleration.BenchmarkResult.InferenceOutput;\nimport com.google.android.gms.tflite.acceleration.CpuAccelerationConfig;\nimport com.google.android.gms.tflite.acceleration.CustomValidationConfig;\nimport com.google.android.gms.tflite.acceleration.CustomValidationConfig.AccuracyValidator;\nimport com.google.android.gms.tflite.acceleration.GpuAccelerationConfig;\nimport com.google.android.gms.tflite.acceleration.Model;\nimport com.google.android.gms.tflite.acceleration.ValidatedAccelerationConfigResult;\nimport com.google.android.gms.tflite.acceleration.ValidationConfig;\nimport com.google.android.gms.tflite.client.TfLiteInitializationOptions;\nimport com.google.android.gms.tflite.gpu.support.TfLiteGpu;\nimport com.google.android.gms.tflite.java.TfLite;\nimport java.nio.ByteBuffer;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.Executors;\nimport org.tensorflow.lite.InterpreterApi;\nimport org.tensorflow.lite.InterpreterApi.Options;\nimport org.tensorflow.lite.InterpreterApi.Options.TfLiteRuntime;\nimport org.tensorflow.lite.examples.accelerationservice.logger.Logger;\nimport org.tensorflow.lite.examples.accelerationservice.logger.TextViewLogger;\nimport org.tensorflow.lite.examples.accelerationservice.model.AssetModel;\nimport org.tensorflow.lite.examples.accelerationservice.model.AssetModelFactory;\nimport org.tensorflow.lite.examples.accelerationservice.model.AssetModelFactory.ModelType;\nimport org.tensorflow.lite.examples.accelerationservice.validator.MeanSquaredErrorValidator;\n\n/** Sample activity used for Acceleration Service tests. */\npublic class MainActivity extends AppCompatActivity {\n\n  /** Maximum Mean-Squared-Error threshold used for accuracy validation. */\n  public static final double MSE_THRESHOLD = 0.003;\n\n  public final Context context = MainActivity.this;\n\n  private final Executor executor = Executors.newSingleThreadExecutor();\n\n  private Logger logger;\n  private AssetModelFactory assetModelFactory;\n  private AccuracyValidator validator;\n\n  @Override\n  protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    setContentView(R.layout.activity_main);\n\n    TextView logOutputView = findViewById(R.id.log_output);\n    logOutputView.setMovementMethod(new ScrollingMovementMethod());\n    logger = new TextViewLogger(this, logOutputView);\n\n    assetModelFactory = new AssetModelFactory(context, executor, logger);\n    validator = new MeanSquaredErrorValidator(logger, MSE_THRESHOLD);\n\n    findViewById(R.id.cpu_validation_plain_addition_model_btn)\n        .setOnClickListener(\n            v -> {\n              logger.clear();\n              logTaskFailures(runPlainAdditionCpuValidation());\n            });\n    findViewById(R.id.gpu_validation_plain_addition_model_btn)\n        .setOnClickListener(\n            v -> {\n              logger.clear();\n              logTaskFailures(runPlainAdditionGpuValidation());\n            });\n\n    findViewById(R.id.cpu_validation_mobilenet_model_btn)\n        .setOnClickListener(\n            v -> {\n              logger.clear();\n              logTaskFailures(runMobileNetV1CpuValidation());\n            });\n    findViewById(R.id.gpu_validation_mobilenet_model_btn)\n        .setOnClickListener(\n            v -> {\n              logger.clear();\n              logTaskFailures(runMobileNetV1GpuValidation());\n            });\n  }\n\n  /**\n   * Uses Acceleration Service to check if the {@code model} passes accuracy checks defined by the\n   * {@code validationConfig} when running a benchmark on a configuration specified by the {@code\n   * accelerationConfig}. If the {@code accelerationConfig} is safe and valid, it can be applied on\n   * the {@link #InterpreterApi.Options} and passed to the {@link InterpreterApi}.\n   */\n  public Task<Boolean> runScenario(\n      Executor executor,\n      AssetModel assetModel,\n      AccelerationConfig accelerationConfig,\n      ValidationConfig validationConfig) {\n\n    // 1. Initialize TFLite in Google Play services.\n    Task<Void> initializeTask = initializeTfLite();\n\n    return initializeTask\n        .onSuccessTask(\n            aVoid -> {\n              // 2. Validate the acceleration config.\n              logger.info(\"TFLite initialized successfully.\");\n              Model model = assetModel.getModel();\n              AccelerationService service = AccelerationService.create(context);\n\n              return service.validateConfig(model, accelerationConfig, validationConfig);\n            })\n        .onSuccessTask(\n            executor,\n            validatedAccelerationConfig -> {\n              logger.info(\"Validated acceleration config result: \" + validatedAccelerationConfig);\n\n              // 3. Check if validated config is safe and applies it on interpreter options.\n              Options options = createInterpreterOptions(validatedAccelerationConfig);\n\n              // 4. Check if the benchmark output is valid.\n              boolean validBenchmarkOutput =\n                  validateBenchmarkOutputs(\n                      assetModel, validatedAccelerationConfig.benchmarkResult().actualOutput());\n              logger.info(\n                  \"AccelerationService: Benchmark model output is valid: \" + validBenchmarkOutput);\n\n              // 5. Create an interpreter and run inference using sample input.\n              boolean validInterpreterOutput = runInference(assetModel, options);\n\n              // 6. Return true if benchmark output and interpreter outputs are valid.\n              return Tasks.forResult(validBenchmarkOutput && validInterpreterOutput);\n            })\n        .addOnSuccessListener(aVoid -> logger.info(\"Scenario successful!\"));\n  }\n\n  private Task<Boolean> runPlainAdditionCpuValidation() {\n    logger.info(\"Running CPU validation test on Plain Addition model.\");\n    Task<AssetModel> model = assetModelFactory.load(ModelType.PLAIN_ADDITION);\n    AccelerationConfig accelerationConfig = new CpuAccelerationConfig.Builder().build();\n    return runValidation(model, accelerationConfig);\n  }\n\n  private Task<Boolean> runMobileNetV1CpuValidation() {\n    logger.info(\"Running CPU validation test on MobileNetV1 model.\");\n    Task<AssetModel> model = assetModelFactory.load(ModelType.MOBILENET_V1);\n    AccelerationConfig accelerationConfig = new CpuAccelerationConfig.Builder().build();\n    return runValidation(model, accelerationConfig);\n  }\n\n  private Task<Boolean> runPlainAdditionGpuValidation() {\n    logger.info(\"Running GPU validation test on Plain Addition model.\");\n    Task<AssetModel> model = assetModelFactory.load(ModelType.PLAIN_ADDITION);\n    AccelerationConfig accelerationConfig = new GpuAccelerationConfig.Builder().build();\n    return runValidation(model, accelerationConfig);\n  }\n\n  private Task<Boolean> runMobileNetV1GpuValidation() {\n    logger.info(\"Running GPU validation test on MobileNetV1 model.\");\n    Task<AssetModel> model = assetModelFactory.load(ModelType.MOBILENET_V1);\n    AccelerationConfig accelerationConfig = new GpuAccelerationConfig.Builder().build();\n    return runValidation(model, accelerationConfig);\n  }\n\n  private Task<Boolean> runValidation(\n      Task<AssetModel> modelTask, AccelerationConfig accelerationConfig) {\n    return modelTask.onSuccessTask(\n        model -> {\n          ValidationConfig validationConfig =\n              new CustomValidationConfig.Builder()\n                  .setGoldenInputs(model.getInputs())\n                  .setAccuracyValidator(validator)\n                  .setBatchSize(model.getBatchSize())\n                  .build();\n          logger.info(\"Starting validation scenario.\");\n          return runScenario(executor, model, accelerationConfig, validationConfig);\n        });\n  }\n\n  @WorkerThread\n  private boolean runInference(AssetModel assetModel, InterpreterApi.Options options) {\n    ByteBuffer model = assetModel.getModel().modelBuffer();\n    Object[] inputs = assetModel.getInputs();\n    Map<Integer, Object> outputs = assetModel.allocateOutputs();\n    try (InterpreterApi interpreter = InterpreterApi.create(model, options)) {\n      interpreter.runForMultipleInputsOutputs(inputs, outputs);\n    }\n    return assetModel.validateInterpreterOutputs(getValues(outputs));\n  }\n\n  private Task<Void> initializeTfLite() {\n    return TfLiteGpu.isGpuDelegateAvailable(context)\n        .onSuccessTask(\n            gpuAvailable -> {\n              findViewById(R.id.gpu_validation_plain_addition_model_btn).setEnabled(gpuAvailable);\n              findViewById(R.id.gpu_validation_mobilenet_model_btn).setEnabled(gpuAvailable);\n              return TfLite.initialize(\n                  context,\n                  TfLiteInitializationOptions.builder()\n                      .setEnableGpuDelegateSupport(gpuAvailable)\n                      .build());\n            });\n  }\n\n  private boolean validateBenchmarkOutputs(\n      AssetModel assetModel, List<InferenceOutput> benchmarkOutputs) {\n    int size = benchmarkOutputs.size();\n    ByteBuffer[] outputs = new ByteBuffer[size];\n    for (int i = 0; i < size; i++) {\n      outputs[i] = benchmarkOutputs.get(i).getValue();\n    }\n    return assetModel.validateBenchmarkOutputs(outputs);\n  }\n\n  private Options createInterpreterOptions(ValidatedAccelerationConfigResult result) {\n    Options options = new Options().setRuntime(TfLiteRuntime.FROM_SYSTEM_ONLY);\n    if (!result.isValid()\n        || result.benchmarkResult() == null\n        || !result.benchmarkResult().hasPassedAccuracyCheck()) {\n      // If the validation failed, do not apply acceleration config to the interpreter options.\n      logger.info(\n          \"Acceleration config is not safe! Creating Interpreter Options without acceleration\"\n              + \" config.\");\n      if (result.benchmarkError() != null) {\n        logger.info(\"Benchmark error: \" + result.benchmarkError());\n      }\n    } else {\n      for (BenchmarkMetric benchmarkMetric : result.benchmarkResult().metrics()) {\n        logger.info(\"Metric name: \" + benchmarkMetric.getName());\n        logger.info(\"Metric values: \");\n        for (float val : benchmarkMetric.getValues()) {\n          logger.info(String.valueOf(val));\n        }\n        options.setAccelerationConfig(result);\n      }\n    }\n    return options;\n  }\n\n  private Object[] getValues(Map<Integer, Object> map) {\n    return map.values().toArray(new Object[0]);\n  }\n\n  private void logTaskFailures(Task<Boolean> task) {\n    task.addOnSuccessListener(isValid -> logger.info(\"Scenario passed: \" + isValid))\n        .addOnFailureListener(e -> logger.error(\"Scenario failed\", e));\n  }\n}\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/app/src/main/java/org/tensorflow/lite/examples/accelerationservice/logger/Logger.java",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.accelerationservice.logger;\n\n/** Test app logger. */\npublic interface Logger {\n  /** Logs error. */\n  void error(String message, Exception e);\n\n  /** Logs info. */\n  void info(String message);\n\n  /** Clear logs. */\n  void clear();\n}\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/app/src/main/java/org/tensorflow/lite/examples/accelerationservice/logger/TextViewLogger.java",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.accelerationservice.logger;\n\nimport android.util.Log;\nimport android.widget.TextView;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.UiThread;\nimport androidx.appcompat.app.AppCompatActivity;\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\n\n/** Logs messages to the {@code TextView} component. */\npublic class TextViewLogger implements Logger {\n\n  private static final String TAG = \"Logger\";\n\n  private final TextView output;\n  private final AppCompatActivity activity;\n\n  public TextViewLogger(AppCompatActivity activity, TextView output) {\n    this.activity = activity;\n    this.output = output;\n  }\n\n  @Override\n  public void error(String message, Exception e) {\n    activity.runOnUiThread((Runnable) () -> logEvent(message, e));\n  }\n\n  @Override\n  public void info(String message) {\n    activity.runOnUiThread((Runnable) () -> logEvent(message, null));\n  }\n\n  @Override\n  @UiThread\n  public void clear() {\n    output.setText(\"\");\n  }\n\n  @UiThread\n  private void logEvent(String message, @Nullable Throwable throwable) {\n    Log.e(TAG, message, throwable);\n    output.append(\"• \");\n    output.append(String.valueOf(message)); // valueOf converts null to \"null\"\n    output.append(\"\\n\");\n    if (throwable != null) {\n      StringWriter sw = new StringWriter();\n      throwable.printStackTrace(new PrintWriter(sw));\n      output.append(sw.toString());\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/app/src/main/java/org/tensorflow/lite/examples/accelerationservice/model/AssetModel.java",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.accelerationservice.model;\n\nimport com.google.android.gms.tflite.acceleration.Model;\nimport java.nio.ByteBuffer;\nimport java.util.Map;\n\n/** Provides methods for interacting with tflite models stored as assets. */\npublic interface AssetModel {\n\n  /** Returns {@link Model} instance. */\n  Model getModel();\n\n  /** Returns sample input batch size. */\n  int getBatchSize();\n\n  /** Returns sample input. */\n  Object[] getInputs();\n\n  /** Returns sample output allocation. */\n  Map<Integer, Object> allocateOutputs();\n\n  /** Checks if the benchmark model output is valid. */\n  boolean validateBenchmarkOutputs(ByteBuffer[] outputs);\n\n  /** Checks if the {@link InterpreterApi} model output is valid. */\n  boolean validateInterpreterOutputs(Object[] outputs);\n}\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/app/src/main/java/org/tensorflow/lite/examples/accelerationservice/model/AssetModelFactory.java",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.accelerationservice.model;\n\nimport android.content.Context;\nimport com.google.android.gms.tasks.Task;\nimport com.google.android.gms.tasks.Tasks;\nimport java.util.concurrent.Executor;\nimport org.tensorflow.lite.examples.accelerationservice.logger.Logger;\n\n/** Loads tflite models stored as assets. */\npublic final class AssetModelFactory {\n\n  private final Context context;\n  private final Executor executor;\n  private final Logger logger;\n\n  public AssetModelFactory(Context context, Executor executor, Logger logger) {\n    this.context = context;\n    this.executor = executor;\n    this.logger = logger;\n  }\n\n  /** Asset models. */\n  public enum ModelType {\n    PLAIN_ADDITION,\n    MOBILENET_V1,\n  }\n\n  /** Loads model from assets. */\n  public Task<AssetModel> load(ModelType type) {\n    switch (type) {\n      case PLAIN_ADDITION:\n        return Tasks.forResult(null)\n            .onSuccessTask(executor, unused -> Tasks.forResult(new PlainAddition(context, logger)));\n      case MOBILENET_V1:\n        return Tasks.forResult(null)\n            .onSuccessTask(executor, unused -> Tasks.forResult(new MobileNetV1(context, logger)));\n      default:\n        throw new IllegalArgumentException(\"Invalid ModelType: \" + type);\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/app/src/main/java/org/tensorflow/lite/examples/accelerationservice/model/MobileNetV1.java",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.accelerationservice.model;\n\nimport static java.lang.Math.min;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport com.google.android.gms.tflite.acceleration.Model;\nimport com.google.android.gms.tflite.acceleration.Model.ModelLocation;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.ByteBuffer;\nimport java.nio.FloatBuffer;\nimport java.util.AbstractMap;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.PriorityQueue;\nimport org.tensorflow.lite.DataType;\nimport org.tensorflow.lite.examples.accelerationservice.logger.Logger;\nimport org.tensorflow.lite.support.common.FileUtil;\nimport org.tensorflow.lite.support.common.TensorOperator;\nimport org.tensorflow.lite.support.common.ops.NormalizeOp;\nimport org.tensorflow.lite.support.image.ImageProcessor;\nimport org.tensorflow.lite.support.image.TensorImage;\nimport org.tensorflow.lite.support.image.ops.ResizeOp;\nimport org.tensorflow.lite.support.image.ops.ResizeWithCropOrPadOp;\n\n/** Helper class for interaction with the MobileNetV1 model asset. */\nfinal class MobileNetV1 implements AssetModel {\n\n  private static final float IMAGE_MEAN = 127.0f;\n  private static final float IMAGE_STD = 128.0f;\n\n  private static final TensorOperator PREPROCESS_NORMALIZE_OP =\n      new NormalizeOp(IMAGE_MEAN, IMAGE_STD);\n\n  private static final String MODEL_ID = \"mobilenet_v1_1.0_224\";\n  private static final String MODEL_NAMESPACE = \"tflite\";\n  private static final String MODEL_PATH = MODEL_ID + \".\" + MODEL_NAMESPACE;\n\n  private static final int TEST_IMAGE_HEIGHT = 224;\n  private static final int TEST_IMAGE_WIDTH = 224;\n  private static final int OUTPUT_CLASSES = 1001;\n\n  private static final int MODEL_OUTPUT_DIMENSIONS = product(1, OUTPUT_CLASSES);\n\n  private static final String TEST_IMAGE = \"grace_hopper_224.jpg\";\n  private static final int TEST_IMAGE_OUTPUT_CLASS = 653; // 653 == \"military uniform\"\n\n  private final Model model;\n  private final Logger logger;\n  private final TensorImage tfInputBuffer = new TensorImage(DataType.UINT8);\n\n  private final Context context;\n  private ImageProcessor tfImageProcessor;\n\n  public MobileNetV1(Context context, Logger logger) throws IOException {\n    ByteBuffer modelBuffer = FileUtil.loadMappedFile(context, MODEL_PATH);\n    this.context = context;\n    this.logger = logger;\n    this.model =\n        new Model.Builder()\n            .setModelId(MODEL_ID)\n            .setModelNamespace(MODEL_NAMESPACE)\n            .setModelLocation(ModelLocation.fromByteBuffer(modelBuffer))\n            .build();\n  }\n\n  @Override\n  public Model getModel() {\n    return model;\n  }\n\n  @Override\n  public int getBatchSize() {\n    return 1;\n  }\n\n  @Override\n  public Object[] getInputs() {\n    Bitmap image = readImage(TEST_IMAGE);\n    ByteBuffer imageBuffer = loadImage(image).getBuffer();\n    return new Object[] {imageBuffer};\n  }\n\n  @Override\n  public Map<Integer, Object> allocateOutputs() {\n    Map<Integer, Object> outputs = new HashMap<>();\n    outputs.put(0, FloatBuffer.allocate(MODEL_OUTPUT_DIMENSIONS));\n    return outputs;\n  }\n\n  @Override\n  public boolean validateBenchmarkOutputs(ByteBuffer[] outputs) {\n    for (int i = 0; i < outputs.length; i++) {\n      float[] predictions = toFloatArray(outputs[i].asFloatBuffer());\n      if (!validatePredictions(predictions)) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  @Override\n  public boolean validateInterpreterOutputs(Object[] outputs) {\n    for (int i = 0; i < outputs.length; i++) {\n      float[] predictions = ((FloatBuffer) outputs[i]).array();\n      if (!validatePredictions(predictions)) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  private boolean validatePredictions(float[] predictions) {\n    if (predictions.length != OUTPUT_CLASSES) {\n      logger.info(\n          \"Output sizes do not match: Got \"\n              + predictions.length\n              + \"-  Expected: \"\n              + OUTPUT_CLASSES);\n      return false;\n    }\n    List<Map.Entry<Integer, Float>> results = getTopKLabels(predictions, 10);\n    for (Map.Entry<Integer, Float> result : results) {\n      logger.info(\"Class index: \" + result.getKey() + \" Confidence: \" + result.getValue());\n    }\n    return results.get(0).getKey() == TEST_IMAGE_OUTPUT_CLASS;\n  }\n\n  private Bitmap readImage(String path) {\n    try (InputStream stream = context.getAssets().open(path)) {\n      return BitmapFactory.decodeStream(stream);\n    } catch (IOException e) {\n      throw new IllegalStateException(\"Couldn't read image: \", e);\n    }\n  }\n\n  private TensorImage loadImage(final Bitmap bitmapBuffer) {\n    // Initializes preprocessor if null\n    if (tfImageProcessor == null) {\n      int cropSize = min(bitmapBuffer.getWidth(), bitmapBuffer.getHeight());\n      tfImageProcessor =\n          new ImageProcessor.Builder()\n              .add(new ResizeWithCropOrPadOp(cropSize, cropSize))\n              .add(\n                  new ResizeOp(\n                      TEST_IMAGE_HEIGHT, TEST_IMAGE_WIDTH, ResizeOp.ResizeMethod.NEAREST_NEIGHBOR))\n              .add(PREPROCESS_NORMALIZE_OP)\n              .build();\n    }\n    tfInputBuffer.load(bitmapBuffer);\n    return tfImageProcessor.process(tfInputBuffer);\n  }\n\n  private static final int product(int... values) {\n    int total = 1;\n    for (int v : values) {\n      total *= v;\n    }\n    return total;\n  }\n\n  private static float[] toFloatArray(FloatBuffer output) {\n    float[] predictions = new float[output.remaining()];\n    output.get(predictions);\n    return predictions;\n  }\n\n  private static List<Map.Entry<Integer, Float>> getTopKLabels(float[] labels, int k) {\n    PriorityQueue<Map.Entry<Integer, Float>> pq =\n        new PriorityQueue<>(\n            k,\n            (o1, o2) ->\n                // Intentionally reversed to put high confidence at the head of the queue.\n                o2.getValue().compareTo(o1.getValue()));\n\n    for (int i = 0; i < labels.length; ++i) {\n      pq.add(new AbstractMap.SimpleEntry<>(i, labels[i]));\n    }\n    List<Map.Entry<Integer, Float>> topKLabels = new ArrayList<>();\n    for (int i = 0; i < min(pq.size(), k); ++i) {\n      topKLabels.add(Objects.requireNonNull(pq.poll()));\n    }\n    return topKLabels;\n  }\n}\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/app/src/main/java/org/tensorflow/lite/examples/accelerationservice/model/PlainAddition.java",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.accelerationservice.model;\n\nimport android.content.Context;\nimport com.google.android.gms.tflite.acceleration.Model;\nimport com.google.android.gms.tflite.acceleration.Model.ModelLocation;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.FloatBuffer;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\nimport org.tensorflow.lite.examples.accelerationservice.logger.Logger;\nimport org.tensorflow.lite.support.common.FileUtil;\n\nfinal class PlainAddition implements AssetModel {\n\n  private static final String MODEL_ID = \"add\";\n  private static final String MODEL_NAMESPACE = \"tflite\";\n  private static final String MODEL_PATH = \"add.tflite\";\n\n  private static final int BATCH_SIZE = 5;\n  private static final int INPUT_TENSOR_SIZE = 1;\n\n  private static final int MODEL_INPUT_DIMENSIONS = product(BATCH_SIZE, 8, 8, 3);\n  private static final int MODEL_OUTPUT_DIMENSIONS = product(BATCH_SIZE, 8, 8, 3);\n\n  private final Model model;\n  private final Logger logger;\n\n  public PlainAddition(Context context, Logger logger) throws IOException {\n    ByteBuffer modelBuffer = FileUtil.loadMappedFile(context, MODEL_PATH);\n    this.model =\n        new Model.Builder()\n            .setModelId(MODEL_ID)\n            .setModelNamespace(MODEL_NAMESPACE)\n            .setModelLocation(ModelLocation.fromByteBuffer(modelBuffer))\n            .build();\n    this.logger = logger;\n  }\n\n  @Override\n  public Model getModel() {\n    return model;\n  }\n\n  @Override\n  public int getBatchSize() {\n    return BATCH_SIZE;\n  }\n\n  @Override\n  public Object[] getInputs() {\n    Object[] batchedInputs = new Object[INPUT_TENSOR_SIZE];\n    for (int i = 0; i < batchedInputs.length; i++) {\n      float[] batchedInput = new float[MODEL_INPUT_DIMENSIONS];\n      for (int j = 0; j < batchedInput.length; j++) {\n        batchedInput[j] = generateInput(j);\n      }\n      batchedInputs[i] = batchedInput;\n    }\n    return batchedInputs;\n  }\n\n  @Override\n  public Map<Integer, Object> allocateOutputs() {\n    Map<Integer, Object> outputs = new HashMap<>();\n    for (int i = 0; i < INPUT_TENSOR_SIZE; i++) {\n      outputs.put(i, new float[MODEL_OUTPUT_DIMENSIONS]);\n    }\n    return outputs;\n  }\n\n  @Override\n  public boolean validateBenchmarkOutputs(ByteBuffer[] outputs) {\n    Object[] floatOutputs = new Object[outputs.length];\n    if (outputs.length != INPUT_TENSOR_SIZE) {\n      return false;\n    }\n    for (int i = 0; i < outputs.length; i++) {\n      floatOutputs[i] = toFloatArray(outputs[i]);\n    }\n    return validateInterpreterOutputs(floatOutputs);\n  }\n\n  @Override\n  public boolean validateInterpreterOutputs(Object[] outputs) {\n    if (outputs.length != INPUT_TENSOR_SIZE) {\n      return false;\n    }\n    Object[] inputs = getInputs();\n    for (int i = 0; i < outputs.length; i++) {\n      float[] input = (float[]) inputs[i];\n      float[] output = (float[]) outputs[i];\n      if (!validateOutput(input, output)) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  private boolean validateOutput(float[] input, float[] output) {\n    if (output.length != MODEL_OUTPUT_DIMENSIONS) {\n      logger.info(\n          \"Output sizes do not match: Got \"\n              + output.length\n              + \"-  Expected: \"\n              + MODEL_OUTPUT_DIMENSIONS);\n      return false;\n    }\n    for (int i = 0; i < MODEL_OUTPUT_DIMENSIONS; i++) {\n      float got = output[i];\n      float expected = input[i] * 3;\n      if (Float.compare(got, expected) != 0) {\n        logger.info(\"Values do not match; Got: \" + got + \"- Expected: \" + expected);\n        return false;\n      }\n    }\n    logger.info(\"Printing first 10 elements: \");\n    logger.info(Arrays.toString(Arrays.copyOfRange(output, 0, 10)));\n    return true;\n  }\n\n  /** Generates sample input. */\n  private static float generateInput(int x) {\n    return 55.25f + 2 * x;\n  }\n\n  private static final int product(int... values) {\n    int total = 1;\n    for (int v : values) {\n      total *= v;\n    }\n    return total;\n  }\n\n  private static float[] toFloatArray(ByteBuffer output) {\n    FloatBuffer outputFloats = output.asFloatBuffer();\n    float[] predictions = new float[outputFloats.remaining()];\n    outputFloats.get(predictions);\n    return predictions;\n  }\n}\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/app/src/main/java/org/tensorflow/lite/examples/accelerationservice/validator/MeanSquaredErrorValidator.java",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.accelerationservice.validator;\n\nimport com.google.android.gms.tflite.acceleration.BenchmarkResult;\nimport com.google.android.gms.tflite.acceleration.CustomValidationConfig.AccuracyValidator;\nimport java.nio.ByteBuffer;\nimport java.nio.FloatBuffer;\nimport org.tensorflow.lite.examples.accelerationservice.logger.Logger;\n\n/**\n * Accuracy validator that computes mean squared error of benchmark output and golden output. If the\n * MSE is above the defined threshold, validation run will be considered unsuccessful.\n */\npublic class MeanSquaredErrorValidator implements AccuracyValidator {\n\n  private final double threshold;\n  private final Logger logger;\n\n  public MeanSquaredErrorValidator(Logger logger, double threshold) {\n    this.logger = logger;\n    this.threshold = threshold;\n  }\n\n  /**\n   * Validates if {@code benchmarkResult} and {@code goldenOutputs} match. When {@link\n   * CustomValidationConfig.Builder#setGoldenOutputs} is not invoked, {@code goldenOutputs} is\n   * provided by the Acceleration SDK as the output of running golden config.\n   */\n  @Override\n  public boolean validate(BenchmarkResult benchmarkResult, ByteBuffer[] goldenOutputs) {\n    if (benchmarkResult.actualOutput().size() != goldenOutputs.length) {\n      logger.info(\n          \"Accuracy validator: benchmark result and golden output\"\n              + \" dimensions do not match.\"\n              + \" Benchmark result length: \"\n              + benchmarkResult.actualOutput().size()\n              + \" Golden output length: \"\n              + goldenOutputs.length);\n      return false;\n    }\n    // Compare benchmark result buffer tensors with golden output tensors\n    for (int i = 0; i < benchmarkResult.actualOutput().size(); i++) {\n      FloatBuffer goldenOutputBuffer = goldenOutputs[i].asFloatBuffer();\n      FloatBuffer benchmarkOutputBuffer =\n          benchmarkResult.actualOutput().get(i).getValue().asFloatBuffer();\n      boolean ok = checkMeanSquaredError(goldenOutputBuffer, benchmarkOutputBuffer);\n      if (!ok) {\n        logger.info(\n            \"Accuracy validator: benchmark result and golden output buffers do not match at\"\n                + \" position: \"\n                + i);\n        return false;\n      }\n    }\n    logger.info(\"Accuracy validator: benchmark result and golden output match.\");\n    return true;\n  }\n\n  private boolean checkMeanSquaredError(FloatBuffer expected, FloatBuffer actual) {\n    int length = expected.remaining();\n    if (length != actual.remaining()) {\n      logger.info(\n          \"Accuracy validator: benchmark result and golden output vector lengths do not\"\n              + \" match. Got: \"\n              + actual.remaining()\n              + \" - Expected: \"\n              + expected.remaining()\n              + \".\");\n      return false;\n    }\n    double sum = 0;\n    for (int i = 0; i < length; i++) {\n      sum += Math.pow(expected.get(i) - (double) actual.get(i), 2);\n    }\n    double mse = length == 0 ? 0 : sum / length;\n    logger.info(\"Mean Squared Error: \" + mse);\n    return mse < threshold;\n  }\n}\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_marginTop=\"20dp\"\n    android:layout_marginStart=\"20dp\"\n    android:layout_marginEnd=\"20dp\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\">\n  <Button\n      android:id=\"@+id/cpu_validation_plain_addition_model_btn\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginBottom=\"10dp\"\n      android:gravity=\"center_horizontal\"\n      android:text=\"@string/cpu_validation_plain_addition_model\" />\n  <Button\n      android:id=\"@+id/gpu_validation_plain_addition_model_btn\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginBottom=\"10dp\"\n      android:gravity=\"center_horizontal\"\n      android:text=\"@string/gpu_validation_plain_addition_model\" />\n  <Button\n      android:id=\"@+id/cpu_validation_mobilenet_model_btn\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginBottom=\"10dp\"\n      android:gravity=\"center_horizontal\"\n      android:text=\"@string/cpu_validation_mobilenet_model\" />\n  <Button\n      android:id=\"@+id/gpu_validation_mobilenet_model_btn\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginBottom=\"10dp\"\n      android:gravity=\"center_horizontal\"\n      android:text=\"@string/gpu_validation_mobilenet_model\" />\n  <TextView\n      android:id=\"@+id/log_output\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\" />\n</LinearLayout>\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <color name=\"colorPrimary\">#6200EE</color>\n  <color name=\"colorPrimaryDark\">#3700B3</color>\n  <color name=\"colorAccent\">#03DAC5</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/app/src/main/res/values/strings.xml",
    "content": "<resources>\n  <string name=\"app_name\" translatable=\"false\">Acceleration Service Test Application</string>\n  <string name=\"cpu_validation_plain_addition_model\">Run CPU Validation on Plain Addition model</string>\n  <string name=\"gpu_validation_plain_addition_model\">Run GPU Validation on Plain Addition model</string>\n  <string name=\"cpu_validation_mobilenet_model\">Run CPU Validation on MobileNet model</string>\n  <string name=\"gpu_validation_mobilenet_model\">Run GPU Validation on MobileNet model</string>\n</resources>\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/app/src/main/res/values/styles.xml",
    "content": "<resources>\n  <!-- Base application theme. -->\n  <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n    <!-- Customize your theme here. -->\n    <item name=\"colorPrimary\">@color/colorPrimary</item>\n    <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n    <item name=\"colorAccent\">@color/colorAccent</item>\n  </style>\n\n</resources>\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath \"com.android.tools.build:gradle:7.1.0\"\n\n        classpath 'com.google.android.gms:strict-version-matcher-plugin:1.2.1'\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n        maven {\n          name 'ossrh-snapshot'\n          url 'https://oss.sonatype.org/content/repositories/snapshots'\n        }\n        mavenLocal()\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app\"s APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Automatically convert third-party libraries to use AndroidX\nandroid.enableJetifier=true\n\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/proguard.pgcfg",
    "content": "-keep,allowshrinking public class com.google.android.gms.tflite.acceleration.MainActivity {\n  <methods>; # Needed for runScenario()\n}\n\n-keep,allowshrinking public class com.google.android.gms.tflite.java.TfLiteJavaInitializer {\n  <methods>; # Needed for initialize()\n}\n\n-keep public class com.google.android.gms.tasks.* {\n  public static <methods>;\n  public <methods>;\n}\n\n-keep public class org.tensorflow.lite.* {\n  public static <methods>;\n  public static <fields>; # Needed for the Runtime enum\n  public <methods>;\n}\n"
  },
  {
    "path": "lite/examples/acceleration_service/android_play_services/settings.gradle",
    "content": "include ':app'\n"
  },
  {
    "path": "lite/examples/audio_classification/android/README.md",
    "content": "# TensorFlow Lite Audio Classification Android Demo\n\n### Overview\n\nThis sample will record audio on a physical Android device and attempt to\nclassify those recordings. The supported classification models include\n[YAMNET](https://tfhub.dev/google/lite-model/yamnet/classification/tflite/1) and\na custom speech command model trained using\n[TensorFlow's Model Maker](https://www.tensorflow.org/lite/models/modify/model_maker/speech_recognition).\nThese instructions walk you through building and running the demo on an Android\ndevice.\n\nThe model files are downloaded via Gradle scripts when you build and run the\napp. You don't need to do any steps to download TFLite models into the project\nexplicitly.\n\nThis application should be run on a physical Android device.\n\n## Build the demo using Android Studio\n\n### Prerequisites\n\n*   The **[Android Studio](https://developer.android.com/studio/index.html)**\n    IDE. This sample has been tested on Android Studio Bumblebee.\n\n*   A physical Android device with a minimum OS version of SDK 23 (Android 6.0)\n    with developer mode enabled. The process of enabling developer mode may vary\n    by device.\n\n### Building\n\n*   Open Android Studio. From the Welcome screen, select Open an existing\n    Android Studio project.\n\n*   From the Open File or Project window that appears, navigate to and select\n    the tensorflow-lite/examples/audio_classification/android\n    directory. Click OK.\n\n*   If it asks you to do a Gradle Sync, click OK.\n\n*   With your Android device connected to your computer and developer mode\n    enabled, click on the green Run arrow in Android Studio.\n\n### Models used\n\nDownloading, extraction, and placing the models into the assets folder is\nmanaged automatically by the download_model.gradle file."
  },
  {
    "path": "lite/examples/audio_classification/android/app/build.gradle",
    "content": "apply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-kapt'\napply plugin: \"androidx.navigation.safeargs\"\napply plugin: 'de.undercouch.download'\n\nandroid {\n    compileSdk 32\n\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.audio\"\n        minSdk 23\n        targetSdk 32\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    buildFeatures {\n        viewBinding true\n    }\n    androidResources {\n        noCompress 'tflite'\n    }\n\n    compileOptions {\n        sourceCompatibility rootProject.ext.java_version\n        targetCompatibility rootProject.ext.java_version\n    }\n\n    kotlinOptions {\n        jvmTarget = rootProject.ext.java_version\n    }\n}\n\n// import DownloadModels task\nproject.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'\nproject.ext.TEST_ASSET_DIR = projectDir.toString() + '/src/androidTest/assets'\n\napply from:'download_model.gradle'\n\ndependencies {\n\n    // Kotlin lang\n    implementation 'androidx.core:core-ktx:1.6.0'\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version\"\n    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0'\n\n    // App compat and UI\n    implementation 'androidx.appcompat:appcompat:1.3.1'\n    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'\n    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'\n    implementation 'com.google.android.material:material:1.0.0'\n    implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'\n\n    // Navigation library\n    def nav_version = \"2.3.5\"\n    implementation \"androidx.navigation:navigation-fragment-ktx:$nav_version\"\n    implementation \"androidx.navigation:navigation-ui-ktx:$nav_version\"\n\n    // Unit testing\n    testImplementation 'androidx.test.ext:junit:1.1.3'\n    testImplementation 'androidx.test:rules:1.4.0'\n    testImplementation 'androidx.test:runner:1.4.0'\n    testImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n    testImplementation 'org.robolectric:robolectric:4.4'\n\n    // Instrumented testing\n    androidTestImplementation \"androidx.test.ext:junit:1.1.3\"\n    androidTestImplementation \"androidx.test:core:1.4.0\"\n    androidTestImplementation \"androidx.test:rules:1.4.0\"\n    androidTestImplementation \"androidx.test:runner:1.4.0\"\n    androidTestImplementation \"androidx.test.espresso:espresso-core:3.4.0\"\n\n    implementation 'org.tensorflow:tensorflow-lite-task-audio:0.4.0'\n}"
  },
  {
    "path": "lite/examples/audio_classification/android/app/download_model.gradle",
    "content": "task downloadAudioClassifierModel(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/audio_classification/android/lite-model_yamnet_classification_tflite_1.tflite'\n    dest project.ext.ASSET_DIR + '/yamnet.tflite'\n    overwrite false\n}\n\ntask downloadSpeechClassifierModel(type: Download) {\n    // This model is custom made using Model Maker. A detailed guide can be found here:\n    // https://www.tensorflow.org/lite/models/modify/model_maker/speech_recognition\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/audio_classification/android/speech_commands.tflite'\n    dest project.ext.ASSET_DIR + '/speech.tflite'\n    overwrite false\n}\n\npreBuild.dependsOn downloadAudioClassifierModel, downloadSpeechClassifierModel\n\n"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.tensorflow.lite.examples.audio\">\n\n    <uses-permission android:name=\"android.permission.RECORD_AUDIO\" />\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:taskAffinity=\"\"\n        android:theme=\"@style/AppTheme\">\n        <activity\n            android:name=\"org.tensorflow.lite.examples.audio.MainActivity\"\n            android:exported=\"true\"\n            android:screenOrientation=\"portrait\" >\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/java/org/tensorflow/lite/examples/audio/AudioClassificationHelper.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.audio\n\nimport android.content.Context\nimport android.media.AudioRecord\nimport android.os.SystemClock\nimport android.util.Log\nimport java.util.concurrent.ScheduledThreadPoolExecutor\nimport java.util.concurrent.TimeUnit\nimport org.tensorflow.lite.examples.audio.fragments.AudioClassificationListener\nimport org.tensorflow.lite.support.audio.TensorAudio\nimport org.tensorflow.lite.task.audio.classifier.AudioClassifier\nimport org.tensorflow.lite.task.core.BaseOptions\n\nclass AudioClassificationHelper(\n  val context: Context,\n  val listener: AudioClassificationListener,\n  var currentModel: String = YAMNET_MODEL,\n  var classificationThreshold: Float = DISPLAY_THRESHOLD,\n  var overlap: Float = DEFAULT_OVERLAP_VALUE,\n  var numOfResults: Int = DEFAULT_NUM_OF_RESULTS,\n  var currentDelegate: Int = 0,\n  var numThreads: Int = 2\n) {\n    private lateinit var classifier: AudioClassifier\n    private lateinit var tensorAudio: TensorAudio\n    private lateinit var recorder: AudioRecord\n    private lateinit var executor: ScheduledThreadPoolExecutor\n\n    private val classifyRunnable = Runnable {\n        classifyAudio()\n    }\n\n    init {\n        initClassifier()\n    }\n\n    fun initClassifier() {\n        // Set general detection options, e.g. number of used threads\n        val baseOptionsBuilder = BaseOptions.builder()\n            .setNumThreads(numThreads)\n\n        // Use the specified hardware for running the model. Default to CPU.\n        // Possible to also use a GPU delegate, but this requires that the classifier be created\n        // on the same thread that is using the classifier, which is outside of the scope of this\n        // sample's design.\n        when (currentDelegate) {\n            DELEGATE_CPU -> {\n                // Default\n            }\n            DELEGATE_NNAPI -> {\n                baseOptionsBuilder.useNnapi()\n            }\n        }\n\n        // Configures a set of parameters for the classifier and what results will be returned.\n        val options = AudioClassifier.AudioClassifierOptions.builder()\n            .setScoreThreshold(classificationThreshold)\n            .setMaxResults(numOfResults)\n            .setBaseOptions(baseOptionsBuilder.build())\n            .build()\n\n        try {\n            // Create the classifier and required supporting objects\n            classifier = AudioClassifier.createFromFileAndOptions(context, currentModel, options)\n            tensorAudio = classifier.createInputTensorAudio()\n            recorder = classifier.createAudioRecord()\n            startAudioClassification()\n        } catch (e: IllegalStateException) {\n            listener.onError(\n                \"Audio Classifier failed to initialize. See error logs for details\"\n            )\n\n            Log.e(\"AudioClassification\", \"TFLite failed to load with error: \" + e.message)\n        }\n    }\n\n    fun startAudioClassification() {\n        if (recorder.recordingState == AudioRecord.RECORDSTATE_RECORDING) {\n            return\n        }\n\n        recorder.startRecording()\n        executor = ScheduledThreadPoolExecutor(1)\n\n        // Each model will expect a specific audio recording length. This formula calculates that\n        // length using the input buffer size and tensor format sample rate.\n        // For example, YAMNET expects 0.975 second length recordings.\n        // This needs to be in milliseconds to avoid the required Long value dropping decimals.\n        val lengthInMilliSeconds = ((classifier.requiredInputBufferSize * 1.0f) /\n                classifier.requiredTensorAudioFormat.sampleRate) * 1000\n\n        val interval = (lengthInMilliSeconds * (1 - overlap)).toLong()\n\n        executor.scheduleAtFixedRate(\n            classifyRunnable,\n            0,\n            interval,\n            TimeUnit.MILLISECONDS)\n    }\n\n    private fun classifyAudio() {\n        tensorAudio.load(recorder)\n        var inferenceTime = SystemClock.uptimeMillis()\n        val output = classifier.classify(tensorAudio)\n        inferenceTime = SystemClock.uptimeMillis() - inferenceTime\n        listener.onResult(output[0].categories, inferenceTime)\n    }\n\n    fun stopAudioClassification() {\n        recorder.stop()\n        executor.shutdownNow()\n    }\n\n    companion object {\n        const val DELEGATE_CPU = 0\n        const val DELEGATE_NNAPI = 1\n        const val DISPLAY_THRESHOLD = 0.3f\n        const val DEFAULT_NUM_OF_RESULTS = 2\n        const val DEFAULT_OVERLAP_VALUE = 0.5f\n        const val YAMNET_MODEL = \"yamnet.tflite\"\n        const val SPEECH_COMMAND_MODEL = \"speech.tflite\"\n    }\n}\n"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/java/org/tensorflow/lite/examples/audio/MainActivity.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.audio\n\nimport android.os.Build\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport org.tensorflow.lite.examples.audio.databinding.ActivityMainBinding\n\nclass MainActivity : AppCompatActivity() {\n    private lateinit var activityMainBinding: ActivityMainBinding\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)\n        setContentView(activityMainBinding.root)\n    }\n\n    override fun onBackPressed() {\n        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {\n            // Workaround for Android Q memory leak issue in IRequestFinishCallback$Stub.\n            // (https://issuetracker.google.com/issues/139738913)\n            finishAfterTransition()\n        } else {\n            super.onBackPressed()\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/java/org/tensorflow/lite/examples/audio/fragments/AudioFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.audio.fragments\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.AdapterView\nimport android.widget.RadioGroup\nimport android.widget.Toast\nimport androidx.fragment.app.Fragment\nimport androidx.navigation.Navigation\nimport org.tensorflow.lite.examples.audio.AudioClassificationHelper\nimport org.tensorflow.lite.examples.audio.R\nimport org.tensorflow.lite.examples.audio.databinding.FragmentAudioBinding\nimport org.tensorflow.lite.examples.audio.ui.ProbabilitiesAdapter\nimport org.tensorflow.lite.support.label.Category\n\ninterface AudioClassificationListener {\n    fun onError(error: String)\n    fun onResult(results: List<Category>, inferenceTime: Long)\n}\n\nclass AudioFragment : Fragment() {\n    private var _fragmentBinding: FragmentAudioBinding? = null\n    private val fragmentAudioBinding get() = _fragmentBinding!!\n    private val adapter by lazy { ProbabilitiesAdapter() }\n\n    private lateinit var audioHelper: AudioClassificationHelper\n\n    private val audioClassificationListener = object : AudioClassificationListener {\n        override fun onResult(results: List<Category>, inferenceTime: Long) {\n            requireActivity().runOnUiThread {\n                adapter.categoryList = results\n                adapter.notifyDataSetChanged()\n                fragmentAudioBinding.bottomSheetLayout.inferenceTimeVal.text =\n                    String.format(\"%d ms\", inferenceTime)\n            }\n        }\n\n        override fun onError(error: String) {\n            requireActivity().runOnUiThread {\n                Toast.makeText(requireContext(), error, Toast.LENGTH_SHORT).show()\n                adapter.categoryList = emptyList()\n                adapter.notifyDataSetChanged()\n            }\n        }\n    }\n\n    override fun onCreateView(\n      inflater: LayoutInflater,\n      container: ViewGroup?,\n      savedInstanceState: Bundle?\n    ): View {\n        _fragmentBinding = FragmentAudioBinding.inflate(inflater, container, false)\n        return fragmentAudioBinding.root\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n        fragmentAudioBinding.recyclerView.adapter = adapter\n        audioHelper = AudioClassificationHelper(\n            requireContext(),\n            audioClassificationListener\n        )\n\n        // Allow the user to select between multiple supported audio models.\n        // The original location and documentation for these models is listed in\n        // the `download_model.gradle` file within this sample. You can also create your own\n        // audio model by following the documentation here:\n        // https://www.tensorflow.org/lite/models/modify/model_maker/speech_recognition\n        fragmentAudioBinding.bottomSheetLayout.modelSelector.setOnCheckedChangeListener(\n            object : RadioGroup.OnCheckedChangeListener {\n            override fun onCheckedChanged(group: RadioGroup?, checkedId: Int) {\n                when (checkedId) {\n                    R.id.yamnet -> {\n                        audioHelper.stopAudioClassification()\n                        audioHelper.currentModel = AudioClassificationHelper.YAMNET_MODEL\n                        audioHelper.initClassifier()\n                    }\n                    R.id.speech_command -> {\n                        audioHelper.stopAudioClassification()\n                        audioHelper.currentModel = AudioClassificationHelper.SPEECH_COMMAND_MODEL\n                        audioHelper.initClassifier()\n                    }\n                }\n            }\n        })\n\n        // Allow the user to change the amount of overlap used in classification. More overlap\n        // can lead to more accurate resolves in classification.\n        fragmentAudioBinding.bottomSheetLayout.spinnerOverlap.onItemSelectedListener =\n            object : AdapterView.OnItemSelectedListener {\n                override fun onItemSelected(\n                  parent: AdapterView<*>?,\n                  view: View?,\n                  position: Int,\n                  id: Long\n                ) {\n                    audioHelper.stopAudioClassification()\n                    audioHelper.overlap = 0.25f * position\n                    audioHelper.startAudioClassification()\n                }\n\n                override fun onNothingSelected(p0: AdapterView<*>?) {\n                    // no op\n                }\n            }\n\n        // Allow the user to change the max number of results returned by the audio classifier.\n        // Currently allows between 1 and 5 results, but can be edited here.\n        fragmentAudioBinding.bottomSheetLayout.resultsMinus.setOnClickListener {\n            if (audioHelper.numOfResults > 1) {\n                audioHelper.numOfResults--\n                audioHelper.stopAudioClassification()\n                audioHelper.initClassifier()\n                fragmentAudioBinding.bottomSheetLayout.resultsValue.text =\n                    audioHelper.numOfResults.toString()\n            }\n        }\n\n        fragmentAudioBinding.bottomSheetLayout.resultsPlus.setOnClickListener {\n            if (audioHelper.numOfResults < 5) {\n                audioHelper.numOfResults++\n                audioHelper.stopAudioClassification()\n                audioHelper.initClassifier()\n                fragmentAudioBinding.bottomSheetLayout.resultsValue.text =\n                    audioHelper.numOfResults.toString()\n            }\n        }\n\n        // Allow the user to change the confidence threshold required for the classifier to return\n        // a result. Increments in steps of 10%.\n        fragmentAudioBinding.bottomSheetLayout.thresholdMinus.setOnClickListener {\n            if (audioHelper.classificationThreshold >= 0.2) {\n                audioHelper.stopAudioClassification()\n                audioHelper.classificationThreshold -= 0.1f\n                audioHelper.initClassifier()\n                fragmentAudioBinding.bottomSheetLayout.thresholdValue.text =\n                    String.format(\"%.2f\", audioHelper.classificationThreshold)\n            }\n        }\n\n        fragmentAudioBinding.bottomSheetLayout.thresholdPlus.setOnClickListener {\n            if (audioHelper.classificationThreshold <= 0.8) {\n                audioHelper.stopAudioClassification()\n                audioHelper.classificationThreshold += 0.1f\n                audioHelper.initClassifier()\n                fragmentAudioBinding.bottomSheetLayout.thresholdValue.text =\n                    String.format(\"%.2f\", audioHelper.classificationThreshold)\n            }\n        }\n\n        // Allow the user to change the number of threads used for classification\n        fragmentAudioBinding.bottomSheetLayout.threadsMinus.setOnClickListener {\n            if (audioHelper.numThreads > 1) {\n                audioHelper.stopAudioClassification()\n                audioHelper.numThreads--\n                fragmentAudioBinding.bottomSheetLayout.threadsValue.text = audioHelper\n                    .numThreads\n                    .toString()\n                audioHelper.initClassifier()\n            }\n        }\n\n        fragmentAudioBinding.bottomSheetLayout.threadsPlus.setOnClickListener {\n            if (audioHelper.numThreads < 4) {\n                audioHelper.stopAudioClassification()\n                audioHelper.numThreads++\n                fragmentAudioBinding.bottomSheetLayout.threadsValue.text = audioHelper\n                    .numThreads\n                    .toString()\n                audioHelper.initClassifier()\n            }\n        }\n\n        // When clicked, change the underlying hardware used for inference. Current options are CPU\n        // and NNAPI. GPU is another available option, but when using this option you will need\n        // to initialize the classifier on the thread that does the classifying. This requires a\n        // different app structure than is used in this sample.\n        fragmentAudioBinding.bottomSheetLayout.spinnerDelegate.onItemSelectedListener =\n            object : AdapterView.OnItemSelectedListener {\n                override fun onItemSelected(\n                  parent: AdapterView<*>?,\n                  view: View?,\n                  position: Int,\n                  id: Long\n                ) {\n                    audioHelper.stopAudioClassification()\n                    audioHelper.currentDelegate = position\n                    audioHelper.initClassifier()\n                }\n\n                override fun onNothingSelected(p0: AdapterView<*>?) {\n                    /* no op */\n                }\n            }\n\n        fragmentAudioBinding.bottomSheetLayout.spinnerOverlap.setSelection(\n            2,\n            false\n        )\n        fragmentAudioBinding.bottomSheetLayout.spinnerDelegate.setSelection(\n            0,\n            false\n        )\n    }\n\n    override fun onResume() {\n        super.onResume()\n        // Make sure that all permissions are still present, since the\n        // user could have removed them while the app was in paused state.\n        if (!PermissionsFragment.hasPermissions(requireContext())) {\n            Navigation.findNavController(requireActivity(), R.id.fragment_container)\n                .navigate(AudioFragmentDirections.actionAudioToPermissions())\n        }\n\n        if (::audioHelper.isInitialized ) {\n            audioHelper.startAudioClassification()\n        }\n    }\n\n    override fun onPause() {\n        super.onPause()\n        if (::audioHelper.isInitialized ) {\n            audioHelper.stopAudioClassification()\n        }\n    }\n\n    override fun onDestroyView() {\n        _fragmentBinding = null\n        super.onDestroyView()\n    }\n}\n"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/java/org/tensorflow/lite/examples/audio/fragments/PermissionsFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.audio.fragments\n\nimport android.Manifest\nimport android.content.Context\nimport android.content.pm.PackageManager\nimport android.os.Bundle\nimport android.widget.Toast\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.core.content.ContextCompat\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.lifecycleScope\nimport androidx.navigation.Navigation\nimport org.tensorflow.lite.examples.audio.R\n\nprivate val PERMISSIONS_REQUIRED = arrayOf(Manifest.permission.RECORD_AUDIO)\n\n/**\n * The sole purpose of this fragment is to request permissions and, once granted, display the\n * audio fragment to the user.\n */\nclass PermissionsFragment : Fragment() {\n\n    val requestPermissionLauncher =\n        registerForActivityResult(\n            ActivityResultContracts.RequestPermission()\n        ) { isGranted: Boolean ->\n            if (isGranted) {\n                Toast.makeText(context, \"Permission request granted\", Toast.LENGTH_LONG).show()\n                navigateToAudioFragment()\n            } else {\n                Toast.makeText(context, \"Permission request denied\", Toast.LENGTH_LONG).show()\n            }\n        }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        when {\n            ContextCompat.checkSelfPermission(\n                requireContext(),\n                Manifest.permission.RECORD_AUDIO\n            ) == PackageManager.PERMISSION_GRANTED -> {\n                navigateToAudioFragment()\n            }\n            else -> {\n                requestPermissionLauncher.launch(\n                    Manifest.permission.RECORD_AUDIO)\n            }\n        }\n    }\n\n    private fun navigateToAudioFragment() {\n        lifecycleScope.launchWhenStarted {\n            Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(\n                PermissionsFragmentDirections.actionPermissionsToAudio()\n            )\n        }\n    }\n\n    companion object {\n        /** Convenience method used to check if all permissions required by this app are granted */\n        fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all {\n            ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/java/org/tensorflow/lite/examples/audio/ui/ProbabilitiesAdapter.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.audio.ui\n\nimport android.content.res.ColorStateList\nimport android.view.LayoutInflater\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport org.tensorflow.lite.examples.audio.R\nimport org.tensorflow.lite.examples.audio.databinding.ItemProbabilityBinding\nimport org.tensorflow.lite.support.label.Category\n\ninternal class ProbabilitiesAdapter : RecyclerView.Adapter<ProbabilitiesAdapter.ViewHolder>() {\n    var categoryList: List<Category> = emptyList()\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {\n        val binding =\n            ItemProbabilityBinding.inflate(\n                LayoutInflater.from(parent.context),\n                parent,\n                false)\n        return ViewHolder(binding)\n    }\n\n    override fun onBindViewHolder(holder: ViewHolder, position: Int) {\n        val category = categoryList[position]\n        holder.bind(category.label, category.score, category.index)\n    }\n\n    override fun getItemCount(): Int {\n        return categoryList.size\n    }\n\n    class ViewHolder(private val binding: ItemProbabilityBinding) :\n        RecyclerView.ViewHolder(binding.root) {\n        private var primaryProgressColorList: IntArray\n        private var backgroundProgressColorList: IntArray\n\n        init {\n            primaryProgressColorList =\n                binding.root.resources.getIntArray((R.array.colors_progress_primary))\n            backgroundProgressColorList =\n                binding.root.resources.getIntArray((R.array.colors_progress_background))\n        }\n\n        fun bind(label: String, score: Float, index: Int) {\n            with(binding) {\n                labelTextView.text = label\n\n                progressBar.progressBackgroundTintList =\n                    ColorStateList.valueOf(\n                        backgroundProgressColorList[index % backgroundProgressColorList.size])\n\n                progressBar.progressTintList =\n                    ColorStateList.valueOf(\n                        primaryProgressColorList[index % primaryProgressColorList.size])\n\n                val newValue = (score * 100).toInt()\n                progressBar.progress = newValue\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path\n        android:fillColor=\"#3DDC84\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n</vector>\n"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/res/drawable/ic_minus.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13H5v-2h14v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/res/drawable/ic_plus.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path android:pathData=\"M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"85.84757\"\n                android:endY=\"92.4963\"\n                android:startX=\"42.9492\"\n                android:startY=\"49.59793\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:background=\"@android:color/transparent\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n    <androidx.fragment.app.FragmentContainerView\n        android:id=\"@+id/fragment_container\"\n        android:name=\"androidx.navigation.fragment.NavHostFragment\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"@android:color/transparent\"\n        android:keepScreenOn=\"true\"\n        app:defaultNavHost=\"true\"\n        app:navGraph=\"@navigation/nav_graph\"\n        android:layout_marginTop=\"?android:attr/actionBarSize\"\n        tools:context=\"org.tensorflow.lite.examples.audio.MainActivity\"/>\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:layout_alignParentTop=\"true\"\n            android:background=\"@color/toolbar_background\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/tfl_logo\" />\n        </androidx.appcompat.widget.Toolbar>\n\n    </RelativeLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/res/layout/controls_bottom_sheet.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.core.widget.NestedScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/bottom_sheet_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_gravity=\"center_horizontal\"\n    android:background=\"@color/bottom_sheet_background\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:padding=\"@dimen/margin_default\"\n    android:layout_height=\"wrap_content\"\n    app:behavior_hideable=\"false\"\n    app:behavior_peekHeight=\"@dimen/bottom_sheet_peek_height\"\n    app:layout_behavior=\"com.google.android.material.bottomsheet.BottomSheetBehavior\">\n\n    <androidx.appcompat.widget.LinearLayoutCompat xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        xmlns:tools=\"http://schemas.android.com/tools\"\n        xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n        android:id=\"@+id/audio_container\"\n        android:orientation=\"vertical\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <!-- Chevron indicating that the bottom sheet is expandable -->\n        <RelativeLayout\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\">\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerHorizontal=\"true\"\n                android:layout_centerVertical=\"true\"\n                android:src=\"@drawable/icn_chevron_up\"\n                android:contentDescription=\"@string/alt_bottom_sheet_chevron\" />\n        </RelativeLayout>\n\n        <!-- Inference time row -->\n        <androidx.appcompat.widget.LinearLayoutCompat\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_margin=\"@dimen/margin_default\"\n            android:orientation=\"horizontal\">\n            <TextView\n                android:id=\"@+id/inference_time_label\"\n                android:text=\"@string/label_interence_time\"\n                android:textColor=\"@color/black\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:textSize=\"@dimen/label_text_size\" />\n            <TextView\n                android:id=\"@+id/inference_time_val\"\n                android:text=\"0ms\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"end\"\n                android:layout_centerVertical=\"true\"\n                android:textColor=\"@color/black\"\n                android:textSize=\"@dimen/label_text_size\" />\n        </androidx.appcompat.widget.LinearLayoutCompat>\n\n        <RadioGroup\n            android:id=\"@+id/model_selector\"\n            android:layout_marginTop=\"@dimen/margin_default\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\">\n            <RadioButton\n                android:id=\"@+id/yamnet\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:checked=\"true\"\n                android:text=\"YAMNET\"/>\n\n            <RadioButton\n                android:id=\"@+id/speech_command\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"Speech Command\"/>\n        </RadioGroup>\n\n        <!-- Delegate selection row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_margin=\"@dimen/margin_default\">\n\n            <TextView\n                android:text=\"@string/label_delegate\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textSize=\"@dimen/label_text_size\"\n                android:textColor=\"@color/black\" />\n\n            <androidx.appcompat.widget.AppCompatSpinner\n                android:id=\"@+id/spinner_delegate\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:minWidth=\"@dimen/spinner_min_width\"\n                android:spinnerMode=\"dropdown\"\n                android:theme=\"@style/BottomSheetSpinnerItemStyle\"\n                android:layout_alignParentRight=\"true\"\n                android:entries=\"@array/delegate_spinner_titles\"/>\n\n        </RelativeLayout>\n\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_margin=\"@dimen/margin_default\">\n\n            <TextView\n                android:text=\"@string/label_overlap\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textSize=\"@dimen/label_text_size\"\n                android:layout_centerVertical=\"true\"\n                android:textColor=\"@color/black\" />\n\n            <androidx.appcompat.widget.AppCompatSpinner\n                android:id=\"@+id/spinner_overlap\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:minWidth=\"@dimen/spinner_min_width\"\n                android:spinnerMode=\"dropdown\"\n                android:theme=\"@style/BottomSheetSpinnerItemStyle\"\n                android:layout_alignParentRight=\"true\"\n                android:entries=\"@array/overlap_spinner_titles\"/>\n        </RelativeLayout>\n\n        <!-- Number of max results adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_margin=\"@dimen/margin_default\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/label_max_results\"\n                android:layout_centerVertical=\"true\"\n                android:textSize=\"@dimen/label_text_size\"\n                android:textColor=\"@color/black\" />\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\"\n                >\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/results_minus\"\n                    android:layout_width=\"@dimen/control_btn_size\"\n                    android:layout_height=\"@dimen/control_btn_size\"\n                    android:contentDescription=\"\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/results_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_margin=\"@dimen/margin_default\"\n                    android:gravity=\"center\"\n                    android:minEms=\"3\"\n                    android:text=\"2\"\n                    android:textColor=\"@color/black\"\n                    android:textSize=\"@dimen/label_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/results_plus\"\n                    android:layout_width=\"@dimen/control_btn_size\"\n                    android:layout_height=\"@dimen/control_btn_size\"\n                    android:contentDescription=\"\"\n                    android:src=\"@drawable/ic_plus\"  />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- ML confidence threshold adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_margin=\"@dimen/margin_default\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/label_confidence_threshold\"\n                android:layout_centerVertical=\"true\"\n                android:textSize=\"@dimen/label_text_size\"\n                android:textColor=\"@color/black\" />\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\"\n                >\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threshold_minus\"\n                    android:layout_width=\"@dimen/control_btn_size\"\n                    android:layout_height=\"@dimen/control_btn_size\"\n                    android:contentDescription=\"@string/alt_threshold_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/threshold_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/margin_default\"\n                    android:layout_marginRight=\"@dimen/margin_default\"\n                    android:gravity=\"center\"\n                    android:minEms=\"3\"\n                    android:text=\"0.30\"\n                    android:textColor=\"@color/black\"\n                    android:textSize=\"@dimen/label_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threshold_plus\"\n                    android:layout_width=\"@dimen/control_btn_size\"\n                    android:layout_height=\"@dimen/control_btn_size\"\n                    android:contentDescription=\"@string/alt_threshold_button_plus\"\n                    android:src=\"@drawable/ic_plus\"  />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- Number of threads adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_margin=\"@dimen/margin_default\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/label_threads\"\n                android:layout_centerVertical=\"true\"\n                android:textSize=\"@dimen/label_text_size\"\n                android:textColor=\"@color/black\" />\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\"\n                >\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threads_minus\"\n                    android:layout_width=\"@dimen/control_btn_size\"\n                    android:layout_height=\"@dimen/control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_thread_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/threads_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/margin_default\"\n                    android:layout_marginRight=\"@dimen/margin_default\"\n                    android:gravity=\"center\"\n                    android:minEms=\"3\"\n                    android:text=\"2\"\n                    android:textColor=\"@color/black\"\n                    android:textSize=\"@dimen/label_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threads_plus\"\n                    android:layout_width=\"@dimen/control_btn_size\"\n                    android:layout_height=\"@dimen/control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_thread_button_plus\"\n                    android:src=\"@drawable/ic_plus\"  />\n            </LinearLayout>\n        </RelativeLayout>\n\n    </androidx.appcompat.widget.LinearLayoutCompat>\n\n</androidx.core.widget.NestedScrollView>"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/res/layout/fragment_audio.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/audio_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/recycler_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_margin=\"@dimen/margin_default\"\n        android:scrollbars=\"vertical\"\n        app:layoutManager=\"LinearLayoutManager\"\n        tools:listitem=\"@layout/item_probability\" />\n\n    <include\n        android:id=\"@+id/bottom_sheet_layout\"\n        layout=\"@layout/controls_bottom_sheet\" />\n</androidx.coordinatorlayout.widget.CoordinatorLayout>"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/res/layout/item_probability.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <TextView\n        android:id=\"@+id/label_text_view\"\n        android:layout_width=\"@dimen/label_width\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"@dimen/margin_default\"\n        android:textColor=\"@android:color/black\"\n        android:textSize=\"@dimen/label_text_size\"\n        android:textStyle=\"bold\"\n        app:layout_constraintBottom_toBottomOf=\"@+id/progress_bar\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"@+id/progress_bar\"\n        tools:text=\"Background Noise\" />\n\n    <ProgressBar\n        android:id=\"@+id/progress_bar\"\n        style=\"@android:style/Widget.ProgressBar.Horizontal\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"@dimen/progress_bar_height\"\n        android:layout_marginStart=\"@dimen/progress_bar_margin_start\"\n        android:layout_marginTop=\"@dimen/margin_default\"\n        android:layout_marginEnd=\"@dimen/margin_default\"\n        android:layout_marginBottom=\"@dimen/margin_default\"\n        android:progressBackgroundTint=\"@color/progress_bar_background_tint\"\n        android:progressTint=\"@color/progress_bar_tint\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@id/label_text_view\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:progress=\"25\" />\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/res/navigation/nav_graph.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<navigation\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/nav_graph\"\n    app:startDestination=\"@id/permissions_fragment\">\n\n    <fragment\n        android:id=\"@+id/permissions_fragment\"\n        android:name=\"org.tensorflow.lite.examples.audio.fragments.PermissionsFragment\"\n        android:label=\"PermissionsFragment\" >\n\n        <action\n            android:id=\"@+id/action_permissions_to_audio\"\n            app:destination=\"@id/audio_fragment\"\n            app:popUpTo=\"@id/permissions_fragment\"\n            app:popUpToInclusive=\"true\" />\n\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/audio_fragment\"\n        android:name=\"org.tensorflow.lite.examples.audio.fragments.AudioFragment\"\n        android:label=\"AudioFragment\" >\n\n\n        <action\n            android:id=\"@+id/action_audio_to_permissions\"\n            app:destination=\"@id/permissions_fragment\"\n            app:popUpTo=\"@id/audio_fragment\"\n            app:popUpToInclusive=\"true\"/>\n\n    </fragment>\n</navigation>"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n    <color name=\"black\">#FF000000</color>\n    <color name=\"white\">#FFFFFFFF</color>\n\n    <color name=\"bottom_sheet_background\">#EEEEEE</color>\n\n    <color name=\"toolbar_background\">#EEEEEE</color>\n    <color name=\"ic_launcher_background\">#FFFFFF</color>\n\n    <color name=\"progress_bar_tint\">#d97c2e</color>\n    <color name=\"progress_bar_background_tint\">#f9e7e4</color>\n\n    <!-- Using material palette #500 -->\n    <color name=\"progress_red\">#f44336</color>\n    <color name=\"progress_pink\">#e91e63</color>\n    <color name=\"progress_purple\">#9c27b0</color>\n    <color name=\"progress_indigo\">#3f51b5</color>\n    <color name=\"progress_blue\">#2196f3</color>\n    <color name=\"progress_cyan\">#00bcd4</color>\n    <color name=\"progress_teal\">#009688</color>\n    <color name=\"progress_green\">#4caf50</color>\n    <color name=\"progress_yellow\">#ffeb3b</color>\n    <color name=\"progress_amber\">#ffc107</color>\n    <color name=\"progress_orange\">#ff9800</color>\n\n    <color name=\"background_red\">#44ff7961</color>\n    <color name=\"background_pink\">#44ff6090</color>\n    <color name=\"background_purple\">#44d05ce3</color>\n    <color name=\"background_indigo\">#44757de8</color>\n    <color name=\"background_blue\">#446ec6ff</color>\n    <color name=\"background_cyan\">#62efff</color>\n    <color name=\"background_teal\">#4452c7b8</color>\n    <color name=\"background_green\">#4480e27e</color>\n    <color name=\"background_yellow\">#44ffff72</color>\n    <color name=\"background_amber\">#44fff350</color>\n    <color name=\"background_orange\">#44ffc947</color>\n\n    <integer-array name=\"colors_progress_primary\">\n        <item>@color/progress_red</item>\n        <item>@color/progress_pink</item>\n        <item>@color/progress_purple</item>\n        <item>@color/progress_indigo</item>\n        <item>@color/progress_blue</item>\n        <item>@color/progress_cyan</item>\n        <item>@color/progress_teal</item>\n        <item>@color/progress_green</item>\n        <item>@color/progress_yellow</item>\n        <item>@color/progress_amber</item>\n        <item>@color/progress_orange</item>\n    </integer-array>\n\n    <integer-array name=\"colors_progress_background\">\n        <item>@color/background_red</item>\n        <item>@color/background_pink</item>\n        <item>@color/background_purple</item>\n        <item>@color/background_indigo</item>\n        <item>@color/background_blue</item>\n        <item>@color/background_cyan</item>\n        <item>@color/background_teal</item>\n        <item>@color/background_green</item>\n        <item>@color/background_yellow</item>\n        <item>@color/background_amber</item>\n        <item>@color/background_orange</item>\n    </integer-array>\n</resources>"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n\n    <dimen name=\"label_text_size\">16sp</dimen>\n    <dimen name=\"label_width\">92dp</dimen>\n    <dimen name=\"margin_default\">16dp</dimen>\n    <dimen name=\"progress_bar_margin_start\">12dp</dimen>\n    <dimen name=\"progress_bar_height\">20dp</dimen>\n    <dimen name=\"control_btn_size\">48dp</dimen>\n    <dimen name=\"spinner_min_width\">160dp</dimen>\n    <dimen name=\"bottom_sheet_peek_height\">50dp</dimen>\n</resources>"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/res/values/strings.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n    <string name=\"app_name\">TFL Audio Classification</string>\n\n    <string name=\"alt_threshold_button_plus\">Increasing threshold of detected sound\n        results button</string>\n    <string name=\"alt_threshold_button_minus\">Decreasing threshold of detected sound\n        results button</string>\n    <string name=\"alt_bottom_sheet_chevron\">Bottom sheet expandable indicator</string>\n    <string name=\"alt_bottom_sheet_thread_button_minus\">Decrease the number of threads used</string>\n    <string name=\"alt_bottom_sheet_thread_button_plus\">Increase the number of threads used</string>\n\n    <string name=\"label_interence_time\">Inference Time</string>\n    <string name=\"label_confidence_threshold\">Threshold</string>\n    <string name=\"label_delegate\">Delegate</string>\n    <string name=\"label_max_results\">Max results</string>\n    <string name=\"label_overlap\">Overlap</string>\n    <string name=\"label_threads\">Threads</string>\n\n    <string-array name=\"overlap_spinner_titles\">\n        <item>0%</item>\n        <item>25%</item>\n        <item>50%</item>\n        <item>75%</item>\n    </string-array>\n\n    <string-array name=\"delegate_spinner_titles\">\n        <item>CPU</item>\n        <item>NNAPI</item>\n    </string-array>\n</resources>"
  },
  {
    "path": "lite/examples/audio_classification/android/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\" />\n    <style name=\"BottomSheetSpinnerItemStyle\" parent=\"Widget.AppCompat.Light.DropDownItem.Spinner\">\n        <item name=\"android:textSize\">20sp</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "lite/examples/audio_classification/android/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    // Top-level variables used for versioning\n    ext.kotlin_version = '1.5.21'\n    ext.java_version = JavaVersion.VERSION_1_8\n\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:7.1.2'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n        classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.4.1'\n        classpath 'de.undercouch:gradle-download-task:4.1.2'\n\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}"
  },
  {
    "path": "lite/examples/audio_classification/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/audio_classification/android/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app\"s APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official\n# Enables namespacing of each library's R class so that its R class includes only the\n# resources declared in the library itself and none from the library's dependencies,\n# thereby reducing the size of the R class for that library\nandroid.nonTransitiveRClass=true"
  },
  {
    "path": "lite/examples/audio_classification/android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/audio_classification/android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/audio_classification/android/settings.gradle",
    "content": "include ':app'"
  },
  {
    "path": "lite/examples/audio_classification/ios/AudioClassification/AppDelegate.swift",
    "content": "// Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n  var window: UIWindow?\n\n  func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n  ) -> Bool {\n    // Override point for customization after application launch.\n    return true\n  }\n}\n"
  },
  {
    "path": "lite/examples/audio_classification/ios/AudioClassification/Assets.xcassets/AccentColor.colorset/Contents.json",
    "content": "{\n  \"colors\" : [\n    {\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "lite/examples/audio_classification/ios/AudioClassification/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"60x60\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"60x60\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"76x76\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"76x76\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"83.5x83.5\"\n    },\n    {\n      \"idiom\" : \"ios-marketing\",\n      \"scale\" : \"1x\",\n      \"size\" : \"1024x1024\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "lite/examples/audio_classification/ios/AudioClassification/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "lite/examples/audio_classification/ios/AudioClassification/Assets.xcassets/icn_chevron_down.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"filename\" : \"icn_chevron_down.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "lite/examples/audio_classification/ios/AudioClassification/Assets.xcassets/icn_chevron_up.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"filename\" : \"icn_chevron_up.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "lite/examples/audio_classification/ios/AudioClassification/Assets.xcassets/tfl_logo.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"tfl_logo.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"filename\" : \"tfl_logo@2x.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"filename\" : \"tfl_logo@3x.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "lite/examples/audio_classification/ios/AudioClassification/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"13122.16\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"13104.12\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" xcode11CocoaTouchSystemColor=\"systemBackgroundColor\" cocoaTouchSystemColor=\"whiteColor\"/>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "lite/examples/audio_classification/ios/AudioClassification/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"20037\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"20020\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"System colors in document resources\" minToolsVersion=\"11.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--Home View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"HomeViewController\" customModule=\"AudioClassification\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"600\" height=\"600\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <tableView clipsSubviews=\"YES\" contentMode=\"scaleToFill\" alwaysBounceVertical=\"YES\" dataMode=\"prototypes\" style=\"plain\" separatorStyle=\"default\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"-1\" estimatedSectionHeaderHeight=\"-1\" sectionFooterHeight=\"-1\" estimatedSectionFooterHeight=\"-1\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"MEL-oY-YWf\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"44\" width=\"600\" height=\"556\"/>\n                                <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\"/>\n                                <prototypes>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" reuseIdentifier=\"ResultCell\" rowHeight=\"60\" id=\"asY-eb-7fX\" customClass=\"ResultTableViewCell\" customModule=\"AudioClassification\" customModuleProvider=\"target\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"44.5\" width=\"600\" height=\"60\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"asY-eb-7fX\" id=\"Abw-lR-8BG\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"600\" height=\"60\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Name\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" minimumFontSize=\"12\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"3GQ-9P-grh\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"21\" width=\"320\" height=\"18\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"15\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                                <view clipsSubviews=\"YES\" contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"V4u-ra-KR7\">\n                                                    <rect key=\"frame\" x=\"344\" y=\"22\" width=\"240\" height=\"16\"/>\n                                                    <subviews>\n                                                        <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"BvE-bd-pbm\">\n                                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"100\" height=\"16\"/>\n                                                            <color key=\"backgroundColor\" systemColor=\"systemBrownColor\"/>\n                                                            <constraints>\n                                                                <constraint firstAttribute=\"width\" constant=\"100\" id=\"Wff-ci-O2e\"/>\n                                                            </constraints>\n                                                        </view>\n                                                    </subviews>\n                                                    <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\"/>\n                                                    <constraints>\n                                                        <constraint firstAttribute=\"bottom\" secondItem=\"BvE-bd-pbm\" secondAttribute=\"bottom\" id=\"1sl-lK-Snk\"/>\n                                                        <constraint firstItem=\"BvE-bd-pbm\" firstAttribute=\"top\" secondItem=\"V4u-ra-KR7\" secondAttribute=\"top\" id=\"286-To-qAR\"/>\n                                                        <constraint firstAttribute=\"height\" constant=\"16\" id=\"LlG-Hp-OFg\"/>\n                                                        <constraint firstItem=\"BvE-bd-pbm\" firstAttribute=\"leading\" secondItem=\"V4u-ra-KR7\" secondAttribute=\"leading\" id=\"cMm-Rw-Mwt\"/>\n                                                    </constraints>\n                                                    <userDefinedRuntimeAttributes>\n                                                        <userDefinedRuntimeAttribute type=\"string\" keyPath=\"layer.cornerRadius\" value=\"5\"/>\n                                                    </userDefinedRuntimeAttributes>\n                                                </view>\n                                            </subviews>\n                                            <constraints>\n                                                <constraint firstItem=\"3GQ-9P-grh\" firstAttribute=\"centerY\" secondItem=\"Abw-lR-8BG\" secondAttribute=\"centerY\" id=\"40y-E9-ele\"/>\n                                                <constraint firstItem=\"V4u-ra-KR7\" firstAttribute=\"centerY\" secondItem=\"3GQ-9P-grh\" secondAttribute=\"centerY\" id=\"Ixu-da-Lwa\"/>\n                                                <constraint firstAttribute=\"trailingMargin\" secondItem=\"V4u-ra-KR7\" secondAttribute=\"trailing\" id=\"JCw-kX-EVT\"/>\n                                                <constraint firstItem=\"V4u-ra-KR7\" firstAttribute=\"leading\" secondItem=\"3GQ-9P-grh\" secondAttribute=\"trailing\" constant=\"8\" id=\"cn5-tH-PEB\"/>\n                                                <constraint firstItem=\"3GQ-9P-grh\" firstAttribute=\"leading\" secondItem=\"Abw-lR-8BG\" secondAttribute=\"leadingMargin\" id=\"dLy-Xq-Woi\"/>\n                                                <constraint firstItem=\"V4u-ra-KR7\" firstAttribute=\"width\" secondItem=\"Abw-lR-8BG\" secondAttribute=\"width\" multiplier=\"0.4\" id=\"n7w-yE-jNz\"/>\n                                            </constraints>\n                                        </tableViewCellContentView>\n                                        <connections>\n                                            <outlet property=\"nameLabel\" destination=\"3GQ-9P-grh\" id=\"PPF-tp-UWS\"/>\n                                            <outlet property=\"scoreWidthLayoutConstraint\" destination=\"Wff-ci-O2e\" id=\"p6m-gB-Ovi\"/>\n                                        </connections>\n                                    </tableViewCell>\n                                </prototypes>\n                                <connections>\n                                    <outlet property=\"dataSource\" destination=\"BYZ-38-t0r\" id=\"opk-4G-vJq\"/>\n                                    <outlet property=\"delegate\" destination=\"BYZ-38-t0r\" id=\"erB-MO-070\"/>\n                                </connections>\n                            </tableView>\n                            <view autoresizesSubviews=\"NO\" clipsSubviews=\"YES\" contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"iph-Fi-onr\" customClass=\"InferenceView\" customModule=\"AudioClassification\" customModuleProvider=\"target\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"560\" width=\"600\" height=\"40\"/>\n                                <subviews>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Inference Time:\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"AnK-Ht-EgW\">\n                                        <rect key=\"frame\" x=\"12\" y=\"60\" width=\"127\" height=\"21\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"17\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Overlap:\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"6H2-kM-cpc\">\n                                        <rect key=\"frame\" x=\"12\" y=\"141.5\" width=\"69.5\" height=\"21\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"17\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Max results:\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"32q-7i-Ohk\">\n                                        <rect key=\"frame\" x=\"12\" y=\"180.5\" width=\"100\" height=\"21\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"17\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Threshold:\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"BJ7-0I-mhy\">\n                                        <rect key=\"frame\" x=\"12\" y=\"219.5\" width=\"88\" height=\"21\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"17\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <stepper opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" value=\"0.5\" minimumValue=\"0.10000000000000001\" maximumValue=\"0.90000000000000002\" stepValue=\"0.10000000000000001\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"sIf-44-PZi\">\n                                        <rect key=\"frame\" x=\"486\" y=\"136\" width=\"94\" height=\"32\"/>\n                                        <connections>\n                                            <action selector=\"overlapStepperValueChanged:\" destination=\"iph-Fi-onr\" eventType=\"valueChanged\" id=\"nlE-hl-zLm\"/>\n                                        </connections>\n                                    </stepper>\n                                    <stepper opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" value=\"3\" minimumValue=\"1\" maximumValue=\"10\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"9cJ-hv-uzx\">\n                                        <rect key=\"frame\" x=\"486\" y=\"175\" width=\"94\" height=\"32\"/>\n                                        <connections>\n                                            <action selector=\"maxResultsStepperValueChanged:\" destination=\"iph-Fi-onr\" eventType=\"valueChanged\" id=\"zgS-ft-bQT\"/>\n                                        </connections>\n                                    </stepper>\n                                    <stepper opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" value=\"0.29999999999999999\" maximumValue=\"1\" stepValue=\"0.10000000000000001\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"0aJ-B4-Rqn\">\n                                        <rect key=\"frame\" x=\"486\" y=\"214\" width=\"94\" height=\"32\"/>\n                                        <connections>\n                                            <action selector=\"thresholdStepperValueChanged:\" destination=\"iph-Fi-onr\" eventType=\"valueChanged\" id=\"P5X-ib-irD\"/>\n                                        </connections>\n                                    </stepper>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"50%\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"irD-Zw-BdY\">\n                                        <rect key=\"frame\" x=\"446\" y=\"141.5\" width=\"36\" height=\"21\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"3\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"P9Y-WW-0A5\">\n                                        <rect key=\"frame\" x=\"471.5\" y=\"180.5\" width=\"10.5\" height=\"21\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"0.3\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"PwS-Q3-V2E\">\n                                        <rect key=\"frame\" x=\"458\" y=\"219.5\" width=\"24\" height=\"21\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"13ms\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"d6j-0A-C1x\">\n                                        <rect key=\"frame\" x=\"159\" y=\"60\" width=\"44\" height=\"21\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"17\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <segmentedControl opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" apportionsSegmentWidthsByContent=\"YES\" segmentControlStyle=\"plain\" selectedSegmentIndex=\"0\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"zGs-gP-mUD\">\n                                        <rect key=\"frame\" x=\"375\" y=\"96\" width=\"205\" height=\"32\"/>\n                                        <segments>\n                                            <segment title=\"YAMNET\"/>\n                                            <segment title=\"Speech Command\"/>\n                                        </segments>\n                                        <connections>\n                                            <action selector=\"modelSegmentedValueChanged:\" destination=\"iph-Fi-onr\" eventType=\"valueChanged\" id=\"MnP-e5-AgW\"/>\n                                        </connections>\n                                    </segmentedControl>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Model:\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"EUU-kO-GvP\">\n                                        <rect key=\"frame\" x=\"12\" y=\"101\" width=\"56\" height=\"20.5\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"17\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Threads:\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"KTX-2P-FQc\">\n                                        <rect key=\"frame\" x=\"12\" y=\"260.5\" width=\"73\" height=\"21\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"17\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <stepper opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" value=\"2\" minimumValue=\"1\" maximumValue=\"5\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"rQE-Ag-amu\">\n                                        <rect key=\"frame\" x=\"486\" y=\"255\" width=\"94\" height=\"32\"/>\n                                        <connections>\n                                            <action selector=\"threadsStepperValueChanged:\" destination=\"iph-Fi-onr\" eventType=\"valueChanged\" id=\"b33-Rq-IUg\"/>\n                                        </connections>\n                                    </stepper>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"2\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"rO3-A3-Bdl\">\n                                        <rect key=\"frame\" x=\"472\" y=\"260.5\" width=\"10\" height=\"21\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <button opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"zMK-Oq-ZDa\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"8\" width=\"600\" height=\"40\"/>\n                                        <constraints>\n                                            <constraint firstAttribute=\"height\" constant=\"40\" id=\"uNv-tI-gd6\"/>\n                                        </constraints>\n                                        <inset key=\"imageEdgeInsets\" minX=\"0.0\" minY=\"0.0\" maxX=\"2.2250738585072014e-308\" maxY=\"0.0\"/>\n                                        <state key=\"normal\" image=\"icn_chevron_up\"/>\n                                        <state key=\"selected\" image=\"icn_chevron_down\"/>\n                                        <connections>\n                                            <action selector=\"showHidenButtonTouchUpInside:\" destination=\"iph-Fi-onr\" eventType=\"touchUpInside\" id=\"46E-6m-MbN\"/>\n                                        </connections>\n                                    </button>\n                                </subviews>\n                                <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\"/>\n                                <constraints>\n                                    <constraint firstItem=\"P9Y-WW-0A5\" firstAttribute=\"centerY\" secondItem=\"9cJ-hv-uzx\" secondAttribute=\"centerY\" id=\"0pR-Xc-dNx\"/>\n                                    <constraint firstItem=\"rQE-Ag-amu\" firstAttribute=\"centerY\" secondItem=\"KTX-2P-FQc\" secondAttribute=\"centerY\" id=\"4iZ-gA-mRf\"/>\n                                    <constraint firstItem=\"0aJ-B4-Rqn\" firstAttribute=\"centerY\" secondItem=\"BJ7-0I-mhy\" secondAttribute=\"centerY\" id=\"7OE-zQ-o9v\"/>\n                                    <constraint firstItem=\"AnK-Ht-EgW\" firstAttribute=\"leading\" secondItem=\"iph-Fi-onr\" secondAttribute=\"leading\" constant=\"12\" id=\"7b4-Xc-thN\"/>\n                                    <constraint firstItem=\"d6j-0A-C1x\" firstAttribute=\"centerY\" secondItem=\"AnK-Ht-EgW\" secondAttribute=\"centerY\" id=\"93P-oK-ZVf\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"zGs-gP-mUD\" secondAttribute=\"trailing\" constant=\"20\" id=\"CQE-Is-0JQ\"/>\n                                    <constraint firstItem=\"BJ7-0I-mhy\" firstAttribute=\"top\" secondItem=\"32q-7i-Ohk\" secondAttribute=\"bottom\" constant=\"18\" id=\"FGI-CW-Vhu\"/>\n                                    <constraint firstItem=\"9cJ-hv-uzx\" firstAttribute=\"leading\" secondItem=\"P9Y-WW-0A5\" secondAttribute=\"trailing\" constant=\"4\" id=\"FQ4-g8-rVb\"/>\n                                    <constraint firstItem=\"9cJ-hv-uzx\" firstAttribute=\"trailing\" secondItem=\"sIf-44-PZi\" secondAttribute=\"trailing\" id=\"Gwi-BJ-g4J\"/>\n                                    <constraint firstItem=\"9cJ-hv-uzx\" firstAttribute=\"centerY\" secondItem=\"32q-7i-Ohk\" secondAttribute=\"centerY\" id=\"JGd-YM-1jI\"/>\n                                    <constraint firstItem=\"32q-7i-Ohk\" firstAttribute=\"leading\" secondItem=\"AnK-Ht-EgW\" secondAttribute=\"leading\" id=\"Kpb-R5-m5F\"/>\n                                    <constraint firstItem=\"0aJ-B4-Rqn\" firstAttribute=\"trailing\" secondItem=\"sIf-44-PZi\" secondAttribute=\"trailing\" id=\"MF0-oB-M4k\"/>\n                                    <constraint firstItem=\"zMK-Oq-ZDa\" firstAttribute=\"leading\" secondItem=\"iph-Fi-onr\" secondAttribute=\"leading\" id=\"N0k-41-CF3\"/>\n                                    <constraint firstItem=\"AnK-Ht-EgW\" firstAttribute=\"top\" secondItem=\"zMK-Oq-ZDa\" secondAttribute=\"bottom\" constant=\"12\" id=\"NDz-4F-mR4\"/>\n                                    <constraint firstItem=\"zGs-gP-mUD\" firstAttribute=\"centerY\" secondItem=\"EUU-kO-GvP\" secondAttribute=\"centerY\" id=\"OFa-yI-wNR\"/>\n                                    <constraint firstItem=\"0aJ-B4-Rqn\" firstAttribute=\"leading\" secondItem=\"PwS-Q3-V2E\" secondAttribute=\"trailing\" constant=\"4\" id=\"QDW-fa-5Xa\"/>\n                                    <constraint firstItem=\"6H2-kM-cpc\" firstAttribute=\"top\" secondItem=\"EUU-kO-GvP\" secondAttribute=\"bottom\" constant=\"20\" id=\"QDa-hg-UHw\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"sIf-44-PZi\" secondAttribute=\"trailing\" constant=\"20\" id=\"QFb-Z1-7UV\"/>\n                                    <constraint firstItem=\"EUU-kO-GvP\" firstAttribute=\"leading\" secondItem=\"iph-Fi-onr\" secondAttribute=\"leading\" constant=\"12\" id=\"STQ-Og-rcr\"/>\n                                    <constraint firstItem=\"rO3-A3-Bdl\" firstAttribute=\"centerY\" secondItem=\"KTX-2P-FQc\" secondAttribute=\"centerY\" id=\"U8c-XU-EQI\"/>\n                                    <constraint firstItem=\"KTX-2P-FQc\" firstAttribute=\"leading\" secondItem=\"iph-Fi-onr\" secondAttribute=\"leading\" constant=\"12\" id=\"USI-PT-mOa\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"zMK-Oq-ZDa\" secondAttribute=\"trailing\" id=\"YgR-HO-l5a\"/>\n                                    <constraint firstItem=\"rQE-Ag-amu\" firstAttribute=\"leading\" secondItem=\"rO3-A3-Bdl\" secondAttribute=\"trailing\" constant=\"4\" id=\"alm-vX-6ma\"/>\n                                    <constraint firstItem=\"irD-Zw-BdY\" firstAttribute=\"centerY\" secondItem=\"sIf-44-PZi\" secondAttribute=\"centerY\" id=\"bBY-ds-NeZ\"/>\n                                    <constraint firstItem=\"KTX-2P-FQc\" firstAttribute=\"top\" secondItem=\"BJ7-0I-mhy\" secondAttribute=\"bottom\" constant=\"20\" id=\"dHL-vn-YVk\"/>\n                                    <constraint firstItem=\"zMK-Oq-ZDa\" firstAttribute=\"top\" secondItem=\"iph-Fi-onr\" secondAttribute=\"top\" constant=\"8\" id=\"gtf-vK-MY3\"/>\n                                    <constraint firstItem=\"32q-7i-Ohk\" firstAttribute=\"top\" secondItem=\"6H2-kM-cpc\" secondAttribute=\"bottom\" constant=\"18\" id=\"igM-4Y-ga4\"/>\n                                    <constraint firstItem=\"BJ7-0I-mhy\" firstAttribute=\"leading\" secondItem=\"AnK-Ht-EgW\" secondAttribute=\"leading\" id=\"jsY-CT-9sX\"/>\n                                    <constraint firstItem=\"sIf-44-PZi\" firstAttribute=\"leading\" secondItem=\"irD-Zw-BdY\" secondAttribute=\"trailing\" constant=\"4\" id=\"k7n-dX-whY\"/>\n                                    <constraint firstItem=\"PwS-Q3-V2E\" firstAttribute=\"centerY\" secondItem=\"0aJ-B4-Rqn\" secondAttribute=\"centerY\" id=\"nI2-Jh-m7J\"/>\n                                    <constraint firstItem=\"6H2-kM-cpc\" firstAttribute=\"leading\" secondItem=\"iph-Fi-onr\" secondAttribute=\"leading\" constant=\"12\" id=\"rb6-eT-BSo\"/>\n                                    <constraint firstAttribute=\"height\" constant=\"40\" id=\"uSc-8S-3eG\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"rQE-Ag-amu\" secondAttribute=\"trailing\" constant=\"20\" id=\"yDl-Ub-FN7\"/>\n                                    <constraint firstItem=\"EUU-kO-GvP\" firstAttribute=\"top\" secondItem=\"AnK-Ht-EgW\" secondAttribute=\"bottom\" constant=\"20\" id=\"yZw-zw-Oej\"/>\n                                    <constraint firstItem=\"sIf-44-PZi\" firstAttribute=\"centerY\" secondItem=\"6H2-kM-cpc\" secondAttribute=\"centerY\" id=\"zQh-Jt-p9t\"/>\n                                    <constraint firstItem=\"d6j-0A-C1x\" firstAttribute=\"leading\" secondItem=\"AnK-Ht-EgW\" secondAttribute=\"trailing\" constant=\"20\" id=\"zpH-4c-03W\"/>\n                                </constraints>\n                                <connections>\n                                    <outlet property=\"inferenceTimeLabel\" destination=\"d6j-0A-C1x\" id=\"dAu-8q-Dtb\"/>\n                                    <outlet property=\"maxResulteLabel\" destination=\"P9Y-WW-0A5\" id=\"QRh-zP-fXJ\"/>\n                                    <outlet property=\"maxResultsStepper\" destination=\"9cJ-hv-uzx\" id=\"3T2-lZ-efN\"/>\n                                    <outlet property=\"modelSegmentedControl\" destination=\"zGs-gP-mUD\" id=\"0Rk-PI-Qtb\"/>\n                                    <outlet property=\"overLapStepper\" destination=\"sIf-44-PZi\" id=\"bq6-i9-XP8\"/>\n                                    <outlet property=\"overlabLabel\" destination=\"irD-Zw-BdY\" id=\"os8-JF-L7U\"/>\n                                    <outlet property=\"showHidenButton\" destination=\"zMK-Oq-ZDa\" id=\"6Le-xr-KZI\"/>\n                                    <outlet property=\"showHidenButtonLayoutConstraint\" destination=\"uSc-8S-3eG\" id=\"tLP-2q-EST\"/>\n                                    <outlet property=\"threadsLabel\" destination=\"rO3-A3-Bdl\" id=\"EHR-An-d7y\"/>\n                                    <outlet property=\"threadsStepper\" destination=\"rQE-Ag-amu\" id=\"ipK-i9-8ak\"/>\n                                    <outlet property=\"thresholdLabel\" destination=\"PwS-Q3-V2E\" id=\"MXk-eM-dFn\"/>\n                                    <outlet property=\"thresholdStepper\" destination=\"0aJ-B4-Rqn\" id=\"aYc-iY-l3I\"/>\n                                </connections>\n                            </view>\n                            <imageView clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleAspectFit\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" image=\"tfl_logo\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"7K0-XB-7ff\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"600\" height=\"44\"/>\n                                <color key=\"backgroundColor\" red=\"0.59999999999999998\" green=\"0.59999999999999998\" blue=\"0.59999999999999998\" alpha=\"0.47696109693877553\" colorSpace=\"custom\" customColorSpace=\"calibratedRGB\"/>\n                            </imageView>\n                        </subviews>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                        <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\"/>\n                        <constraints>\n                            <constraint firstItem=\"iph-Fi-onr\" firstAttribute=\"trailing\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"trailing\" id=\"9TG-hq-iy2\"/>\n                            <constraint firstItem=\"6Tk-OE-BBY\" firstAttribute=\"bottom\" secondItem=\"MEL-oY-YWf\" secondAttribute=\"bottom\" id=\"K0l-3h-XqO\"/>\n                            <constraint firstItem=\"7K0-XB-7ff\" firstAttribute=\"top\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"top\" id=\"Ozd-1O-mDX\"/>\n                            <constraint firstItem=\"6Tk-OE-BBY\" firstAttribute=\"trailing\" secondItem=\"7K0-XB-7ff\" secondAttribute=\"trailing\" id=\"TeZ-ak-i36\"/>\n                            <constraint firstItem=\"MEL-oY-YWf\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" id=\"Ysz-tc-eCZ\"/>\n                            <constraint firstItem=\"7K0-XB-7ff\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" id=\"ab3-mA-0MS\"/>\n                            <constraint firstItem=\"MEL-oY-YWf\" firstAttribute=\"top\" secondItem=\"7K0-XB-7ff\" secondAttribute=\"bottom\" id=\"gqd-2U-IzF\"/>\n                            <constraint firstItem=\"iph-Fi-onr\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" id=\"hbf-z3-Acp\"/>\n                            <constraint firstItem=\"6Tk-OE-BBY\" firstAttribute=\"trailing\" secondItem=\"MEL-oY-YWf\" secondAttribute=\"trailing\" id=\"hxW-Ew-mqA\"/>\n                            <constraint firstItem=\"MEL-oY-YWf\" firstAttribute=\"top\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"top\" constant=\"44\" id=\"wHm-6a-m6s\"/>\n                            <constraint firstItem=\"6Tk-OE-BBY\" firstAttribute=\"bottom\" secondItem=\"iph-Fi-onr\" secondAttribute=\"bottom\" id=\"wIl-Ag-dMb\"/>\n                        </constraints>\n                    </view>\n                    <extendedEdge key=\"edgesForExtendedLayout\" bottom=\"YES\"/>\n                    <connections>\n                        <outlet property=\"inferenceView\" destination=\"iph-Fi-onr\" id=\"Bwq-mD-tEx\"/>\n                        <outlet property=\"tableView\" destination=\"MEL-oY-YWf\" id=\"SZV-J9-XMA\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"18.399999999999999\" y=\"85.714285714285722\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"icn_chevron_down\" width=\"30\" height=\"9\"/>\n        <image name=\"icn_chevron_up\" width=\"30\" height=\"9\"/>\n        <image name=\"tfl_logo\" width=\"266.66665649414062\" height=\"42.666667938232422\"/>\n        <systemColor name=\"systemBackgroundColor\">\n            <color white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n        </systemColor>\n        <systemColor name=\"systemBrownColor\">\n            <color red=\"0.63529411764705879\" green=\"0.51764705882352946\" blue=\"0.36862745098039218\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n        </systemColor>\n    </resources>\n</document>\n"
  },
  {
    "path": "lite/examples/audio_classification/ios/AudioClassification/HomeViewController.swift",
    "content": "// Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport AVFoundation\nimport TensorFlowLiteTaskAudio\nimport UIKit\n\n/// The sample app's home screen.\nclass HomeViewController: UIViewController {\n\n  // MARK: - Variables\n\n  @IBOutlet private weak var tableView: UITableView!\n  @IBOutlet private weak var inferenceView: InferenceView!\n\n  private var inferenceResults: [ClassificationCategory] = []\n\n  private var modelType: ModelType = .Yamnet\n  private var overLap = 0.5\n  private var maxResults = 3\n  private var threshold: Float = 0.0\n  private var threadCount = 2\n\n  private var audioClassificationHelper: AudioClassificationHelper?\n\n  // MARK: - View controller lifecycle methods\n  override func viewDidLoad() {\n    super.viewDidLoad()\n    inferenceView.setDefault(\n      model: modelType, overLab: overLap, maxResult: maxResults, threshold: threshold,\n      threads: threadCount)\n    inferenceView.delegate = self\n    startAudioClassification()\n  }\n\n  // MARK: - Private Methods\n  /// Request permission and start audio classification if granted.\n  private func startAudioClassification() {\n    AVAudioSession.sharedInstance().requestRecordPermission { [weak self] granted in\n      if granted {\n        DispatchQueue.main.async {\n          self?.restartClassifier()\n        }\n      } else {\n        self?.checkPermissions()\n      }\n    }\n  }\n\n  /// Check permission and show error if user denied permission.\n  private func checkPermissions() {\n    switch AVAudioSession.sharedInstance().recordPermission {\n    case .granted, .undetermined:\n      startAudioClassification()\n    case .denied:\n      showPermissionsErrorAlert()\n    @unknown default:\n      fatalError(\"Microphone permission check returned unexpected result.\")\n    }\n  }\n\n  /// Start a new audio classification routine.\n  private func restartClassifier() {\n    // Stop the existing classifier if one is running.\n    audioClassificationHelper?.stopClassifier()\n\n    // Create a new classifier instance.\n    audioClassificationHelper = AudioClassificationHelper(\n      modelType: modelType,\n      threadCount: threadCount,\n      scoreThreshold: threshold,\n      maxResults: maxResults)\n\n    // Start the new classification routine.\n    audioClassificationHelper?.delegate = self\n    audioClassificationHelper?.startClassifier(overlap: overLap)\n  }\n}\n\nextension HomeViewController {\n  private func showPermissionsErrorAlert() {\n    let alertController = UIAlertController(\n      title: \"Microphone Permissions Denied\",\n      message:\n        \"Microphone permissions have been denied for this app. You can change this by going to Settings\",\n      preferredStyle: .alert\n    )\n\n    let cancelAction = UIAlertAction(title: \"Cancel\", style: .cancel, handler: nil)\n    let settingsAction = UIAlertAction(title: \"Settings\", style: .default) { _ in\n      UIApplication.shared.open(\n        URL(string: UIApplication.openSettingsURLString)!,\n        options: [:],\n        completionHandler: nil\n      )\n    }\n    alertController.addAction(cancelAction)\n    alertController.addAction(settingsAction)\n\n    present(alertController, animated: true, completion: nil)\n  }\n}\n\nenum ModelType: String {\n  case Yamnet = \"YAMNet\"\n  case speechCommandModel = \"Speech Command\"\n\n  var fileName: String {\n    switch self {\n    case .Yamnet:\n      return \"yamnet\"\n    case .speechCommandModel:\n      return \"speech_commands\"\n    }\n  }\n}\n\nextension HomeViewController: UITableViewDataSource, UITableViewDelegate {\n\n  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {\n    guard\n      let cell = tableView.dequeueReusableCell(withIdentifier: \"ResultCell\") as? ResultTableViewCell\n    else { fatalError() }\n    cell.setData(inferenceResults[indexPath.row])\n    return cell\n  }\n\n  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {\n    return inferenceResults.count\n  }\n}\n\nextension HomeViewController: AudioClassificationHelperDelegate {\n  func audioClassificationHelper(_ helper: AudioClassificationHelper, didSucceed result: Result) {\n    inferenceResults = result.categories\n    tableView.reloadData()\n    inferenceView.setInferenceTime(result.inferenceTime)\n  }\n\n  func audioClassificationHelper(_ helper: AudioClassificationHelper, didFail error: Error) {\n    let errorMessage =\n      \"An error occured while running audio classification: \\(error.localizedDescription)\"\n    let alert = UIAlertController(\n      title: \"Error\", message: errorMessage, preferredStyle: UIAlertController.Style.alert)\n    alert.addAction(UIAlertAction(title: \"OK\", style: UIAlertAction.Style.default, handler: nil))\n    present(alert, animated: true, completion: nil)\n  }\n}\n\nextension HomeViewController: InferenceViewDelegate {\n  func view(_ view: InferenceView, needPerformActions action: InferenceView.Action) {\n    switch action {\n    case .changeModel(let modelType):\n      self.modelType = modelType\n    case .changeOverlap(let overLap):\n      self.overLap = overLap\n    case .changeMaxResults(let maxResults):\n      self.maxResults = maxResults\n    case .changeScoreThreshold(let threshold):\n      self.threshold = threshold\n    case .changeThreadCount(let threadCount):\n      self.threadCount = threadCount\n    }\n\n    // Restart the audio classifier as the config as changed.\n    restartClassifier()\n  }\n}\n"
  },
  {
    "path": "lite/examples/audio_classification/ios/AudioClassification/InferenceView.swift",
    "content": "// Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\nprotocol InferenceViewDelegate: AnyObject {\n  /// This method is called when the user changes the value to update model used for inference.\n  func view(_ view: InferenceView, needPerformActions action: InferenceView.Action)\n}\n\n/// View to allows users changing the inference configs.\nclass InferenceView: UIView {\n\n  enum Action {\n    case changeModel(ModelType)\n    case changeOverlap(Double)\n    case changeMaxResults(Int)\n    case changeScoreThreshold(Float)\n    case changeThreadCount(Int)\n  }\n\n  weak var delegate: InferenceViewDelegate?\n\n  @IBOutlet private weak var inferenceTimeLabel: UILabel!\n  @IBOutlet private weak var overlabLabel: UILabel!\n  @IBOutlet private weak var maxResulteLabel: UILabel!\n  @IBOutlet private weak var thresholdLabel: UILabel!\n  @IBOutlet private weak var threadsLabel: UILabel!\n  @IBOutlet private weak var overLapStepper: UIStepper!\n  @IBOutlet private weak var maxResultsStepper: UIStepper!\n  @IBOutlet private weak var thresholdStepper: UIStepper!\n  @IBOutlet private weak var threadsStepper: UIStepper!\n  @IBOutlet private weak var modelSegmentedControl: UISegmentedControl!\n  @IBOutlet private weak var showHidenButtonLayoutConstraint: NSLayoutConstraint!\n  @IBOutlet private weak var showHidenButton: UIButton!\n\n  /// Set the default settings.\n  func setDefault(model: ModelType, overLab: Double, maxResult: Int, threshold: Float, threads: Int)\n  {\n    modelSegmentedControl.selectedSegmentIndex = model == .Yamnet ? 0 : 1\n    overlabLabel.text = \"\\(Int(overLab * 100))%\"\n    overLapStepper.value = overLab\n    maxResulteLabel.text = \"\\(maxResult)\"\n    maxResultsStepper.value = Double(maxResult)\n    thresholdLabel.text = String(format: \"%.1f\", threshold)\n    thresholdStepper.value = Double(threshold)\n    threadsLabel.text = \"\\(threads)\"\n    threadsStepper.value = Double(threads)\n  }\n\n  func setInferenceTime(_ inferenceTime: TimeInterval) {\n    inferenceTimeLabel.text = \"\\(Int(inferenceTime * 1000)) ms\"\n  }\n\n  @IBAction func modelSegmentedValueChanged(_ sender: UISegmentedControl) {\n    let modelSelect: ModelType = sender.selectedSegmentIndex == 0 ? .Yamnet : .speechCommandModel\n    delegate?.view(self, needPerformActions: .changeModel(modelSelect))\n  }\n\n  @IBAction func overlapStepperValueChanged(_ sender: UIStepper) {\n    overlabLabel.text = String(format: \"%.0f\", sender.value * 100) + \"%\"\n    delegate?.view(self, needPerformActions: .changeOverlap(sender.value))\n  }\n\n  @IBAction func maxResultsStepperValueChanged(_ sender: UIStepper) {\n    maxResulteLabel.text = \"\\(Int(sender.value))\"\n    delegate?.view(self, needPerformActions: .changeMaxResults(Int(sender.value)))\n  }\n\n  @IBAction func thresholdStepperValueChanged(_ sender: UIStepper) {\n    thresholdLabel.text = String(format: \"%.1f\", sender.value)\n    delegate?.view(self, needPerformActions: .changeScoreThreshold(Float(sender.value)))\n  }\n\n  @IBAction func threadsStepperValueChanged(_ sender: UIStepper) {\n    threadsLabel.text = \"\\(Int(sender.value))\"\n    delegate?.view(self, needPerformActions: .changeThreadCount(Int(sender.value)))\n  }\n\n  @IBAction func showHidenButtonTouchUpInside(_ sender: UIButton) {\n    sender.isSelected.toggle()\n    showHidenButtonLayoutConstraint.constant = sender.isSelected ? 300 : 40\n    UIView.animate(\n      withDuration: 0.3,\n      animations: {\n        self.superview?.layoutIfNeeded()\n      }, completion: nil)\n  }\n}\n"
  },
  {
    "path": "lite/examples/audio_classification/ios/AudioClassification/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict/>\n</plist>\n"
  },
  {
    "path": "lite/examples/audio_classification/ios/AudioClassification/ResultTableViewCell.swift",
    "content": "// Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport TensorFlowLiteTaskAudio\nimport UIKit\n\n/// TableViewCell to display the inference results. Each cell corresponds to a single category.\nclass ResultTableViewCell: UITableViewCell {\n\n  @IBOutlet weak var nameLabel: UILabel!\n  @IBOutlet weak var scoreWidthLayoutConstraint: NSLayoutConstraint!\n\n  func setData(_ data: ClassificationCategory) {\n    nameLabel.text = data.label\n    if !data.score.isNaN {\n      // score view width is equal 1/4 screen with\n      scoreWidthLayoutConstraint.constant = UIScreen.main.bounds.width / 4 * CGFloat(data.score)\n    } else {\n      scoreWidthLayoutConstraint.constant = 0\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/audio_classification/ios/AudioClassification/TFLite/AudioClassificationHelper.swift",
    "content": "// Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport TensorFlowLiteTaskAudio\n\n/// Delegate to returns the classification results.\nprotocol AudioClassificationHelperDelegate: AnyObject {\n  func audioClassificationHelper(_ helper: AudioClassificationHelper, didSucceed result: Result)\n  func audioClassificationHelper(_ helper: AudioClassificationHelper, didFail error: Error)\n}\n\nprivate let errorDomain = \"org.tensorflow.lite.examples\"\n\n/// Stores results for a particular audio snipprt that was successfully classified.\nstruct Result {\n  let inferenceTime: Double\n  let categories: [ClassificationCategory]\n}\n\n/// Information about a model file.\ntypealias FileInfo = (name: String, extension: String)\n\n/// This class handles all data preprocessing and makes calls to run inference on a audio snippet\n/// by invoking the Task Library's `AudioClassifier`.\nclass AudioClassificationHelper {\n\n  // MARK: Public properties\n\n  weak var delegate: AudioClassificationHelperDelegate?\n\n  // MARK: Private properties\n\n  /// An `AudioClassifier` object for performing audio classification using a given model.\n  private let classifier: AudioClassifier\n\n  /// An object to continously record audio using the device's microphone.\n  private let audioRecord: AudioRecord\n\n  /// A tensor to store the input audio for the model.\n  private let inputAudioTensor: AudioTensor\n\n  /// A timer to schedule classification routine to run periodically.\n  private var timer: Timer?\n\n  /// A queue to offload the classification routine to a background thread.\n  private let processQueue = DispatchQueue(label: \"processQueue\")\n\n  // MARK: - Initialization\n\n  /// A failable initializer for `AudioClassificationHelper`.\n  ///\n  /// A new instance is created if the model is successfully loaded from the app's main bundle.\n  init?(modelType: ModelType, threadCount: Int, scoreThreshold: Float, maxResults: Int) {\n    let modelFilename = modelType.fileName\n\n    // Construct the path to the model file.\n    guard\n      let modelPath = Bundle.main.path(\n        forResource: modelFilename,\n        ofType: \"tflite\"\n      )\n    else {\n      print(\"Failed to load the model file \\(modelFilename).tflite.\")\n      return nil\n    }\n\n    // Specify the options for the classifier.\n    let classifierOptions = AudioClassifierOptions(modelPath: modelPath)\n    classifierOptions.baseOptions.computeSettings.cpuSettings.numThreads = threadCount\n    classifierOptions.classificationOptions.maxResults = maxResults\n    classifierOptions.classificationOptions.scoreThreshold = scoreThreshold\n\n    do {\n      // Create the classifier.\n      classifier = try AudioClassifier.classifier(options: classifierOptions)\n\n      // Create an `AudioRecord` instance to record input audio that satisfies\n      // the model's requirements.\n      audioRecord = try classifier.createAudioRecord()\n      inputAudioTensor = classifier.createInputAudioTensor()\n    } catch {\n      print(\"Failed to create the classifier with error: \\(error.localizedDescription)\")\n      return nil\n    }\n  }\n\n  func stopClassifier() {\n    audioRecord.stop()\n    timer?.invalidate()\n    timer = nil\n  }\n\n  /// Start the audio classification routine in the background.\n  ///\n  /// Classification results are periodically returned to the delegate.\n  /// - Parameters overlap: Overlapping factor between consecutive audio snippet to be classified.\n  ///   Value must be >= 0 and < 1.\n  func startClassifier(overlap: Double) {\n    if overlap < 0 {\n      let error = NSError(\n        domain: errorDomain,\n        code: 0,\n        userInfo: [NSLocalizedDescriptionKey: \"overlap must be equal or larger than 0.\"])\n      delegate?.audioClassificationHelper(self, didFail: error)\n    }\n\n    if overlap >= 1 {\n      let error = NSError(\n        domain: errorDomain, code: 0,\n        userInfo: [NSLocalizedDescriptionKey: \"overlap must be smaller than 1.\"])\n      delegate?.audioClassificationHelper(self, didFail: error)\n    }\n\n    do {\n      // Start recording audio.\n      try audioRecord.startRecording()\n\n      // Calculate interval between sampling based on overlap.\n      let audioFormat = inputAudioTensor.audioFormat\n      let lengthInMilliSeconds =\n        Double(inputAudioTensor.bufferSize) / Double(audioFormat.sampleRate)\n      let interval = lengthInMilliSeconds * Double(1 - overlap)\n      timer?.invalidate()\n\n      // Schedule the classification routine to run every fixed interval.\n      timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) {\n        [weak self] _ in\n        self?.processQueue.async {\n          self?.runClassification()\n        }\n      }\n    } catch {\n      delegate?.audioClassificationHelper(self, didFail: error)\n    }\n  }\n\n  /// Run the classification routine with the latest audio stored in the `AudioRecord` instance's\n  /// buffer.\n  private func runClassification() {\n    let startTime = Date().timeIntervalSince1970\n    do {\n      // Grab the latest audio chunk in the audio record and run classification.\n      try inputAudioTensor.load(audioRecord: audioRecord)\n      let results = try classifier.classify(audioTensor: inputAudioTensor)\n      let inferenceTime = Date().timeIntervalSince1970 - startTime\n\n      // Return the classification result to the delegate.\n      DispatchQueue.main.async {\n        let result = Result(\n          inferenceTime: inferenceTime,\n          categories: results.classifications[0].categories)\n        // Send classification result to the delegate.\n        self.delegate?.audioClassificationHelper(self, didSucceed: result)\n      }\n    } catch {\n      delegate?.audioClassificationHelper(self, didFail: error)\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/audio_classification/ios/AudioClassification.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 55;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t9E2DC7A5D9D560890EC07075 /* Pods_AudioClassification.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8CB90CD43F9B8F013EA69E62 /* Pods_AudioClassification.framework */; };\n\t\tBF646A7528A4DBD7006741D8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF646A7428A4DBD7006741D8 /* AppDelegate.swift */; };\n\t\tBF646A7928A4DBD7006741D8 /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF646A7828A4DBD7006741D8 /* HomeViewController.swift */; };\n\t\tBF646A7C28A4DBD7006741D8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF646A7A28A4DBD7006741D8 /* Main.storyboard */; };\n\t\tBF646A8128A4DBD9006741D8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF646A7F28A4DBD9006741D8 /* LaunchScreen.storyboard */; };\n\t\tBF646A9E28A4DDEA006741D8 /* InferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF646A9728A4DDEA006741D8 /* InferenceView.swift */; };\n\t\tBF646A9F28A4DDEA006741D8 /* AudioClassificationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF646A9928A4DDEA006741D8 /* AudioClassificationHelper.swift */; };\n\t\tBF646AA028A4DDEA006741D8 /* yamnet.tflite in Resources */ = {isa = PBXBuildFile; fileRef = BF646A9A28A4DDEA006741D8 /* yamnet.tflite */; };\n\t\tBF646AA128A4DDEA006741D8 /* speech_commands.tflite in Resources */ = {isa = PBXBuildFile; fileRef = BF646A9B28A4DDEA006741D8 /* speech_commands.tflite */; };\n\t\tBF646AA228A4DDEA006741D8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF646A9C28A4DDEA006741D8 /* Assets.xcassets */; };\n\t\tBF646AA328A4DDEA006741D8 /* ResultTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF646A9D28A4DDEA006741D8 /* ResultTableViewCell.swift */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\t816D964E1C100FB246C3D6A2 /* Pods-AudioClassification.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-AudioClassification.release.xcconfig\"; path = \"Target Support Files/Pods-AudioClassification/Pods-AudioClassification.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t8CB90CD43F9B8F013EA69E62 /* Pods_AudioClassification.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AudioClassification.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBF646A7128A4DBD7006741D8 /* AudioClassification.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AudioClassification.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBF646A7428A4DBD7006741D8 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\tBF646A7828A4DBD7006741D8 /* HomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = \"<group>\"; };\n\t\tBF646A7B28A4DBD7006741D8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\tBF646A8028A4DBD9006741D8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\tBF646A8228A4DBD9006741D8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tBF646A9728A4DDEA006741D8 /* InferenceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InferenceView.swift; sourceTree = \"<group>\"; };\n\t\tBF646A9928A4DDEA006741D8 /* AudioClassificationHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioClassificationHelper.swift; sourceTree = \"<group>\"; };\n\t\tBF646A9A28A4DDEA006741D8 /* yamnet.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = yamnet.tflite; sourceTree = \"<group>\"; };\n\t\tBF646A9B28A4DDEA006741D8 /* speech_commands.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = speech_commands.tflite; sourceTree = \"<group>\"; };\n\t\tBF646A9C28A4DDEA006741D8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tBF646A9D28A4DDEA006741D8 /* ResultTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResultTableViewCell.swift; sourceTree = \"<group>\"; };\n\t\tDA7425FEF4C4974DA750B8B2 /* Pods-AudioClassification.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-AudioClassification.debug.xcconfig\"; path = \"Target Support Files/Pods-AudioClassification/Pods-AudioClassification.debug.xcconfig\"; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tBF646A6E28A4DBD7006741D8 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t9E2DC7A5D9D560890EC07075 /* Pods_AudioClassification.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t5D022BF2F241C787DF24AA65 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t8CB90CD43F9B8F013EA69E62 /* Pods_AudioClassification.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBF646A6828A4DBD7006741D8 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBF646A7328A4DBD7006741D8 /* AudioClassification */,\n\t\t\t\tBF646A7228A4DBD7006741D8 /* Products */,\n\t\t\t\tF700903006C2F56FB9F961AD /* Pods */,\n\t\t\t\t5D022BF2F241C787DF24AA65 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBF646A7228A4DBD7006741D8 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBF646A7128A4DBD7006741D8 /* AudioClassification.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBF646A7328A4DBD7006741D8 /* AudioClassification */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBF646A9828A4DDEA006741D8 /* TFLite */,\n\t\t\t\tBF646A7828A4DBD7006741D8 /* HomeViewController.swift */,\n\t\t\t\tBF646A9728A4DDEA006741D8 /* InferenceView.swift */,\n\t\t\t\tBF646A9D28A4DDEA006741D8 /* ResultTableViewCell.swift */,\n\t\t\t\tBF646A7428A4DBD7006741D8 /* AppDelegate.swift */,\n\t\t\t\tBF646A7A28A4DBD7006741D8 /* Main.storyboard */,\n\t\t\t\tBF646A7F28A4DBD9006741D8 /* LaunchScreen.storyboard */,\n\t\t\t\tBF646A8228A4DBD9006741D8 /* Info.plist */,\n\t\t\t\tBF646A9C28A4DDEA006741D8 /* Assets.xcassets */,\n\t\t\t);\n\t\t\tpath = AudioClassification;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBF646A9828A4DDEA006741D8 /* TFLite */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBF646A9928A4DDEA006741D8 /* AudioClassificationHelper.swift */,\n\t\t\t\tBF646A9A28A4DDEA006741D8 /* yamnet.tflite */,\n\t\t\t\tBF646A9B28A4DDEA006741D8 /* speech_commands.tflite */,\n\t\t\t);\n\t\t\tpath = TFLite;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF700903006C2F56FB9F961AD /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tDA7425FEF4C4974DA750B8B2 /* Pods-AudioClassification.debug.xcconfig */,\n\t\t\t\t816D964E1C100FB246C3D6A2 /* Pods-AudioClassification.release.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\tBF646A7028A4DBD7006741D8 /* AudioClassification */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BF646A8528A4DBD9006741D8 /* Build configuration list for PBXNativeTarget \"AudioClassification\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tC98995D72BE0711017D783BE /* [CP] Check Pods Manifest.lock */,\n\t\t\t\tBF2F021F28A4E08500B948E3 /* ShellScript */,\n\t\t\t\tBF646A6D28A4DBD7006741D8 /* Sources */,\n\t\t\t\tBF646A6E28A4DBD7006741D8 /* Frameworks */,\n\t\t\t\tBF646A6F28A4DBD7006741D8 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = AudioClassification;\n\t\t\tproductName = AudioClassification;\n\t\t\tproductReference = BF646A7128A4DBD7006741D8 /* AudioClassification.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tBF646A6928A4DBD7006741D8 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tBuildIndependentTargetsInParallel = 1;\n\t\t\t\tLastSwiftUpdateCheck = 1340;\n\t\t\t\tLastUpgradeCheck = 1340;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tBF646A7028A4DBD7006741D8 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 13.4.1;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = BF646A6C28A4DBD7006741D8 /* Build configuration list for PBXProject \"AudioClassification\" */;\n\t\t\tcompatibilityVersion = \"Xcode 13.0\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = BF646A6828A4DBD7006741D8;\n\t\t\tproductRefGroup = BF646A7228A4DBD7006741D8 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tBF646A7028A4DBD7006741D8 /* AudioClassification */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\tBF646A6F28A4DBD7006741D8 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBF646A8128A4DBD9006741D8 /* LaunchScreen.storyboard in Resources */,\n\t\t\t\tBF646AA128A4DDEA006741D8 /* speech_commands.tflite in Resources */,\n\t\t\t\tBF646AA028A4DDEA006741D8 /* yamnet.tflite in Resources */,\n\t\t\t\tBF646A7C28A4DBD7006741D8 /* Main.storyboard in Resources */,\n\t\t\t\tBF646AA228A4DDEA006741D8 /* Assets.xcassets in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\tBF2F021F28A4E08500B948E3 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"# Type a script or drag a script file from your workspace to insert its path.\\n\\\"$SRCROOT/RunScripts/download_models.sh\\\"\\n\";\n\t\t};\n\t\tC98995D72BE0711017D783BE /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-AudioClassification-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tBF646A6D28A4DBD7006741D8 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBF646A9E28A4DDEA006741D8 /* InferenceView.swift in Sources */,\n\t\t\t\tBF646A7928A4DBD7006741D8 /* HomeViewController.swift in Sources */,\n\t\t\t\tBF646A7528A4DBD7006741D8 /* AppDelegate.swift in Sources */,\n\t\t\t\tBF646AA328A4DDEA006741D8 /* ResultTableViewCell.swift in Sources */,\n\t\t\t\tBF646A9F28A4DDEA006741D8 /* AudioClassificationHelper.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\tBF646A7A28A4DBD7006741D8 /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tBF646A7B28A4DBD7006741D8 /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBF646A7F28A4DBD9006741D8 /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tBF646A8028A4DBD9006741D8 /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\tBF646A8328A4DBD9006741D8 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.5;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tBF646A8428A4DBD9006741D8 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.5;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBF646A8628A4DBD9006741D8 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = DA7425FEF4C4974DA750B8B2 /* Pods-AudioClassification.debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = AudioClassification/Info.plist;\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"This app will use the microphone to get audio input in order to classify the sound made by user.\";\n\t\t\t\tINFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;\n\t\t\t\tINFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;\n\t\t\t\tINFOPLIST_KEY_UIMainStoryboardFile = Main;\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = \"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = \"UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.tensorflow.lite.examples.AudioClassification;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tBF646A8728A4DBD9006741D8 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 816D964E1C100FB246C3D6A2 /* Pods-AudioClassification.release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = AudioClassification/Info.plist;\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"This app will use the microphone to get audio input in order to classify the sound made by user.\";\n\t\t\t\tINFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;\n\t\t\t\tINFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;\n\t\t\t\tINFOPLIST_KEY_UIMainStoryboardFile = Main;\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = \"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = \"UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.tensorflow.lite.examples.AudioClassification;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tBF646A6C28A4DBD7006741D8 /* Build configuration list for PBXProject \"AudioClassification\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBF646A8328A4DBD9006741D8 /* Debug */,\n\t\t\t\tBF646A8428A4DBD9006741D8 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBF646A8528A4DBD9006741D8 /* Build configuration list for PBXNativeTarget \"AudioClassification\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBF646A8628A4DBD9006741D8 /* Debug */,\n\t\t\t\tBF646A8728A4DBD9006741D8 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = BF646A6928A4DBD7006741D8 /* Project object */;\n}\n"
  },
  {
    "path": "lite/examples/audio_classification/ios/Podfile",
    "content": "# Uncomment the next line to define a global platform for your project\n# platform :ios, '9.0'\n\ntarget 'AudioClassification' do\n  # Comment the next line if you don't want to use dynamic frameworks\n  use_frameworks!\n  pod 'TensorFlowLiteTaskAudio', '~> 0.0.1-nightly'\n\n  # Pods for AudioClassification\n\nend\n"
  },
  {
    "path": "lite/examples/audio_classification/ios/README.md",
    "content": "# TensorFlow Lite audio classification iOS example application\n\n## Overview\n\nThis is an example application for [TensorFlow Lite](https://tensorflow.org/lite)\non iOS.\n\n### Model\n\nThe model will be downloaded as part of the build process. There are two models\nincluded in this sample app: *\n[YAMNet](https://tfhub.dev/google/lite-model/yamnet/classification/tflite/1) is\na general purpose audio classification model that can detects 521 different type\nof sounds. *\n[Speech command](https://www.tensorflow.org/lite/models/modify/model_maker/speech_recognition)\nis a demonstrative model that can recognize a handful of single-word audio\ncommand.\n\nAlso, you can use your own model generated on\n[Teachable Machine](https://teachablemachine.withgoogle.com/train/audio) or\n[Model Maker](https://www.tensorflow.org/lite/models/modify/model_maker/audio_classification).\n\n### iOS app details\n\nThe app is written entirely in Swift and uses the TensorFlow Lite\n[Swift library](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/experimental/swift)\nfor performing sound classification.\n\n## Requirements\n\n*   Device with iOS 12.0 or above\n\n*   Xcode 13.0 or above\n\n*   Xcode command-line tools (run `xcode-select --install`)\n\n*   [CocoaPods](https://cocoapods.org/) (run `sudo gem install cocoapods`)\n\n*   (Optional) Valid Apple Developer ID. If you don't have one, you can run the\n    sample app on an iOS Simulator.\n\nIf this is a new install, you will need to run the Xcode application once to\nagree to the license before continuing.\n\n## Build and run\n\n1.  Clone this GitHub repository to your workstation. `git clone\n    https://github.com/tensorflow/examples.git`\n\n2.  Install the pod to generate the workspace file: `cd\n    examples/lite/examples/sound_classification/ios && pod install`\n\nNote: If you have installed this pod before and that command doesn't work, try\n`pod update`.\n\nAt the end of this step you should have a directory called\n`AudioClassification.xcworkspace`.\n\n1.  Open the project in Xcode with the following command: `open\n    AudioClassification.xcworkspace`\n\nThis launches Xcode and opens the `AudioClassification` project. You can run the\napp on an iOS Sumi\n\nFollow these steps to run the sample app on a physical device.\n\n1.  Select the `AudioClassification` project in the left hand navigation to open\n    the project configuration. In the **Signing** section of the **General**\n    tab, select your development team from the dropdown.\n\n2.  In order to build the project, you must modify the **Bundle Identifier** in\n    the **Identity** section so that it is unique across all Xcode projects. To\n    create a unique identifier, try adding your initials and a number to the end\n    of the string.\n\n3.  With an iOS device connected, build and run the app in Xcode.\n\nYou'll have to grant permissions for the app to use the device's camera. Point\nthe camera at various objects and enjoy seeing how the model classifies things!\n\n## Model references\n\n*Do not delete the empty references* to the .tflite files after you clone the\nrepo and open the project. These references will be fulfilled once the model and\nlabel files are downloaded when the application is built and run for the first\ntime. If you delete the references to them, you can still find that the .tflite\nand .txt files are downloaded to the Model folder, the next time you build the\napplication. You will have to add the references to these files in the bundle\nseparately in that case.\n"
  },
  {
    "path": "lite/examples/audio_classification/ios/RunScripts/download_models.sh",
    "content": "#!/bin/bash\n# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\nset -ex\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nYAMNET_URL=\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/audio_classification/ios/lite-model_yamnet_classification_tflite_1.tflite\"\nSPEECH_COMMANDS_URL=\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/audio_classification/ios/speech_commands.tflite\"\n\nYAMNET_NAME=\"yamnet.tflite\"\nSPEECH_COMMANDS_NAME=\"speech_commands.tflite\"\n\nDOWNLOADS_DIR=$(mktemp -d)\n\ncd \"$SCRIPT_DIR\"\n\ndownload() {\n  local usage=\"Usage: download_and_extract URL DIR\"\n  local url=\"${1:?${usage}}\"\n  local dir=\"${2:?${usage}}\"\n  local name=\"${3:?${usage}}\"\n  echo \"downloading ${url}\" >&2\n  mkdir -p \"${dir}\"\n  tempdir=$(mktemp -d)\n\n  curl -L ${url} > ${tempdir}/${name}\n  cp -R ${tempdir}/* ${dir}/\n  rm -rf ${tempdir}\n}\n\nhas_download=false\n\nif [ -f ../AudioClassification/TFLite/${YAMNET_NAME} ]\nthen\necho \"File ${YAMNET_NAME} exists.\"\nelse\nhas_download=true\ndownload \"${YAMNET_URL}\" \"${DOWNLOADS_DIR}/models\" \"${YAMNET_NAME}\"\nfile ${DOWNLOADS_DIR}/models\nfi\n\nif [ -f ../AudioClassification/TFLite/${SPEECH_COMMANDS_NAME} ]\nthen\necho \"File ${SPEECH_COMMANDS_NAME} exists.\"\nelse\nhas_download=true\ndownload \"${SPEECH_COMMANDS_URL}\" \"${DOWNLOADS_DIR}/models\" \"${SPEECH_COMMANDS_NAME}\"\nfile ${DOWNLOADS_DIR}/models\nfi\n\nif ${has_download}\nthen\ncp ${DOWNLOADS_DIR}/models/* ../AudioClassification/TFLite\nrm -rf ${DOWNLOADS_DIR}\nfi\n\n"
  },
  {
    "path": "lite/examples/audio_classification/raspberry_pi/README.md",
    "content": "# TensorFlow Lite Python audio classification example with Raspberry Pi.\n\nThis example uses [TensorFlow Lite](https://tensorflow.org/lite) with Python on\na Raspberry Pi to perform real-time audio classification using audio streamed\nfrom the microphone.\n\nAt the end of this page, there are extra steps to accelerate the example using\nthe Coral USB Accelerator to increase inference speed.\n\n## Set up your hardware\n\nBefore you begin, you need to\n[set up your Raspberry Pi](https://projects.raspberrypi.org/en/projects/raspberry-pi-setting-up)\nwith Raspberry Pi OS (preferably updated to Buster).\n\nRaspberry Pi doesn't have a microphone integrated on its board, so you need to\nplug in a USB microphone to record audio.\n\n## Install the TensorFlow Lite runtime\n\nIn this project, all you need from the TensorFlow Lite API is the `Interpreter`\nclass. So instead of installing the large `tensorflow` package, we're using the\nmuch smaller `tflite_runtime` package.\n\nTo install this on your Raspberry Pi, follow the instructions in the\n[Python quickstart](https://www.tensorflow.org/lite/guide/python#install_tensorflow_lite_for_python).\n\nYou can install the TFLite runtime using this script.\n\n```\nsh setup.sh\n```\n\n## Download the example files\n\nFirst, clone this Git repo onto your Raspberry Pi like this:\n\n```\ngit clone https://github.com/tensorflow/examples.git --depth 1\n```\n\nThen use our script to install a couple Python packages, and download the TFLite\nmodel:\n\n```\ncd lite/examples/audio_classification/raspberry_pi\n\n# Run this script to install the required dependencies and download the TFLite models.\nsh setup.sh\n```\n\n## Run the example\n\n```\npython3 classify.py\n```\n\n*   You can optionally specify the `model` parameter to set the TensorFlow Lite\n    model to be used:\n    *   The default value is `yamnet.tflite`\n*   You can optionally specify the `maxResults` parameter to limit the list of\n    classification results:\n    *   Supported value: A positive integer.\n    *   Default value: `5`.\n*   Example usage:\n\n```\npython3 classify.py \\\n  --model yamnet.tflite \\\n  --maxResults 5\n```\n\n## Speed up the inference time (optional)\n\nIf you want to speed up the inference time, you can attach an ML accelerator\nsuch as the\n[Coral USB Accelerator](https://coral.withgoogle.com/products/accelerator)—a USB\naccessory that adds the\n[Edge TPU ML accelerator](https://coral.withgoogle.com/docs/edgetpu/faq/) to any\nLinux-based system.\n\nIf you have a Coral USB Accelerator, you can run the sample with it enabled:\n\n1.  First, be sure you have completed the\n    [USB Accelerator setup instructions](https://coral.withgoogle.com/docs/accelerator/get-started/).\n\n2.  Run the audio classification script using the Edge TPU TFLite model and\n    enable the Edge TPU option.\n\n```\npython3 classify.py \\\n  --model yamnet_edgetpu.tflite \\\n  --enableEdgeTPU\n```\n\nFor more information about creating and running TensorFlow Lite models with\nCoral devices, read\n[TensorFlow models on the Edge TPU](https://coral.withgoogle.com/docs/edgetpu/models-intro/).\n\nFor more information about executing inferences with TensorFlow Lite, read\n[TensorFlow Lite inference](https://www.tensorflow.org/lite/guide/inference).\n"
  },
  {
    "path": "lite/examples/audio_classification/raspberry_pi/classify.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Main scripts to run audio classification.\"\"\"\n\nimport argparse\nimport time\n\nfrom tflite_support.task import audio\nfrom tflite_support.task import core\nfrom tflite_support.task import processor\nfrom utils import Plotter\n\n\ndef run(model: str, max_results: int, score_threshold: float,\n        overlapping_factor: float, num_threads: int,\n        enable_edgetpu: bool) -> None:\n  \"\"\"Continuously run inference on audio data acquired from the device.\n\n  Args:\n    model: Name of the TFLite audio classification model.\n    max_results: Maximum number of classification results to display.\n    score_threshold: The score threshold of classification results.\n    overlapping_factor: Target overlapping between adjacent inferences.\n    num_threads: Number of CPU threads to run the model.\n    enable_edgetpu: Whether to run the model on EdgeTPU.\n  \"\"\"\n\n  if (overlapping_factor <= 0) or (overlapping_factor >= 1.0):\n    raise ValueError('Overlapping factor must be between 0 and 1.')\n\n  if (score_threshold < 0) or (score_threshold > 1.0):\n    raise ValueError('Score threshold must be between (inclusive) 0 and 1.')\n\n  # Initialize the audio classification model.\n  base_options = core.BaseOptions(\n      file_name=model, use_coral=enable_edgetpu, num_threads=num_threads)\n  classification_options = processor.ClassificationOptions(\n      max_results=max_results, score_threshold=score_threshold)\n  options = audio.AudioClassifierOptions(\n      base_options=base_options, classification_options=classification_options)\n  classifier = audio.AudioClassifier.create_from_options(options)\n\n  # Initialize the audio recorder and a tensor to store the audio input.\n  audio_record = classifier.create_audio_record()\n  tensor_audio = classifier.create_input_tensor_audio()\n\n  # We'll try to run inference every interval_between_inference seconds.\n  # This is usually half of the model's input length to create an overlapping\n  # between incoming audio segments to improve classification accuracy.\n  input_length_in_second = float(len(\n      tensor_audio.buffer)) / tensor_audio.format.sample_rate\n  interval_between_inference = input_length_in_second * (1 - overlapping_factor)\n  pause_time = interval_between_inference * 0.1\n  last_inference_time = time.time()\n\n  # Initialize a plotter instance to display the classification results.\n  plotter = Plotter()\n\n  # Start audio recording in the background.\n  audio_record.start_recording()\n\n  # Loop until the user close the classification results plot.\n  while True:\n    # Wait until at least interval_between_inference seconds has passed since\n    # the last inference.\n    now = time.time()\n    diff = now - last_inference_time\n    if diff < interval_between_inference:\n      time.sleep(pause_time)\n      continue\n    last_inference_time = now\n\n    # Load the input audio and run classify.\n    tensor_audio.load_from_audio_record(audio_record)\n    result = classifier.classify(tensor_audio)\n\n    # Plot the classification results.\n    plotter.plot(result)\n\n\ndef main():\n  parser = argparse.ArgumentParser(\n      formatter_class=argparse.ArgumentDefaultsHelpFormatter)\n  parser.add_argument(\n      '--model',\n      help='Name of the audio classification model.',\n      required=False,\n      default='yamnet.tflite')\n  parser.add_argument(\n      '--maxResults',\n      help='Maximum number of results to show.',\n      required=False,\n      default=5)\n  parser.add_argument(\n      '--overlappingFactor',\n      help='Target overlapping between adjacent inferences. Value must be in (0, 1)',\n      required=False,\n      default=0.5)\n  parser.add_argument(\n      '--scoreThreshold',\n      help='The score threshold of classification results.',\n      required=False,\n      default=0.0)\n  parser.add_argument(\n      '--numThreads',\n      help='Number of CPU threads to run the model.',\n      required=False,\n      default=4)\n  parser.add_argument(\n      '--enableEdgeTPU',\n      help='Whether to run the model on EdgeTPU.',\n      action='store_true',\n      required=False,\n      default=False)\n  args = parser.parse_args()\n\n  run(args.model, int(args.maxResults), float(args.scoreThreshold),\n      float(args.overlappingFactor), int(args.numThreads),\n      bool(args.enableEdgeTPU))\n\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "lite/examples/audio_classification/raspberry_pi/requirements.txt",
    "content": "argparse\nmatplotlib>=3.2.1\nsounddevice>=0.4.1\nscipy>=1.7.3 # Only required for test code.\ntflite-support>=0.4.2\n"
  },
  {
    "path": "lite/examples/audio_classification/raspberry_pi/setup.sh",
    "content": "#!/bin/bash\n\nif [ $# -eq 0 ]; then\n  DATA_DIR=\"./\"\nelse\n  DATA_DIR=\"$1\"\nfi\n\n# Install Python dependencies\npython3 -m pip install -r requirements.txt\n\n# Download TF Lite models with metadata.\nFILE=${DATA_DIR}/yamnet.tflite\nif [ ! -f \"$FILE\" ]; then\n  curl \\\n    -L 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/audio_classification/rpi/lite-model_yamnet_classification_tflite_1.tflite' \\\n    -o ${FILE}\nfi\n\nFILE=${DATA_DIR}/yamnet_edgetpu.tflite\nif [ ! -f \"$FILE\" ]; then\n  curl \\\n    -L 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/audio_classification/rpi/coral-model_yamnet_classification_coral_1.tflite' \\\n    -o ${FILE}\nfi\necho -e \"Downloaded files are in ${DATA_DIR}\"\n"
  },
  {
    "path": "lite/examples/audio_classification/raspberry_pi/test_data/ground_truth.csv",
    "content": "label,score\nCat,0.73828125\nAnimal,0.66796875\n\"Domestic animals, pets\",0.66796875\nMeow,0.4140625\nCaterwaul,0.33203125\n"
  },
  {
    "path": "lite/examples/audio_classification/raspberry_pi/utils.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"A module with util functions.\"\"\"\nimport sys\n\nfrom matplotlib import rcParams\nimport matplotlib.pyplot as plt\n\nrcParams.update({\n    # Set the plot left margin so that the labels are visible.\n    'figure.subplot.left': 0.3,\n\n    # Hide the bottom toolbar.\n    'toolbar': 'None'\n})\n\n\nclass Plotter(object):\n  \"\"\"An util class to display the classification results.\"\"\"\n\n  _PAUSE_TIME = 0.05\n  \"\"\"Time for matplotlib to wait for UI event.\"\"\"\n\n  def __init__(self) -> None:\n    fig, self._axes = plt.subplots()\n    fig.canvas.manager.set_window_title('Audio classification')\n\n    # Stop the program when the ESC key is pressed.\n    def event_callback(event):\n      if event.key == 'escape':\n        sys.exit(0)\n\n    fig.canvas.mpl_connect('key_press_event', event_callback)\n\n    plt.show(block=False)\n\n  # TODO(khanhlvg): Add type hint for result once ClassificationResult added\n  # to tflite_support.task.processor module\n  def plot(self, result) -> None:\n    \"\"\"Plot the audio classification result.\n\n    Args:\n      result: Classification results returned by an audio classification\n        model.\n    \"\"\"\n    # Clear the axes\n    self._axes.cla()\n    self._axes.set_title('Press ESC to exit.')\n    self._axes.set_xlim((0, 1))\n\n    # Plot the results so that the most probable category comes at the top.\n    classification = result.classifications[0]\n    label_list = [\n        category.category_name for category in classification.categories\n    ]\n    score_list = [category.score for category in classification.categories]\n    self._axes.barh(label_list[::-1], score_list[::-1])\n\n    # Wait for the UI event.\n    plt.pause(self._PAUSE_TIME)\n"
  },
  {
    "path": "lite/examples/bert_qa/android/README.md",
    "content": "# TensorFlow Lite BERT Question & Answer Demo Application\n\n### Overview\n\nThis is an end-to-end example of BERT Question & Answer application built with\nTensorFlow 2.0, and tested on SQuAD dataset. The demo app provides 48 passages\nfrom the dataset for users to choose from, and gives 5 most possible answers\ncorresponding to the input passage and query. These instructions walk you\nthrough building and running the demo on an Android device.\n\nThe model files are downloaded via Gradle scripts when you build and run\nthe app. You don't need to do any steps to download TFLite models into\nthe project explicitly.\n\nThis application should be run on a physical Android device.\n\n![App example UI.](screenshot.png?raw=true \"Screenshot QA screen\")\n\n## Build the demo using Android Studio\n\n### Prerequisites\n\n* The **[Android Studio](https://developer.android.com/studio/index.html)**\n  IDE (Android Studio 2021.2.1 or newer). This sample has been tested on\n  Android Studio Chipmunk.\n\n* A physical Android device with a minimum OS version of SDK 23 (Android 6.0 -\n  Marshmallow) with developer mode enabled. The process of enabling developer\n  mode may vary by device.\n\n### Building\n\n* Open Android Studio. From the Welcome screen, select Open an existing\n  Android Studio project.\n\n* From the Open File or Project window that appears, navigate to and select the\n  tensorflow-lite/examples/bert_qa/android directory. Click OK.\n\n* If it asks you to do a Gradle Sync, click OK.\n\n* With your Android device connected to your computer and developer mode\nenabled, click on the green Run arrow in Android Studio.\n\n### Models used\n\nDownloading, extraction, and placing the models into the assets folder is\nmanaged automatically by the download.gradle file.\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/build.gradle",
    "content": "plugins {\n    id 'com.android.application'\n    id 'org.jetbrains.kotlin.android'\n    id 'kotlin-kapt'\n    id 'androidx.navigation.safeargs'\n    id 'de.undercouch.download'\n}\n\nandroid {\n    compileSdk 32\n\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.bertqa\"\n        minSdk 23\n        targetSdk 32\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n    kotlinOptions {\n        jvmTarget = '1.8'\n    }\n    buildFeatures {\n        viewBinding true\n    }\n    androidResources {\n        noCompress 'tflite'\n    }\n}\n\n// import DownloadModels task\nproject.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'\nproject.ext.TEST_ASSETS_DIR = projectDir.toString() + '/src/androidTest/assets'\n\n// Download default models; if you wish to use your own models then\n// place them in the \"assets\" directory and comment out this line.\napply from: 'download_models.gradle'\n\ndependencies {\n    // Kotlin lang\n    implementation 'androidx.core:core-ktx:1.15.0'\n\n    // App compat and UI things\n    implementation 'androidx.appcompat:appcompat:1.4.2'\n    implementation 'com.google.android.material:material:1.6.1'\n    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'\n\n    // Navigation library\n    def nav_version = \"2.5.0\"\n    implementation \"androidx.navigation:navigation-fragment-ktx:$nav_version\"\n    implementation \"androidx.navigation:navigation-ui-ktx:$nav_version\"\n\n    // CameraX core library\n    def camerax_version = '1.2.0-alpha03'\n    implementation \"androidx.camera:camera-core:$camerax_version\"\n\n    // CameraX Camera2 extensions\n    implementation \"androidx.camera:camera-camera2:$camerax_version\"\n\n    // CameraX Lifecycle library\n    implementation \"androidx.camera:camera-lifecycle:$camerax_version\"\n\n    // CameraX View class\n    implementation \"androidx.camera:camera-view:$camerax_version\"\n\n    //WindowManager\n    implementation 'androidx.window:window:1.1.0-alpha02'\n\n    // Unit testing\n    testImplementation 'junit:junit:4.13.2'\n\n    // Instrumented testing\n    androidTestImplementation 'androidx.test.ext:junit:1.1.3'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n\n    // Gson library\n    implementation 'com.google.code.gson:gson:2.9.0'\n    implementation 'com.google.guava:guava:28.1-android'\n\n    // Import tensorflow library\n    implementation 'org.tensorflow:tensorflow-lite-task-text:0.3.0'\n    // Import the GPU delegate plugin Library for GPU inference\n    implementation 'org.tensorflow:tensorflow-lite-gpu-delegate-plugin:0.4.0'\n    implementation 'org.tensorflow:tensorflow-lite-gpu:2.9.0'\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/download_models.gradle",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ntask downloadModelFile(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/bert_qa/android/models_tflite_task_library_bert_qa_lite-model_mobilebert_1_metadata_1.tflite'\n    dest project.ext.ASSET_DIR + '/mobilebert.tflite'\n    overwrite false\n}\n\ntask downloadQAJson(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/bert_qa/contents_from_squad.json'\n    dest project.ext.ASSET_DIR + '/qa.json'\n    overwrite false\n}\n\ntask copyTestModel(type: Copy, dependsOn: downloadModelFile) {\n    from project.ext.ASSET_DIR + '/mobilebert.tflite'\n    into project.ext.TEST_ASSETS_DIR\n}\n\npreBuild.dependsOn downloadModelFile, downloadQAJson, copyTestModel\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/androidTest/java/org/tensorflow/lite/examples/bertqa/BertQaHelperTest.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.bertqa\n\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.ext.junit.runners.AndroidJUnit4\n\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\nimport org.junit.Assert.*\nimport org.junit.Before\nimport org.tensorflow.lite.task.text.qa.QaAnswer\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass BertQaHelperTest {\n    private val content =\n        \"Doraemon is a blue cat automaton corresponding (tints of pink-orange in earlier comic chapters and media) from the 22nd century, who weighs 129.3kg (285.05lbs) and measures at 129.3cm (4'3\\\") tall.\"\n    private val question = \"What color is Doraemon?\"\n    private val answer = \"blue\"\n\n    private lateinit var helper: BertQaHelper\n\n    @Test\n    fun bertAnswersShouldNotChange() {\n        helper = BertQaHelper(context = InstrumentationRegistry.getInstrumentation().context,\n            answererListener = object : BertQaHelper.AnswererListener {\n                override fun onError(error: String) {\n                    // no op\n                }\n\n                override fun onResults(results: List<QaAnswer>?, inferenceTime: Long) {\n                    assert(!results.isNullOrEmpty())\n\n                    // verify bert qa answer and expected answer.\n                    assertEquals(answer, results!![0].text)\n                }\n            })\n        helper.answer(content, question)\n    }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"org.tensorflow.lite.examples.bertqa\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:taskAffinity=\"\"\n        tools:ignore=\"AllowBackup\">\n        <activity\n            android:name=\".MainActivity\"\n            android:clearTaskOnLaunch=\"true\"\n            android:configChanges=\"orientation|screenLayout|screenSize|smallestScreenSize\"\n            android:exported=\"true\"\n            android:resizeableActivity=\"true\"\n            android:rotationAnimation=\"seamless\"\n            android:theme=\"@style/AppTheme\"\n            tools:targetApi=\"O\">\n\n            <!-- Main app intent filter -->\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n\n            <!-- Declare notch support -->\n            <meta-data\n                android:name=\"android.notch_support\"\n                android:value=\"true\" />\n        </activity>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/java/org/tensorflow/lite/examples/bertqa/BertQaHelper.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.bertqa\n\nimport android.content.Context\nimport android.os.SystemClock\nimport android.util.Log\nimport org.tensorflow.lite.gpu.CompatibilityList\nimport org.tensorflow.lite.task.core.BaseOptions\nimport org.tensorflow.lite.task.text.qa.BertQuestionAnswerer\nimport org.tensorflow.lite.task.text.qa.BertQuestionAnswerer.BertQuestionAnswererOptions\nimport org.tensorflow.lite.task.text.qa.QaAnswer\nimport java.lang.IllegalStateException\n\nclass BertQaHelper(\n    val context: Context,\n    var numThreads: Int = 2,\n    var currentDelegate: Int = 0,\n    val answererListener: AnswererListener?\n) {\n\n    private var bertQuestionAnswerer: BertQuestionAnswerer? = null\n\n    init {\n        setupBertQuestionAnswerer()\n    }\n\n    fun clearBertQuestionAnswerer() {\n        bertQuestionAnswerer = null\n    }\n\n    private fun setupBertQuestionAnswerer() {\n        val baseOptionsBuilder = BaseOptions.builder().setNumThreads(numThreads)\n\n        when (currentDelegate) {\n            DELEGATE_CPU -> {\n                // Default\n            }\n            DELEGATE_GPU -> {\n                if (CompatibilityList().isDelegateSupportedOnThisDevice) {\n                    baseOptionsBuilder.useGpu()\n                } else {\n                    answererListener?.onError(\"GPU is not supported on this device\")\n                }\n            }\n            DELEGATE_NNAPI -> {\n                baseOptionsBuilder.useNnapi()\n            }\n        }\n\n        val options = BertQuestionAnswererOptions.builder()\n            .setBaseOptions(baseOptionsBuilder.build())\n            .build()\n\n        try {\n            bertQuestionAnswerer =\n                BertQuestionAnswerer.createFromFileAndOptions(context, BERT_QA_MODEL, options)\n        } catch (e: IllegalStateException) {\n            answererListener\n                ?.onError(\"Bert Question Answerer failed to initialize. See error logs for details\")\n            Log.e(TAG, \"TFLite failed to load model with error: \" + e.message)\n        }\n    }\n\n    fun answer(contextOfQuestion: String, question: String) {\n        if (bertQuestionAnswerer == null) {\n            setupBertQuestionAnswerer()\n        }\n\n        // Inference time is the difference between the system time at the start and finish of the\n        // process\n        var inferenceTime = SystemClock.uptimeMillis()\n\n        val answers = bertQuestionAnswerer?.answer(contextOfQuestion, question)\n        inferenceTime = SystemClock.uptimeMillis() - inferenceTime\n        answererListener?.onResults(answers, inferenceTime)\n    }\n\n    interface AnswererListener {\n        fun onError(error: String)\n        fun onResults(\n            results: List<QaAnswer>?,\n            inferenceTime: Long\n        )\n    }\n\n    companion object {\n        private const val BERT_QA_MODEL = \"mobilebert.tflite\"\n        private const val TAG = \"BertQaHelper\"\n        const val DELEGATE_CPU = 0\n        const val DELEGATE_GPU = 1\n        const val DELEGATE_NNAPI = 2\n    }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/java/org/tensorflow/lite/examples/bertqa/MainActivity.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.bertqa\n\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.navigation.findNavController\nimport androidx.navigation.fragment.NavHostFragment\nimport androidx.navigation.ui.AppBarConfiguration\nimport androidx.navigation.ui.navigateUp\nimport androidx.navigation.ui.setupActionBarWithNavController\nimport org.tensorflow.lite.examples.bertqa.databinding.ActivityMainBinding\n\nclass MainActivity : AppCompatActivity() {\n    private lateinit var binding: ActivityMainBinding\n    private lateinit var appBarConfiguration: AppBarConfiguration\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        binding = ActivityMainBinding.inflate(layoutInflater)\n        setContentView(binding.root)\n\n        // setup toolbar\n        setSupportActionBar(binding.toolbar)\n        val navHostFragment =\n            (supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment)\n        val navController = navHostFragment.navController\n        appBarConfiguration = AppBarConfiguration(navController.graph)\n        setupActionBarWithNavController(navController, appBarConfiguration)\n        supportActionBar?.setDisplayShowTitleEnabled(false)\n    }\n\n    override fun onSupportNavigateUp(): Boolean {\n        val navController = findNavController(R.id.fragment_container)\n        return navController.navigateUp(appBarConfiguration)\n                || super.onSupportNavigateUp()\n    }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/java/org/tensorflow/lite/examples/bertqa/dataset/DataSet.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.bertqa.dataset\n\nimport com.google.gson.annotations.SerializedName\n\ndata class DataSet(\n    @SerializedName(\"titles\")\n    private val titles: List<List<String>>,\n    @SerializedName(\"contents\")\n    private val contents: List<List<String>>,\n    @SerializedName(\"questions\")\n    val questions: List<List<String>>\n) {\n    fun getTitles(): List<String> {\n        return titles.map { it[0] }\n    }\n\n    fun getContents(): List<String> {\n        return contents.map { it[0] }\n    }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/java/org/tensorflow/lite/examples/bertqa/dataset/LoadDataSetClient.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.bertqa.dataset\n\nimport android.content.Context\nimport android.util.Log\nimport com.google.gson.Gson\nimport com.google.gson.reflect.TypeToken\nimport java.io.IOException\nimport java.io.InputStream\n\nclass LoadDataSetClient(private val context: Context) {\n\n    private companion object {\n        private const val TAG = \"BertAppDemo\"\n        private const val JSON_DIR = \"qa.json\"\n    }\n\n    // Load json file into a data object.\n    fun loadJson(): DataSet? {\n        var dataSet: DataSet? = null\n        try {\n            val inputStream: InputStream = context.assets.open(JSON_DIR)\n            val bufferReader = inputStream.bufferedReader()\n            val stringJson: String = bufferReader.use { it.readText() }\n            val datasetType = object : TypeToken<DataSet>() {}.type\n            dataSet = Gson().fromJson(stringJson, datasetType)\n        } catch (e: IOException) {\n            Log.e(TAG, e.message.toString())\n        }\n        return dataSet\n    }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/java/org/tensorflow/lite/examples/bertqa/fragments/DatasetAdapter.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.bertqa.fragments\n\nimport android.view.LayoutInflater\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport org.tensorflow.lite.examples.bertqa.databinding.ItemDatasetBinding\n\nclass DatasetAdapter(\n    private val dataSetTitle: List<String>,\n    private val onSelected: (Int) -> Unit\n) :\n    RecyclerView.Adapter<DatasetAdapter.ViewHolder>() {\n\n    inner class ViewHolder(private val binding: ItemDatasetBinding) :\n        RecyclerView.ViewHolder(binding.root) {\n        init {\n            binding.tvDataTitle.setOnClickListener {\n                onSelected.invoke(adapterPosition)\n            }\n        }\n\n        fun bind(title: String) {\n            with(binding) {\n                tvDataTitle.text = title\n            }\n        }\n    }\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {\n        val binding = ItemDatasetBinding.inflate(\n            LayoutInflater.from(parent.context),\n            parent,\n            false\n        )\n        return ViewHolder(binding)\n    }\n\n    override fun onBindViewHolder(holder: ViewHolder, position: Int) {\n        holder.bind(dataSetTitle[position])\n    }\n\n    override fun getItemCount() = dataSetTitle.size\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/java/org/tensorflow/lite/examples/bertqa/fragments/DatasetFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.bertqa.fragments\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.fragment.app.Fragment\nimport androidx.navigation.Navigation\nimport androidx.recyclerview.widget.DividerItemDecoration\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport org.tensorflow.lite.examples.bertqa.R\nimport org.tensorflow.lite.examples.bertqa.databinding.FragmentDatasetBinding\nimport org.tensorflow.lite.examples.bertqa.dataset.LoadDataSetClient\n\nclass DatasetFragment : Fragment() {\n\n    private var _fragmentDatasetList: FragmentDatasetBinding? = null\n    private val fragmentDatasetList get() = _fragmentDatasetList!!\n    private var titles : List<String> = emptyList()\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View {\n        _fragmentDatasetList = FragmentDatasetBinding.inflate(inflater, container, false)\n\n        return fragmentDatasetList.root\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n        val client = LoadDataSetClient(requireActivity())\n        client.loadJson()?.let {\n            titles = it.getTitles()\n        }\n        initRecyclerView()\n    }\n\n    private fun initRecyclerView() {\n            val dataSetAdapter = DatasetAdapter(titles) {\n                startQaScreen(it)\n            }\n            val linearLayoutManager = LinearLayoutManager(requireContext())\n            val decoration = DividerItemDecoration(\n                fragmentDatasetList.recyclerView.context,\n                linearLayoutManager.orientation\n            )\n            with(fragmentDatasetList.recyclerView) {\n                layoutManager = linearLayoutManager\n                adapter = dataSetAdapter\n                addItemDecoration(decoration)\n            }\n    }\n\n    private fun startQaScreen(position: Int) {\n        val action = DatasetFragmentDirections.actionDatasetFragmentToQaFragment(position)\n        Navigation.findNavController(requireActivity(), R.id.fragment_container)\n            .navigate(action)\n    }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/java/org/tensorflow/lite/examples/bertqa/fragments/QaAdapter.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.bertqa.fragments\n\nimport android.view.LayoutInflater\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport org.tensorflow.lite.examples.bertqa.databinding.ItemQuestionBinding\n\nclass QaAdapter(private val question: List<String>, private val select: (Int) -> Unit) :\n    RecyclerView.Adapter<QaAdapter.ViewHolder>() {\n\n    inner class ViewHolder(private val binding: ItemQuestionBinding) :\n        RecyclerView.ViewHolder(binding.root) {\n        init {\n            binding.tvQuestionSuggestion.setOnClickListener {\n                select.invoke(adapterPosition)\n            }\n        }\n\n        fun bind(question: String) {\n            binding.tvQuestionSuggestion.text = question\n        }\n    }\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {\n        val binding = ItemQuestionBinding.inflate(\n            LayoutInflater.from(parent.context),\n            parent,\n            false\n        )\n        return ViewHolder(binding)\n    }\n\n    override fun onBindViewHolder(holder: ViewHolder, position: Int) {\n        holder.bind(question[position])\n    }\n\n    override fun getItemCount() = question.size\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/java/org/tensorflow/lite/examples/bertqa/fragments/QaFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.bertqa.fragments\n\nimport android.os.Bundle\nimport android.text.Editable\nimport android.text.Spannable\nimport android.text.SpannableString\nimport android.text.TextWatcher\nimport android.text.style.BackgroundColorSpan\nimport android.text.style.ForegroundColorSpan\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.AdapterView\nimport android.widget.Toast\nimport androidx.fragment.app.Fragment\nimport androidx.navigation.fragment.navArgs\nimport androidx.recyclerview.widget.DividerItemDecoration\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport org.tensorflow.lite.examples.bertqa.BertQaHelper\nimport org.tensorflow.lite.examples.bertqa.R\nimport org.tensorflow.lite.examples.bertqa.databinding.FragmentQaBinding\nimport org.tensorflow.lite.examples.bertqa.dataset.LoadDataSetClient\nimport org.tensorflow.lite.task.text.qa.QaAnswer\n\nclass QaFragment : Fragment(), BertQaHelper.AnswererListener {\n    private var _fragmentQaBinding: FragmentQaBinding? = null\n    private val fragmentQaBinding get() = _fragmentQaBinding!!\n\n    private lateinit var bertQaHelper: BertQaHelper\n    private val args: QaFragmentArgs by navArgs()\n    private var content: String = \"\"\n    private var questions: List<String> = emptyList()\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View {\n        _fragmentQaBinding = FragmentQaBinding.inflate(inflater, container, false)\n\n        return fragmentQaBinding.root\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n        bertQaHelper = BertQaHelper(context = requireContext(), answererListener = this)\n        val client = LoadDataSetClient(requireActivity())\n        client.loadJson()?.let {\n            content = it.getContents()[args.datasetPosition]\n            questions = it.questions[args.datasetPosition]\n        }\n\n        fragmentQaBinding.tvDatasetContent.text = content\n        initRecyclerView()\n        handleListener()\n    }\n\n    private fun initRecyclerView() {\n        val decoration = DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL)\n        with(fragmentQaBinding.recyclerView) {\n            adapter = QaAdapter(questions) {\n                setQuestion(it)\n            }\n            layoutManager =\n                LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)\n            addItemDecoration(decoration)\n        }\n    }\n\n    private fun handleListener() {\n        fragmentQaBinding.edtQuestion.addTextChangedListener(object : TextWatcher {\n            override fun beforeTextChanged(\n                charSequence: CharSequence?,\n                start: Int,\n                count: Int,\n                after: Int\n            ) {\n                // no op\n            }\n\n            override fun onTextChanged(\n                charSequence: CharSequence?,\n                start: Int,\n                before: Int,\n                count: Int\n            ) {\n                // Only allow clicking Ask button if there is a question.\n                val shouldAskButtonActive: Boolean = charSequence.toString().isNotEmpty()\n                fragmentQaBinding.imgBtnAsk.isClickable = shouldAskButtonActive\n                fragmentQaBinding.imgBtnAsk.setImageResource(\n                    if (shouldAskButtonActive) R.drawable.ic_ask_active else R.drawable.ic_ask_inactive\n                )\n            }\n\n            override fun afterTextChanged(editable: Editable?) {\n                // no op\n            }\n\n        })\n\n        // When clicked, add the question in suggestion question list to query edittext.\n        fragmentQaBinding.imgBtnAsk.setOnClickListener {\n            answerQuestion(fragmentQaBinding.edtQuestion.text.toString())\n        }\n\n        // When clicked, decrease the number of threads used for answer\n        fragmentQaBinding.bottomSheetLayout.threadsMinus.setOnClickListener {\n            if (bertQaHelper.numThreads > 1) {\n                bertQaHelper.numThreads--\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, increase the number of threads used for answer\n        fragmentQaBinding.bottomSheetLayout.threadsPlus.setOnClickListener {\n            if (bertQaHelper.numThreads < 4) {\n                bertQaHelper.numThreads++\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, change the underlying hardware used for inference. Current options are CPU\n        // GPU, and NNAPI\n        fragmentQaBinding.bottomSheetLayout.spinnerDelegate.setSelection(0, false)\n        fragmentQaBinding.bottomSheetLayout.spinnerDelegate.onItemSelectedListener =\n            object : AdapterView.OnItemSelectedListener {\n                override fun onItemSelected(\n                    parent: AdapterView<*>?,\n                    view: View?,\n                    position: Int,\n                    id: Long\n                ) {\n                    bertQaHelper.currentDelegate = position\n                    updateControlsUi()\n                }\n\n                override fun onNothingSelected(parent: AdapterView<*>?) {\n                    /* no op */\n                }\n            }\n    }\n\n    // Update the values displayed in the bottom sheet. Reset answerer.\n    private fun updateControlsUi() {\n        fragmentQaBinding.bottomSheetLayout.threadsValue.text = bertQaHelper.numThreads.toString()\n        // Needs to be cleared instead of reinitialized because the GPU\n        // delegate needs to be initialized on the thread using it when applicable\n        bertQaHelper.clearBertQuestionAnswerer()\n    }\n\n    private fun setQuestion(position: Int) {\n        fragmentQaBinding.edtQuestion.setText(questions[position])\n    }\n\n    private fun answerQuestion(question: String) {\n        bertQaHelper.answer(content, question)\n    }\n\n    // Highlight the answer\n    private fun highlightAnswer(answer: String) {\n        val start = content.indexOf(answer)\n        val end = start + answer.length\n\n        val str = SpannableString(content)\n        str.setSpan(\n            BackgroundColorSpan(requireActivity().getColor(R.color.highlight_background_color)),\n            start,\n            end,\n            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE\n        )\n        str.setSpan(\n            ForegroundColorSpan(requireActivity().getColor(R.color.highlight_text_color)),\n            start,\n            end,\n            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE\n        )\n        fragmentQaBinding.tvDatasetContent.text = str\n    }\n\n    override fun onError(error: String) {\n        Toast.makeText(requireContext(), error, Toast.LENGTH_SHORT).show()\n    }\n\n    override fun onResults(results: List<QaAnswer>?, inferenceTime: Long) {\n        results?.first()?.let {\n            highlightAnswer(it.text)\n        }\n\n        fragmentQaBinding.tvInferenceTime.text = String.format(\n            requireActivity().getString(R.string.bottom_view_inference_time),\n            inferenceTime\n        )\n    }\n\n    override fun onDestroyView() {\n        fragmentQaBinding.edtQuestion.addTextChangedListener(null)\n        super.onDestroyView()\n    }\n\n    override fun onDestroy() {\n        bertQaHelper.clearBertQuestionAnswerer()\n        super.onDestroy()\n    }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/res/drawable/bg_question_items.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <solid android:color=\"@android:color/darker_gray\" />\n    <corners android:radius=\"10dp\" />\n</shape>\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/res/drawable/ic_ask_active.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"48dp\"\n    android:height=\"36dp\"\n    android:viewportWidth=\"24.0\"\n    android:viewportHeight=\"24.0\">\n    <path\n        android:name=\"square\"\n        android:fillColor=\"@color/primary_color\"\n        android:pathData=\"M0,0 L24,0 L24,24 L0,24 z\" />\n    <path\n        android:fillColor=\"#FFFFFFFF\"\n        android:pathData=\"M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z\" />\n</vector>\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/res/drawable/ic_ask_inactive.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"48dp\"\n    android:height=\"36dp\"\n    android:viewportWidth=\"24.0\"\n    android:viewportHeight=\"24.0\">\n    <path\n        android:name=\"square\"\n        android:fillColor=\"@android:color/darker_gray\"\n        android:pathData=\"M0,0 L24,0 L24,24 L0,24 z\" />\n    <path\n        android:fillColor=\"#FFFFFFFF\"\n        android:pathData=\"M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z\" />\n</vector>\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path\n        android:fillColor=\"#3DDC84\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n</vector>\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/res/drawable/ic_minus.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13H5v-2h14v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/res/drawable/ic_plus.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path android:pathData=\"M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"85.84757\"\n                android:endY=\"92.4963\"\n                android:startX=\"42.9492\"\n                android:startY=\"49.59793\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".MainActivity\">\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"@color/content_background_color\">\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"@color/toolbar_background\"\n            app:layout_constraintTop_toTopOf=\"parent\" />\n\n        <ImageView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"0dp\"\n            android:contentDescription=\"@null\"\n            android:src=\"@drawable/tfl_logo\"\n            app:layout_constraintBottom_toBottomOf=\"@id/toolbar\"\n            app:layout_constraintLeft_toLeftOf=\"parent\"\n            app:layout_constraintRight_toRightOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"@id/toolbar\" />\n\n        <androidx.fragment.app.FragmentContainerView\n            android:id=\"@+id/fragment_container\"\n            android:name=\"androidx.navigation.fragment.NavHostFragment\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"0dp\"\n            android:background=\"@android:color/transparent\"\n            android:keepScreenOn=\"true\"\n            app:defaultNavHost=\"true\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@id/toolbar\"\n            app:navGraph=\"@navigation/nav_graph\"\n            tools:context=\".MainActivity\" />\n\n    </androidx.constraintlayout.widget.ConstraintLayout>\n\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/res/layout/fragment_dataset.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <TextView\n        android:id=\"@+id/tvHint\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:padding=\"@dimen/textview_hint_padding\"\n        android:text=\"@string/tv_select_action\"\n        android:textStyle=\"bold\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/recyclerView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/tvHint\" />\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/res/layout/fragment_qa.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <ScrollView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"0dp\"\n            app:layout_constraintBottom_toTopOf=\"@id/bottomView\"\n            app:layout_constraintTop_toTopOf=\"parent\">\n\n            <TextView\n                android:id=\"@+id/tvDatasetContent\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:padding=\"@dimen/textview_dataset_content_padding\" />\n        </ScrollView>\n\n        <androidx.constraintlayout.widget.ConstraintLayout\n            android:id=\"@+id/bottomView\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginBottom=\"@dimen/bottom_sheet_peek_height\"\n            android:background=\"@android:color/white\"\n            android:elevation=\"@dimen/bottom_view_elevation\"\n            android:padding=\"@dimen/bottom_view_padding\"\n            app:layout_constraintBottom_toBottomOf=\"parent\">\n\n            <TextView\n                android:id=\"@+id/tvSuggestion\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/tv_suggestion\"\n                android:textStyle=\"bold\"\n                app:layout_constraintLeft_toLeftOf=\"parent\"\n                app:layout_constraintTop_toTopOf=\"parent\" />\n\n            <androidx.recyclerview.widget.RecyclerView\n                android:id=\"@+id/recyclerView\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                app:layout_constraintBottom_toTopOf=\"@+id/inputLayout\"\n                app:layout_constraintHeight_max=\"@dimen/rl_suggestion_questions_max_height\"\n                app:layout_constraintTop_toBottomOf=\"@+id/tvSuggestion\" />\n\n            <com.google.android.material.textfield.TextInputLayout\n                android:id=\"@+id/inputLayout\"\n                style=\"@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox\"\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                app:boxStrokeColor=\"@color/primary_color\"\n                app:endIconMode=\"clear_text\"\n                app:layout_constraintBottom_toTopOf=\"@id/tvInferenceTime\"\n                app:layout_constraintLeft_toLeftOf=\"parent\"\n                app:layout_constraintRight_toLeftOf=\"@id/imgBtnAsk\">\n\n                <com.google.android.material.textfield.TextInputEditText\n                    android:id=\"@+id/edtQuestion\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:inputType=\"text\"\n                    android:maxLines=\"@integer/bottom_view_edittext_max_line\" />\n            </com.google.android.material.textfield.TextInputLayout>\n\n            <ImageButton\n                android:id=\"@+id/imgBtnAsk\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"?attr/selectableItemBackgroundBorderless\"\n                android:clickable=\"false\"\n                android:contentDescription=\"@null\"\n                android:padding=\"@dimen/button_ask_padding\"\n                android:src=\"@drawable/ic_ask_inactive\"\n                app:layout_constraintBottom_toBottomOf=\"@id/inputLayout\"\n                app:layout_constraintRight_toRightOf=\"parent\"\n                app:layout_constraintTop_toTopOf=\"@id/inputLayout\" />\n\n            <TextView\n                android:id=\"@+id/tvInferenceTime\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                app:layout_constraintBottom_toBottomOf=\"parent\"\n                app:layout_constraintStart_toStartOf=\"parent\" />\n        </androidx.constraintlayout.widget.ConstraintLayout>\n    </androidx.constraintlayout.widget.ConstraintLayout>\n\n    <include\n        android:id=\"@+id/bottom_sheet_layout\"\n        layout=\"@layout/info_bottom_sheet\" />\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/res/layout/info_bottom_sheet.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.core.widget.NestedScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/bottom_sheet_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_gravity=\"center_horizontal\"\n    android:background=\"@color/bottom_sheet_background\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:padding=\"@dimen/bottom_view_padding\"\n    app:behavior_hideable=\"false\"\n    app:behavior_peekHeight=\"@dimen/bottom_sheet_peek_height\"\n    app:layout_behavior=\"com.google.android.material.bottomsheet.BottomSheetBehavior\">\n\n    <androidx.appcompat.widget.LinearLayoutCompat\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <!-- Chevron indicating that the bottom sheet is expandable -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerHorizontal=\"true\"\n                android:layout_centerVertical=\"true\"\n                android:contentDescription=\"@string/alt_bottom_sheet_chevron\"\n                android:src=\"@drawable/icn_chevron_up\" />\n        </RelativeLayout>\n\n        <!-- Number of threads adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:text=\"@string/bottom_sheet_label_threads\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentEnd=\"true\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\">\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threads_minus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_thread_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/threads_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:gravity=\"center\"\n                    android:minEms=\"@integer/bottom_sheet_control_text_min_ems\"\n                    android:text=\"@string/bottom_sheet_default_num_thread\"\n                    android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threads_plus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_thread_button_plus\"\n                    android:src=\"@drawable/ic_plus\" />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- Delegate selection row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/bottom_sheet_label_delegate\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <androidx.appcompat.widget.AppCompatSpinner\n                android:id=\"@+id/spinner_delegate\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentEnd=\"true\"\n                android:entries=\"@array/delegate_spinner_titles\"\n                android:minWidth=\"@dimen/bottom_sheet_spinner_delegate_min_width\"\n                android:spinnerMode=\"dropdown\"\n                android:theme=\"@style/BottomSheetSpinnerItemStyle\" />\n\n        </RelativeLayout>\n    </androidx.appcompat.widget.LinearLayoutCompat>\n</androidx.core.widget.NestedScrollView>\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/res/layout/item_dataset.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <CheckedTextView\n        android:id=\"@+id/tvDataTitle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?android:attr/listPreferredItemHeight\"\n        android:background=\"?android:attr/listChoiceBackgroundIndicator\"\n        android:gravity=\"center_vertical\"\n        android:paddingStart=\"@dimen/textview_dataset_title_padding\"\n        android:paddingEnd=\"@dimen/textview_dataset_title_padding\"\n        android:textAppearance=\"?android:attr/textAppearanceListItem\" />\n</RelativeLayout>\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/res/layout/item_question.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <CheckedTextView\n        android:id=\"@+id/tvQuestionSuggestion\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?android:attr/listChoiceBackgroundIndicator\"\n        android:paddingStart=\"@dimen/suggestion_questions_padding_horizontal\"\n        android:paddingTop=\"@dimen/suggestion_questions_padding_vertical\"\n        android:paddingEnd=\"@dimen/suggestion_questions_padding_horizontal\"\n        android:paddingBottom=\"@dimen/suggestion_questions_padding_vertical\" />\n</RelativeLayout>\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/res/navigation/nav_graph.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/nav_graph\"\n    app:startDestination=\"@id/datasetFragment\">\n\n    <fragment\n        android:id=\"@+id/datasetFragment\"\n        android:name=\"org.tensorflow.lite.examples.bertqa.fragments.DatasetFragment\"\n        android:label=\"DatasetFragment\">\n        <action\n            android:id=\"@+id/action_datasetFragment_to_qaFragment\"\n            app:destination=\"@id/qaFragment\" />\n    </fragment>\n    <fragment\n        android:id=\"@+id/qaFragment\"\n        android:name=\"org.tensorflow.lite.examples.bertqa.fragments.QaFragment\"\n        android:label=\"QaFragment\">\n        <argument\n            android:name=\"datasetPosition\"\n            app:argType=\"integer\" />\n    </fragment>\n</navigation>\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n    <!-- TF Branding Orange -->\n    <color name=\"primary_color\">#FF6F00</color>\n    <color name=\"toolbar_background\">#EEEEEE</color>\n    <color name=\"ic_launcher_background\">#FFFFFF</color>\n    <color name=\"text_color\">@android:color/black</color>\n    <color name=\"highlight_background_color\">#FF6F00</color>\n    <color name=\"highlight_text_color\">#FFFFFF</color>\n    <color name=\"content_background_color\">#FFFFFF</color>\n    <color name=\"bottom_sheet_background\">#EEEEEE</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n\n    <!-- Bottom Sheet -->\n    <dimen name=\"bottom_view_elevation\">16dp</dimen>\n    <dimen name=\"bottom_view_padding\">10dp</dimen>\n    <dimen name=\"suggestion_questions_padding_horizontal\">5dp</dimen>\n    <dimen name=\"suggestion_questions_padding_vertical\">15dp</dimen>\n    <dimen name=\"textview_dataset_title_padding\">8dp</dimen>\n    <dimen name=\"textview_dataset_title_margin\">10dp</dimen>\n    <dimen name=\"textview_dataset_content_padding\">20dp</dimen>\n    <dimen name=\"button_ask_padding\">10dp</dimen>\n    <dimen name=\"textview_hint_padding\">10dp</dimen>\n    <integer name=\"bottom_view_edittext_max_line\">3</integer>\n\n    <dimen name=\"bottom_sheet_padding\">16dp</dimen>\n    <dimen name=\"bottom_sheet_peek_height\">50dp</dimen>\n    <dimen name=\"bottom_sheet_default_row_margin\">16dp</dimen>\n    <dimen name=\"bottom_sheet_control_btn_size\">48dp</dimen>\n    <dimen name=\"bottom_sheet_control_text_side_margin\">10dp</dimen>\n    <dimen name=\"bottom_sheet_spinner_delegate_min_width\">160dp</dimen>\n    <dimen name=\"bottom_sheet_spinner_model_min_width\">240dp</dimen>\n    <dimen name=\"bottom_sheet_text_size\">20sp</dimen>\n    <dimen name=\"rl_suggestion_questions_max_height\">200dp</dimen>\n    <integer name=\"bottom_sheet_control_text_min_ems\">3</integer>\n</resources>\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/res/values/strings.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n    <string name=\"app_name\">TFLite Bert QA Demo App</string>\n    <string name=\"tv_select_action\">Please select an article below.</string>\n    <string name=\"tv_suggestion\">You may want to ask&#8230;</string>\n    <string name=\"tv_setting_options\">Model options</string>\n    <string name=\"alt_bottom_sheet_chevron\">Bottom sheet expandable indicator</string>\n    <string name=\"alt_bottom_sheet_thread_button_minus\">Decrease the number of threads used</string>\n    <string name=\"alt_bottom_sheet_thread_button_plus\">Increase the number of threads used</string>\n    <string name=\"bottom_sheet_default_num_thread\">2</string>\n    <string name=\"bottom_sheet_label_threads\">Number of Threads</string>\n    <string name=\"bottom_sheet_label_delegate\">Delegate</string>\n    <string name=\"bottom_view_inference_time\">Inference Time: %d ms</string>\n\n    <string-array name=\"delegate_spinner_titles\">\n        <item>CPU</item>\n        <item>GPU</item>\n        <item>NNAPI</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "lite/examples/bert_qa/android/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.MaterialComponents.Light.NoActionBar\">\n        <item name=\"android:textColor\">@color/text_color</item>\n        <item name=\"colorPrimary\">@color/primary_color</item>\n        <item name=\"colorControlNormal\">@android:color/black</item>\n    </style>\n\n    <style name=\"BottomSheetSpinnerItemStyle\" parent=\"Widget.AppCompat.Light.DropDownItem.Spinner\">\n        <item name=\"android:textSize\">@dimen/bottom_sheet_text_size</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "lite/examples/bert_qa/android/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n    dependencies {\n        classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.5.0'\n        classpath 'de.undercouch:gradle-download-task:4.1.2'\n    }\n}\nplugins {\n    id 'com.android.application' version '7.2.0' apply false\n    id 'com.android.library' version '7.2.0' apply false\n    id 'org.jetbrains.kotlin.android' version '1.6.21' apply false\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}"
  },
  {
    "path": "lite/examples/bert_qa/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/bert_qa/android/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app\"s APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official\n# Enables namespacing of each library's R class so that its R class includes only the\n# resources declared in the library itself and none from the library's dependencies,\n# thereby reducing the size of the R class for that library\nandroid.nonTransitiveRClass=true"
  },
  {
    "path": "lite/examples/bert_qa/android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/bert_qa/android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/bert_qa/android/settings.gradle",
    "content": "pluginManagement {\n    repositories {\n        gradlePluginPortal()\n        google()\n        mavenCentral()\n    }\n}\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\nrootProject.name = \"TFLite Bert QA Demo App\"\ninclude ':app'\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/.gitignore",
    "content": "vocab.txt\ncontents_from_squad_dict_format.json\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\": [\n    {\n      \"size\": \"60x60\",\n      \"expected-size\": \"180\",\n      \"filename\": \"180.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"3x\"\n    },\n    {\n      \"size\": \"40x40\",\n      \"expected-size\": \"80\",\n      \"filename\": \"80.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"40x40\",\n      \"expected-size\": \"120\",\n      \"filename\": \"120.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"3x\"\n    },\n    {\n      \"size\": \"60x60\",\n      \"expected-size\": \"120\",\n      \"filename\": \"120.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"58\",\n      \"filename\": \"58.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"29\",\n      \"filename\": \"29.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"87\",\n      \"filename\": \"87.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"3x\"\n    },\n    {\n      \"size\": \"20x20\",\n      \"expected-size\": \"40\",\n      \"filename\": \"40.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"20x20\",\n      \"expected-size\": \"60\",\n      \"filename\": \"60.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"3x\"\n    },\n    {\n      \"size\": \"1024x1024\",\n      \"filename\": \"1024.png\",\n      \"expected-size\": \"1024\",\n      \"idiom\": \"ios-marketing\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"40x40\",\n      \"expected-size\": \"80\",\n      \"filename\": \"80.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"72x72\",\n      \"expected-size\": \"72\",\n      \"filename\": \"72.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"76x76\",\n      \"expected-size\": \"152\",\n      \"filename\": \"152.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"50x50\",\n      \"expected-size\": \"100\",\n      \"filename\": \"100.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"58\",\n      \"filename\": \"58.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"76x76\",\n      \"expected-size\": \"76\",\n      \"filename\": \"76.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"29\",\n      \"filename\": \"29.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"50x50\",\n      \"expected-size\": \"50\",\n      \"filename\": \"50.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"72x72\",\n      \"expected-size\": \"144\",\n      \"filename\": \"144.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"40x40\",\n      \"expected-size\": \"40\",\n      \"filename\": \"40.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"83.5x83.5\",\n      \"expected-size\": \"167\",\n      \"filename\": \"167.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"20x20\",\n      \"expected-size\": \"20\",\n      \"filename\": \"20.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"20x20\",\n      \"expected-size\": \"40\",\n      \"filename\": \"40.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    }\n  ]\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQA.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t3A4D441F43E606AFFBEDC0F4 /* Pods_BertQA_UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C806A982D14ED6BC003A9A /* Pods_BertQA_UIKit.framework */; };\n\t\t8073F32F9E31519DA1C60690 /* Pods_BertQA_SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D0CCBAA9B671F431851D96A /* Pods_BertQA_SwiftUI.framework */; };\n\t\t8402440423D9BFDE00704ABD /* FullTokenizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8402440323D9BFDE00704ABD /* FullTokenizer.swift */; };\n\t\t8402440623D9C18600704ABD /* WordpieceTokenizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8402440523D9C18600704ABD /* WordpieceTokenizer.swift */; };\n\t\t8402440823D9C1FB00704ABD /* BasicTokenizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8402440723D9C1FB00704ABD /* BasicTokenizer.swift */; };\n\t\t840245262424CB63002D1DAD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840245252424CB63002D1DAD /* AppDelegate.swift */; };\n\t\t840245282424CB63002D1DAD /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840245272424CB63002D1DAD /* SceneDelegate.swift */; };\n\t\t840245322424CB64002D1DAD /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 840245302424CB64002D1DAD /* LaunchScreen.storyboard */; };\n\t\t840C6F3F2403A39E00E6905E /* DataExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840C6F3B2403A39E00E6905E /* DataExtension.swift */; };\n\t\t840C6F402403A39E00E6905E /* InputFeatures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840C6F3C2403A39E00E6905E /* InputFeatures.swift */; };\n\t\t840C6F412403A39E00E6905E /* BertQAHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840C6F3D2403A39E00E6905E /* BertQAHandler.swift */; };\n\t\t840C6F422403A39E00E6905E /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840C6F3E2403A39E00E6905E /* Result.swift */; };\n\t\t840C6F442403A5C900E6905E /* mobilebert_float_20191023.tflite in Resources */ = {isa = PBXBuildFile; fileRef = 840C6F432403A5C900E6905E /* mobilebert_float_20191023.tflite */; };\n\t\t842E65D123CEDD6F00AE9416 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842E65D023CEDD6F00AE9416 /* Constants.swift */; };\n\t\t842E65D423CEDF7200AE9416 /* DatasetTitleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842E65D323CEDF7200AE9416 /* DatasetTitleCell.swift */; };\n\t\t8433178E23E933AC001B0AE8 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8433178D23E933AC001B0AE8 /* StringExtension.swift */; };\n\t\t84433D4D240EA8EF00C983EA /* ContentData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84433D4C240EA8EF00C983EA /* ContentData.swift */; };\n\t\t844C87012424CE29006DA707 /* DatasetListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844C86FC2424CD7F006DA707 /* DatasetListView.swift */; };\n\t\t844C87022424CE2E006DA707 /* DatasetDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844C86FE2424CD97006DA707 /* DatasetDetailView.swift */; };\n\t\t844C87032424CFD0006DA707 /* contents_from_squad_dict_format.json in Resources */ = {isa = PBXBuildFile; fileRef = 8498AA6C240FA5DF003ABA5E /* contents_from_squad_dict_format.json */; };\n\t\t844C87072424D019006DA707 /* KeyboardHeightObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844C87062424D019006DA707 /* KeyboardHeightObserver.swift */; };\n\t\t844C87082424D03E006DA707 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842E65D023CEDD6F00AE9416 /* Constants.swift */; };\n\t\t84551A6C23F2710100C71C16 /* vocab.txt in Resources */ = {isa = PBXBuildFile; fileRef = 84551A6B23F2710100C71C16 /* vocab.txt */; };\n\t\t845B4FD42434856500943980 /* Dataset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845B4FD32434856500943980 /* Dataset.swift */; };\n\t\t845B4FD52434856A00943980 /* Dataset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845B4FD32434856500943980 /* Dataset.swift */; };\n\t\t846918EB2425212B00A90808 /* DataExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840C6F3B2403A39E00E6905E /* DataExtension.swift */; };\n\t\t846918EC2425212F00A90808 /* UnicodeScalarExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C62F6123F6BD3A00E4BB77 /* UnicodeScalarExtension.swift */; };\n\t\t846918ED2425213100A90808 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8433178D23E933AC001B0AE8 /* StringExtension.swift */; };\n\t\t846918EF2425213800A90808 /* FileLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E81C6023FFC2B400D50692 /* FileLoader.swift */; };\n\t\t846918F12425213B00A90808 /* FullTokenizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8402440323D9BFDE00704ABD /* FullTokenizer.swift */; };\n\t\t846918F32425213E00A90808 /* BasicTokenizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8402440723D9C1FB00704ABD /* BasicTokenizer.swift */; };\n\t\t846918F52425214200A90808 /* WordpieceTokenizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8402440523D9C18600704ABD /* WordpieceTokenizer.swift */; };\n\t\t846918F82425214B00A90808 /* mobilebert_float_20191023.tflite in Resources */ = {isa = PBXBuildFile; fileRef = 840C6F432403A5C900E6905E /* mobilebert_float_20191023.tflite */; };\n\t\t846918FA2425214D00A90808 /* vocab.txt in Resources */ = {isa = PBXBuildFile; fileRef = 84551A6B23F2710100C71C16 /* vocab.txt */; };\n\t\t8475814E23CDC1660003472A /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 8475814D23CDC1660003472A /* README.md */; };\n\t\t8475815023CDC24D0003472A /* DataSetDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8475814F23CDC24D0003472A /* DataSetDetailViewController.swift */; };\n\t\t8475815223CDC28C0003472A /* DataSetsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8475815123CDC28C0003472A /* DataSetsTableViewController.swift */; };\n\t\t849401F824447883005F0C25 /* StringExtensionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849401F024447883005F0C25 /* StringExtensionTest.swift */; };\n\t\t849401F924447883005F0C25 /* UnicodeScalarExtensionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849401F124447883005F0C25 /* UnicodeScalarExtensionTest.swift */; };\n\t\t849401FA24447883005F0C25 /* WordpieceTokenizerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849401F324447883005F0C25 /* WordpieceTokenizerTest.swift */; };\n\t\t849401FB24447883005F0C25 /* FullTokenizerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849401F424447883005F0C25 /* FullTokenizerTest.swift */; };\n\t\t849401FC24447883005F0C25 /* BasicTokenizerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849401F524447883005F0C25 /* BasicTokenizerTest.swift */; };\n\t\t849401FD24447883005F0C25 /* BertQAHandlerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849401F724447883005F0C25 /* BertQAHandlerTest.swift */; };\n\t\t8496E7E123CF019C00386218 /* SuggestedQuestionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8496E7E023CF019C00386218 /* SuggestedQuestionButton.swift */; };\n\t\t8498AA6D240FA5DF003ABA5E /* contents_from_squad_dict_format.json in Resources */ = {isa = PBXBuildFile; fileRef = 8498AA6C240FA5DF003ABA5E /* contents_from_squad_dict_format.json */; };\n\t\t84C3284F23B49E2E00A7980D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C3284E23B49E2E00A7980D /* AppDelegate.swift */; };\n\t\t84C3285423B49E2E00A7980D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 84C3285223B49E2E00A7980D /* Main.storyboard */; };\n\t\t84C3285623B49E2F00A7980D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 84C3285523B49E2F00A7980D /* Assets.xcassets */; };\n\t\t84C3285923B49E2F00A7980D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 84C3285723B49E2F00A7980D /* LaunchScreen.storyboard */; };\n\t\t84C62F6223F6BD3A00E4BB77 /* UnicodeScalarExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C62F6123F6BD3A00E4BB77 /* UnicodeScalarExtension.swift */; };\n\t\t84CBD8F92424DCBF00DC20AA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 84C3285523B49E2F00A7980D /* Assets.xcassets */; };\n\t\t84D84026243C4DFB00E0B769 /* BertQAHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840C6F3D2403A39E00E6905E /* BertQAHandler.swift */; };\n\t\t84D84027243C4E0100E0B769 /* InputFeatures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840C6F3C2403A39E00E6905E /* InputFeatures.swift */; };\n\t\t84D84028243C4E0300E0B769 /* ContentData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84433D4C240EA8EF00C983EA /* ContentData.swift */; };\n\t\t84D84029243C4E0600E0B769 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840C6F3E2403A39E00E6905E /* Result.swift */; };\n\t\t84D8402E243C4E3E00E0B769 /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D8402B243C4E3E00E0B769 /* StatusView.swift */; };\n\t\t84D8402F243C4E3E00E0B769 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D8402C243C4E3E00E0B769 /* ContentView.swift */; };\n\t\t84D84030243C4E3E00E0B769 /* SuggestedQuestionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D8402D243C4E3E00E0B769 /* SuggestedQuestionsView.swift */; };\n\t\t84E81C6123FFC2B400D50692 /* FileLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E81C6023FFC2B400D50692 /* FileLoader.swift */; };\n\t\t84F0354E2418B7380039B5D5 /* OptionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F0354D2418B7380039B5D5 /* OptionViewController.swift */; };\n\t\t84F035502418B7800039B5D5 /* OptionTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F0354F2418B7800039B5D5 /* OptionTableViewController.swift */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\t849401DB244462AB005F0C25 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 84C3284323B49E2E00A7980D /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 84C3284A23B49E2E00A7980D;\n\t\t\tremoteInfo = \"BertQA-UIKit\";\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXFileReference section */\n\t\t20076681A0E239448C8D4B05 /* Pods-BertQA.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-BertQA.release.xcconfig\"; path = \"Target Support Files/Pods-BertQA/Pods-BertQA.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t2D0CCBAA9B671F431851D96A /* Pods_BertQA_SwiftUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BertQA_SwiftUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t37F95CAA32D15283DD33CF2F /* Pods-BertQA.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-BertQA.debug.xcconfig\"; path = \"Target Support Files/Pods-BertQA/Pods-BertQA.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t64C806A982D14ED6BC003A9A /* Pods_BertQA_UIKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BertQA_UIKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t67EE9FD8BFDA0BBBC2A8C538 /* Pods-BertQA-UIKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-BertQA-UIKit.release.xcconfig\"; path = \"Target Support Files/Pods-BertQA-UIKit/Pods-BertQA-UIKit.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t6D06722FADA8CA7F3CCC3492 /* Pods-BertQA-SwiftUI.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-BertQA-SwiftUI.release.xcconfig\"; path = \"Target Support Files/Pods-BertQA-SwiftUI/Pods-BertQA-SwiftUI.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t7595E796644E0735F1BD68A4 /* Pods-BertQA-SwiftUI.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-BertQA-SwiftUI.debug.xcconfig\"; path = \"Target Support Files/Pods-BertQA-SwiftUI/Pods-BertQA-SwiftUI.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t7E5B19F92467606B003BC3C2 /* download_resources.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = download_resources.sh; sourceTree = \"<group>\"; };\n\t\t8402440323D9BFDE00704ABD /* FullTokenizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullTokenizer.swift; sourceTree = \"<group>\"; };\n\t\t8402440523D9C18600704ABD /* WordpieceTokenizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordpieceTokenizer.swift; sourceTree = \"<group>\"; };\n\t\t8402440723D9C1FB00704ABD /* BasicTokenizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicTokenizer.swift; sourceTree = \"<group>\"; };\n\t\t840245232424CB63002D1DAD /* BertQA-SwiftUI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = \"BertQA-SwiftUI.app\"; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t840245252424CB63002D1DAD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t840245272424CB63002D1DAD /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = \"<group>\"; };\n\t\t840245312424CB64002D1DAD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t840245332424CB64002D1DAD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t840C6F3B2403A39E00E6905E /* DataExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataExtension.swift; sourceTree = \"<group>\"; };\n\t\t840C6F3C2403A39E00E6905E /* InputFeatures.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputFeatures.swift; sourceTree = \"<group>\"; };\n\t\t840C6F3D2403A39E00E6905E /* BertQAHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BertQAHandler.swift; sourceTree = \"<group>\"; };\n\t\t840C6F3E2403A39E00E6905E /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = \"<group>\"; };\n\t\t840C6F432403A5C900E6905E /* mobilebert_float_20191023.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = mobilebert_float_20191023.tflite; sourceTree = \"<group>\"; };\n\t\t84126CCB24111D5E00349A2A /* ControlPanelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlPanelView.swift; sourceTree = \"<group>\"; };\n\t\t842E65D023CEDD6F00AE9416 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = \"<group>\"; };\n\t\t842E65D323CEDF7200AE9416 /* DatasetTitleCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatasetTitleCell.swift; sourceTree = \"<group>\"; };\n\t\t8433178D23E933AC001B0AE8 /* StringExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = \"<group>\"; };\n\t\t84433D4C240EA8EF00C983EA /* ContentData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentData.swift; sourceTree = \"<group>\"; };\n\t\t844C86FC2424CD7F006DA707 /* DatasetListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatasetListView.swift; sourceTree = \"<group>\"; };\n\t\t844C86FE2424CD97006DA707 /* DatasetDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatasetDetailView.swift; sourceTree = \"<group>\"; };\n\t\t844C87062424D019006DA707 /* KeyboardHeightObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardHeightObserver.swift; sourceTree = \"<group>\"; };\n\t\t84551A6B23F2710100C71C16 /* vocab.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = vocab.txt; sourceTree = \"<group>\"; };\n\t\t845B4FD32434856500943980 /* Dataset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Dataset.swift; sourceTree = \"<group>\"; };\n\t\t8475814D23CDC1660003472A /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = \"<group>\"; };\n\t\t8475814F23CDC24D0003472A /* DataSetDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataSetDetailViewController.swift; sourceTree = \"<group>\"; };\n\t\t8475815123CDC28C0003472A /* DataSetsTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataSetsTableViewController.swift; sourceTree = \"<group>\"; };\n\t\t848BC39924002091009434DE /* BertQATests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BertQATests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t849401D6244462AB005F0C25 /* BertQA-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = \"BertQA-Tests.xctest\"; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t849401DA244462AB005F0C25 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t849401F024447883005F0C25 /* StringExtensionTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringExtensionTest.swift; sourceTree = \"<group>\"; };\n\t\t849401F124447883005F0C25 /* UnicodeScalarExtensionTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnicodeScalarExtensionTest.swift; sourceTree = \"<group>\"; };\n\t\t849401F324447883005F0C25 /* WordpieceTokenizerTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WordpieceTokenizerTest.swift; sourceTree = \"<group>\"; };\n\t\t849401F424447883005F0C25 /* FullTokenizerTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FullTokenizerTest.swift; sourceTree = \"<group>\"; };\n\t\t849401F524447883005F0C25 /* BasicTokenizerTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicTokenizerTest.swift; sourceTree = \"<group>\"; };\n\t\t849401F724447883005F0C25 /* BertQAHandlerTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BertQAHandlerTest.swift; sourceTree = \"<group>\"; };\n\t\t8496E7E023CF019C00386218 /* SuggestedQuestionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestedQuestionButton.swift; sourceTree = \"<group>\"; };\n\t\t8498AA6C240FA5DF003ABA5E /* contents_from_squad_dict_format.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = contents_from_squad_dict_format.json; sourceTree = \"<group>\"; };\n\t\t84C3284B23B49E2E00A7980D /* BertQA-UIKit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = \"BertQA-UIKit.app\"; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t84C3284E23B49E2E00A7980D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t84C3285323B49E2E00A7980D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t84C3285523B49E2F00A7980D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t84C3285823B49E2F00A7980D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t84C3285A23B49E2F00A7980D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t84C62F6123F6BD3A00E4BB77 /* UnicodeScalarExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnicodeScalarExtension.swift; sourceTree = \"<group>\"; };\n\t\t84D8402B243C4E3E00E0B769 /* StatusView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusView.swift; sourceTree = \"<group>\"; };\n\t\t84D8402C243C4E3E00E0B769 /* ContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = \"<group>\"; };\n\t\t84D8402D243C4E3E00E0B769 /* SuggestedQuestionsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SuggestedQuestionsView.swift; sourceTree = \"<group>\"; };\n\t\t84E81C6023FFC2B400D50692 /* FileLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileLoader.swift; sourceTree = \"<group>\"; };\n\t\t84F0354D2418B7380039B5D5 /* OptionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionViewController.swift; sourceTree = \"<group>\"; };\n\t\t84F0354F2418B7800039B5D5 /* OptionTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionTableViewController.swift; sourceTree = \"<group>\"; };\n\t\tFA8F6BA054A264013FFDBD91 /* Pods-BertQA-UIKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-BertQA-UIKit.debug.xcconfig\"; path = \"Target Support Files/Pods-BertQA-UIKit/Pods-BertQA-UIKit.debug.xcconfig\"; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t840245202424CB63002D1DAD /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t8073F32F9E31519DA1C60690 /* Pods_BertQA_SwiftUI.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t849401D3244462AB005F0C25 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t84C3284823B49E2E00A7980D /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t3A4D441F43E606AFFBEDC0F4 /* Pods_BertQA_UIKit.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t5A57E91BD94E10B803571A3B /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2D0CCBAA9B671F431851D96A /* Pods_BertQA_SwiftUI.framework */,\n\t\t\t\t64C806A982D14ED6BC003A9A /* Pods_BertQA_UIKit.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t781038914BC670D9D4E03C4B /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t37F95CAA32D15283DD33CF2F /* Pods-BertQA.debug.xcconfig */,\n\t\t\t\t20076681A0E239448C8D4B05 /* Pods-BertQA.release.xcconfig */,\n\t\t\t\t7595E796644E0735F1BD68A4 /* Pods-BertQA-SwiftUI.debug.xcconfig */,\n\t\t\t\t6D06722FADA8CA7F3CCC3492 /* Pods-BertQA-SwiftUI.release.xcconfig */,\n\t\t\t\tFA8F6BA054A264013FFDBD91 /* Pods-BertQA-UIKit.debug.xcconfig */,\n\t\t\t\t67EE9FD8BFDA0BBBC2A8C538 /* Pods-BertQA-UIKit.release.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t8402440223D9BC0600704ABD /* Tokenizers */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t8402440323D9BFDE00704ABD /* FullTokenizer.swift */,\n\t\t\t\t8402440723D9C1FB00704ABD /* BasicTokenizer.swift */,\n\t\t\t\t8402440523D9C18600704ABD /* WordpieceTokenizer.swift */,\n\t\t\t);\n\t\t\tpath = Tokenizers;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t840245242424CB63002D1DAD /* ViewInSwiftUI */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t844C86FB2424CD67006DA707 /* Views */,\n\t\t\t\t840245252424CB63002D1DAD /* AppDelegate.swift */,\n\t\t\t\t840245272424CB63002D1DAD /* SceneDelegate.swift */,\n\t\t\t\t844C87062424D019006DA707 /* KeyboardHeightObserver.swift */,\n\t\t\t\t840245302424CB64002D1DAD /* LaunchScreen.storyboard */,\n\t\t\t\t840245332424CB64002D1DAD /* Info.plist */,\n\t\t\t);\n\t\t\tpath = ViewInSwiftUI;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t840C6F322403A36000E6905E /* ML */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t840C6F3C2403A39E00E6905E /* InputFeatures.swift */,\n\t\t\t\t84433D4C240EA8EF00C983EA /* ContentData.swift */,\n\t\t\t\t840C6F3E2403A39E00E6905E /* Result.swift */,\n\t\t\t\t840C6F3D2403A39E00E6905E /* BertQAHandler.swift */,\n\t\t\t);\n\t\t\tpath = ML;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t841BD7242422698B0050D71C /* BertQACore */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t842E65CD23CEDBA900AE9416 /* Resources */,\n\t\t\t\t8475815523CDC64B0003472A /* Models */,\n\t\t\t\t84E81C6223FFC2F600D50692 /* Extensions */,\n\t\t\t\t842E65D023CEDD6F00AE9416 /* Constants.swift */,\n\t\t\t);\n\t\t\tpath = BertQACore;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t841BD7262422698B0050D71C /* ViewInUIKit */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t842E65D223CEDF4700AE9416 /* Views */,\n\t\t\t\t84F96F0023CDACA0009FBDF5 /* Controllers */,\n\t\t\t\t84C3284E23B49E2E00A7980D /* AppDelegate.swift */,\n\t\t\t\t84C3285223B49E2E00A7980D /* Main.storyboard */,\n\t\t\t\t84C3285723B49E2F00A7980D /* LaunchScreen.storyboard */,\n\t\t\t\t84C3285A23B49E2F00A7980D /* Info.plist */,\n\t\t\t);\n\t\t\tpath = ViewInUIKit;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t842E65CD23CEDBA900AE9416 /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t8498AA6C240FA5DF003ABA5E /* contents_from_squad_dict_format.json */,\n\t\t\t\t840C6F432403A5C900E6905E /* mobilebert_float_20191023.tflite */,\n\t\t\t\t84551A6B23F2710100C71C16 /* vocab.txt */,\n\t\t\t);\n\t\t\tpath = Resources;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t842E65D223CEDF4700AE9416 /* Views */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t842E65D323CEDF7200AE9416 /* DatasetTitleCell.swift */,\n\t\t\t\t8496E7E023CF019C00386218 /* SuggestedQuestionButton.swift */,\n\t\t\t\t84126CCB24111D5E00349A2A /* ControlPanelView.swift */,\n\t\t\t);\n\t\t\tpath = Views;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t844C86FB2424CD67006DA707 /* Views */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t84D8402C243C4E3E00E0B769 /* ContentView.swift */,\n\t\t\t\t84D8402B243C4E3E00E0B769 /* StatusView.swift */,\n\t\t\t\t84D8402D243C4E3E00E0B769 /* SuggestedQuestionsView.swift */,\n\t\t\t\t844C86FC2424CD7F006DA707 /* DatasetListView.swift */,\n\t\t\t\t844C86FE2424CD97006DA707 /* DatasetDetailView.swift */,\n\t\t\t);\n\t\t\tpath = Views;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t8475815523CDC64B0003472A /* Models */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t845B4FD32434856500943980 /* Dataset.swift */,\n\t\t\t\t84E81C6023FFC2B400D50692 /* FileLoader.swift */,\n\t\t\t\t8402440223D9BC0600704ABD /* Tokenizers */,\n\t\t\t\t840C6F322403A36000E6905E /* ML */,\n\t\t\t);\n\t\t\tpath = Models;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t849401D7244462AB005F0C25 /* BertQATests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t849401EF24447883005F0C25 /* ExtensionTest */,\n\t\t\t\t849401F624447883005F0C25 /* MLTest */,\n\t\t\t\t849401F224447883005F0C25 /* TokenizerTest */,\n\t\t\t\t849401DA244462AB005F0C25 /* Info.plist */,\n\t\t\t);\n\t\t\tpath = BertQATests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t849401EF24447883005F0C25 /* ExtensionTest */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t849401F024447883005F0C25 /* StringExtensionTest.swift */,\n\t\t\t\t849401F124447883005F0C25 /* UnicodeScalarExtensionTest.swift */,\n\t\t\t);\n\t\t\tpath = ExtensionTest;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t849401F224447883005F0C25 /* TokenizerTest */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t849401F324447883005F0C25 /* WordpieceTokenizerTest.swift */,\n\t\t\t\t849401F424447883005F0C25 /* FullTokenizerTest.swift */,\n\t\t\t\t849401F524447883005F0C25 /* BasicTokenizerTest.swift */,\n\t\t\t);\n\t\t\tpath = TokenizerTest;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t849401F624447883005F0C25 /* MLTest */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t849401F724447883005F0C25 /* BertQAHandlerTest.swift */,\n\t\t\t);\n\t\t\tpath = MLTest;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t84B3C6A9240F74EA00BA6F5E /* RunScripts */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t7E5B19F92467606B003BC3C2 /* download_resources.sh */,\n\t\t\t);\n\t\t\tpath = RunScripts;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t84C3284223B49E2E00A7980D = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t8475814D23CDC1660003472A /* README.md */,\n\t\t\t\t841BD7242422698B0050D71C /* BertQACore */,\n\t\t\t\t841BD7262422698B0050D71C /* ViewInUIKit */,\n\t\t\t\t840245242424CB63002D1DAD /* ViewInSwiftUI */,\n\t\t\t\t849401D7244462AB005F0C25 /* BertQATests */,\n\t\t\t\t84C3285523B49E2F00A7980D /* Assets.xcassets */,\n\t\t\t\t84B3C6A9240F74EA00BA6F5E /* RunScripts */,\n\t\t\t\t84C3284C23B49E2E00A7980D /* Products */,\n\t\t\t\t781038914BC670D9D4E03C4B /* Pods */,\n\t\t\t\t5A57E91BD94E10B803571A3B /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t84C3284C23B49E2E00A7980D /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t84C3284B23B49E2E00A7980D /* BertQA-UIKit.app */,\n\t\t\t\t848BC39924002091009434DE /* BertQATests.xctest */,\n\t\t\t\t840245232424CB63002D1DAD /* BertQA-SwiftUI.app */,\n\t\t\t\t849401D6244462AB005F0C25 /* BertQA-Tests.xctest */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t84E81C6223FFC2F600D50692 /* Extensions */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t8433178D23E933AC001B0AE8 /* StringExtension.swift */,\n\t\t\t\t84C62F6123F6BD3A00E4BB77 /* UnicodeScalarExtension.swift */,\n\t\t\t\t840C6F3B2403A39E00E6905E /* DataExtension.swift */,\n\t\t\t);\n\t\t\tpath = Extensions;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t84F96F0023CDACA0009FBDF5 /* Controllers */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t8475815123CDC28C0003472A /* DataSetsTableViewController.swift */,\n\t\t\t\t8475814F23CDC24D0003472A /* DataSetDetailViewController.swift */,\n\t\t\t\t84F0354D2418B7380039B5D5 /* OptionViewController.swift */,\n\t\t\t\t84F0354F2418B7800039B5D5 /* OptionTableViewController.swift */,\n\t\t\t);\n\t\t\tpath = Controllers;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t840245222424CB63002D1DAD /* BertQA-SwiftUI */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 840245342424CB64002D1DAD /* Build configuration list for PBXNativeTarget \"BertQA-SwiftUI\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tF746FAE7EA2F78CAB43DFA3B /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t846918AB2425200800A90808 /* ShellScript */,\n\t\t\t\t8402451F2424CB63002D1DAD /* Sources */,\n\t\t\t\t840245202424CB63002D1DAD /* Frameworks */,\n\t\t\t\t840245212424CB63002D1DAD /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = \"BertQA-SwiftUI\";\n\t\t\tproductName = ViewInSwiftUI;\n\t\t\tproductReference = 840245232424CB63002D1DAD /* BertQA-SwiftUI.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\t849401D5244462AB005F0C25 /* BertQA-Tests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 849401DD244462AB005F0C25 /* Build configuration list for PBXNativeTarget \"BertQA-Tests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t849401D2244462AB005F0C25 /* Sources */,\n\t\t\t\t849401D3244462AB005F0C25 /* Frameworks */,\n\t\t\t\t849401D4244462AB005F0C25 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t849401DC244462AB005F0C25 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = \"BertQA-Tests\";\n\t\t\tproductName = \"BertQA-Tests\";\n\t\t\tproductReference = 849401D6244462AB005F0C25 /* BertQA-Tests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\n\t\t84C3284A23B49E2E00A7980D /* BertQA-UIKit */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 84C3285D23B49E2F00A7980D /* Build configuration list for PBXNativeTarget \"BertQA-UIKit\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tF18482CEB971E4CB207BA1C9 /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t84B3C6AC240F758B00BA6F5E /* ShellScript */,\n\t\t\t\t84C3284723B49E2E00A7980D /* Sources */,\n\t\t\t\t84C3284823B49E2E00A7980D /* Frameworks */,\n\t\t\t\t84C3284923B49E2E00A7980D /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = \"BertQA-UIKit\";\n\t\t\tproductName = BertQA;\n\t\t\tproductReference = 84C3284B23B49E2E00A7980D /* BertQA-UIKit.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t84C3284323B49E2E00A7980D /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 1120;\n\t\t\t\tLastUpgradeCheck = 1030;\n\t\t\t\tORGANIZATIONNAME = tensorflow;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t840245222424CB63002D1DAD = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 11.2.1;\n\t\t\t\t\t};\n\t\t\t\t\t849401D5244462AB005F0C25 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 11.2.1;\n\t\t\t\t\t\tTestTargetID = 84C3284A23B49E2E00A7980D;\n\t\t\t\t\t};\n\t\t\t\t\t84C3284A23B49E2E00A7980D = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 10.3;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 84C3284623B49E2E00A7980D /* Build configuration list for PBXProject \"BertQA\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 84C3284223B49E2E00A7980D;\n\t\t\tproductRefGroup = 84C3284C23B49E2E00A7980D /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t84C3284A23B49E2E00A7980D /* BertQA-UIKit */,\n\t\t\t\t840245222424CB63002D1DAD /* BertQA-SwiftUI */,\n\t\t\t\t849401D5244462AB005F0C25 /* BertQA-Tests */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t840245212424CB63002D1DAD /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t846918FA2425214D00A90808 /* vocab.txt in Resources */,\n\t\t\t\t840245322424CB64002D1DAD /* LaunchScreen.storyboard in Resources */,\n\t\t\t\t844C87032424CFD0006DA707 /* contents_from_squad_dict_format.json in Resources */,\n\t\t\t\t846918F82425214B00A90808 /* mobilebert_float_20191023.tflite in Resources */,\n\t\t\t\t84CBD8F92424DCBF00DC20AA /* Assets.xcassets in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t849401D4244462AB005F0C25 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t84C3284923B49E2E00A7980D /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t84551A6C23F2710100C71C16 /* vocab.txt in Resources */,\n\t\t\t\t84C3285923B49E2F00A7980D /* LaunchScreen.storyboard in Resources */,\n\t\t\t\t84C3285623B49E2F00A7980D /* Assets.xcassets in Resources */,\n\t\t\t\t840C6F442403A5C900E6905E /* mobilebert_float_20191023.tflite in Resources */,\n\t\t\t\t8475814E23CDC1660003472A /* README.md in Resources */,\n\t\t\t\t8498AA6D240FA5DF003ABA5E /* contents_from_squad_dict_format.json in Resources */,\n\t\t\t\t84C3285423B49E2E00A7980D /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t846918AB2425200800A90808 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"$SRCROOT/RunScripts/download_resources.sh\\n\";\n\t\t};\n\t\t84B3C6AC240F758B00BA6F5E /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"$SRCROOT/RunScripts/download_resources.sh\\n\";\n\t\t};\n\t\tF18482CEB971E4CB207BA1C9 /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-BertQA-UIKit-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\tF746FAE7EA2F78CAB43DFA3B /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-BertQA-SwiftUI-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t8402451F2424CB63002D1DAD /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t84D84029243C4E0600E0B769 /* Result.swift in Sources */,\n\t\t\t\t846918F32425213E00A90808 /* BasicTokenizer.swift in Sources */,\n\t\t\t\t84D8402F243C4E3E00E0B769 /* ContentView.swift in Sources */,\n\t\t\t\t846918F12425213B00A90808 /* FullTokenizer.swift in Sources */,\n\t\t\t\t84D84030243C4E3E00E0B769 /* SuggestedQuestionsView.swift in Sources */,\n\t\t\t\t840245262424CB63002D1DAD /* AppDelegate.swift in Sources */,\n\t\t\t\t846918EC2425212F00A90808 /* UnicodeScalarExtension.swift in Sources */,\n\t\t\t\t844C87072424D019006DA707 /* KeyboardHeightObserver.swift in Sources */,\n\t\t\t\t844C87012424CE29006DA707 /* DatasetListView.swift in Sources */,\n\t\t\t\t84D84026243C4DFB00E0B769 /* BertQAHandler.swift in Sources */,\n\t\t\t\t846918EF2425213800A90808 /* FileLoader.swift in Sources */,\n\t\t\t\t844C87022424CE2E006DA707 /* DatasetDetailView.swift in Sources */,\n\t\t\t\t840245282424CB63002D1DAD /* SceneDelegate.swift in Sources */,\n\t\t\t\t846918EB2425212B00A90808 /* DataExtension.swift in Sources */,\n\t\t\t\t846918ED2425213100A90808 /* StringExtension.swift in Sources */,\n\t\t\t\t84D8402E243C4E3E00E0B769 /* StatusView.swift in Sources */,\n\t\t\t\t844C87082424D03E006DA707 /* Constants.swift in Sources */,\n\t\t\t\t84D84027243C4E0100E0B769 /* InputFeatures.swift in Sources */,\n\t\t\t\t84D84028243C4E0300E0B769 /* ContentData.swift in Sources */,\n\t\t\t\t845B4FD52434856A00943980 /* Dataset.swift in Sources */,\n\t\t\t\t846918F52425214200A90808 /* WordpieceTokenizer.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t849401D2244462AB005F0C25 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t849401FB24447883005F0C25 /* FullTokenizerTest.swift in Sources */,\n\t\t\t\t849401F924447883005F0C25 /* UnicodeScalarExtensionTest.swift in Sources */,\n\t\t\t\t849401F824447883005F0C25 /* StringExtensionTest.swift in Sources */,\n\t\t\t\t849401FC24447883005F0C25 /* BasicTokenizerTest.swift in Sources */,\n\t\t\t\t849401FA24447883005F0C25 /* WordpieceTokenizerTest.swift in Sources */,\n\t\t\t\t849401FD24447883005F0C25 /* BertQAHandlerTest.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t84C3284723B49E2E00A7980D /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t845B4FD42434856500943980 /* Dataset.swift in Sources */,\n\t\t\t\t840C6F402403A39E00E6905E /* InputFeatures.swift in Sources */,\n\t\t\t\t84F0354E2418B7380039B5D5 /* OptionViewController.swift in Sources */,\n\t\t\t\t842E65D423CEDF7200AE9416 /* DatasetTitleCell.swift in Sources */,\n\t\t\t\t842E65D123CEDD6F00AE9416 /* Constants.swift in Sources */,\n\t\t\t\t84433D4D240EA8EF00C983EA /* ContentData.swift in Sources */,\n\t\t\t\t8475815223CDC28C0003472A /* DataSetsTableViewController.swift in Sources */,\n\t\t\t\t8402440623D9C18600704ABD /* WordpieceTokenizer.swift in Sources */,\n\t\t\t\t8433178E23E933AC001B0AE8 /* StringExtension.swift in Sources */,\n\t\t\t\t8402440823D9C1FB00704ABD /* BasicTokenizer.swift in Sources */,\n\t\t\t\t840C6F422403A39E00E6905E /* Result.swift in Sources */,\n\t\t\t\t84C3284F23B49E2E00A7980D /* AppDelegate.swift in Sources */,\n\t\t\t\t840C6F3F2403A39E00E6905E /* DataExtension.swift in Sources */,\n\t\t\t\t84C62F6223F6BD3A00E4BB77 /* UnicodeScalarExtension.swift in Sources */,\n\t\t\t\t8402440423D9BFDE00704ABD /* FullTokenizer.swift in Sources */,\n\t\t\t\t840C6F412403A39E00E6905E /* BertQAHandler.swift in Sources */,\n\t\t\t\t84E81C6123FFC2B400D50692 /* FileLoader.swift in Sources */,\n\t\t\t\t84F035502418B7800039B5D5 /* OptionTableViewController.swift in Sources */,\n\t\t\t\t8475815023CDC24D0003472A /* DataSetDetailViewController.swift in Sources */,\n\t\t\t\t8496E7E123CF019C00386218 /* SuggestedQuestionButton.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\t849401DC244462AB005F0C25 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 84C3284A23B49E2E00A7980D /* BertQA-UIKit */;\n\t\t\ttargetProxy = 849401DB244462AB005F0C25 /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin PBXVariantGroup section */\n\t\t840245302424CB64002D1DAD /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t840245312424CB64002D1DAD /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t84C3285223B49E2E00A7980D /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t84C3285323B49E2E00A7980D /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t84C3285723B49E2F00A7980D /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t84C3285823B49E2F00A7980D /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t840245352424CB64002D1DAD /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7595E796644E0735F1BD68A4 /* Pods-BertQA-SwiftUI.debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tINFOPLIST_FILE = ViewInSwiftUI/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"org.tensorflow.BertQA-SwiftUI\";\n\t\t\t\tPRODUCT_NAME = \"BertQA-SwiftUI\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t840245362424CB64002D1DAD /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 6D06722FADA8CA7F3CCC3492 /* Pods-BertQA-SwiftUI.release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tINFOPLIST_FILE = ViewInSwiftUI/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"org.tensorflow.BertQA-SwiftUI\";\n\t\t\t\tPRODUCT_NAME = \"BertQA-SwiftUI\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t849401DE244462AB005F0C25 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tINFOPLIST_FILE = BertQATests/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.2;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"org.tensorflow.BertQA.BertQA-Tests\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/BertQA-UIKit.app/BertQA-UIKit\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t849401DF244462AB005F0C25 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tINFOPLIST_FILE = BertQATests/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.2;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"org.tensorflow.BertQA.BertQA-Tests\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/BertQA-UIKit.app/BertQA-UIKit\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t84C3285B23B49E2F00A7980D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.4;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t84C3285C23B49E2F00A7980D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.4;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t84C3285E23B49E2F00A7980D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = FA8F6BA054A264013FFDBD91 /* Pods-BertQA-UIKit.debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tINFOPLIST_FILE = ViewInUIKit/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"org.tensorflow.BertQA-UIKit\";\n\t\t\t\tPRODUCT_NAME = \"BertQA-UIKit\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t84C3285F23B49E2F00A7980D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 67EE9FD8BFDA0BBBC2A8C538 /* Pods-BertQA-UIKit.release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tINFOPLIST_FILE = ViewInUIKit/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"org.tensorflow.BertQA-UIKit\";\n\t\t\t\tPRODUCT_NAME = \"BertQA-UIKit\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t840245342424CB64002D1DAD /* Build configuration list for PBXNativeTarget \"BertQA-SwiftUI\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t840245352424CB64002D1DAD /* Debug */,\n\t\t\t\t840245362424CB64002D1DAD /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t849401DD244462AB005F0C25 /* Build configuration list for PBXNativeTarget \"BertQA-Tests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t849401DE244462AB005F0C25 /* Debug */,\n\t\t\t\t849401DF244462AB005F0C25 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t84C3284623B49E2E00A7980D /* Build configuration list for PBXProject \"BertQA\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t84C3285B23B49E2F00A7980D /* Debug */,\n\t\t\t\t84C3285C23B49E2F00A7980D /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t84C3285D23B49E2F00A7980D /* Build configuration list for PBXNativeTarget \"BertQA-UIKit\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t84C3285E23B49E2F00A7980D /* Debug */,\n\t\t\t\t84C3285F23B49E2F00A7980D /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 84C3284323B49E2E00A7980D /* Project object */;\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQACore/Constants.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\nenum InterpreterOptions {\n  // Default thread count is 2, unless maximum thread count is 1.\n  static let threadCount = (\n    defaultValue: 2,\n    minimumValue: 1,\n    maximumValue: Int(ProcessInfo.processInfo.activeProcessorCount),\n    id: \"threadCount\"\n  )\n}\n\nenum MobileBERT {\n  static let maxAnsLen = 32\n  static let maxQueryLen = 64\n  static let maxSeqLen = 384\n\n  static let predictAnsNum = 5\n  static let outputOffset = 1  // Need to shift 1 for outputs ([CLS])\n\n  static let doLowerCase = true\n\n  static let inputDimension = [1, MobileBERT.maxSeqLen]\n  static let outputDimension = [1, MobileBERT.maxSeqLen]\n\n  static let dataset = File(name: \"contents_from_squad_dict_format\", ext: \"json\")\n  static let vocabulary = File(name: \"vocab\", ext: \"txt\")\n  static let model = File(name: \"mobilebert_float_20191023\", ext: \"tflite\")\n}\n\nstruct File {\n  let name: String\n  let ext: String\n  let description: String\n\n  init(name: String, ext: String) {\n    self.name = name\n    self.ext = ext\n    self.description = \"\\(name).\\(ext)\"\n  }\n}\n\nenum CustomUI {\n  static let textHighlightColor = UIColor(red: 1.0, green: 0.7, blue: 0.0, alpha: 0.3)\n\n  static let runButtonOpacity = 0.8\n\n  static let statusTextViewCornerRadius = CGFloat(7)\n  static let suggestedQuestionCornerRadius = CGFloat(10)\n\n  static let keyboardAnimationDuration = 0.23\n\n  static let stackSpacing = CGFloat(5)\n  static let padding = CGFloat(5)\n  static let contentViewPadding = CGFloat(7)\n  static let controlViewPadding = CGFloat(10)\n  static let textSidePadding = CGFloat(4)\n  static let textPadding = CGFloat(3)\n\n  static let statusFontSize = CGFloat(14)\n}\n\nenum StatusMessage {\n  static let askRun = \"Tap ▶︎ button to get the answer.\"\n  static let warnEmptyQuery = \"⚠️Got empty question.\\nPlease enter non-empty question.\"\n  static let inferenceFailError = \"❗️Failed to inference the answer.\"\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQACore/Extensions/DataExtension.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n\nimport Foundation\nimport TensorFlowLite\n\n// MARK: - Data extension\nextension Data {\n  /// Creates a new buffer by copying the buffer pointer of the given array.\n  ///\n  /// - Warning: The given array's element type `T` must be trivial in that it can be copied bit\n  ///     for bit with no indirection or reference-counting operations; otherwise, reinterpreting\n  ///     data from the resulting buffer has undefined behavior.\n  /// - Parameter array: An array with elements of type `T`.\n  init<T>(copyingBufferOf array: [T]) {\n    self = array.withUnsafeBufferPointer(Data.init)\n  }\n\n  /// Convert a Data instance to Array representation.\n  func toArray<T>(type: T.Type) -> [T] where T: AdditiveArithmetic {\n    var array = [T](repeating: T.zero, count: self.count / MemoryLayout<T>.stride)\n    _ = array.withUnsafeMutableBytes { self.copyBytes(to: $0) }\n    return array\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQACore/Extensions/StringExtension.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Foundation\nimport os\n\n/// Helper functions used for tokenizing.\nextension String {\n  /// Performs invalid character removal and whitespace cleanup on text.\n  ///\n  /// Replaces all whitespace code points with spaces and control characters including \\t, \\n, \\r.\n  ///\n  /// - Returns: Cleaned text.\n  func cleaned() -> String {\n    return String(\n      // Normalize string to NFC(Normalization Form Canonical Composition).\n      self.precomposedStringWithCanonicalMapping\n        .unicodeScalars.compactMap { unicodeScalar in\n          if unicodeScalar.isWhitespaceForBert {\n            return \" \"\n          } else if !unicodeScalar.isControlForBert && !unicodeScalar.shouldBeRemovedForBert {\n            return Character(unicodeScalar)\n          }\n          return nil\n        })\n\n  }\n\n  /// Splits this string on whitespace.\n  func splitByWhitespace() -> [String] {\n    // Normalize string to NFC(Normalization Form Canonical Composition).\n    return self.precomposedStringWithCanonicalMapping\n      .unicodeScalars.split { $0.isWhitespaceForBert }.map { String($0) }\n  }\n\n  /// Tokenizes this string into word and punctuation tokens.\n  ///\n  /// For example:\n  /// ```\n  /// input: \"Hi,there.\"\n  /// output: [\"Hi\", \",\", \"there\", \".\"]\n  /// ```\n  /// ```\n  /// input: \"Hi, there.\\n\"\n  /// output: [\"Hi\", \",\", \" there\", \".\", \"\\n\"]\n  /// ```\n  func tokenizedWithPunctuation() -> [String] {\n    var tokens = [String]()\n    var currentToken = \"\"\n    // Normalize string to NFC(Normalization Form Canonical Composition).\n    self.precomposedStringWithCanonicalMapping\n      .unicodeScalars.forEach { unicode in\n        if unicode.isPunctuationForBert {\n          if !currentToken.isEmpty {\n            // Add current token before the punctuation mark to the list of tokens.\n            tokens.append(currentToken)\n          }\n          tokens.append(String(unicode))\n          currentToken = \"\"\n        } else {\n          // As it is not a punctuation mark, keep building current token.\n          currentToken += String(unicode)\n        }\n      }\n\n    if !currentToken.isEmpty {\n      tokens.append(currentToken)\n    }\n    return tokens\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQACore/Extensions/UnicodeScalarExtension.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Foundation\n\n/// Provides some functions that make it easy to classify the character.\nextension UnicodeScalar {\n  /// Whether `self` is a whitespace character.\n  ///\n  /// \\t, \\n, and \\r are technically control characters but we treat them as whitespace since they\n  /// are generally considered as such.\n  var isWhitespaceForBert: Bool {\n    switch self {\n    case \" \", \"\\t\", \"\\n\", \"\\r\":\n      return true\n    default:\n      return properties.generalCategory == .spaceSeparator\n    }\n  }\n\n  /// Whether `self` is a control character.\n  var isControlForBert: Bool {\n    // These are technically control characters but we count them as whitespace characters.\n    if isWhitespaceForBert {\n      return false\n    }\n\n    switch properties.generalCategory {\n    case .control, .format: return true\n    default: return false\n    }\n  }\n\n  /// Whether `self` should be removed for Bert tokenization.\n  var shouldBeRemovedForBert: Bool {\n    return self == UnicodeScalar(0) || self == UnicodeScalar(0xfffd)\n  }\n\n  /// Whether `self` is a punctuation character.\n  ///\n  /// We treat all non-letter/number ASCII as punctuation, except ASCII character 0 to 32.\n  /// Characters such as \"^\", \"$\", and \"`\" are not in the Unicode Punctuation class but we treat\n  /// them as punctuation anyways, for consistency.\n  var isPunctuationForBert: Bool {\n    if isASCII && value > 32 && !properties.isAlphabetic && properties.numericType == nil {\n      return true\n    }\n    switch properties.generalCategory {\n    case .closePunctuation,\n      .connectorPunctuation,\n      .dashPunctuation,\n      .finalPunctuation,\n      .initialPunctuation,\n      .openPunctuation,\n      .otherPunctuation:\n      return true\n    default:\n      return false\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQACore/Models/Dataset.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n/// Data set to run the TensorFlow Lite model.\nstruct Dataset: Decodable {\n  let title: String\n  let content: String\n  let questions: [String]\n\n  /// Wrapper to decode json file into `Decodable` struct.\n  static func load<T: Decodable>(_ file: File = MobileBERT.dataset) -> T {\n    let data: Data\n\n    guard let fileUrl = Bundle.main.url(forResource: file.name, withExtension: file.ext)\n    else {\n      fatalError(\"Couldn't find \\(file.description) in main bundle.\")\n    }\n\n    do {\n      data = try Data(contentsOf: fileUrl)\n    } catch {\n      fatalError(\"Couldn't load \\(file.description) from main bundle:\\n\\(error)\")\n    }\n\n    do {\n      let decoder = JSONDecoder()\n      return try decoder.decode(T.self, from: data)\n    } catch {\n      fatalError(\"Couldn't parse \\(file.description) as \\(T.self):\\n\\(error)\")\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQACore/Models/FileLoader.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Foundation\nimport os\n\nclass FileLoader {\n  /// Loads a vocabulary file into a dictionary of vocabulary to its ID.\n  ///\n  /// - Parameter file: `File` of a vocabulary.\n  /// - Returns: Vocabulary IDs from given `file` data.\n  static func loadVocabularies(from file: File) -> [String: Int32] {\n    guard\n      let path = Bundle(for: FileLoader.self).path(forResource: file.name, ofType: file.ext)\n    else {\n      fatalError(\"Cannot read the file: \\(file.description)\")\n    }\n\n    var vocabularyIDs = [String: Int32]()\n    do {\n      let data = try String(contentsOfFile: path, encoding: .utf8)\n      for (index, string) in data.components(separatedBy: .newlines).enumerated() {\n        vocabularyIDs[string] = Int32(index)\n      }\n    } catch {\n      os_log(\"%s\", type: .error, error.localizedDescription)\n    }\n    return vocabularyIDs\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQACore/Models/ML/BertQAHandler.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Foundation\nimport TensorFlowLite\nimport os\n\n/// Handles Bert model with TensorFlow Lite.\nfinal class BertQAHandler {\n  private let interpreter: Interpreter\n\n  private let dic: [String: Int32]\n  private let tokenizer: FullTokenizer\n\n  init(\n    with modelFile: File = MobileBERT.model,\n    threadCount: Int = InterpreterOptions.threadCount.defaultValue\n  ) throws {\n    os_log(\n      \"[BertQAHandler] Initialize interpreter with %d thread(s).\", threadCount)\n    // Load dictionary from vocabulary file.\n    dic = FileLoader.loadVocabularies(from: MobileBERT.vocabulary)\n\n    // Initialize `FullTokenizer` with given `dic`.\n    tokenizer = FullTokenizer(with: dic, isCaseInsensitive: MobileBERT.doLowerCase)\n\n    // Construct the path to the model file.\n    guard\n      let modelPath = Bundle(for: type(of: self)).path(\n        forResource: modelFile.name,\n        ofType: modelFile.ext)\n    else {\n      fatalError(\"Failed to load the model file: \\(modelFile.description)\")\n    }\n\n    // Specify the options for the `Interpreter`.\n    var options = Interpreter.Options()\n    options.threadCount = threadCount\n\n    // Create the `Interpreter`.\n    interpreter = try Interpreter(modelPath: modelPath, options: options)\n\n    // Initialize input and output `Tensor`s.\n    try interpreter.allocateTensors()\n\n    // Get allocated input `Tensor`s.\n    let inputIdsTensor: Tensor\n    let inputMaskTensor: Tensor\n    let segmentIdsTensor: Tensor\n\n    inputIdsTensor = try interpreter.input(at: 0)\n    inputMaskTensor = try interpreter.input(at: 1)\n    segmentIdsTensor = try interpreter.input(at: 2)\n\n    // Get allocated output `Tensor`s.\n    let endLogitsTensor: Tensor\n    let startLogitsTensor: Tensor\n\n    endLogitsTensor = try interpreter.output(at: 0)\n    startLogitsTensor = try interpreter.output(at: 1)\n\n    // Check if input and output `Tensor`s are in the expected formats.\n    guard\n      inputIdsTensor.shape.dimensions == MobileBERT.inputDimension\n        && inputMaskTensor.shape.dimensions == MobileBERT.inputDimension\n        && segmentIdsTensor.shape.dimensions == MobileBERT.inputDimension\n    else {\n      fatalError(\"Unexpected model: Input Tensor shape\")\n    }\n\n    guard\n      endLogitsTensor.shape.dimensions == MobileBERT.outputDimension\n        && startLogitsTensor.shape.dimensions == MobileBERT.outputDimension\n    else {\n      fatalError(\"Unexpected model: Output Tensor shape\")\n    }\n  }\n\n  func run(query: String, content: String) -> Result? {\n    // MARK: - Preprocessing\n    let (features, contentData) = preprocessing(query: query, content: content)\n\n    // Convert input arrays to `Data` type.\n    os_log(\"[BertQAHandler] Setting inputs..\")\n    let inputIdsData = Data(copyingBufferOf: features.inputIds)\n    let inputMaskData = Data(copyingBufferOf: features.inputMask)\n    let segmentIdsData = Data(copyingBufferOf: features.segmentIds)\n\n    // MARK: - Inferencing\n    let inferenceStartTime = Date()\n\n    let endLogitsTensor: Tensor\n    let startLogitsTensor: Tensor\n\n    do {\n      // Assign input `Data` to the `interpreter`.\n      try interpreter.copy(inputIdsData, toInputAt: 0)\n      try interpreter.copy(inputMaskData, toInputAt: 1)\n      try interpreter.copy(segmentIdsData, toInputAt: 2)\n\n      // Run inference by invoking the `Interpreter`.\n      os_log(\"[BertQAHandler] Runing inference..\")\n      try interpreter.invoke()\n\n      // Get the output `Tensor` to process the inference results\n      endLogitsTensor = try interpreter.output(at: 0)\n      startLogitsTensor = try interpreter.output(at: 1)\n    } catch let error {\n      os_log(\n        \"[BertQAHandler] Failed to invoke the interpreter with error: %s\",\n        type: .error,\n        error.localizedDescription)\n      return nil\n    }\n\n    let inferenceTime = Date().timeIntervalSince(inferenceStartTime) * 1000\n\n    // MARK: - Postprocessing\n    os_log(\"[BertQAHandler] Getting answer..\")\n    let answers = postprocessing(\n      startLogits: startLogitsTensor.data.toArray(type: Float32.self),\n      endLogits: endLogitsTensor.data.toArray(type: Float32.self),\n      contentData: contentData)\n    os_log(\"[BertQAHandler] Finished.\")\n\n    guard let answer = answers.first else {\n      return nil\n    }\n    return Result(answer: answer, inferenceTime: inferenceTime)\n  }\n\n  // MARK: - Private functions\n\n  /// Tokenizes input query and content to `InputFeatures` and make `ContentData` to find the\n  /// original string in the content.\n  ///\n  /// - Parameters:\n  ///   - query: Input query to run the model.\n  ///   - content: Input content to run the model.\n  /// - Returns: A tuple of `InputFeatures` and `ContentData`.\n  private func preprocessing(query: String, content: String) -> (InputFeatures, ContentData) {\n    var queryTokens = tokenizer.tokenize(query)\n    queryTokens = Array(queryTokens.prefix(MobileBERT.maxQueryLen))\n\n    let contentWords = content.splitByWhitespace()\n    var contentTokenIdxToWordIdxMapping = [Int]()\n    var contentTokens = [String]()\n    for (i, token) in contentWords.enumerated() {\n      tokenizer.tokenize(token).forEach { subToken in\n        contentTokenIdxToWordIdxMapping.append(i)\n        contentTokens.append(subToken)\n      }\n    }\n\n    // -3 accounts for [CLS], [SEP] and [SEP].\n    let maxContentLen = MobileBERT.maxSeqLen - queryTokens.count - 3\n    contentTokens = Array(contentTokens.prefix(maxContentLen))\n\n    var tokens = [String]()\n    var segmentIds = [Int32]()\n\n    // Map token index to original index (in feature.origTokens).\n    var tokenIdxToWordIdxMapping = [Int: Int]()\n\n    // Start of generating the `InputFeatures`.\n    tokens.append(\"[CLS]\")\n    segmentIds.append(0)\n\n    // For query input.\n    queryTokens.forEach {\n      tokens.append($0)\n      segmentIds.append(0)\n    }\n\n    // For separation.\n    tokens.append(\"[SEP]\")\n    segmentIds.append(0)\n\n    // For text input.\n    for (i, docToken) in contentTokens.enumerated() {\n      tokens.append(docToken)\n      segmentIds.append(1)\n      tokenIdxToWordIdxMapping[tokens.count] = contentTokenIdxToWordIdxMapping[i]\n    }\n\n    // For ending mark.\n    tokens.append(\"[SEP]\")\n    segmentIds.append(1)\n\n    var inputIds = tokenizer.convertToIDs(tokens: tokens)\n    var inputMask = [Int32](repeating: 1, count: inputIds.count)\n\n    while inputIds.count < MobileBERT.maxSeqLen {\n      inputIds.append(0)\n      inputMask.append(0)\n      segmentIds.append(0)\n    }\n\n    let inputFeatures = InputFeatures(\n      inputIds: inputIds,\n      inputMask: inputMask,\n      segmentIds: segmentIds)\n    let contentData = ContentData(\n      contentWords: contentWords,\n      tokenIdxToWordIdxMapping: tokenIdxToWordIdxMapping,\n      originalContent: content)\n    return (inputFeatures, contentData)\n  }\n\n  /// Get a list of most possible `Answer`s up to `Model.predictAnsNum`.\n  ///\n  /// - Parameters:\n  ///   - startLogits: List of `Logit` if the index can be a start token index of an answer.\n  ///   - endLogits: List of `Logit` if the index can be a end token index of an answer.\n  ///   - features: `InputFeatures` used to run the model.\n  /// - Returns: List of `Answer`s.\n  private func postprocessing(\n    startLogits: [Float],\n    endLogits: [Float],\n    contentData: ContentData\n  ) -> [Answer] {\n    // Get the candidate start/end indexes of answer from `startLogits` and `endLogits`.\n    let startIndexes = candidateAnswerIndexes(from: startLogits)\n    let endIndexes = candidateAnswerIndexes(from: endLogits)\n\n    // Make list which stores prediction and its range to find original results and filter invalid\n    // pairs.\n    let candidates: [Prediction] = startIndexes.flatMap { start in\n      endIndexes.compactMap { end -> Prediction? in\n        // Initialize logit struct with given indexes.\n        guard\n          let prediction = Prediction(\n            logit: startLogits[start] + endLogits[end],\n            start: start, end: end,\n            tokenIdxToWordIdxMapping: contentData.tokenIdxToWordIdxMapping)\n        else {\n          return nil\n        }\n        return prediction\n      }\n    }.sorted { $0.logit > $1.logit }\n\n    // Extract firstmost `Model.predictAnsNum` of predictions and calculate score from logits array\n    // with softmax.\n    let scores = softmaxed(Array(candidates.prefix(MobileBERT.predictAnsNum)))\n\n    // Return answer list.\n    return scores.compactMap { score in\n      guard\n        let excerpted = contentData.excerptWords(from: score.range)\n      else {\n        return nil\n      }\n      return Answer(text: excerpted, score: score)\n    }\n  }\n\n  /// Get the `Model.prediectAnsNum` number of indexes of the candidate answers from given logit\n  /// list.\n  ///\n  /// - Parameter from: The array of logits.\n  /// - Returns: `Model.predictAnsNum` number of indexes.\n  private func candidateAnswerIndexes(from logits: [Float]) -> [Int] {\n    return logits.prefix(MobileBERT.maxSeqLen)\n      .enumerated()\n      .sorted { $0.element > $1.element }\n      .prefix(MobileBERT.predictAnsNum)\n      .map { $0.offset }\n  }\n\n  /// Compute softmax probability score over raw logits.\n  ///\n  /// - Parameter predictions: Array of logit and it range sorted by the logit value in decreasing\n  ///   order.\n  private func softmaxed(_ predictions: [Prediction]) -> [Score] {\n    // Find maximum logit value.\n    guard let maximumLogit = predictions.first?.logit else {\n      return []\n    }\n\n    // Calculate numerator array of the softmaxed values and its sum.\n    let numerators: [(Float, Prediction)] = predictions.map { prediction in\n      let numerator = exp(prediction.logit - maximumLogit)\n      return (numerator, prediction)\n    }\n\n    let sum: Float = numerators.reduce(0) { $0 + $1.0 }\n\n    return numerators.compactMap { (numerator, prediction) in\n      Score(value: numerator / sum, range: prediction.wordRange, logit: prediction.logit)\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQACore/Models/ML/ContentData.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Foundation\n\n/// Manages content data to excerpt answer string with a token index.\nstruct ContentData {\n  let contentWords: [String]\n  let tokenIdxToWordIdxMapping: [Int: Int]\n  let originalContent: String\n\n  /// Find and excerpt the original string with given index.\n  ///\n  /// - Parameters:\n  ///   - feature: `InputFeature` of the query.\n  ///   - range: Range of token ID of the string to find.\n  /// - Returns: Returns the original string with given range. `nil` if  the index is not correct.\n  func excerptWords(from range: ClosedRange<Int>) -> Excerpt? {\n    let pattern: String = contentWords[range].map {\n      NSRegularExpression.escapedPattern(for: $0)\n    }.joined(separator: \"\\\\s+\")\n\n    let compareOptions: NSString.CompareOptions\n    if range.count == 1 {\n      compareOptions = []\n    } else {\n      compareOptions = .regularExpression\n    }\n\n    guard\n      let range = originalContent.range(of: pattern, options: compareOptions)\n    else {\n      return nil\n    }\n    return Excerpt(value: String(originalContent[range]), range: range)\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQACore/Models/ML/InputFeatures.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Foundation\n\n/// Stores input features for BERT model.\nstruct InputFeatures {\n  let inputIds: [Int32]\n  let inputMask: [Int32]\n  let segmentIds: [Int32]\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQACore/Models/ML/Result.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Foundation\n\nstruct Result {\n  let answer: Answer\n  let inferenceTime: Double\n  var description: String {\n    \"\"\"\n    Inference time: \\(String(format: \"%.2lf ms\", inferenceTime))\n    Score: \\(String(format: \"%.2lf\", answer.score.value))\n    \"\"\"\n  }\n}\n\nstruct Answer {\n  let text: Excerpt\n  let score: Score\n}\n\n/// Stores exceprted text and its range from the original text.\nstruct Excerpt {\n  let value: String\n  let range: Range<String.Index>\n}\n\n/// Stores probability score of given range of words in the original content.\nstruct Score {\n  let value: Float\n  /// Score's range of original word.\n  let range: ClosedRange<Int>\n  /// Logit value of this score.\n  let logit: Float\n}\n\n/// Stores logit value and its range in token and word list.\nstruct Prediction {\n  /// Logit value.\n  let logit: Float\n  /// Logit's range of result token.\n  let tokenRange: ClosedRange<Int>\n  /// Logit's range of original word.\n  let wordRange: ClosedRange<Int>\n\n  init?(logit: Float, start: Int, end: Int, tokenIdxToWordIdxMapping: [Int: Int]) {\n    self.logit = logit\n    guard start <= end else { return nil }\n    self.tokenRange = start...end\n\n    guard\n      let wordRange = Prediction.convert(from: tokenRange, with: tokenIdxToWordIdxMapping)\n    else {\n      return nil\n    }\n    self.wordRange = wordRange\n  }\n\n  private static func convert(from tokenRange: ClosedRange<Int>, with map: [Int: Int])\n    -> ClosedRange<Int>?\n  {\n    guard\n      tokenRange.count <= MobileBERT.maxAnsLen,\n      let start = tokenRange.first,\n      let end = tokenRange.last,\n      let startIndex = map[start + MobileBERT.outputOffset],\n      let endIndex = map[end + MobileBERT.outputOffset]\n    else {\n      return nil\n    }\n\n    guard startIndex <= endIndex else { return nil }\n    return startIndex...endIndex\n  }\n}\n\nextension Prediction: Equatable {\n  static func == (lhs: Prediction, rhs: Prediction) -> Bool {\n    return lhs.logit == rhs.logit && lhs.tokenRange == rhs.tokenRange\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQACore/Models/Tokenizers/BasicTokenizer.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Foundation\n\n/// Runs basic tokenization such as punctuation spliting, lower casing.\n///\n/// Name of functions and variables are from\n/// [google-research/bert]( https://github.com/google-research/bert/blob/d66a146741588fb208450bde15aa7db143baaa69/tokenization.py#L185).\nstruct BasicTokenizer {\n  let isCaseInsensitive: Bool\n\n  /// Constructs a BasicTokenizer.\n  ///\n  /// - Parameter isCaseInsensitive: Whether to lower case the input.\n  init(isCaseInsensitive: Bool) {\n    self.isCaseInsensitive = isCaseInsensitive\n  }\n\n  /// Tokenizes a piece of text.\n  ///\n  /// - Parameter text: Text to be tokenized.\n  func tokenize(_ text: String) -> [String] {\n    var cleanedText = text.cleaned()\n    if isCaseInsensitive {\n      cleanedText = cleanedText.lowercased()\n    }\n\n    return cleanedText.splitByWhitespace().flatMap { $0.tokenizedWithPunctuation() }\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQACore/Models/Tokenizers/FullTokenizer.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Foundation\n\n/// Runs end-to-end tokenization.\n///\n/// Name of functions and variables are from\n/// [google-research/bert](https://github.com/google-research/bert/blob/d66a146741588fb208450bde15aa7db143baaa69/tokenization.py#L161).\nclass FullTokenizer {\n  let basicTokenizer: BasicTokenizer\n  let wordpieceTokenizer: WordpieceTokenizer\n  /// A mapping of strings to IDs, where the IDs come from\n  let vocabularyIDs: [String: Int32]\n\n  /// Initialize `Fulltokenizer`.\n  /// - Parameters\n  ///   - vocabularyIDs: A mapping of strings to IDs, where the IDs come from the number order of\n  ///   vocabulary from the vocabulary file.\n  ///   - isCaseInsensitive: `true` if the tokenizer ignores the case.\n  init(with vocabularyIDs: [String: Int32], isCaseInsensitive: Bool) {\n    self.vocabularyIDs = vocabularyIDs\n    basicTokenizer = BasicTokenizer(isCaseInsensitive: isCaseInsensitive)\n    wordpieceTokenizer = WordpieceTokenizer(with: vocabularyIDs)\n  }\n\n  func tokenize(_ text: String) -> [String] {\n    return basicTokenizer.tokenize(text).flatMap { wordpieceTokenizer.tokenize($0) }\n  }\n\n  func convertToIDs(tokens: [String]) -> [Int32] {\n    return tokens.compactMap { vocabularyIDs[$0] }\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQACore/Models/Tokenizers/WordpieceTokenizer.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Foundation\n\n/// Runs WordPiece tokenziation.\n///\n/// Name of functions and variables are from\n/// [google-research/bert](https://github.com/google-research/bert/blob/d66a146741588fb208450bde15aa7db143baaa69/tokenization.py#L300).\nstruct WordpieceTokenizer {\n  let vocabularyIDs: [String: Int32]\n\n  private static let UNKNOWN_TOKEN = \"[UNK]\"  // For unknown words.\n  private static let MAX_INPUT_CHARS_PER_WORD = 200\n\n  init(with vocaburalyID: [String: Int32]) {\n    self.vocabularyIDs = vocaburalyID\n  }\n\n  /// Tokenizes a piece of text into its word pieces.\n  ///\n  /// This uses a greedy longest-match-first algorithm to perform tokenization using the given\n  /// vocabulary.\n  ///\n  /// For example:\n  ///   ```\n  ///   input = \"unaffable\"\n  ///   output = [\"un\", \"##aff\", \"##able\"]\n  ///   ```\n  ///\n  ///   ```\n  ///   input = \"unaffableX\"\n  ///   output = [\"[UNK]\"]\n  ///   ```\n  ///\n  /// - Parameter text: A single token or whitespace separated tokens. This should have already been\n  ///   passed through `BasicTokenizer.\n  /// - Returns: A list of wordpiece tokens.\n  func tokenize(_ text: String) -> [String] {\n    var outputTokens = [String]()\n    text.splitByWhitespace().forEach { token in\n      if token.count > WordpieceTokenizer.MAX_INPUT_CHARS_PER_WORD {\n        outputTokens.append(WordpieceTokenizer.UNKNOWN_TOKEN)\n        return\n      }\n\n      var start = token.startIndex\n      var subWords = [String]()\n\n      // Find all subwords in `token`.\n      while start < token.endIndex {\n        var end = token.endIndex\n        var hasFound = false\n\n        // Find longest known subword in the `token` from its `start` index.\n        while start < end {\n          var subStr = String(token[start..<end])\n          if start > token.startIndex {\n            subStr = \"##\" + subStr\n          }\n\n          if vocabularyIDs[subStr] != nil {\n            // `subStr` is the longest subword that can be found.\n            hasFound = true\n            subWords.append(subStr)\n            break\n          } else {\n            end = token.index(before: end)\n          }\n        }\n\n        if hasFound {\n          // Proceed to tokenize the residual string.\n          start = end\n        } else {\n          // The `token` contains unknown subwords. It can't be tokenized into subwords.\n          subWords = [WordpieceTokenizer.UNKNOWN_TOKEN]\n          break\n        }\n      }\n      outputTokens.append(contentsOf: subWords)\n    }\n    return outputTokens\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQATests/ExtensionTest/StringExtensionTest.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport XCTest\n\n@testable import BertQA_UIKit\n\nclass StringExtensionTest: XCTestCase {\n  func testCleaned() {\n    let testExample1 = \"This is an\\rexample.\\n\"\n    let testExample2 = testExample1 + \"\\u{0}\"\n    let testExample3 = testExample1 + \"\\u{fffd}\"\n\n    let expectedResult = \"This is an example. \"\n\n    XCTAssertEqual(testExample1.cleaned(), expectedResult)\n    XCTAssertEqual(testExample2.cleaned(), expectedResult)\n    XCTAssertEqual(testExample3.cleaned(), expectedResult)\n  }\n\n  func testSplitByWhitespace() {\n    let testExample = \"Hi ,\\n This is an example. \"\n    let expectedResult = [\"Hi\", \",\", \"This\", \"is\", \"an\", \"example.\"]\n    let reversedResult = [String](expectedResult.reversed())\n\n    XCTAssertNotEqual(testExample.splitByWhitespace(), reversedResult)\n    XCTAssertEqual(testExample.splitByWhitespace(), expectedResult)\n    XCTAssertEqual(\"       \".splitByWhitespace(), [])\n    XCTAssertEqual(\"\".splitByWhitespace(), [])\n  }\n\n  func testTokenizedWithPunctuation() {\n    let testExample1 = \"Hi,there.\"\n    let expectedResult1 = [\"Hi\", \",\", \"there\", \".\"]\n    let reversedResult1 = [String](expectedResult1.reversed())\n\n    XCTAssertEqual(testExample1.tokenizedWithPunctuation(), expectedResult1)\n    XCTAssertNotEqual(testExample1.tokenizedWithPunctuation(), reversedResult1)\n\n    let testExample2 = \"I\\'m \\\"Spider-Man\\\"\"  // Input: I'm \"Spider-Man\"\n    let expectedResult2 = [\"I\", \"\\'\", \"m \", \"\\\"\", \"Spider\", \"-\", \"Man\", \"\\\"\"]\n    let reversedResult2 = [String](expectedResult2.reversed())\n\n    XCTAssertNotEqual(testExample2.tokenizedWithPunctuation(), reversedResult2)\n    XCTAssertEqual(testExample2.tokenizedWithPunctuation(), expectedResult2)\n    XCTAssertEqual(\"\".tokenizedWithPunctuation(), [])\n\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQATests/ExtensionTest/UnicodeScalarExtensionTest.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport XCTest\n\n@testable import BertQA_UIKit\n\nclass UnicodeScalarExtensionTest: XCTestCase {\n\n  func testIsWhitespaceForBert() {\n    XCTAssertTrue(UnicodeScalar(\" \").isWhitespaceForBert)\n    XCTAssertTrue(UnicodeScalar(\"\\t\").isWhitespaceForBert)\n    XCTAssertTrue(UnicodeScalar(\"\\r\").isWhitespaceForBert)\n    XCTAssertTrue(UnicodeScalar(\"\\n\").isWhitespaceForBert)\n    XCTAssertTrue(UnicodeScalar(0x00A0).isWhitespaceForBert)\n\n    XCTAssertFalse(UnicodeScalar(\"A\").isWhitespaceForBert)\n    XCTAssertFalse(UnicodeScalar(\"-\").isWhitespaceForBert)\n  }\n\n  func testIsControlForBert() {\n    XCTAssertTrue(UnicodeScalar(0x0005).isControlForBert)\n\n    XCTAssertFalse(UnicodeScalar(\"A\").isControlForBert)\n    XCTAssertFalse(UnicodeScalar(\" \").isControlForBert)\n    XCTAssertFalse(UnicodeScalar(\"\\t\").isControlForBert)\n    XCTAssertFalse(UnicodeScalar(\"\\r\").isControlForBert)\n    XCTAssertFalse(UnicodeScalar(\"\\u{1F4A9}\").isControlForBert)\n  }\n\n  func testIsPunctuationForBert() {\n    XCTAssertTrue(UnicodeScalar(\"-\").isPunctuationForBert)\n    XCTAssertTrue(UnicodeScalar(\"$\").isPunctuationForBert)\n    XCTAssertTrue(UnicodeScalar(\"`\").isPunctuationForBert)\n    XCTAssertTrue(UnicodeScalar(\".\").isPunctuationForBert)\n\n    XCTAssertFalse(UnicodeScalar(\"A\").isPunctuationForBert)\n    XCTAssertFalse(UnicodeScalar(\" \").isPunctuationForBert)\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQATests/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQATests/MLTest/BertQAHandlerTest.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport XCTest\n\n@testable import BertQA_UIKit\n\nclass BertQAHandlerTest: XCTestCase {\n  /// Test BertQA handler with ASCII code only content & question.\n  func testBertQAHandler() {\n    let bertQA: BertQAHandler\n    do {\n      bertQA = try BertQAHandler()\n\n      let content =\n        \"TensorFlow is a free and open-source software library for dataflow and \"\n        + \"differentiable programming across a range of tasks. It is a symbolic math library, and \"\n        + \"is also used for machine learning applications such as neural networks. It is used for \"\n        + \"both research and production at Google. TensorFlow was developed by the Google Brain \"\n        + \"team for internal Google use. It was released under the Apache License 2.0 on November \"\n        + \"9, 2015.\"\n\n      let question1 = \"What is TensorFlow\"\n      let answer1 =\n        \"a free and open-source software library for dataflow and differentiable \"\n        + \"programming across a range of tasks\"\n      if let result1 = bertQA.run(query: question1, content: content) {\n        XCTAssert(result1.answer.text.value.contains(answer1))\n      } else {\n        XCTFail(\"Failed to run BertQA with \\(question1).\")\n      }\n\n      let question2 = \"Who developed TensorFlow?\"\n      let answer2 = \"Google Brain team\"\n      if let result2 = bertQA.run(query: question2, content: content) {\n        XCTAssert(result2.answer.text.value.contains(answer2))\n      } else {\n        XCTFail(\"Failed to run BertQA with \\(question2).\")\n      }\n\n      let question3 = \"When was TensorFlow released?\"\n      let answer3 = \"November 9, 2015\"\n      if let result3 = bertQA.run(query: question3, content: content) {\n        XCTAssert(result3.answer.text.value.contains(answer3))\n      } else {\n        XCTFail(\"Failed to run BertQA with \\(question3).\")\n      }\n\n      let question4 = \"What is TensorFlow used for?\"\n      let answer4 =\n        \"symbolic math library, and is also used for machine learning applications such as neural \"\n        + \"networks\"\n      if let result4 = bertQA.run(query: question4, content: content) {\n        XCTAssert(result4.answer.text.value.contains(answer4))\n      } else {\n        XCTFail(\"Failed to run BertQA with \\(question4).\")\n      }\n\n      let question5 = \"How is TensorFlow used in Google?\"\n      let answer5 = \"both research and production\"\n      if let result5 = bertQA.run(query: question5, content: content) {\n        XCTAssert(result5.answer.text.value.contains(answer5))\n      } else {\n        XCTFail(\"Failed to run BertQA with \\(question5).\")\n      }\n\n      let question6 = \"Which license does TensorFlow use?\"\n      let answer6 = \"Apache License 2.0\"\n      if let result6 = bertQA.run(query: question6, content: content) {\n        XCTAssert(result6.answer.text.value.contains(answer6))\n      } else {\n        XCTFail(\"Failed to run BertQA with \\(question6).\")\n      }\n\n    } catch let error {\n      XCTFail(error.localizedDescription)\n    }\n  }\n\n  /// Test BertQA handler with a content & question including unicode.\n  func testBertQAHandlerWithUnicode() {\n    let bertQA: BertQAHandler\n    do {\n      bertQA = try BertQAHandler()\n\n      let content =\n        \"Nikola Tesla (Serbian Cyrillic: \\u{041d}\\u{0438}\\u{043a}\\u{043e}\\u{043b}\"\n        + \"\\u{0430} \\u{0422}\\u{0435}\\u{0441}\\u{043b}\\u{0430}; 10 July 1856 \\u{2013} 7 January 1943) \"\n        + \"was a Serbian American inventor, electrical engineer, mechanical engineer, physicist, and \"\n        + \"futurist best known for his contributions to the design of the modern alternating current \"\n        + \"(AC) electricity supply system.\"\n\n      let question1 = \"What is Tesla's home country?\"\n      let answer1 = \"Serbian\"\n      if let result1 = bertQA.run(query: question1, content: content) {\n        XCTAssert(result1.answer.text.value.contains(answer1))\n      } else {\n        XCTFail()\n        XCTFail(\"Failed to run BertQA with \\(question1).\")\n      }\n\n      let question2 = \"What was Nikola Tesla's ethnicity?\"\n      let answer2 = \"Serbian\"\n      if let result2 = bertQA.run(query: question2, content: content) {\n        XCTAssert(result2.answer.text.value.contains(answer2))\n      } else {\n        XCTFail(\"Failed to run BertQA with \\(question2).\")\n      }\n\n      let question3 = \"What does AC stand for?\"\n      let answer3 = \"alternating current\"\n      if let result3 = bertQA.run(query: question3, content: content) {\n        XCTAssert(result3.answer.text.value.contains(answer3))\n      } else {\n        XCTFail(\"Failed to run BertQA with \\(question3).\")\n      }\n\n      let question4 = \"When was Tesla born?\"\n      let answer4 = \"10 July 1856\"\n      if let result4 = bertQA.run(query: question4, content: content) {\n        XCTAssert(result4.answer.text.value.contains(answer4))\n      } else {\n        XCTFail(\"Failed to run BertQA with \\(question4).\")\n      }\n\n      let question5 = \"In what year did Tesla die?\"\n      let answer5 = \"1943\"\n      if let result5 = bertQA.run(query: question5, content: content) {\n        XCTAssert(result5.answer.text.value.contains(answer5))\n      } else {\n        XCTFail(\"Failed to run BertQA with \\(question5).\")\n      }\n\n    } catch let error {\n      XCTFail(error.localizedDescription)\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQATests/TokenizerTest/BasicTokenizerTest.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport XCTest\n\n@testable import BertQA_UIKit\n\nclass BasicTokenizerTest: XCTestCase {\n  func testTokenize() {\n    let tokenizer = BasicTokenizer(isCaseInsensitive: false)\n    let testInput1 = \"  Hi, This\\tis an example.\\n\"\n    let expectedResult1 = [\"Hi\", \",\", \"This\", \"is\", \"an\", \"example\", \".\"]\n\n    XCTAssertEqual(tokenizer.tokenize(testInput1), expectedResult1)\n\n    let testInput2 = \"Hello,How are you?\"\n    let expectedResult2 = [\"Hello\", \",\", \"How\", \"are\", \"you\", \"?\"]\n\n    XCTAssertEqual(tokenizer.tokenize(testInput2), expectedResult2)\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQATests/TokenizerTest/FullTokenizerTest.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport XCTest\nimport os\n\n@testable import BertQA_UIKit\n\nclass FullTokenizerTest: XCTestCase {\n  func testTokenize() {\n    let vocabularyIDs = FileLoader.loadVocabularies(from: MobileBERT.vocabulary)\n    let tokenizer = FullTokenizer(with: vocabularyIDs, isCaseInsensitive: true)\n    XCTAssertEqual(tokenizer.tokenize(\"\"), [])\n\n    let testInput1 = \"Good morning, I'm your teacher.\\n\"\n    let expectedResult1 = [\"good\", \"morning\", \",\", \"i\", \"'\", \"m\", \"your\", \"teacher\", \".\"]\n    XCTAssertEqual(tokenizer.tokenize(testInput1), expectedResult1)\n\n    let testInput2 = \"Nikola Tesla\\t(Serbian Cyrillic: 10 July 1856 ~ 7 January 1943)\"\n    let expectedResult2 = [\n      \"nikola\", \"tesla\", \"(\", \"serbian\", \"cyrillic\", \":\", \"10\", \"july\", \"1856\", \"~\", \"7\", \"january\",\n      \"1943\", \")\",\n    ]\n    XCTAssertEqual(tokenizer.tokenize(testInput2), expectedResult2)\n  }\n\n  func testConvertTokensToIds() {\n    let vocabularyIDs = FileLoader.loadVocabularies(from: MobileBERT.vocabulary)\n    let tokenizer = FullTokenizer(with: vocabularyIDs, isCaseInsensitive: true)\n    let testInput = [\"good\", \"morning\", \",\", \"i\", \"'\", \"m\", \"your\", \"teacher\", \".\"]\n    let expectedResult: [Int32] = [2204, 2851, 1010, 1045, 1005, 1049, 2115, 3836, 1012]\n\n    XCTAssertEqual(tokenizer.convertToIDs(tokens: testInput), expectedResult)\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/BertQATests/TokenizerTest/WordpieceTokenizerTest.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport XCTest\n\n@testable import BertQA_UIKit\n\nclass WordpieceTokenizerTest: XCTestCase {\n  func testTokenize() {\n    let vocabularyIDs = FileLoader.loadVocabularies(from: MobileBERT.vocabulary)\n    let tokenizer = WordpieceTokenizer(with: vocabularyIDs)\n\n    XCTAssertEqual(tokenizer.tokenize(\"meaningfully\"), [\"meaningful\", \"##ly\"])\n    XCTAssertEqual(tokenizer.tokenize(\"teacher\"), [\"teacher\"])\n  }\n\n  func testTokenizerWithCustomvocabularyIDs() {\n    let vocab = [\"[UNK]\", \"[CLS]\", \"[SEP]\", \"want\", \"##want\", \"##ed\", \"wa\", \"un\", \"runn\", \"##ing\"]\n    var vocabularyIDs = [String: Int32]()\n    for (index, string) in vocab.enumerated() {\n      vocabularyIDs[string] = Int32(index)\n    }\n    let tokenizer = WordpieceTokenizer(with: vocabularyIDs)\n\n    XCTAssertEqual(tokenizer.tokenize(\"\"), [])\n    XCTAssertEqual(\n      tokenizer.tokenize(\"unwanted running\"), [\"un\", \"##want\", \"##ed\", \"runn\", \"##ing\"])\n    XCTAssertEqual(tokenizer.tokenize(\"unwantedX running\"), [\"[UNK]\", \"runn\", \"##ing\"])\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/Podfile",
    "content": "# Uncomment the next line to define a global platform for your project\nplatform :ios, '12.0'\n\ntarget 'BertQA-UIKit' do\n  # Comment the next line if you don't want to use dynamic frameworks\n  use_frameworks!\n\n  # Pods for BertQA-UIKit\n  pod 'TensorFlowLiteSwift', '~> 0.0.1-nightly'\nend\n\ntarget 'BertQA-SwiftUI' do\n  # Comment the next line if you don't want to use dynamic frameworks\n  use_frameworks!\n\n  # Pods for BertQA-SwiftUI\n  pod 'TensorFlowLiteSwift', '~> 0.0.1-nightly'\nend\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/README.md",
    "content": "# TensorFlow Lite BERT QA iOS Example Application\n\n![UIKit screencast]       | ![SwiftUI screencast]\n:-----------------------: | :-------------------------:\nUIKit version screen cast | SwiftUI version screen cast\n\n## Overview\n\nThis is an end-to-end example of [BERT] Question & Answer application built with\nTensorFlow 2.0, and tested on [SQuAD] dataset version 1.1. The demo app provides\n48 passages from the dataset for users to choose from, and gives 5 most possible\nanswers corresponding to the input passage and query.\n\nThis example includes two types of application and a test set, one application\nis developed with [UIKit], and the other is developed with [SwiftUI]. Each\napplication can be run with this XCode project, by choosing the target to build.\nEach application shares the core logic needed to run the BertQA model. The test\nset tests this core logic.\n\nQuestion input to the application is discarded after inference.\n\n### Model used\n\n[BERT], or Bidirectional Encoder Representations from Transformers, is a method\nof pre-training language representations which obtains state-of-the-art results\non a wide array of Natural Language Processing tasks.\n\nThis app uses MobileBERT, a compressed version of [BERT] that runs 4x faster and\nhas 4x smaller model size.\n\nFor more information, refer to the [BERT github page][BERT].\n\n### Preprocessing\n\nWhile preprocessing, it tokenizes input query and content with given vocabulary\ndata. Tokenization process mostly followed the [bert tokenization], except\nhandling Chinese characters as we don’t have passages written in Chinese.\n\nIt hands over the IDs of tokens with two other data. One is its segment ID to\nverify whether it is a question or a content. The other is a mask, which\nindicates if the given token is valid input to be processed or just a padding to\nfit the input tensor. As the model requires a fixed size of token ID array, some\nentries of the array could have invalid data.\n\n### Inference\n\nAssign preprocessed data to the input tensor and run the model. Output data is\nassigned to the output tensors as a result.\n\n### Postprocessing\n\nWhile postprocessing, it retrieves original string from the arrays of start &\nend logits in the output tensor. A logit of a token from `start` to `end` is\nderived from a summation of start logit array’s `start`th value and end logit\narray’s `end`th value. The higher sum of two logits, the more likely the token\nbetween starting point and end point could be an answer.\n\nAfter finding an answer, it retrieves the original string in the given range and\ncalculates the score. The score of the answer is calculated by the softmax\nfunction.\n\n## Requirements\n\n*   Xcode 11.0 or above\n\n*   Valid Apple Developer ID\n\n*   Real iOS device\n\n    Note: You can also use an iOS emulator, but some of the functionality may\n    not be fully supported.\n\n*   iOS version 12.0 or above\n\n*   Xcode command line tools (to install, `run xcode-select --install`)\n\n*   CocoaPods (to install, `run sudo gem install cocoapods`)\n\n## Build and run\n\n1.  Clone the TensorFlow examples GitHub repository to your computer to get the\n    demo application: `git clone https://github.com/tensorflow/examples`\n\n1.  Install the pod to generate the workspace file: `cd\n    examples/lite/examples/bert_qa/ios && pod install`\n\n    Note: If you have installed this pod before and that command doesn't work,\n    try `pod update`. At the end of this step you should have a directory called\n    `BertQA.xcworkspace`.\n\n1.  Open the project in Xcode with the following command: `open\n    BertQA.xcworkspace`\n\n    This launches Xcode and opens the BertQA project.\n\n1.  In the Menu bar, select `Product` → `Destination` and choose your device.\n\n1. Follow the direction below if you want to:\n    *   Run the application:\n        1.  In the Menu bar, select `Product` → `Scheme` and choose\n            `BertQA-UIKit` or `BertQA-SwiftUI`.\n        1.  In the Menu bar, select `Product` → `Run` to install the app on your\n            device.\n    *   Test the core logic:\n\n        1.  In the Menu bar, select `Product` --> `Scheme` and choose\n            `BertQA-UIKit`.\n        1.  In the Menu bar, select `Product` --> `Test`.\n\n[UIKit screencast]: https://storage.googleapis.com/download.tensorflow.org/models/tflite/screenshots/bertqa_ios_uikit_demo.gif\n[SwiftUI screencast]: https://storage.googleapis.com/download.tensorflow.org/models/tflite/screenshots/bertqa_ios_swiftui_demo.gif\n[BERT]: https://github.com/google-research/bert\n[SQuAD]: https://rajpurkar.github.io/SQuAD-explorer/\n[UIKit]: https://developer.apple.com/documentation/uikit\n[SwiftUI]: https://developer.apple.com/documentation/swiftui\n[bert tokenization]: https://github.com/google-research/bert#tokenization\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/RunScripts/download_resources.sh",
    "content": "#!/bin/bash\n# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\n# Download TF Lite model from the internet if it does not exist.\n\nTFLITE_RESOURCES=\"mobilebert_qa_vocab.zip\"\nTFLITE_MODEL=\"mobilebert_float_20191023.tflite\"\nTFLITE_VOCA=\"vocab.txt\"\nTFLITE_DIC=\"contents_from_squad_dict_format.json\"\n\nTFLITE_URL=\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/bert_qa\"\n\nTFLITE_MODEL_REMOTE_PATH=\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/bert_qa/ios/models_tflite_bert_qa_mobilebert_float_20191023.tflite\"\n\nRESOURCES_DIR=\"BertQACore/Resources\"\nRESOURCES_ZIP_PATH=\"${RESOURCES_DIR}/${TFLITE_RESOURCES}\"\nMODEL_PATH=\"${RESOURCES_DIR}/${TFLITE_MODEL}\"\nVOCA_PATH=\"${RESOURCES_DIR}/${TFLITE_VOCA}\"\nDIC_PATH=\"${RESOURCES_DIR}/${TFLITE_DIC}\"\n\nRESOURCES=($MODEL_PATH $VOCA_PATH)\n\nfunction is_all_existing() {\n    local bool=\"true\"\n    local files=(\"$@\")\n    for file in ${files[@]}\n    do\n        if [ ! -f \"${file}\" ]; then\n            bool=\"false\"\n        fi\n    done\n    echo \"${bool}\"\n}\n\ntf_resource_exists=$( is_all_existing \"${RESOURCES[@]}\" )\n\nif [ \"${tf_resource_exists}\" = \"false\" ]; then\n    # Download zipped resources.\n    curl --create-dirs -o \"${RESOURCES_ZIP_PATH}\" \"${TFLITE_URL}/${TFLITE_RESOURCES}\"\n    unzip -n \"${RESOURCES_ZIP_PATH}\" -d \"${RESOURCES_DIR}\"\n    rm \"${RESOURCES_ZIP_PATH}\"\n    \n    # Remove old tflite model.\n    rm \"${MODEL_PATH}\"\n    \n    # Download new tflite model.\n    curl --create-dirs -o \"${MODEL_PATH}\" \"${TFLITE_MODEL_REMOTE_PATH}\"\n    echo \"INFO: Downloaded TensorFlow Lite resources to ${RESOURCES_DIR}.\"\nfi\n\nif [ ! -f \"${DIC_PATH}\" ]; then\n    # Donwload content data.\n    curl --create-dirs -o \"${DIC_PATH}\" \"${TFLITE_URL}/${TFLITE_DIC}\"\n    echo \"INFO: Downloaded content and question data to ${RESOURCES_DIR}.\"\nfi\n\necho \"INFO: All resources are prepared.\"\n\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInSwiftUI/AppDelegate.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n  var window: UIWindow?\n\n  func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n  ) -> Bool {\n    return true\n  }\n\n  // MARK: UISceneSession Lifecycle\n  func application(\n    _ application: UIApplication,\n    configurationForConnecting connectingSceneSession: UISceneSession,\n    options: UIScene.ConnectionOptions\n  ) -> UISceneConfiguration {\n    return UISceneConfiguration(\n      name: \"Default Configuration\",\n      sessionRole: connectingSceneSession.role)\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInSwiftUI/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"13122.16\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"13104.12\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" xcode11CocoaTouchSystemColor=\"systemBackgroundColor\" cocoaTouchSystemColor=\"whiteColor\"/>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInSwiftUI/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UIApplicationSceneManifest</key>\n\t<dict>\n\t\t<key>UIApplicationSupportsMultipleScenes</key>\n\t\t<false/>\n\t\t<key>UISceneConfigurations</key>\n\t\t<dict>\n\t\t\t<key>UIWindowSceneSessionRoleApplication</key>\n\t\t\t<array>\n\t\t\t\t<dict>\n\t\t\t\t\t<key>UISceneConfigurationName</key>\n\t\t\t\t\t<string>Default Configuration</string>\n\t\t\t\t\t<key>UISceneDelegateClassName</key>\n\t\t\t\t\t<string>BertQA_SwiftUI.SceneDelegate</string>\n\t\t\t\t</dict>\n\t\t\t</array>\n\t\t</dict>\n\t</dict>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UIUserInterfaceStyle</key>\n\t<string>Light</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInSwiftUI/KeyboardHeightObserver.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport SwiftUI\n\n/// Observes keyboard height events.\nfinal class KeyboardHeightObserver: ObservableObject {\n  @Published private(set) var height: CGFloat = 0\n\n  init() {\n    NotificationCenter.default.addObserver(\n      self, selector: #selector(keyBoardWillShow(notification:)),\n      name: UIResponder.keyboardWillShowNotification, object: nil)\n    NotificationCenter.default.addObserver(\n      self, selector: #selector(keyBoardWillHide(notification:)),\n      name: UIResponder.keyboardWillHideNotification, object: nil)\n  }\n\n  deinit {\n    NotificationCenter.default.removeObserver(self)\n  }\n\n  @objc func keyBoardWillShow(notification: Notification) {\n    if let keyboardFrame =\n      (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?\n      .cgRectValue\n    {\n      height = keyboardFrame.height\n    } else {\n      height = 0\n    }\n  }\n\n  @objc func keyBoardWillHide(notification: Notification) {\n    height = 0\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInSwiftUI/SceneDelegate.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport SwiftUI\nimport UIKit\n\nclass SceneDelegate: UIResponder, UIWindowSceneDelegate {\n\n  var window: UIWindow?\n\n  func scene(\n    _ scene: UIScene, willConnectTo session: UISceneSession,\n    options connectionOptions: UIScene.ConnectionOptions\n  ) {\n    // Use a UIHostingController as window root view controller\n    if let windowScene = scene as? UIWindowScene {\n      let bertQA: BertQAHandler\n      do {\n        bertQA = try BertQAHandler()\n      } catch let error {\n        fatalError(error.localizedDescription)\n      }\n\n      let window = UIWindow(windowScene: windowScene)\n      window.rootViewController = UIHostingController(\n        rootView: DatasetListView(datasets: Dataset.load(), bertQA: bertQA))\n      self.window = window\n      window.makeKeyAndVisible()\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInSwiftUI/Views/ContentView.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport SwiftUI\n\n/// Content view to show dataset's content and highlight answer of the question if exists.\nstruct ContentView: View {\n  var highlightRange: Range<String.Index>?\n  var content: String\n\n  var body: some View {\n    guard let range = highlightRange else {\n      return Text(content)\n    }\n\n    // If there's range to highlight, change the color of that range.\n    return Text(content[..<range.lowerBound])\n      + Text(content[range]).foregroundColor(.orange)\n      + Text(content[range.upperBound...])\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInSwiftUI/Views/DatasetDetailView.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport SwiftUI\nimport os\n\n/// Detailed view of data set.\nstruct DatasetDetailView: View {\n  var dataset: Dataset\n  let bertQA: BertQAHandler\n\n  @State private var question: String = \"\"\n  @State private var statusMessage: String = \"Enter a question.\"\n  @State private var highlightRange: Range<String.Index>? = nil\n\n  @EnvironmentObject var keyboard: KeyboardHeightObserver\n\n  var body: some View {\n\n    GeometryReader { geometry in\n      VStack {\n        Group {\n          ScrollView(.vertical, showsIndicators: true) {\n            ContentView(highlightRange: self.highlightRange, content: self.dataset.content)\n          }\n        }\n        .padding(CustomUI.contentViewPadding)\n        .onTapGesture { self.hideKeyboard() }\n\n        Spacer()\n\n        VStack(spacing: CustomUI.stackSpacing) {\n          StatusView(statusMessage: self.$statusMessage)\n\n          HStack {\n            Text(\"You might want to ask:\")\n              .font(.footnote)\n            Spacer()\n          }\n\n          SuggestedQuestionsView(questions: self.dataset.questions, toFill: self.$question)\n\n          HStack {\n            TextField(\n              \"Enter question\",\n              text: self.$question,\n              onEditingChanged: { _ in }\n            )\n            .textFieldStyle(RoundedBorderTextFieldStyle())\n              .animation(.easeOut(duration: CustomUI.keyboardAnimationDuration))\n\n            Button(action: self.tapRunButton) {\n              Text(\"▶︎\").font(.title)\n            }\n            .foregroundColor(Color.orange.opacity(CustomUI.runButtonOpacity))\n          }\n        }\n        .navigationBarTitle(Text(self.dataset.title), displayMode: .inline)\n        .padding([.leading, .trailing, .bottom], CGFloat(CustomUI.controlViewPadding))\n        .padding(.all, CustomUI.padding)\n        .padding(.bottom, max(0, self.keyboard.height - geometry.safeAreaInsets.bottom))\n      }\n    }\n  }\n\n  func tapRunButton() {\n    // Clean up previous result.\n    highlightRange = nil\n\n    // Hide keyboard.\n    self.hideKeyboard()\n\n    // Trim the whitespaces and newlines in the first and end.\n    var query = question.trimmingCharacters(in: .whitespacesAndNewlines)\n    guard !query.isEmpty else {\n      os_log(\"Textfield failed to filter the empty query.\")\n      statusMessage = StatusMessage.warnEmptyQuery\n      return\n    }\n\n    // A query must end with question mark.\n    if query.last != \"?\" {\n      query.append(\"?\")\n    }\n\n    // Inference the answer with BertQA model.\n    guard let result = bertQA.run(query: query, content: dataset.content) else {\n      os_log(\"Failed to inference the answer.\")\n      statusMessage = StatusMessage.inferenceFailError\n      return\n    }\n\n    statusMessage = result.description\n\n    // Render the answer in the `contentView`.\n    highlightRange = result.answer.text.range\n  }\n\n  func hideKeyboard() {\n    UIApplication.shared.sendAction(\n      #selector(UIResponder.resignFirstResponder),\n      to: nil, from: nil, for: nil)\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInSwiftUI/Views/DatasetListView.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport SwiftUI\n\n/// List of Data Sets.\nstruct DatasetListView: View {\n  let datasets: [Dataset]\n  let bertQA: BertQAHandler\n\n  var keyboardHeightObserver = KeyboardHeightObserver()\n  var body: some View {\n    NavigationView {\n      List(datasets, id: \\.title) { dataset in\n        NavigationLink(\n          destination: DatasetDetailView(dataset: dataset, bertQA: self.bertQA)\n            .environmentObject(self.keyboardHeightObserver)\n        ) { DatasetRow(dataset: dataset) }\n      }\n      .navigationBarTitle(Text(\"Question and Answer\"))\n    }\n  }\n}\n\n/// Row for each data set.\nstruct DatasetRow: View {\n  var dataset: Dataset\n\n  var body: some View {\n    HStack {\n      Text(dataset.title)\n      Spacer()\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInSwiftUI/Views/StatusView.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport SwiftUI\n\n/// Status View to inform the result of inference or error.\nstruct StatusView: View {\n  @Binding var statusMessage: String\n\n  var body: some View {\n    Text(self.statusMessage)\n      .frame(maxWidth: .infinity, minHeight: 40, maxHeight: 40, alignment: .topLeading)\n      .padding(.all, CustomUI.padding)\n      .font(.system(size: CustomUI.statusFontSize, weight: .semibold))\n      .lineLimit(2)\n      .background(Color(red: 255 / 255, green: 244 / 255, blue: 229 / 255))\n      .cornerRadius(CustomUI.statusTextViewCornerRadius)\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInSwiftUI/Views/SuggestedQuestionsView.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport SwiftUI\n\n/// A container view of a list of suggested question buttons.\nstruct SuggestedQuestionsView: View {\n  var questions: [String]\n\n  @Binding var toFill: String\n\n  var body: some View {\n    ScrollView(.horizontal, showsIndicators: false) {\n      HStack {\n        ForEach(questions, id: \\.self) { question in\n          Button(action: { self.toFill = question }) {\n            SuggestedQuestionText(text: question)\n          }\n          .buttonStyle(SuggestedQuestionStyle())\n        }\n      }\n    }\n  }\n}\n\n/// Text on a suggested question button.\nstruct SuggestedQuestionText: View {\n  var text: String\n\n  var body: some View {\n    Text(text)\n      .font(.caption)\n      .fontWeight(.medium)\n      .foregroundColor(.black)\n      .padding(CustomUI.textPadding)\n      .padding([.leading, .trailing], CustomUI.textSidePadding)\n  }\n}\n\n/// Style of a suggested question button.\nstruct SuggestedQuestionStyle: ButtonStyle {\n  func makeBody(configuration: Self.Configuration) -> some View {\n    configuration.label.background(\n      RoundedRectangle(cornerRadius: CustomUI.suggestedQuestionCornerRadius)\n        .stroke(\n          LinearGradient(\n            gradient: Gradient(colors: [Color.orange, Color.orange.opacity(0.7), Color.yellow]),\n            startPoint: .topLeading, endPoint: .bottomTrailing),\n          lineWidth: 1\n        )\n        .padding(1)\n    ).scaleEffect(configuration.isPressed ? 0.95 : 1.0)\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInUIKit/AppDelegate.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n  var window: UIWindow?\n\n  func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n  ) -> Bool {\n    // Set default value of the application options.\n    UserDefaults.standard.register(defaults: [\n      InterpreterOptions.threadCount.id: InterpreterOptions.threadCount.defaultValue,\n    ])\n\n    return true\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInUIKit/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"13122.16\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"13104.12\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInUIKit/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"15505\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"h8f-wX-DXt\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"15510\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--Question and Answer-->\n        <scene sceneID=\"AYx-ig-YTK\">\n            <objects>\n                <tableViewController id=\"b1p-x4-UpE\" customClass=\"DatasetsTableViewController\" customModule=\"BertQA_UIKit\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <tableView key=\"view\" clipsSubviews=\"YES\" contentMode=\"scaleToFill\" alwaysBounceVertical=\"YES\" dataMode=\"prototypes\" style=\"plain\" separatorStyle=\"default\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"28\" sectionFooterHeight=\"28\" id=\"pxA-NQ-luC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"600\" height=\"600\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\" cocoaTouchSystemColor=\"whiteColor\"/>\n                        <prototypes>\n                            <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" reuseIdentifier=\"DatasetTitleCell\" textLabel=\"VeU-zp-Kov\" style=\"IBUITableViewCellStyleDefault\" id=\"PaK-jX-BHm\" customClass=\"DatasetTitleCell\" customModule=\"BertQA_UIKit\" customModuleProvider=\"target\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"28\" width=\"600\" height=\"43.5\"/>\n                                <autoresizingMask key=\"autoresizingMask\"/>\n                                <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"PaK-jX-BHm\" id=\"t1f-vc-FJ2\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"600\" height=\"43.5\"/>\n                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                    <subviews>\n                                        <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"VeU-zp-Kov\">\n                                            <rect key=\"frame\" x=\"20\" y=\"0.0\" width=\"560\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                            <nil key=\"textColor\"/>\n                                            <nil key=\"highlightedColor\"/>\n                                        </label>\n                                    </subviews>\n                                </tableViewCellContentView>\n                                <connections>\n                                    <outlet property=\"titleLabel\" destination=\"VeU-zp-Kov\" id=\"HG7-8m-HcN\"/>\n                                    <segue destination=\"YWM-a5-WKy\" kind=\"show\" identifier=\"ChooseDataset\" id=\"GU8-Bd-JP9\"/>\n                                </connections>\n                            </tableViewCell>\n                        </prototypes>\n                        <sections/>\n                        <connections>\n                            <outlet property=\"dataSource\" destination=\"b1p-x4-UpE\" id=\"sm0-A3-AU3\"/>\n                            <outlet property=\"delegate\" destination=\"b1p-x4-UpE\" id=\"g9p-uZ-w6b\"/>\n                        </connections>\n                    </tableView>\n                    <navigationItem key=\"navigationItem\" title=\"Question and Answer\" id=\"LUO-30-rm0\">\n                        <barButtonItem key=\"rightBarButtonItem\" id=\"G55-P5-E3t\">\n                            <button key=\"customView\" opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"right\" contentVerticalAlignment=\"center\" buttonType=\"roundedRect\" lineBreakMode=\"middleTruncation\" id=\"ETt-cy-hZY\">\n                                <rect key=\"frame\" x=\"276\" y=\"7\" width=\"83\" height=\"30\"/>\n                                <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMaxY=\"YES\"/>\n                                <state key=\"normal\" title=\"Option\"/>\n                                <connections>\n                                    <segue destination=\"AfA-Gx-YAd\" kind=\"presentation\" identifier=\"optionPresentingSegue\" id=\"v8L-uW-z5C\"/>\n                                </connections>\n                            </button>\n                        </barButtonItem>\n                    </navigationItem>\n                </tableViewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"Gsa-Dp-kMc\" userLabel=\"First Responder\" customClass=\"UIResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"908.69565217391312\" y=\"134.59821428571428\"/>\n        </scene>\n        <!--Navigation Controller-->\n        <scene sceneID=\"BjJ-tq-0Zq\">\n            <objects>\n                <navigationController automaticallyAdjustsScrollViewInsets=\"NO\" id=\"h8f-wX-DXt\" sceneMemberID=\"viewController\">\n                    <toolbarItems/>\n                    <simulatedNavigationBarMetrics key=\"simulatedTopBarMetrics\" prompted=\"NO\"/>\n                    <splitViewMasterSimulatedSizeMetrics key=\"simulatedDestinationMetrics\"/>\n                    <navigationBar key=\"navigationBar\" contentMode=\"scaleToFill\" insetsLayoutMarginsFromSafeArea=\"NO\" id=\"oxh-7y-taZ\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"44\" width=\"375\" height=\"44\"/>\n                        <autoresizingMask key=\"autoresizingMask\"/>\n                    </navigationBar>\n                    <nil name=\"viewControllers\"/>\n                    <connections>\n                        <segue destination=\"b1p-x4-UpE\" kind=\"relationship\" relationship=\"rootViewController\" id=\"mok-zg-0gL\"/>\n                    </connections>\n                </navigationController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"1C6-A8-5LJ\" userLabel=\"First Responder\" customClass=\"UIResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"-1.4492753623188408\" y=\"134.59821428571428\"/>\n        </scene>\n        <!--Dataset Detail View Controller-->\n        <scene sceneID=\"zvE-b7-haS\">\n            <objects>\n                <viewController id=\"YWM-a5-WKy\" customClass=\"DatasetDetailViewController\" customModule=\"BertQA_UIKit\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"VXV-Gv-QrQ\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"600\" height=\"600\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <scrollView clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"200\" showsHorizontalScrollIndicator=\"NO\" bouncesZoom=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"8GS-cV-UlP\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"44\" width=\"600\" height=\"406\"/>\n                                <subviews>\n                                    <textView clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"scaleToFill\" editable=\"NO\" textAlignment=\"natural\" selectable=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"JTs-ne-piy\">\n                                        <rect key=\"frame\" x=\"5\" y=\"0.0\" width=\"590\" height=\"406\"/>\n                                        <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\" cocoaTouchSystemColor=\"whiteColor\"/>\n                                        <string key=\"text\">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>\n                                        <color key=\"textColor\" systemColor=\"labelColor\" cocoaTouchSystemColor=\"darkTextColor\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                        <textInputTraits key=\"textInputTraits\" autocapitalizationType=\"sentences\"/>\n                                        <connections>\n                                            <outletCollection property=\"gestureRecognizers\" destination=\"WT9-jR-xm3\" appends=\"YES\" id=\"i2c-9J-Xis\"/>\n                                        </connections>\n                                    </textView>\n                                </subviews>\n                                <constraints>\n                                    <constraint firstItem=\"JTs-ne-piy\" firstAttribute=\"leading\" secondItem=\"8GS-cV-UlP\" secondAttribute=\"leading\" constant=\"5\" id=\"HIa-z9-Dqa\"/>\n                                    <constraint firstItem=\"JTs-ne-piy\" firstAttribute=\"top\" secondItem=\"8GS-cV-UlP\" secondAttribute=\"top\" id=\"Hbr-Eb-gu1\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"JTs-ne-piy\" secondAttribute=\"trailing\" constant=\"-5\" id=\"M1K-Cy-ylp\"/>\n                                    <constraint firstItem=\"JTs-ne-piy\" firstAttribute=\"centerY\" secondItem=\"8GS-cV-UlP\" secondAttribute=\"centerY\" id=\"MVx-sl-51y\"/>\n                                    <constraint firstItem=\"JTs-ne-piy\" firstAttribute=\"height\" secondItem=\"8GS-cV-UlP\" secondAttribute=\"height\" priority=\"250\" id=\"bpm-Yq-TXy\"/>\n                                    <constraint firstItem=\"JTs-ne-piy\" firstAttribute=\"width\" secondItem=\"8GS-cV-UlP\" secondAttribute=\"width\" constant=\"-10\" id=\"dgK-8R-xv6\"/>\n                                    <constraint firstItem=\"JTs-ne-piy\" firstAttribute=\"centerX\" secondItem=\"8GS-cV-UlP\" secondAttribute=\"centerX\" id=\"e9n-zS-Xak\"/>\n                                    <constraint firstAttribute=\"bottom\" secondItem=\"JTs-ne-piy\" secondAttribute=\"bottom\" id=\"lLK-jS-Y98\"/>\n                                </constraints>\n                                <inset key=\"scrollIndicatorInsets\" minX=\"0.0\" minY=\"0.0\" maxX=\"9\" maxY=\"0.0\"/>\n                                <viewLayoutGuide key=\"contentLayoutGuide\" id=\"bIf-JF-pW9\"/>\n                                <viewLayoutGuide key=\"frameLayoutGuide\" id=\"apM-ZA-1TZ\"/>\n                            </scrollView>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"B3Q-0w-Usd\" userLabel=\"Control Panel View\" customClass=\"ControlPanelView\" customModule=\"BertQA_UIKit\" customModuleProvider=\"target\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"450\" width=\"600\" height=\"150\"/>\n                                <subviews>\n                                    <stackView opaque=\"NO\" contentMode=\"scaleToFill\" axis=\"vertical\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"8sX-l1-Mae\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"600\" height=\"150\"/>\n                                        <subviews>\n                                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Vux-PH-n6P\" userLabel=\"Status View\">\n                                                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"600\" height=\"60\"/>\n                                                <subviews>\n                                                    <textView clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" bounces=\"NO\" scrollEnabled=\"NO\" showsHorizontalScrollIndicator=\"NO\" showsVerticalScrollIndicator=\"NO\" delaysContentTouches=\"NO\" canCancelContentTouches=\"NO\" bouncesZoom=\"NO\" editable=\"NO\" text=\"Enter a question.\" textAlignment=\"natural\" selectable=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Xz7-Dw-YHN\">\n                                                        <rect key=\"frame\" x=\"10\" y=\"10\" width=\"580\" height=\"50\"/>\n                                                        <color key=\"backgroundColor\" red=\"1\" green=\"0.95686274509803915\" blue=\"0.89803921568627454\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                                        <color key=\"textColor\" systemColor=\"labelColor\" cocoaTouchSystemColor=\"darkTextColor\"/>\n                                                        <fontDescription key=\"fontDescription\" type=\"system\" weight=\"semibold\" pointSize=\"14\"/>\n                                                        <textInputTraits key=\"textInputTraits\" autocapitalizationType=\"sentences\"/>\n                                                    </textView>\n                                                </subviews>\n                                                <constraints>\n                                                    <constraint firstItem=\"Xz7-Dw-YHN\" firstAttribute=\"width\" secondItem=\"Vux-PH-n6P\" secondAttribute=\"width\" constant=\"-20\" id=\"FVg-SR-0DE\"/>\n                                                    <constraint firstItem=\"Xz7-Dw-YHN\" firstAttribute=\"height\" secondItem=\"Vux-PH-n6P\" secondAttribute=\"height\" constant=\"-10\" id=\"RI9-Ig-DWH\"/>\n                                                    <constraint firstAttribute=\"bottom\" secondItem=\"Xz7-Dw-YHN\" secondAttribute=\"bottom\" id=\"hD7-VW-e6c\"/>\n                                                    <constraint firstItem=\"Xz7-Dw-YHN\" firstAttribute=\"centerX\" secondItem=\"Vux-PH-n6P\" secondAttribute=\"centerX\" id=\"jaZ-Zh-pIV\"/>\n                                                    <constraint firstAttribute=\"height\" constant=\"60\" id=\"kRn-fd-Ivb\"/>\n                                                </constraints>\n                                            </view>\n                                            <view autoresizesSubviews=\"NO\" opaque=\"NO\" clearsContextBeforeDrawing=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"g9b-Os-hJG\" userLabel=\"Guide Text View Frame\">\n                                                <rect key=\"frame\" x=\"0.0\" y=\"60\" width=\"600\" height=\"25\"/>\n                                                <subviews>\n                                                    <textView autoresizesSubviews=\"NO\" opaque=\"NO\" clearsContextBeforeDrawing=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" bounces=\"NO\" scrollEnabled=\"NO\" showsHorizontalScrollIndicator=\"NO\" showsVerticalScrollIndicator=\"NO\" delaysContentTouches=\"NO\" canCancelContentTouches=\"NO\" bouncesZoom=\"NO\" editable=\"NO\" text=\"You might ask:\" selectable=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"at6-Su-sBI\">\n                                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"600\" height=\"32\"/>\n                                                        <accessibility key=\"accessibilityConfiguration\">\n                                                            <accessibilityTraits key=\"traits\" notEnabled=\"YES\"/>\n                                                        </accessibility>\n                                                        <color key=\"textColor\" systemColor=\"labelColor\" cocoaTouchSystemColor=\"darkTextColor\"/>\n                                                        <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"13\"/>\n                                                        <textInputTraits key=\"textInputTraits\" autocapitalizationType=\"sentences\"/>\n                                                    </textView>\n                                                </subviews>\n                                                <constraints>\n                                                    <constraint firstItem=\"at6-Su-sBI\" firstAttribute=\"width\" secondItem=\"g9b-Os-hJG\" secondAttribute=\"width\" id=\"D0E-3d-c4l\"/>\n                                                    <constraint firstItem=\"at6-Su-sBI\" firstAttribute=\"centerX\" secondItem=\"g9b-Os-hJG\" secondAttribute=\"centerX\" id=\"MAe-ke-C48\"/>\n                                                    <constraint firstItem=\"at6-Su-sBI\" firstAttribute=\"top\" secondItem=\"g9b-Os-hJG\" secondAttribute=\"top\" id=\"Ra9-ij-rl8\"/>\n                                                    <constraint firstAttribute=\"height\" constant=\"25\" id=\"SzG-mJ-f26\"/>\n                                                </constraints>\n                                            </view>\n                                            <scrollView clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"scaleToFill\" showsHorizontalScrollIndicator=\"NO\" showsVerticalScrollIndicator=\"NO\" bouncesZoom=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"1c1-d6-fdl\">\n                                                <rect key=\"frame\" x=\"0.0\" y=\"85\" width=\"600\" height=\"30\"/>\n                                                <subviews>\n                                                    <stackView autoresizesSubviews=\"NO\" opaque=\"NO\" contentMode=\"scaleToFill\" distribution=\"equalSpacing\" spacing=\"5\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"4hE-4W-AQS\">\n                                                        <rect key=\"frame\" x=\"5\" y=\"0.0\" width=\"600\" height=\"30\"/>\n                                                    </stackView>\n                                                </subviews>\n                                                <constraints>\n                                                    <constraint firstItem=\"4hE-4W-AQS\" firstAttribute=\"height\" secondItem=\"1c1-d6-fdl\" secondAttribute=\"height\" id=\"4dm-2B-8jP\"/>\n                                                    <constraint firstAttribute=\"height\" constant=\"30\" id=\"FVZ-WH-BtS\"/>\n                                                    <constraint firstItem=\"4hE-4W-AQS\" firstAttribute=\"leading\" secondItem=\"1c1-d6-fdl\" secondAttribute=\"leading\" constant=\"5\" id=\"FXi-dw-BfF\"/>\n                                                    <constraint firstItem=\"4hE-4W-AQS\" firstAttribute=\"top\" secondItem=\"1c1-d6-fdl\" secondAttribute=\"top\" id=\"Fjn-Wc-20a\"/>\n                                                    <constraint firstAttribute=\"trailing\" secondItem=\"4hE-4W-AQS\" secondAttribute=\"trailing\" constant=\"5\" id=\"PVA-qG-z4u\"/>\n                                                    <constraint firstItem=\"4hE-4W-AQS\" firstAttribute=\"width\" secondItem=\"1c1-d6-fdl\" secondAttribute=\"width\" priority=\"250\" id=\"pSL-Yr-lSz\"/>\n                                                    <constraint firstAttribute=\"bottom\" secondItem=\"4hE-4W-AQS\" secondAttribute=\"bottom\" id=\"r6I-iz-t4C\"/>\n                                                </constraints>\n                                                <viewLayoutGuide key=\"contentLayoutGuide\" id=\"UHe-KV-X3Y\"/>\n                                                <viewLayoutGuide key=\"frameLayoutGuide\" id=\"0fa-U8-Nat\"/>\n                                            </scrollView>\n                                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"msz-vk-xum\" userLabel=\"Padding\">\n                                                <rect key=\"frame\" x=\"0.0\" y=\"115\" width=\"600\" height=\"5\"/>\n                                                <constraints>\n                                                    <constraint firstAttribute=\"height\" constant=\"5\" id=\"ryO-s0-lkC\"/>\n                                                </constraints>\n                                            </view>\n                                            <stackView autoresizesSubviews=\"NO\" opaque=\"NO\" clearsContextBeforeDrawing=\"NO\" contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"9fx-jx-4ci\">\n                                                <rect key=\"frame\" x=\"0.0\" y=\"120\" width=\"600\" height=\"30\"/>\n                                                <subviews>\n                                                    <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"hck-TT-MaE\" userLabel=\"padding\">\n                                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"5\" height=\"30\"/>\n                                                        <constraints>\n                                                            <constraint firstAttribute=\"width\" constant=\"5\" id=\"F8D-hZ-wdb\"/>\n                                                        </constraints>\n                                                    </view>\n                                                    <textField opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"left\" contentVerticalAlignment=\"center\" borderStyle=\"roundedRect\" placeholder=\"Enter question\" textAlignment=\"natural\" adjustsFontSizeToFit=\"NO\" minimumFontSize=\"17\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"3zG-FI-NiD\">\n                                                        <rect key=\"frame\" x=\"5\" y=\"0.0\" width=\"560\" height=\"30\"/>\n                                                        <color key=\"textColor\" white=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                        <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                                        <textInputTraits key=\"textInputTraits\"/>\n                                                        <connections>\n                                                            <action selector=\"textFieldDidChangeWithTextField:\" destination=\"YWM-a5-WKy\" eventType=\"editingChanged\" id=\"uZy-y6-UJe\"/>\n                                                        </connections>\n                                                    </textField>\n                                                    <button opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"251\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" buttonType=\"roundedRect\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"XhH-3s-oxE\">\n                                                        <rect key=\"frame\" x=\"565\" y=\"0.0\" width=\"30\" height=\"30\"/>\n                                                        <constraints>\n                                                            <constraint firstAttribute=\"height\" constant=\"30\" id=\"HO3-wM-niQ\"/>\n                                                            <constraint firstAttribute=\"width\" constant=\"30\" id=\"Wpv-Rv-Drs\"/>\n                                                        </constraints>\n                                                        <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"25\"/>\n                                                        <state key=\"normal\" title=\"▶︎\">\n                                                            <color key=\"titleColor\" red=\"1\" green=\"0.70833333333333337\" blue=\"0.30000000000000004\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                                        </state>\n                                                        <state key=\"disabled\">\n                                                            <color key=\"titleColor\" red=\"1\" green=\"0.70833333328571468\" blue=\"0.30000000000000004\" alpha=\"0.29999999999999999\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                                        </state>\n                                                        <state key=\"selected\">\n                                                            <color key=\"titleColor\" red=\"1\" green=\"0.70833333330000003\" blue=\"0.29999999999999999\" alpha=\"0.29999999999999999\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                                        </state>\n                                                        <connections>\n                                                            <action selector=\"tapRunButton\" destination=\"YWM-a5-WKy\" eventType=\"touchUpInside\" id=\"ONg-x8-WAB\"/>\n                                                        </connections>\n                                                    </button>\n                                                    <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"z5t-xe-3LU\" userLabel=\"padding\">\n                                                        <rect key=\"frame\" x=\"595\" y=\"0.0\" width=\"5\" height=\"30\"/>\n                                                        <constraints>\n                                                            <constraint firstAttribute=\"width\" constant=\"5\" id=\"UFO-cg-02i\"/>\n                                                        </constraints>\n                                                    </view>\n                                                </subviews>\n                                                <constraints>\n                                                    <constraint firstAttribute=\"height\" constant=\"30\" id=\"dRF-m9-MDw\"/>\n                                                    <constraint firstItem=\"XhH-3s-oxE\" firstAttribute=\"leading\" secondItem=\"3zG-FI-NiD\" secondAttribute=\"trailing\" id=\"hRl-GH-K0s\"/>\n                                                    <constraint firstItem=\"hck-TT-MaE\" firstAttribute=\"trailing\" secondItem=\"3zG-FI-NiD\" secondAttribute=\"leading\" id=\"vak-PF-nKf\"/>\n                                                    <constraint firstItem=\"z5t-xe-3LU\" firstAttribute=\"leading\" secondItem=\"XhH-3s-oxE\" secondAttribute=\"trailing\" id=\"xX8-ff-xkP\"/>\n                                                </constraints>\n                                            </stackView>\n                                        </subviews>\n                                        <constraints>\n                                            <constraint firstItem=\"1c1-d6-fdl\" firstAttribute=\"centerX\" secondItem=\"8sX-l1-Mae\" secondAttribute=\"centerX\" id=\"MNp-ei-3BK\"/>\n                                        </constraints>\n                                    </stackView>\n                                </subviews>\n                                <color key=\"backgroundColor\" red=\"0.98999999999999999\" green=\"0.9822352941176471\" blue=\"0.97835294117647065\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                <constraints>\n                                    <constraint firstItem=\"8sX-l1-Mae\" firstAttribute=\"top\" secondItem=\"B3Q-0w-Usd\" secondAttribute=\"top\" id=\"DDS-Gr-6re\"/>\n                                </constraints>\n                            </view>\n                        </subviews>\n                        <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\" cocoaTouchSystemColor=\"whiteColor\"/>\n                        <constraints>\n                            <constraint firstItem=\"B3Q-0w-Usd\" firstAttribute=\"top\" secondItem=\"8GS-cV-UlP\" secondAttribute=\"bottom\" id=\"2jV-uv-I6q\"/>\n                            <constraint firstItem=\"B3Q-0w-Usd\" firstAttribute=\"centerX\" secondItem=\"Mnh-o1-NF2\" secondAttribute=\"centerX\" id=\"4DP-2L-JRE\"/>\n                            <constraint firstItem=\"B3Q-0w-Usd\" firstAttribute=\"width\" secondItem=\"VXV-Gv-QrQ\" secondAttribute=\"width\" id=\"HnD-JH-EsF\"/>\n                            <constraint firstItem=\"8sX-l1-Mae\" firstAttribute=\"width\" secondItem=\"Mnh-o1-NF2\" secondAttribute=\"width\" id=\"LPO-Ls-ikl\"/>\n                            <constraint firstItem=\"8sX-l1-Mae\" firstAttribute=\"centerX\" secondItem=\"Mnh-o1-NF2\" secondAttribute=\"centerX\" id=\"OpL-Nb-BFa\"/>\n                            <constraint firstAttribute=\"bottom\" secondItem=\"B3Q-0w-Usd\" secondAttribute=\"bottom\" id=\"UIh-Ae-M3K\"/>\n                            <constraint firstItem=\"8GS-cV-UlP\" firstAttribute=\"centerX\" secondItem=\"Mnh-o1-NF2\" secondAttribute=\"centerX\" id=\"fx4-KS-vHS\"/>\n                            <constraint firstItem=\"8GS-cV-UlP\" firstAttribute=\"width\" secondItem=\"Mnh-o1-NF2\" secondAttribute=\"width\" id=\"h0v-aI-vKx\"/>\n                            <constraint firstItem=\"Mnh-o1-NF2\" firstAttribute=\"bottom\" secondItem=\"8sX-l1-Mae\" secondAttribute=\"bottom\" id=\"lST-ta-sjc\"/>\n                            <constraint firstItem=\"8GS-cV-UlP\" firstAttribute=\"top\" secondItem=\"Mnh-o1-NF2\" secondAttribute=\"top\" id=\"o8u-ug-eE9\"/>\n                        </constraints>\n                        <viewLayoutGuide key=\"safeArea\" id=\"Mnh-o1-NF2\"/>\n                    </view>\n                    <navigationItem key=\"navigationItem\" id=\"pcd-E1-lq1\">\n                        <barButtonItem key=\"rightBarButtonItem\" id=\"109-Xv-Wr0\">\n                            <button key=\"customView\" opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"right\" contentVerticalAlignment=\"center\" buttonType=\"roundedRect\" lineBreakMode=\"middleTruncation\" id=\"M0C-zJ-7Az\">\n                                <rect key=\"frame\" x=\"276\" y=\"7\" width=\"83\" height=\"30\"/>\n                                <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMaxY=\"YES\"/>\n                                <state key=\"normal\" title=\"Option\"/>\n                                <connections>\n                                    <segue destination=\"AfA-Gx-YAd\" kind=\"presentation\" identifier=\"optionPresentingSegue\" id=\"Phf-Nt-3Zf\"/>\n                                </connections>\n                            </button>\n                        </barButtonItem>\n                    </navigationItem>\n                    <connections>\n                        <outlet property=\"contentView\" destination=\"JTs-ne-piy\" id=\"q3Y-km-d1n\"/>\n                        <outlet property=\"questionField\" destination=\"3zG-FI-NiD\" id=\"VgC-F9-B05\"/>\n                        <outlet property=\"runButton\" destination=\"XhH-3s-oxE\" id=\"V31-PY-TUd\"/>\n                        <outlet property=\"statusTextView\" destination=\"Xz7-Dw-YHN\" id=\"Mcn-dX-zgh\"/>\n                        <outlet property=\"suggestedQuestionsStackView\" destination=\"4hE-4W-AQS\" id=\"Lwb-iD-Y5v\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"gMD-BK-PtA\" userLabel=\"First Responder\" customClass=\"UIResponder\" sceneMemberID=\"firstResponder\"/>\n                <tapGestureRecognizer id=\"WT9-jR-xm3\">\n                    <connections>\n                        <action selector=\"tapContentView\" destination=\"YWM-a5-WKy\" id=\"NiH-Pe-66X\"/>\n                    </connections>\n                </tapGestureRecognizer>\n            </objects>\n            <point key=\"canvasLocation\" x=\"1828\" y=\"134.48275862068965\"/>\n        </scene>\n        <!--Option View Controller-->\n        <scene sceneID=\"gb7-pP-srR\">\n            <objects>\n                <viewController id=\"AfA-Gx-YAd\" customClass=\"OptionViewController\" customModule=\"BertQA_UIKit\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"eT8-gp-2bh\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"600\" height=\"600\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <containerView opaque=\"NO\" contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"eGr-rf-L3Y\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"56\" width=\"600\" height=\"475\"/>\n                                <connections>\n                                    <segue destination=\"fuD-fr-mza\" kind=\"embed\" identifier=\"optionTableEmbedingSegue\" id=\"o0l-sQ-YUc\"/>\n                                </connections>\n                            </containerView>\n                            <navigationBar contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"QaH-NC-63V\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"600\" height=\"56\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"height\" constant=\"56\" id=\"NTD-Un-dyE\"/>\n                                </constraints>\n                                <items>\n                                    <navigationItem title=\"Option\" id=\"Qyc-PL-vA2\">\n                                        <barButtonItem key=\"leftBarButtonItem\" style=\"done\" id=\"bxM-dc-vLO\">\n                                            <button key=\"customView\" opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" buttonType=\"roundedRect\" lineBreakMode=\"middleTruncation\" id=\"pYU-Qq-AiA\">\n                                                <rect key=\"frame\" x=\"20\" y=\"13\" width=\"83\" height=\"30\"/>\n                                                <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMaxY=\"YES\"/>\n                                                <state key=\"normal\" title=\"Cancel\"/>\n                                                <connections>\n                                                    <action selector=\"cancelButton:\" destination=\"Ofq-MK-vZ0\" eventType=\"touchUpInside\" id=\"xeX-hc-epz\"/>\n                                                    <action selector=\"tapCancelButton\" destination=\"AfA-Gx-YAd\" eventType=\"touchUpInside\" id=\"I7g-Kb-iTu\"/>\n                                                </connections>\n                                            </button>\n                                            <connections>\n                                                <action selector=\"cancelButton:\" destination=\"Ofq-MK-vZ0\" id=\"F6W-1x-rcu\"/>\n                                            </connections>\n                                        </barButtonItem>\n                                        <barButtonItem key=\"rightBarButtonItem\" style=\"done\" id=\"SyX-Vc-SfW\">\n                                            <button key=\"customView\" opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" buttonType=\"roundedRect\" lineBreakMode=\"middleTruncation\" id=\"qUk-GV-PTK\">\n                                                <rect key=\"frame\" x=\"497\" y=\"13\" width=\"83\" height=\"30\"/>\n                                                <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMaxY=\"YES\"/>\n                                                <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"15\"/>\n                                                <state key=\"normal\" title=\"Done\"/>\n                                                <connections>\n                                                    <action selector=\"tapDoneButton\" destination=\"AfA-Gx-YAd\" eventType=\"touchUpInside\" id=\"LnC-bU-jUo\"/>\n                                                </connections>\n                                            </button>\n                                        </barButtonItem>\n                                    </navigationItem>\n                                </items>\n                            </navigationBar>\n                        </subviews>\n                        <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\" cocoaTouchSystemColor=\"whiteColor\"/>\n                        <constraints>\n                            <constraint firstItem=\"eGr-rf-L3Y\" firstAttribute=\"centerX\" secondItem=\"Pwm-MW-qx6\" secondAttribute=\"centerX\" id=\"0K5-FX-BAK\"/>\n                            <constraint firstItem=\"QaH-NC-63V\" firstAttribute=\"top\" secondItem=\"Pwm-MW-qx6\" secondAttribute=\"top\" id=\"8lr-EJ-OpT\"/>\n                            <constraint firstItem=\"Pwm-MW-qx6\" firstAttribute=\"bottom\" secondItem=\"eGr-rf-L3Y\" secondAttribute=\"bottom\" id=\"PtB-xY-sLu\"/>\n                            <constraint firstItem=\"eGr-rf-L3Y\" firstAttribute=\"width\" secondItem=\"Pwm-MW-qx6\" secondAttribute=\"width\" id=\"SnW-Mb-lNg\"/>\n                            <constraint firstItem=\"QaH-NC-63V\" firstAttribute=\"width\" secondItem=\"Pwm-MW-qx6\" secondAttribute=\"width\" id=\"TGt-2C-geo\"/>\n                            <constraint firstItem=\"eGr-rf-L3Y\" firstAttribute=\"top\" secondItem=\"QaH-NC-63V\" secondAttribute=\"bottom\" id=\"euj-Z2-5vK\"/>\n                            <constraint firstItem=\"QaH-NC-63V\" firstAttribute=\"centerX\" secondItem=\"Pwm-MW-qx6\" secondAttribute=\"centerX\" id=\"pgj-hA-o3W\"/>\n                        </constraints>\n                        <viewLayoutGuide key=\"safeArea\" id=\"Pwm-MW-qx6\"/>\n                    </view>\n                    <toolbarItems/>\n                    <simulatedToolbarMetrics key=\"simulatedBottomBarMetrics\"/>\n                    <connections>\n                        <outlet property=\"cancelButton\" destination=\"pYU-Qq-AiA\" id=\"0XE-P7-MAc\"/>\n                        <outlet property=\"doneButton\" destination=\"qUk-GV-PTK\" id=\"fDw-6o-0tU\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"ttx-2B-PAa\" userLabel=\"First Responder\" customClass=\"UIResponder\" sceneMemberID=\"firstResponder\"/>\n                <exit id=\"Ofq-MK-vZ0\" userLabel=\"Exit\" sceneMemberID=\"exit\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"1382\" y=\"961\"/>\n        </scene>\n        <!--Option Table View Controller-->\n        <scene sceneID=\"VOY-uq-KQd\">\n            <objects>\n                <tableViewController restorationIdentifier=\"OptionTableViewController\" id=\"fuD-fr-mza\" customClass=\"OptionTableViewController\" customModule=\"BertQA_UIKit\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <tableView key=\"view\" clipsSubviews=\"YES\" contentMode=\"scaleToFill\" bounces=\"NO\" scrollEnabled=\"NO\" showsHorizontalScrollIndicator=\"NO\" showsVerticalScrollIndicator=\"NO\" bouncesZoom=\"NO\" dataMode=\"static\" style=\"grouped\" separatorStyle=\"default\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"18\" sectionFooterHeight=\"18\" id=\"2Mq-Bd-t0X\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"619\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" cocoaTouchSystemColor=\"groupTableViewBackgroundColor\"/>\n                        <sections>\n                            <tableViewSection headerTitle=\"Interperter Options\" footerTitle=\"Configuration options for TensorFlow Lite Interpreter.\" id=\"0Zd-5v-UQK\">\n                                <cells>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"none\" indentationWidth=\"10\" id=\"ODv-j9-Rd3\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"55.5\" width=\"375\" height=\"43.666667938232422\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"ODv-j9-Rd3\" id=\"b3o-FD-wv3\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"43.666667938232422\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <stackView opaque=\"NO\" contentMode=\"scaleToFill\" spacing=\"25\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"HHM-VU-agW\">\n                                                    <rect key=\"frame\" x=\"19\" y=\"6\" width=\"337.5\" height=\"31.5\"/>\n                                                    <subviews>\n                                                        <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Thread Count\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"wuz-yt-7Mv\">\n                                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"105\" height=\"31.5\"/>\n                                                            <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                            <nil key=\"textColor\"/>\n                                                            <nil key=\"highlightedColor\"/>\n                                                        </label>\n                                                        <view userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ous-P8-MrY\">\n                                                            <rect key=\"frame\" x=\"130\" y=\"0.0\" width=\"55.5\" height=\"31.5\"/>\n                                                            <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\" cocoaTouchSystemColor=\"whiteColor\"/>\n                                                        </view>\n                                                        <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"1\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"PiT-bH-7Kb\">\n                                                            <rect key=\"frame\" x=\"210.5\" y=\"0.0\" width=\"8\" height=\"31.5\"/>\n                                                            <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                            <nil key=\"textColor\"/>\n                                                            <nil key=\"highlightedColor\"/>\n                                                        </label>\n                                                        <stepper opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" maximumValue=\"100\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"6n4-5z-bfC\">\n                                                            <rect key=\"frame\" x=\"243.5\" y=\"0.0\" width=\"94\" height=\"31.5\"/>\n                                                            <connections>\n                                                                <action selector=\"didChangeThreadCount:\" destination=\"fuD-fr-mza\" eventType=\"valueChanged\" id=\"rUQ-0c-WVR\"/>\n                                                            </connections>\n                                                        </stepper>\n                                                    </subviews>\n                                                    <constraints>\n                                                        <constraint firstItem=\"wuz-yt-7Mv\" firstAttribute=\"leading\" secondItem=\"HHM-VU-agW\" secondAttribute=\"leading\" id=\"Nhx-iX-Bao\"/>\n                                                        <constraint firstAttribute=\"trailing\" secondItem=\"6n4-5z-bfC\" secondAttribute=\"trailing\" id=\"tuf-9o-5rM\"/>\n                                                    </constraints>\n                                                </stackView>\n                                            </subviews>\n                                            <constraints>\n                                                <constraint firstItem=\"HHM-VU-agW\" firstAttribute=\"width\" secondItem=\"b3o-FD-wv3\" secondAttribute=\"width\" multiplier=\"0.9\" id=\"2nO-Ge-eGI\"/>\n                                                <constraint firstItem=\"HHM-VU-agW\" firstAttribute=\"height\" secondItem=\"b3o-FD-wv3\" secondAttribute=\"height\" multiplier=\"0.732824\" id=\"I0t-hn-tcP\"/>\n                                                <constraint firstItem=\"HHM-VU-agW\" firstAttribute=\"centerX\" secondItem=\"b3o-FD-wv3\" secondAttribute=\"centerX\" id=\"iJq-Ub-uc5\"/>\n                                                <constraint firstItem=\"HHM-VU-agW\" firstAttribute=\"centerY\" secondItem=\"b3o-FD-wv3\" secondAttribute=\"centerY\" id=\"ncA-tG-gR0\"/>\n                                            </constraints>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                </cells>\n                            </tableViewSection>\n                            <tableViewSection footerTitle=\"Reset all the options to the default value.\" id=\"D9i-QZ-1Ux\">\n                                <cells>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"none\" indentationWidth=\"10\" id=\"ZQb-MV-mgf\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"147.16666793823242\" width=\"375\" height=\"43.666667938232422\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"ZQb-MV-mgf\" id=\"LYY-FS-9vu\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"43.666667938232422\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <button opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"leading\" contentVerticalAlignment=\"center\" buttonType=\"roundedRect\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"akJ-5i-2kg\">\n                                                    <rect key=\"frame\" x=\"19\" y=\"7\" width=\"337.5\" height=\"30\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <state key=\"normal\" title=\"Reset\"/>\n                                                    <connections>\n                                                        <action selector=\"didResetOptions\" destination=\"fuD-fr-mza\" eventType=\"touchUpInside\" id=\"bCN-GV-ujB\"/>\n                                                    </connections>\n                                                </button>\n                                            </subviews>\n                                            <constraints>\n                                                <constraint firstItem=\"akJ-5i-2kg\" firstAttribute=\"width\" secondItem=\"LYY-FS-9vu\" secondAttribute=\"width\" multiplier=\"0.9\" id=\"27L-fo-q6W\"/>\n                                                <constraint firstItem=\"akJ-5i-2kg\" firstAttribute=\"centerY\" secondItem=\"LYY-FS-9vu\" secondAttribute=\"centerY\" id=\"AW0-3O-pbX\"/>\n                                                <constraint firstItem=\"akJ-5i-2kg\" firstAttribute=\"height\" secondItem=\"LYY-FS-9vu\" secondAttribute=\"height\" multiplier=\"0.687023\" id=\"Hrk-VW-cuU\"/>\n                                                <constraint firstItem=\"akJ-5i-2kg\" firstAttribute=\"centerX\" secondItem=\"LYY-FS-9vu\" secondAttribute=\"centerX\" id=\"SVI-ME-HNJ\"/>\n                                            </constraints>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                </cells>\n                            </tableViewSection>\n                        </sections>\n                        <connections>\n                            <outlet property=\"dataSource\" destination=\"fuD-fr-mza\" id=\"HZn-5j-A3R\"/>\n                            <outlet property=\"delegate\" destination=\"fuD-fr-mza\" id=\"fe1-pu-pLm\"/>\n                        </connections>\n                    </tableView>\n                    <connections>\n                        <outlet property=\"threadCountLabel\" destination=\"PiT-bH-7Kb\" id=\"9ZH-4g-Ogt\"/>\n                        <outlet property=\"threadCountStepper\" destination=\"6n4-5z-bfC\" id=\"v22-VE-fsY\"/>\n                    </connections>\n                </tableViewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"YnB-uM-8Wd\" userLabel=\"First Responder\" customClass=\"UIResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"2218\" y=\"961\"/>\n        </scene>\n    </scenes>\n    <inferredMetricsTieBreakers>\n        <segue reference=\"v8L-uW-z5C\"/>\n    </inferredMetricsTieBreakers>\n</document>\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInUIKit/Controllers/DataSetDetailViewController.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\nimport os\n\n/// Controller for detailed view of each `Dataset`.\nclass DatasetDetailViewController: UIViewController {\n  /// Selected `Dataset` to run the Bert model.\n  var dataset: Dataset?\n\n  var bertQA: BertQAHandler?\n\n  // MARK: Views created at Storyboard\n  @IBOutlet weak var contentView: UITextView!\n\n  @IBOutlet weak var statusTextView: UITextView!\n  @IBOutlet weak var questionField: UITextField!\n  @IBOutlet weak var runButton: UIButton!\n\n  // MARK: View created programmatically\n  @IBOutlet weak var suggestedQuestionsStackView: UIStackView!\n\n  // MARK: Custom init and deinit\n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n\n    // Add obserber for keyboard notification to adjust view size.\n    NotificationCenter.default.addObserver(\n      self, selector: #selector(keyboardWillShow(notification:)),\n      name: UIResponder.keyboardWillShowNotification, object: nil\n    )\n    NotificationCenter.default.addObserver(\n      self, selector: #selector(keyboardWillHide(notification:)),\n      name: UIResponder.keyboardWillHideNotification, object: nil\n    )\n  }\n\n  deinit {\n    // Remove added observer.\n    NotificationCenter.default.removeObserver(self)\n  }\n\n  // MARK: - View handling methods\n  override func viewDidLoad() {\n    super.viewDidLoad()\n    guard let dataset = dataset else {\n      fatalError(\"Data set was not passed correctly.\")\n    }\n\n    navigationItem.title = dataset.title\n\n    // Make status text view to have rounded border.\n    statusTextView.layer.cornerRadius = CustomUI.statusTextViewCornerRadius\n\n    // Add content of the data set to the view.\n    contentView.text = dataset.content\n    contentView.font = .systemFont(ofSize: 17)\n\n    // Add suggested questions as buttons to the stack view.\n    for question in dataset.questions {\n      let button = SuggestedQuestionButton(of: question)\n      suggestedQuestionsStackView.addArrangedSubview(button)\n      button.addTarget(\n        self,\n        action: #selector(tapSuggestedQuestionButton(of:)),\n        for: .touchUpInside)\n    }\n\n    // Disable run button at the begining as it is empty.\n    runButton.isEnabled = false\n  }\n\n  // MARK: Action for keyboard notification\n  @objc func keyboardWillShow(notification: Notification) {\n    // Get keyboard height.\n    let keyboardHeight: CGFloat\n    if let keyboardFrame =\n      (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?\n      .cgRectValue\n    {\n      keyboardHeight = keyboardFrame.height\n    } else {\n      keyboardHeight = 0\n    }\n\n    // Resize the view adjusting to the keyboard height.\n    view.frame.size.height = UIScreen.main.bounds.height - keyboardHeight\n  }\n\n  @objc func keyboardWillHide(notification: Notification) {\n    // Resize the view to fil the screen.\n    view.frame.size.height = UIScreen.main.bounds.height\n  }\n\n  // MARK: - Custom actions\n  @IBAction func textFieldDidChange(textField: UITextField) {\n    // Drop empty question.\n    guard let query = textField.text?.trimmingCharacters(in: .whitespacesAndNewlines),\n      !query.isEmpty\n    else {\n      os_log(\"Got empty query.\")\n      statusTextView.text = StatusMessage.warnEmptyQuery\n      runButton.isEnabled = false\n      return\n    }\n\n    // Ask to run if query is not empty.\n    statusTextView.text = StatusMessage.askRun\n    runButton.isEnabled = true\n  }\n\n  @IBAction func tapContentView() {\n    // Hide keyboard.\n    questionField.resignFirstResponder()\n  }\n\n  @IBAction func tapSuggestedQuestionButton(of sender: SuggestedQuestionButton!) {\n    // Fill the `questionField` in the view with selected question.\n    questionField.text = sender.title(for: .normal)\n\n    // Activate the button and ask to run.\n    statusTextView.text = StatusMessage.askRun\n    runButton.isEnabled = true\n  }\n\n  @IBAction func tapRunButton() {\n    // Disable run button until getting the answer.\n    runButton.isEnabled = false\n\n    // Clean up previous result.\n    contentView.textStorage\n      .removeAttribute(\n        .backgroundColor,\n        range: NSRange(location: 0, length: contentView.text.count))\n\n    // Hide keyboard.\n    questionField.resignFirstResponder()\n\n    // Trim the whitespaces and newlines in the first and end.\n    guard var query = questionField.text?.trimmingCharacters(in: .whitespacesAndNewlines),\n      !query.isEmpty\n    else {\n      os_log(\"Textfield failed to filter the empty query.\")\n      statusTextView.text = StatusMessage.warnEmptyQuery\n      return\n    }\n\n    // A query must end with question mark.\n    if query.last != \"?\" {\n      query.append(\"?\")\n    }\n\n    // Inference the answer with BertQA model.\n    guard let result = bertQA?.run(query: query, content: contentView.text) else {\n      os_log(\"Failed to inference the answer.\")\n      statusTextView.text = StatusMessage.inferenceFailError\n      return\n    }\n\n    statusTextView.text = result.description\n\n    // Render the answer in the `contentView`.\n    contentView.textStorage\n      .addAttribute(\n        .backgroundColor,\n        value: CustomUI.textHighlightColor,\n        range: NSRange(result.answer.text.range, in: contentView.text)\n      )\n\n    // Enable button as the process is finished.\n    runButton.isEnabled = true\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInUIKit/Controllers/DataSetsTableViewController.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\nimport os\n\n/// Controller for a table for the list of `Dataset`s.\nclass DatasetsTableViewController: UITableViewController {\n  /// List of `Dataset`s to run the Bert model.\n  var datasets: [Dataset] = Dataset.load()\n  var bertQA: BertQAHandler?\n\n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n\n    // Add obserber for interpreter option changing.\n    NotificationCenter.default.addObserver(\n      self, selector: #selector(interpreterOptionDidChange(notification:)),\n      name: UserDefaults.didChangeNotification, object: nil\n    )\n  }\n\n  deinit {\n    // Remove added observer.\n    NotificationCenter.default.removeObserver(self)\n  }\n\n  // MARK: - View handling methods\n  override func viewDidLoad() {\n    super.viewDidLoad()\n\n    updateBertQA()\n  }\n\n  // MARK: - Table view data source\n\n  override func numberOfSections(in tableView: UITableView) -> Int {\n    return 1\n  }\n\n  override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {\n    return datasets.count\n  }\n\n  override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)\n    -> UITableViewCell\n  {\n    let cell =\n      tableView.dequeueReusableCell(withIdentifier: \"DatasetTitleCell\") as! DatasetTitleCell\n    cell.titleLabel.text = datasets[indexPath.row].title\n\n    return cell\n  }\n\n  // MARK: - Navigation\n\n  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {\n    // Hand over `Dataset` of the selected title to the `DatasetDetailViewController`.\n    guard\n      segue.identifier == \"ChooseDataset\",\n      let datasetDetailViewController = segue.destination as? DatasetDetailViewController,\n      let selectedIndex = tableView.indexPathForSelectedRow?.row\n    else {\n      return\n    }\n    datasetDetailViewController.dataset = datasets[selectedIndex]\n    datasetDetailViewController.bertQA = bertQA\n  }\n\n  // MARK: Update BertQA on changing interpreter option\n  @objc func interpreterOptionDidChange(notification: Notification) {\n    updateBertQA()\n  }\n\n  private func updateBertQA() {\n    let threadCount = UserDefaults.standard.integer(forKey: InterpreterOptions.threadCount.id)\n\n    do {\n      bertQA = try BertQAHandler(threadCount: threadCount)\n    } catch let error {\n      fatalError(error.localizedDescription)\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInUIKit/Controllers/OptionTableViewController.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\nclass OptionTableViewController: UITableViewController {\n  // MARK: Storyboard Connections\n  @IBOutlet weak var threadCountLabel: UILabel!\n  @IBOutlet weak var threadCountStepper: UIStepper!\n\n  // MARK: - View handling methods\n  override func viewDidLoad() {\n    // Initialize UI properties with user default value.\n    let threadCount = UserDefaults.standard.integer(forKey: InterpreterOptions.threadCount.id)\n    threadCountLabel.text = threadCount.description\n    threadCountStepper.value = Double(threadCount)\n\n    // Set thread count limits.\n    threadCountStepper.maximumValue = Double(InterpreterOptions.threadCount.maximumValue)\n    threadCountStepper.minimumValue = Double(InterpreterOptions.threadCount.minimumValue)\n  }\n\n  // MARK: Button Actions\n  @IBAction func didChangeThreadCount(_ sender: UIStepper) {\n    threadCountLabel.text = Int(sender.value).description\n  }\n\n  @IBAction func didResetOptions() {\n    threadCountLabel.text = InterpreterOptions.threadCount.defaultValue.description\n    threadCountStepper.value = Double(InterpreterOptions.threadCount.defaultValue)\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInUIKit/Controllers/OptionViewController.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\nimport os\n\nclass OptionViewController: UIViewController {\n  var optionTableView: OptionTableViewController?\n\n  // MARK: Storyboard Connections\n  @IBOutlet weak var cancelButton: UIButton!\n  @IBOutlet weak var doneButton: UIButton!\n\n  // MARK: - Custom actions\n  @IBAction func tapCancelButton() {\n    self.dismiss(animated: true, completion: nil)\n  }\n\n  @IBAction func tapDoneButton() {\n    guard\n      let newThreadCount = optionTableView?.threadCountStepper.value\n    else {\n      os_log(\"[Option View]: Cannot get the option value\", type: .error)\n      self.dismiss(animated: true, completion: nil)\n      return\n    }\n    let currentThreadCount = Double(\n      UserDefaults.standard.integer(forKey: InterpreterOptions.threadCount.id))\n\n    if currentThreadCount != newThreadCount {\n      UserDefaults.standard.set(Int(newThreadCount), forKey: InterpreterOptions.threadCount.id)\n    }\n\n    self.dismiss(animated: true, completion: nil)\n  }\n\n  // MARK: - Navigation\n  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {\n    if segue.identifier == \"optionTableEmbedingSegue\",\n      let destination = segue.destination as? OptionTableViewController\n    {\n      optionTableView = destination\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInUIKit/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UIUserInterfaceStyle</key>\n\t<string>Light</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInUIKit/Views/ControlPanelView.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n/// Custom view for control panel which has a separating line at the top of it.\nclass ControlPanelView: UIView {\n  lazy var separatingLine = CALayer()\n\n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    addSeparatingLine()\n  }\n\n  override init(frame: CGRect) {\n    super.init(frame: frame)\n    addSeparatingLine()\n  }\n\n  override func layoutSubviews() {\n    super.layoutSubviews()\n    updateSeparatingLine()\n  }\n\n  /// Add seperating line at the top of the view.\n  private func addSeparatingLine() {\n    separatingLine.backgroundColor = UIColor.lightGray.cgColor\n    layer.addSublayer(separatingLine)\n  }\n\n  /// Update separating line.\n  private func updateSeparatingLine() {\n    separatingLine.frame = getSeparatingLineFrame()\n  }\n\n  /// Get border of separating line to fill the width of the view.\n  private func getSeparatingLineFrame() -> CGRect {\n    var width = frame.width\n\n    let left = safeAreaInsets.right\n    let right = safeAreaInsets.right\n    width = width + left + right\n\n    return CGRect(x: -left, y: 0, width: width, height: 0.7)\n  }\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInUIKit/Views/DataSetTitleCell.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n/// Table cell for a title of `Dataset`.\nclass DatasetTitleCell: UITableViewCell {\n  @IBOutlet weak var titleLabel: UILabel!\n}\n"
  },
  {
    "path": "lite/examples/bert_qa/ios/ViewInUIKit/Views/SuggestedQuestionButton.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n/// Button for Sugessted Questions.\n@IBDesignable class SuggestedQuestionButton: UIButton {\n  var question: String?\n  lazy var border = CAShapeLayer()\n  lazy var gradient = CAGradientLayer()\n  lazy var fill = CAShapeLayer()\n\n  lazy var borderPath = CGPath(rect: CGRect(), transform: nil)\n\n  override var bounds: CGRect {\n    didSet {\n      borderPath =\n        UIBezierPath(\n          roundedRect: self.bounds.insetBy(dx: 1, dy: 6),\n          cornerRadius: 10.0\n        ).cgPath\n    }\n  }\n\n  required init?(coder: NSCoder) {\n    super.init(coder: coder)\n    addSublayers()\n  }\n\n  override init(frame: CGRect) {\n    super.init(frame: frame)\n    addSublayers()\n  }\n\n  convenience init(of question: String) {\n    self.init(frame: CGRect.zero)\n    self.question = question\n  }\n\n  override func draw(_ rect: CGRect) {\n    setTitle(question, for: .normal)\n    setTitleColor(.black, for: .normal)\n    titleLabel?.font = .systemFont(ofSize: 11, weight: .medium)\n    contentEdgeInsets = UIEdgeInsets(top: 0, left: 4, bottom: 0, right: 4)\n  }\n\n  override func layoutSubviews() {\n    super.layoutSubviews()\n    updateSublayers()\n  }\n\n  private func addSublayers() {\n    // Add border layer to the button.\n    border.lineWidth = 1.0\n    border.fillColor = UIColor.clear.cgColor\n    border.strokeColor = UIColor.black.cgColor\n\n    gradient.colors = [\n      UIColor.orange.cgColor,\n      UIColor.orange.withAlphaComponent(0.7).cgColor,\n      UIColor.yellow.cgColor,\n    ]\n    gradient.startPoint = CGPoint(x: 0, y: 0)\n    gradient.endPoint = CGPoint(x: 1, y: 1)\n\n    layer.addSublayer(gradient)\n\n    // Add color fill to the button.\n    fill.fillColor = UIColor.white.cgColor\n    layer.addSublayer(fill)\n  }\n\n  private func updateSublayers() {\n    // Update customized border along to the size of the button.\n    border.frame = bounds\n    border.path = borderPath\n\n    gradient.frame = border.bounds\n    gradient.mask = border\n\n    // Update color fill along to the size of the button.\n    fill.path = borderPath\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/.bazelrc",
    "content": "# Align with TensorFlow .bazelrc to build from the source.\n# Please refer to TensorFlow .bazelrc for more details:\n# https://github.com/tensorflow/tensorflow/blob/master/.bazelrc\n\n# Android configs. Bazel needs to have --cpu and --fat_apk_cpu both set to the\n# target CPU to build transient dependencies correctly. See\n# https://docs.bazel.build/versions/master/user-manual.html#flag--fat_apk_cpu\nbuild:android --crosstool_top=//external:android/crosstool\nbuild:android --host_crosstool_top=@bazel_tools//tools/cpp:toolchain\nbuild:android_arm --config=android\nbuild:android_arm --cpu=armeabi-v7a\nbuild:android_arm --fat_apk_cpu=armeabi-v7a\nbuild:android_arm64 --config=android\nbuild:android_arm64 --cpu=arm64-v8a\nbuild:android_arm64 --fat_apk_cpu=arm64-v8a\nbuild:android_x86 --config=android\nbuild:android_x86 --cpu=x86\nbuild:android_x86 --fat_apk_cpu=x86\nbuild:android_x86_64 --config=android\nbuild:android_x86_64 --cpu=x86_64\nbuild:android_x86_64 --fat_apk_cpu=x86_64\n\n# Sets the default Apple platform to macOS.\nbuild --apple_platform_type=macos\n\n# iOS configs for each architecture and the fat binary builds.\nbuild:ios --apple_platform_type=ios\nbuild:ios --apple_bitcode=embedded --copt=-fembed-bitcode\nbuild:ios_armv7 --config=ios\nbuild:ios_armv7 --cpu=ios_armv7\nbuild:ios_armv7 --cops -Wno-c++11-narrowing\nbuild:ios_arm64 --config=ios\nbuild:ios_arm64 --cpu=ios_arm64\nbuild:ios_x86_64 --config=ios\nbuild:ios_x86_64 --cpu=ios_x86_64\nbuild:ios_fat --config=ios\nbuild:ios_fat --ios_multi_cpus=armv7,arm64\nbuild:ios_fat --copt -Wno-c++11-narrowing\n\n# BEGIN OF REMOTE BUILD EXECUTION OPTIONS\n# Options when using remote execution\n# WARNING: THESE OPTIONS WONT WORK IF YOU DO NOT HAVE PROPER AUTHENTICATION AND PERMISSIONS\n# Flag to enable remote config\ncommon --experimental_repo_remote_exec\n\n# Config to use a mostly-static build and disable modular op registration\n# support (this will revert to loading TensorFlow with RTLD_GLOBAL in Python).\n# By default, TensorFlow will build with a dependence on\n# //tensorflow:libtensorflow_framework.so.\nbuild:monolithic --define framework_shared_object=false\n\n# Flags for open source build, always set to be true.\nbuild --define open_source_build=true\ntest --define open_source_build=true\n\n# Flags for fat_apk_cpu build, always set to multiple cpus.\nbuild --fat_apk_cpu=x86,x86_64,arm64-v8a,armeabi-v7a\ntest --fat_apk_cpu=x86,x86_64,arm64-v8a,armeabi-v7a\n\n# Other build flags.\nbuild --define=grpc_no_ares=true\n\n# See https://github.com/bazelbuild/bazel/issues/7362 for information on what\n# --incompatible_remove_legacy_whole_archive flag does.\n# This flag is set to true in Bazel 1.0 and newer versions. We tried to migrate\n# Tensorflow to the default, however test coverage wasn't enough to catch the\n# errors.\n# There is ongoing work on Bazel team's side to provide support for transitive\n# shared libraries. As part of migrating to transitive shared libraries, we\n# hope to provide a better mechanism for control over symbol exporting, and\n# then tackle this issue again.\n#\n# TODO: Remove this line once TF doesn't depend on Bazel wrapping all library\n# archives in -whole_archive -no_whole_archive.\nbuild --noincompatible_remove_legacy_whole_archive\n\n# These are bazel 2.0's incompatible flags. Tensorflow needs to use bazel 2.0.0\n# to use cc_shared_library, as part of the Tensorflow Build Improvements RFC:\n# https://github.com/tensorflow/community/pull/179\nbuild --noincompatible_prohibit_aapt1\n\n# Modular TF build options\nbuild:dynamic_kernels --define=dynamic_loaded_kernels=true\nbuild:dynamic_kernels --copt=-DAUTOLOAD_DYNAMIC_KERNELS\n\n# Build TF with C++ 17 features.\nbuild:c++17 --cxxopt=-std=c++1z\nbuild:c++17 --cxxopt=-stdlib=libc++\nbuild:c++1z --config=c++17\n\n# Enable using platform specific build settings\nbuild --enable_platform_specific_config\n\n# Suppress C++ compiler warnings, otherwise build logs become 10s of MBs.\nbuild:linux --copt=-w\nbuild:macos --copt=-w\nbuild:windows --copt=/w\n\n# Tensorflow uses M_* math constants that only get defined by MSVC headers if\n# _USE_MATH_DEFINES is defined.\nbuild:windows --copt=/D_USE_MATH_DEFINES\nbuild:windows --host_copt=/D_USE_MATH_DEFINES\n\n# Default paths for TF_SYSTEM_LIBS\nbuild:linux --define=PREFIX=/usr\nbuild:linux --define=LIBDIR=$(PREFIX)/lib\nbuild:linux --define=INCLUDEDIR=$(PREFIX)/include\nbuild:macos --define=PREFIX=/usr\nbuild:macos --define=LIBDIR=$(PREFIX)/lib\nbuild:macos --define=INCLUDEDIR=$(PREFIX)/include\n# TF_SYSTEM_LIBS do not work on windows.\n\n# By default, build TF in C++ 14 mode.\nbuild:linux --cxxopt=-std=c++14\nbuild:linux --host_cxxopt=-std=c++14\nbuild:macos --cxxopt=-std=c++14\nbuild:macos --host_cxxopt=-std=c++14\nbuild:windows --cxxopt=/std:c++14\nbuild:windows --host_cxxopt=/std:c++14\n\n# On windows, we still link everything into a single DLL.\nbuild:windows --config=monolithic\n\n# On linux, we dynamically link small amount of kernels\nbuild:linux --config=dynamic_kernels\n\n# Make sure to include as little of windows.h as possible\nbuild:windows --copt=-DWIN32_LEAN_AND_MEAN\nbuild:windows --host_copt=-DWIN32_LEAN_AND_MEAN\nbuild:windows --copt=-DNOGDI\nbuild:windows --host_copt=-DNOGDI\n\n# Misc build options we need for windows.\nbuild:windows --linkopt=/DEBUG\nbuild:windows --host_linkopt=/DEBUG\nbuild:windows --linkopt=/OPT:REF\nbuild:windows --host_linkopt=/OPT:REF\nbuild:windows --linkopt=/OPT:ICF\nbuild:windows --host_linkopt=/OPT:ICF\nbuild:windows --experimental_strict_action_env=true\n\n# Verbose failure logs when something goes wrong\nbuild:windows --verbose_failures\n\n# Suppress all warning messages.\nbuild:short_logs --output_filter=DONT_MATCH_ANYTHING\n\n# Instruction set optimizations\n# TODO(gunan): Create a feature in toolchains for avx/avx2 to\n#   avoid having to define linux/win separately.\nbuild:avx_linux --copt=-mavx\nbuild:avx2_linux --copt=-mavx2\nbuild:native_arch_linux --copt=-march=native\nbuild:avx_win --copt=/arch=AVX\nbuild:avx2_win --copt=/arch=AVX2\n\n# Options to build TensorFlow 1.x or 2.x.\nbuild:v1 --define=tf_api_version=1\nbuild:v2 --define=tf_api_version=2\nbuild:v1 --action_env=TF2_BEHAVIOR=0\nbuild:v2 --action_env=TF2_BEHAVIOR=1\nbuild --config=v2\ntest --config=v2\n\n# Enable XLA\nbuild:xla --action_env=TF_ENABLE_XLA=1\nbuild:xla --define=with_xla_support=true\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/.bazelversion",
    "content": "3.7.2"
  },
  {
    "path": "lite/examples/classification_by_retrieval/README.md",
    "content": "# CbR: Classification-by-Retrieval\n\nClassification-by-retrieval provides an easy way to create a neural\nnetwork-based classifier without computationally expensive backpropagation\ntraining.\nUsing this technology, you can create a lightweight mobile model with as little\nas [one image per class](#model-accuracy-comparison-with-few-shot-learning), or\nyou can create an on-device model that can classify as many as tens of thousands\nof classes.\nFor example, we created [mobile models that can recognize tens of thousands of\nlandmarks](https://tfhub.dev/google/collections/landmarks/1) with the\nclassification-by-retrieval technology.\nWe provide an [iOS app](ios/README.md), where you can choose images from the\nphoto library and label them to create a TfLite classifier within seconds\n(if the number of images are small), and test the created model right away.\nWe also provide a C++ command line tool (in lib/tests) to build a classifier.\n\nThere are many use-cases for classification-by-retrieval, including:\n\n*  Machine learning education (e.g., an educational hackathon event).\n*  Easily prototyping, or demonstrating ML classification.\n*  Custom product recognition (e.g., developing a product recognition app for a\n   small/medium business without the need to gather extensive training data or\n   write lots of code).\n\n\n## Technical background\n\nClassification and retrieval are two distinct methods of image recognition. A\ntypical object recognition approach is to build a neural network classifier and\ntrain it with a large amount of training data (often thousands of images, or\nmore). On the contrary, the retrieval approach uses a pre-trained feature\nextractor (e.g., an image embedding model) with feature matching based on a\nnearest neighbor search algorithm. The retrieval approach is scalable and\nflexible. For example, it can handle a large number of classes (say, > 1\nmillion), and adding or removing classes does not require extra training. One\nwould need as little as a single training data per class, which makes it\neffectively few-shot learning. A downside of the retrieval approach is that\nit requires extra infrastructure, and is less intuitive to use than a\nclassification model.\nClassification-by-retrieval (CbR) is a neural network model with image retrieval\nlayers baked into it. With the CbR technology, you can easily create a\nTensorFlow classification model without any training.\n\n![Conventional Approaches](docs/images/conventional-approaches.svg)\n![Classification-by-Retrieval](docs/images/classification-by-retrieval.svg)\n\n## How do the retrieval layers work?\n\nA classification-by-retrieval model is an extension of an embedding model with\nextra retrieval layers.\nThe retrieval layers are computed (not trained) from the training data, i.e.,\nthe index data.\nThe retrieval layers consists of two components:\n\n*  Nearest neighbor matching component\n*  Result aggregation component\n\nThe nearest neighbor matching component is essentially a fully connected layer\nwhere its weights are the normalized embeddings of the index data.\nNote that a dot-product of two normalized vectors (cos similarity) is linear\n(with a negative coefficient) to the squared L2 distance.\nTherefore, the output of the fully connected layer is effectively identical to\nthe nearest neighbor matching result.\n\nThe retrieval result is given for each training instance, not for each class.\nTherefore, we add another *result aggregation component* on top of the nearest\nneighbor matching layer.\nThe aggregation component consists of a selection layer for each class followed\nby an aggregation (e.g., max) layer for each of them.\nFinally, the results are concatenated to form a single output vector.\n\n## Base Embedding Model\n\nOne may choose a base embedding model that best fits the domain.\nThere are many embedding models available, for example, in\n[TensorFlow hub](http://tfhub.dev), for various domains.\nThe provided [iOS demo](ios/README.md) uses a\n[MobileNet V3 trained with ImageNet](https://tfhub.dev/google/lite-model/imagenet/mobilenet_v3_small_100_224/feature_vector/5/default/1),\nwhich is a generic and efficient on-device model.\n\n## Model Accuracy: Comparison with Few Shot Learning\n\nIn some sense, CbR (indexing) can be considered as a few-shot learning approach\nwithout training.\nAlthough it is not apples to apples to compare CbR with an arbitrary pre-trained\nbase embedding model with a typical few-shot learning approach where the whole\nmodel trained with given training data, there is a\n[research](https://arxiv.org/pdf/1911.04623.pdf) that compares\nnearest neighbor retrieval (which is equivalent to CbR) with few-shot learning\napproaches. It shows that nearest neighbor retrieval can be comparable or even\nbetter than many few-shot learning approaches.\n\n## Responsible Model Building\n\nWe encourage you to build a model that is fair and responsible. To learn more\nabout building a responsible model:\n\n*   https://www.tensorflow.org/responsible_ai\n*   https://design.google/library/fair-not-default/\n*   https://developers.google.com/machine-learning/crash-course/fairness/video-lecture\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/WORKSPACE",
    "content": "\"\"\"Classification-by-Retrieval Workspace\"\"\"\n\nworkspace(name = \"org_tensorflow_lite_examples_classificationbyretrieval\")\n\nload(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\", \"http_file\")\n\n# TFLite Support\nhttp_archive(\n    name = \"org_tensorflow_lite_support\",\n    strip_prefix = \"tflite-support-f5dadc83b36d863700f080ca45877f8b3b3d7f47\",\n    sha256 = \"c07c1e54eced03d568168f03ecb3cde34c6f8b8fad7d29d2b09c39c14d2f0899\",\n    urls = [\"https://github.com/tensorflow/tflite-support/archive/f5dadc83b36d863700f080ca45877f8b3b3d7f47.zip\"],\n)\n\n# TFLite Support dependencies.\nhttp_archive(\n    name = \"com_google_glog\",\n    sha256 = \"1ee310e5d0a19b9d584a855000434bb724aa744745d5b8ab1855c85bff8a8e21\",\n    strip_prefix = \"glog-028d37889a1e80e8a07da1b8945ac706259e5fd8\",\n    urls = [\n        \"https://mirror.bazel.build/github.com/google/glog/archive/028d37889a1e80e8a07da1b8945ac706259e5fd8.tar.gz\",\n        \"https://github.com/google/glog/archive/028d37889a1e80e8a07da1b8945ac706259e5fd8.tar.gz\",\n    ],\n)\n\nhttp_archive(\n    name = \"zlib\",\n    build_file = \"@org_tensorflow_lite_support//third_party:zlib.BUILD\",\n    sha256 = \"c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1\",\n    strip_prefix = \"zlib-1.2.11\",\n    urls = [\n        \"http://mirror.bazel.build/zlib.net/fossils/zlib-1.2.11.tar.gz\",\n        \"http://zlib.net/fossils/zlib-1.2.11.tar.gz\",  # 2017-01-15\n    ],\n)\n\nhttp_archive(\n    name = \"org_libzip\",\n    build_file = \"@org_tensorflow_lite_support//third_party:libzip.BUILD\",\n    sha256 = \"a5d22f0c87a2625450eaa5e10db18b8ee4ef17042102d04c62e311993a2ba363\",\n    strip_prefix = \"libzip-rel-1-5-1\",\n    urls = [\n        # Bazel does not like the official download link at libzip.org,\n        # so use the GitHub release tag.\n        \"https://mirror.bazel.build/github.com/nih-at/libzip/archive/rel-1-5-1.zip\",\n        \"https://github.com/nih-at/libzip/archive/rel-1-5-1.zip\",\n    ],\n)\n\nhttp_archive(\n    name = \"libyuv\",\n    urls = [\"https://chromium.googlesource.com/libyuv/libyuv/+archive/39240f7149cffde62e3620344d222c8ab2c21178.tar.gz\"],\n    # Adding the constrain of sha256 and strip_prefix will cause failure as of\n    # Jan 2021. It seems that the downloaded libyuv was different every time,\n    # so that the specified sha256 and strip_prefix cannot match.\n    # sha256 = \"01c2e30eb8e83880f9ba382f6bece9c38cd5b07f9cadae46ef1d5a69e07fafaf\",\n    # strip_prefix = \"libyuv-39240f7149cffde62e3620344d222c8ab2c21178\",\n    build_file = \"@org_tensorflow_lite_support//third_party:libyuv.BUILD\",\n)\n\nhttp_archive(\n    name = \"com_google_absl\",\n    strip_prefix = \"abseil-cpp-20210324.2\",\n    sha256 = \"59b862f50e710277f8ede96f083a5bb8d7c9595376146838b9580be90374ee1f\",\n    urls = [\"https://github.com/abseil/abseil-cpp/archive/20210324.2.tar.gz\"],\n)\n\n# TF on 2021-09-29.\nhttp_archive(\n    name = \"org_tensorflow\",\n    strip_prefix = \"tensorflow-a221f72e69fea7a46977e35961e5cdb1e51fec36\",\n    sha256 = \"d0e57bcf455df772cfdf65fdf59a94dfef7547c6aafd50b382dab3a182b0c5b3\",\n    urls = [\"https://github.com/tensorflow/tensorflow/archive/a221f72e69fea7a46977e35961e5cdb1e51fec36.tar.gz\"],\n)\n\n# Set up TF.\nload(\"@org_tensorflow//tensorflow:workspace3.bzl\", \"tf_workspace3\")\ntf_workspace3()\nload(\"@org_tensorflow//tensorflow:workspace2.bzl\", \"tf_workspace2\")\ntf_workspace2()\nload(\"@org_tensorflow//tensorflow:workspace1.bzl\", \"tf_workspace1\")\ntf_workspace1()\nload(\"@org_tensorflow//tensorflow:workspace0.bzl\", \"tf_workspace0\")\ntf_workspace0()\n\n# Download the model file.\nhttp_file(\n    name = \"imagenet-mobilenet_v3_small_100_224-feature_vector\",\n    downloaded_file_path = \"imagenet-mobilenet_v3_small_100_224-feature_vector.tflite\",\n    sha256 = \"383220188d049b60b044da89b1a1e9eacb676c63562867875aceaf6885a8c761\",\n    urls = [\"https://tfhub.dev/google/lite-model/imagenet/mobilenet_v3_small_100_224/feature_vector/5/metadata/1?lite-format=tflite\"],\n)\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/BUILD",
    "content": "load(\"//third_party/bazel_rules/rules_cc/cc:objc_library.bzl\", \"objc_library\")\nload(\"@build_bazel_rules_apple//apple:ios.bzl\", \"ios_application\")\nload(\"//tools/build_defs/swift:swift_library.bzl\", \"swift_library\")\n\npackage(\n    default_applicable_licenses = [\"//third_party/py/tensorflow_examples:license\"],\n    default_visibility = [\"//visibility:private\"],\n)\n\nlicenses([\"notice\"])\n\nMINIMUM_OS_VERSION = \"15.0\"\n\nios_application(\n    name = \"ImageClassifierBuilder\",\n    app_icons = glob([\"ImageClassifierBuilder/Assets.xcassets/AppIcon.appiconset/**\"]),\n    bundle_id = \"com.tensorflow.lite.swift.ImageClassifierBuilder\",\n    families = [\"iphone\"],\n    infoplists = [\"ImageClassifierBuilder/Info.plist\"],\n    minimum_os_version = MINIMUM_OS_VERSION,\n    provisioning_profile = \":ProvisioningProfile.mobileprovision\",\n    deps = [\n        \":ImageClassifierBuilderLib\",\n    ],\n)\n\n# Swift files target.\nswift_library(\n    name = \"ImageClassifierBuilderLib\",\n    srcs = glob([\"ImageClassifierBuilder/*.swift\"]),\n    data = glob([\"ImageClassifierBuilder/TFLiteDocumentIcons/**\"]),\n    deps = [\n        \":ImageClassifierBuilderLibObjC\",\n    ],\n)\n\n# Hide header files that contain C++, as otherwise the `objc_library` can't be used in a\n# `swift_library`.\nPRIVATE_HEADERS = [\n    \"ImageClassifierBuilder/NSString+AbseilStringView.h\",\n]\n\n# Objective-C(++) files target.\nobjc_library(\n    name = \"ImageClassifierBuilderLibObjC\",\n    srcs = glob([\n        \"ImageClassifierBuilder/*.m\",\n        \"ImageClassifierBuilder/*.mm\",\n    ]) + PRIVATE_HEADERS,\n    hdrs = glob(\n        [\"ImageClassifierBuilder/*.h\"],\n        exclude = PRIVATE_HEADERS,\n    ),\n    data = [\n        \"@imagenet-mobilenet_v3_small_100_224-feature_vector//file\",\n    ],\n    deps = [\n        \"//third_party/apple_frameworks:Accelerate\",\n        \"//third_party/apple_frameworks:CoreGraphics\",\n        \"//third_party/apple_frameworks:CoreVideo\",\n        \"//third_party/apple_frameworks:Foundation\",\n        \"//third_party/apple_frameworks:UIKit\",\n        \"//lib:model_builder\",\n        \"@com_google_absl//absl/container:flat_hash_set\",\n        \"@com_google_absl//absl/status\",\n        \"@com_google_absl//absl/strings\",\n        \"@flatbuffers//:runtime_cc\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/port:statusor\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/task/vision:image_classifier\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/task/vision/core:frame_buffer\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/task/vision/proto:classifications_proto_inc\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/task/vision/proto:image_classifier_options_proto_inc\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/task/vision/proto:image_embedder_options_proto_inc\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/task/vision/utils:frame_buffer_common_utils\",\n    ],\n)\n\nexports_files([\"ProvisioningProfile.mobileprovision\"])\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/Album.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Foundation\nimport Photos\n\n/// Describes an album from the user Photos Library within the AlbumSelector UI.\nstruct Album {\n  /// The handle to the underlying Photos Library album.\n  let collection: PHAssetCollection\n  /// Whether the album is selected in the AlbumSelector UI.\n  var selected = false\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/AlbumSelector.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Photos\nimport SwiftUI\n\n/// Displays the list of Photos Library albums.\n///\n/// The user can then make a selection before proceeding to the next step.\n///\n/// This screen is part of the model creation UX.\nstruct AlbumSelector: View {\n  /// The metadata of the model to create.\n  let modelMetadata: ModelMetadata\n  /// The closure to call once the Model object will be created.\n  let completion: (Model?) -> Void\n  /// The list of albums from the Photos Library.\n  @State private var albums: [Album] = []\n\n  var body: some View {\n    VStack {\n      if PHPhotoLibrary.authorizationStatus(for: .readWrite) == .authorized {\n        Text(\n          \"The model classes will be named after the Photos Library albums you select. Make sure \"\n            + \"an album contains images of only one class.\"\n        )\n        .padding()\n        List {\n          Section(header: Text(\"Albums\")) {\n            ForEach(albums.indices, id: \\.self) { index in\n              Row(album: albums[index])\n                .onTapGesture {\n                  albums[index].selected.toggle()\n                }\n            }\n          }\n        }\n        .listStyle(GroupedListStyle())\n      } else {\n        Text(\n          \"Classification by Retrieval lets you select albums from your library to train the \"\n            + \"model. Please allow access in Settings.\"\n        )\n        .padding()\n        Button(\"Settings\") {\n          UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)\n        }\n      }\n    }\n    .onAppear {\n      fetchAlbums()\n    }\n    .navigationBarTitle(Text(\"Select Albums\"), displayMode: .inline)\n    .toolbar {\n      ToolbarItem(placement: .primaryAction) {\n        NavigationLink(\n          \"Next\",\n          destination: ModelTrainerView(\n            modelMetadata: modelMetadata, albums: albums.filter(\\.selected), completion: completion)\n        )\n        .accessibilityHint(\n          \"Proceeds to the next model creation step. Dimmed until at least two albums are selected.\"\n        )\n        .disabled(albums.filter(\\.selected).count < 2)\n      }\n    }\n  }\n\n  /// Requests authorization to access the library and fetches the albums if authorized.\n  func fetchAlbums() {\n    PHPhotoLibrary.requestAuthorization(for: .readWrite) { status in\n      switch status {\n      case .notDetermined:\n        print(\"Photo Library authorization not determined.\")\n      case .restricted:\n        print(\"Photo Library authorization restricted.\")\n      case .denied:\n        print(\"Photo Library authorization denied.\")\n      case .authorized:\n        self.albums = PHCollectionList.fetchAlbums()\n      case .limited:\n        print(\"Photo Library authorization limited.\")\n      @unknown default:\n        fatalError()\n      }\n    }\n  }\n\n  /// Displays an album in the list.\n  struct Row: View {\n    /// The represented album.\n    let album: Album\n\n    var body: some View {\n      HStack {\n        Image(systemName: \"photo\")\n        HStack(alignment: .lastTextBaseline) {\n          Text(album.collection.localizedTitle!)\n          Text(\n            \"\\(album.collection.estimatedAssetCount) \"\n              + \"\\(album.collection.estimatedAssetCount > 1 ? \"photos\" : \"photo\")\"\n          )\n          .font(.caption)\n          .foregroundColor(.secondary)\n        }\n        Spacer()\n        Image(systemName: album.selected ? \"checkmark.circle.fill\" : \"circle\")\n      }\n      .contentShape(Rectangle())\n      .accessibilityAddTraits(album.selected ? [.isSelected] : [])\n      .accessibilityElement(children: .combine)\n    }\n  }\n}\n\nextension PHCollectionList {\n  /// Fetches the Photos Library albums.\n  static func fetchAlbums() -> [Album] {\n    var albums = [Album]()\n    let userCollections = fetchTopLevelUserCollections(with: nil)\n    userCollections.enumerateObjects { (collection, index, stop) in\n      if let collection = collection as? PHAssetCollection, collection.localizedTitle != nil {\n        albums.append(Album(collection: collection))\n      }\n    }\n    return albums\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\": [\n    {\n      \"size\": \"60x60\",\n      \"expected-size\": \"180\",\n      \"filename\": \"180.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"3x\"\n    },\n    {\n      \"size\": \"40x40\",\n      \"expected-size\": \"80\",\n      \"filename\": \"80.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"40x40\",\n      \"expected-size\": \"120\",\n      \"filename\": \"120.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"3x\"\n    },\n    {\n      \"size\": \"60x60\",\n      \"expected-size\": \"120\",\n      \"filename\": \"120.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"58\",\n      \"filename\": \"58.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"29\",\n      \"filename\": \"29.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"87\",\n      \"filename\": \"87.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"3x\"\n    },\n    {\n      \"size\": \"20x20\",\n      \"expected-size\": \"40\",\n      \"filename\": \"40.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"20x20\",\n      \"expected-size\": \"60\",\n      \"filename\": \"60.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"3x\"\n    },\n    {\n      \"size\": \"1024x1024\",\n      \"filename\": \"1024.png\",\n      \"expected-size\": \"1024\",\n      \"idiom\": \"ios-marketing\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"40x40\",\n      \"expected-size\": \"80\",\n      \"filename\": \"80.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"72x72\",\n      \"expected-size\": \"72\",\n      \"filename\": \"72.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"76x76\",\n      \"expected-size\": \"152\",\n      \"filename\": \"152.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"50x50\",\n      \"expected-size\": \"100\",\n      \"filename\": \"100.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"58\",\n      \"filename\": \"58.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"76x76\",\n      \"expected-size\": \"76\",\n      \"filename\": \"76.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"29\",\n      \"filename\": \"29.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"50x50\",\n      \"expected-size\": \"50\",\n      \"filename\": \"50.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"72x72\",\n      \"expected-size\": \"144\",\n      \"filename\": \"144.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"40x40\",\n      \"expected-size\": \"40\",\n      \"filename\": \"40.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"83.5x83.5\",\n      \"expected-size\": \"167\",\n      \"filename\": \"167.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"20x20\",\n      \"expected-size\": \"20\",\n      \"filename\": \"20.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"20x20\",\n      \"expected-size\": \"40\",\n      \"filename\": \"40.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    }\n  ]\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/CameraView.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport AVFoundation\nimport CoreVideo\nimport SwiftUI\n\n/// Shows the live feed from the camera and vends individual frames are accessible via the `onFrame`\n/// callback.\nstruct CameraView: UIViewControllerRepresentable {\n  /// The queue on which to call `onFrame`.\n  let queue: DispatchQueue\n  /// The callback providing individual frames as image buffer.\n  ///\n  /// This is called on `queue`.\n  let onFrame: (CVImageBuffer) -> Void\n\n  func makeUIViewController(context: Context) -> CameraViewController {\n    CameraViewController(sampleBufferDelegate: context.coordinator, queue: queue)\n  }\n\n  func updateUIViewController(_ uiViewController: CameraViewController, context: Context) {\n  }\n\n  func makeCoordinator() -> Coordinator {\n    Coordinator(self)\n  }\n\n  /// Registers as `CameraViewController` delegate to receive frames, then calls the `CameraView`'s\n  /// `onFrame` callback.\n  final class Coordinator: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate {\n    private let parent: CameraView\n\n    fileprivate init(_ parent: CameraView) {\n      self.parent = parent\n    }\n\n    func captureOutput(\n      _ output: AVCaptureOutput,\n      didOutput sampleBuffer: CMSampleBuffer,\n      from connection: AVCaptureConnection\n    ) {\n      if let imageBuffer = sampleBuffer.imageBuffer {\n        parent.onFrame(imageBuffer)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/CameraViewController.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport AVFoundation\nimport CoreVideo\nimport UIKit\n\n/// Shows the live feed from the camera and vends individual frames are provided to the\n/// `sampleBufferDelegate` on `queue`.\nclass CameraViewController: UIViewController {\n  /// The delegate receiving the sample buffers.\n  let sampleBufferDelegate: AVCaptureVideoDataOutputSampleBufferDelegate\n  /// The queue on which the delegate is receiving the sample buffers.\n  let queue: DispatchQueue\n\n  /// The underlying capture session.\n  private let captureSession = AVCaptureSession()\n  /// The underlying layer displaying the live camera feed.\n  private var videoPreviewLayer: AVCaptureVideoPreviewLayer?\n\n  /// Initializes a `CameraViewController` with the details to receive camera frames.\n  ///\n  /// - Parameters:\n  ///   - sampleBufferDelegate: The delegate receiving the sample buffers.\n  ///   - queue: The queue on which the delegate is receiving the sample buffers.\n  init(sampleBufferDelegate: AVCaptureVideoDataOutputSampleBufferDelegate, queue: DispatchQueue) {\n    self.sampleBufferDelegate = sampleBufferDelegate\n    self.queue = queue\n    super.init(nibName: nil, bundle: nil)\n  }\n\n  required init(coder: NSCoder) { fatalError() }\n\n  override func viewDidLoad() {\n    super.viewDidLoad()\n    view.backgroundColor = .black\n\n    switch AVCaptureDevice.authorizationStatus(for: .video) {\n    case .authorized:\n      setupCaptureSession()\n    case .notDetermined:\n      AVCaptureDevice.requestAccess(for: .video) { granted in\n        if granted {\n          DispatchQueue.main.async { [weak self] in\n            self?.setupCaptureSession()\n          }\n        }\n      }\n    default:\n      print(\"Camera access not authorized.\")\n    }\n  }\n\n  override func viewWillAppear(_ animated: Bool) {\n    super.viewWillAppear(animated)\n    captureSession.startRunning()\n  }\n\n  override func viewDidDisappear(_ animated: Bool) {\n    super.viewDidDisappear(animated)\n    captureSession.stopRunning()\n  }\n\n  override func viewDidLayoutSubviews() {\n    super.viewDidLayoutSubviews()\n    if let videoPreviewLayer = videoPreviewLayer {\n      videoPreviewLayer.frame = view.bounds\n    }\n  }\n\n  override func viewWillTransition(\n    to size: CGSize,\n    with coordinator: UIViewControllerTransitionCoordinator\n  ) {\n    super.viewWillTransition(to: size, with: coordinator)\n    if let connection = videoPreviewLayer?.connection {\n      switch UIDevice.current.orientation {\n      case .portrait, .unknown, .faceUp, .faceDown:\n        connection.videoOrientation = .portrait\n      case .portraitUpsideDown:\n        connection.videoOrientation = .portraitUpsideDown\n      case .landscapeLeft:\n        connection.videoOrientation = .landscapeRight\n      case .landscapeRight:\n        connection.videoOrientation = .landscapeLeft\n      @unknown default:\n        connection.videoOrientation = .portrait\n      }\n    }\n  }\n\n  /// Configures the capture session inputs and outputs, as well as the layer displaying the live\n  /// camera feed.\n  private func setupCaptureSession() {\n    guard let camera = AVCaptureDevice.default(for: .video) else {\n      print(\"Unable to access camera.\")\n      return\n    }\n    do {\n      let input = try AVCaptureDeviceInput(device: camera)\n      captureSession.addInput(input)\n\n      let output = AVCaptureVideoDataOutput()\n      output.videoSettings =\n        [kCVPixelBufferPixelFormatTypeKey: Int(kCVPixelFormatType_32BGRA)] as [String: Any]\n      output.alwaysDiscardsLateVideoFrames = true\n      output.setSampleBufferDelegate(sampleBufferDelegate, queue: queue)\n      captureSession.addOutput(output)\n\n      videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)\n      if let videoPreviewLayer = videoPreviewLayer {\n        videoPreviewLayer.videoGravity = .resizeAspectFill\n        videoPreviewLayer.connection?.videoOrientation = .portrait\n        view.layer.addSublayer(videoPreviewLayer)\n      }\n    } catch let error {\n      print(\"Error Unable to initialize camera:  \\(error.localizedDescription)\")\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/Classifier.h",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#import <CoreVideo/CoreVideo.h>\n#import <Foundation/Foundation.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@class Label;\n\n/// A classifier infers labels from a given image. This wraps the C++ Image Classifier TFLite Task\n/// API.\n@interface Classifier : NSObject\n\n/// Initializes a classifier, loading the given model.\n/// @param URL The URL of the model file on disk.\n- (instancetype)initWithModelURL:(NSURL *)modelURL NS_SWIFT_NAME(init(modelURL:));\n\n/// MARK: Inference\n\n/// Infers labels from the given image.\n/// @param pixelBuffer The image, as a Core Video Pixel Buffer.\n/// @return The list of labels.\n- (NSArray<Label *> *)classifyPixelBuffer:(CVPixelBufferRef)pixelBuffer\n    NS_SWIFT_NAME(classify(pixelBuffer:));\n\n/// MARK: Model Infos\n\n/// The name of the model.\n@property(nonatomic, readonly) NSString *name;\n\n/// The description of the model. (Note: `description` is already a method on NSObject, thus the\n/// prefixing).\n@property(nonatomic, readonly) NSString *modelDescription;\n\n/// The author of the model.\n@property(nonatomic, readonly) NSString *author;\n\n/// The version of the model.\n@property(nonatomic, readonly) NSString *version;\n\n/// The license under which the model is released.\n@property(nonatomic, readonly) NSString *license;\n\n/// The labels in the labelmap. Note: this is not declared as a property because the implementation\n/// is not trivial. It requires reading in the model file's metadata.\n- (NSArray<NSString *> *)labels;\n\n@end\n\n/// Corresponds to a label from a classification result.\nNS_SWIFT_NAME(Classifier.Label)\n@interface Label : NSObject\n\n/// The name of the label.\n@property(nonatomic, readonly) NSString *name;\n\n/// The score associated to the label. It can be any value, but is usually within [0, 1].\n@property(nonatomic, readonly) float score;\n\n/// Initializes a label.\n/// @param name The name of the label.\n/// @param score The score of the label.\n- (instancetype)initWithName:(NSString *)name score:(float)score;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/Classifier.mm",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#import \"ios/ImageClassifierBuilder/Classifier.h\"\n\n#include <cstdint>\n#include \"absl/container/flat_hash_set.h\"\n#include \"absl/status/status.h\"\n#include \"absl/strings/str_split.h\"\n#include \"absl/strings/string_view.h\"\n#include \"flatbuffers/flatbuffers.h\"\n#import \"ios/ImageClassifierBuilder/NSData+PixelBuffer.h\"\n#import \"ios/ImageClassifierBuilder/NSString+AbseilStringView.h\"\n#include \"tensorflow_lite_support/cc/task/core/base_task_api.h\"\n#include \"tensorflow_lite_support/cc/task/vision/core/frame_buffer.h\"\n#include \"tensorflow_lite_support/cc/task/vision/image_classifier.h\"\n#include \"tensorflow_lite_support/cc/task/vision/proto/classifications_proto_inc.h\"\n#include \"tensorflow_lite_support/cc/task/vision/proto/image_classifier_options_proto_inc.h\"\n#include \"tensorflow_lite_support/cc/task/vision/utils/frame_buffer_common_utils.h\"\n#include \"tensorflow_lite_support/metadata/cc/metadata_extractor.h\"\n#include \"tensorflow_lite_support/metadata/metadata_schema_generated.h\"\n\nusing ::tflite::task::vision::ClassificationResult;\nusing ::tflite::task::vision::Classifications;\nusing ::tflite::task::vision::CreateFromRgbaRawBuffer;\nusing ::tflite::task::vision::FrameBuffer;\nusing ::tflite::task::vision::ImageClassifier;\nusing ::tflite::task::vision::ImageClassifierOptions;\n\n/// Merges similar labels. It keeps the highest score per label.\n/// Courtesy of mbrenon.\n/// @param input The classifications from a classification result.\nstatic Classifications PostProcessClassifications(const Classifications &input) {\n  Classifications output;\n  absl::flat_hash_set<std::string> labels;\n  for (const auto &class_ : input.classes()) {\n    const std::string &label = class_.class_name();\n    // Input classes are sorted by decreasing score, so keep only the first occurrence for each\n    // label, which will also be the one with highest score.\n    if (!labels.contains(label)) {\n      labels.emplace(label);\n      *output.add_classes() = class_;\n    }\n  }\n  return output;\n}\n\n/// Converts C++ results to a list of Objective-C Label objects.\n/// @param classificationResult The C++ classification result.\nstatic NSArray<Label *> *LabelsFromClassificationResult(\n    const ClassificationResult &classificationResult) {\n  NSMutableArray<Label *> *labels = [NSMutableArray array];\n  for (const auto &classifications : classificationResult.classifications()) {\n    const auto processedClassifications = PostProcessClassifications(classifications);\n    for (const auto &label : processedClassifications.classes()) {\n      NSString *name = [NSString stringWithUTF8String:label.class_name().c_str()];\n      [labels addObject:[[Label alloc] initWithName:name score:label.score()]];\n    }\n  }\n  return labels;\n}\n\n@implementation Classifier {\n  /// The underlying C++ image classifier.\n  std::unique_ptr<ImageClassifier> _image_classifier;\n}\n\n- (instancetype)initWithModelURL:(NSURL *)modelURL {\n  self = [super init];\n  if (self) {\n    ImageClassifierOptions options;\n    options.mutable_model_file_with_metadata()->set_file_name(modelURL.path.UTF8String);\n    auto imageClassifier = ImageClassifier::CreateFromOptions(options);\n    NSAssert(imageClassifier.ok(), @\"Couldn't create classifier: %@\",\n             [NSString cbr_stringWithStringView:imageClassifier.status().message()]);\n    _image_classifier = std::move(imageClassifier.value());\n  }\n  return self;\n}\n\n/// MARK: Inference\n\n- (NSArray<Label *> *)classifyPixelBuffer:(CVPixelBufferRef)pixelBuffer {\n  NSData *data = [NSData cbr_RGBA8888DataFromPixelBuffer:pixelBuffer];\n  const uint8 *input = static_cast<const uint8 *>(data.bytes);\n  int width = static_cast<int>(CVPixelBufferGetWidth(pixelBuffer));\n  int height = static_cast<int>(CVPixelBufferGetHeight(pixelBuffer));\n  std::unique_ptr<FrameBuffer> frame_buffer = CreateFromRgbaRawBuffer(input, {width, height});\n  auto result = *_image_classifier->Classify(*frame_buffer);\n  return LabelsFromClassificationResult(result);\n}\n\n/// MARK: Model Infos\n\n- (NSString *)name {\n  auto metadata = _image_classifier->GetMetadataExtractor()->GetModelMetadata();\n  return [NSString stringWithUTF8String:metadata->name()->c_str()];\n}\n\n- (NSString *)modelDescription {\n  auto metadata = _image_classifier->GetMetadataExtractor()->GetModelMetadata();\n  return [NSString stringWithUTF8String:metadata->description()->c_str()];\n}\n\n- (NSString *)author {\n  auto metadata = _image_classifier->GetMetadataExtractor()->GetModelMetadata();\n  return [NSString stringWithUTF8String:metadata->author()->c_str()];\n}\n\n- (NSString *)version {\n  auto metadata = _image_classifier->GetMetadataExtractor()->GetModelMetadata();\n  return [NSString stringWithUTF8String:metadata->version()->c_str()];\n}\n\n- (NSString *)license {\n  auto metadata = _image_classifier->GetMetadataExtractor()->GetModelMetadata();\n  return [NSString stringWithUTF8String:metadata->license()->c_str()];\n}\n\n- (NSArray<NSString *> *)labels {\n  auto labelmap = _image_classifier->GetMetadataExtractor()->GetAssociatedFile(\"labelmap.txt\");\n  NSAssert(labelmap.ok(), @\"Couldn’t extract the model's labelmap: %@\",\n           [NSString cbr_stringWithStringView:labelmap.status().message()]);\n  std::vector<absl::string_view> raw_labels = absl::StrSplit(*labelmap, '\\n');\n  absl::flat_hash_set<std::string> unique_labels(raw_labels.begin(), raw_labels.end());\n  NSMutableArray<NSString *> *labels = [NSMutableArray array];\n  for (const auto& label : unique_labels) {\n    [labels addObject:[NSString stringWithUTF8String:label.c_str()]];\n  }\n  return labels;\n}\n\n@end\n\n@implementation Label\n\n- (instancetype)initWithName:(NSString *)name score:(float)score {\n  self = [super init];\n  if (self) {\n    _name = [name copy];\n    _score = score;\n  }\n  return self;\n}\n\n/// Conveniently describes the label.\n- (NSString *)description {\n  return [NSString stringWithFormat:@\"%@ (%.2f)\", self.name, self.score];\n}\n\n@end\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/Field.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport SwiftUI\n\n/// Displays a key-value row with the value being editable.\nstruct Field: View {\n  let key: String\n  @Binding var value: String\n\n  var body: some View {\n    HStack {\n      Text(key)\n      TextField(\"Tap to edit\", text: $value)\n        .multilineTextAlignment(.trailing)\n        .accessibilityLabel(value)\n    }\n    .accessibilityElement(children: .combine)\n    .accessibilityHint(\"Double tap to edit.\")\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/FileManager+Additions.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\nextension FileManager {\n  /// Returns the file URL to the Documents directory, using the default file manager.\n  static var documentDirectory: URL {\n    do {\n      return try FileManager.default.url(\n        for: .documentDirectory,\n        in: .userDomainMask,\n        appropriateFor: nil,\n        create: false)\n    } catch {\n      fatalError(\"Couldn't find the Documents directory.\")\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/ImageClassifierBuilderApp.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport SwiftUI\n\n@main struct ImageClassifierBuilderApp: App {\n  @AppStorage(\"onBoardingCompleted\") private var onBoardingCompleted = false\n\n  var body: some Scene {\n    WindowGroup {\n      if onBoardingCompleted {\n        ModelList()\n      } else {\n        OnBoardingView {\n          onBoardingCompleted = true\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDocumentTypes</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>CFBundleTypeIconFiles</key>\n\t\t\t<array>\n\t\t\t\t<string>TFLite_22.png</string>\n\t\t\t\t<string>TFLite_44.png</string>\n\t\t\t\t<string>TFLite_64.png</string>\n\t\t\t\t<string>TFLite_320.png</string>\n\t\t\t</array>\n\t\t\t<key>CFBundleTypeName</key>\n\t\t\t<string>TFLite Model</string>\n\t\t\t<key>CFBundleTypeRole</key>\n\t\t\t<string>Editor</string>\n\t\t\t<key>LSHandlerRank</key>\n\t\t\t<string>Owner</string>\n\t\t\t<key>LSItemContentTypes</key>\n\t\t\t<array>\n\t\t\t\t<string>com.tensorflow.lite.swift.ImageClassifierBuilder.tflite</string>\n\t\t\t</array>\n\t\t</dict>\n\t</array>\n\t<key>LSSupportsOpeningDocumentsInPlace</key>\n\t<true/>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>Image Classifier Builder</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>NSCameraUsageDescription</key>\n\t<string>This app uses the camera to let you test models.</string>\n\t<key>NSPhotoLibraryUsageDescription</key>\n\t<string>This app uses your selection of albums from the Photos Library to train models.</string>\n\t<key>UIApplicationSupportsIndirectInputEvents</key>\n\t<true/>\n\t<key>UILaunchScreen</key>\n\t<dict/>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>arm64</string>\n\t</array>\n\t<key>UTExportedTypeDeclarations</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>UTTypeConformsTo</key>\n\t\t\t<array>\n\t\t\t\t<string>public.data</string>\n\t\t\t</array>\n\t\t\t<key>UTTypeDescription</key>\n\t\t\t<string>TFLite Model</string>\n\t\t\t<key>UTTypeIconFiles</key>\n\t\t\t<array>\n\t\t\t\t<string>TFLite_320.png</string>\n\t\t\t\t<string>TFLite_64.png</string>\n\t\t\t\t<string>TFLite_44.png</string>\n\t\t\t\t<string>TFLite_22.png</string>\n\t\t\t</array>\n\t\t\t<key>UTTypeIdentifier</key>\n\t\t\t<string>com.tensorflow.lite.swift.ImageClassifierBuilder.tflite</string>\n\t\t\t<key>UTTypeTagSpecification</key>\n\t\t\t<dict>\n\t\t\t\t<key>public.filename-extension</key>\n\t\t\t\t<string>tflite</string>\n\t\t\t\t<key>public.mime-type</key>\n\t\t\t\t<string>application/octet-stream</string>\n\t\t\t</dict>\n\t\t</dict>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/Model.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Foundation\n\n/// Describes a TFLite model stored locally.\nstruct Model: Codable, Equatable {\n  /// The name of the model.\n  let name: String\n  /// The number of labels in its labelmap.\n  let labelsCount: Int\n  /// The estimated size of the model file on disk.\n  let size: Int64?\n}\n\nextension Model {\n  /// Returns the URL where to store a new Tflite model file.\n  ///\n  /// - Parameter name: The name of the future model.\n  static func makeURLForNewModel(named name: String) -> URL {\n    url(modelNamed: name)\n  }\n\n  /// The `URL` where the associated TFLite model file is stored.\n  var url: URL {\n    Self.url(modelNamed: name)\n  }\n\n  /// Returns the URL of the TFLite model file associated with a model named `name`.\n  ///\n  /// - Parameter name: The name of the model.\n  private static func url(modelNamed name: String) -> URL {\n    FileManager.documentDirectory.appendingPathComponent(name).appendingPathExtension(\"tflite\")\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/ModelCreationView.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport SwiftUI\n\n/// Displays a form to fill information about the model to create.\nstruct ModelCreationView: View {\n  /// The closure to call once the Model object will be created.\n  let completion: (Model?) -> Void\n  /// The metadata of the model to create.\n  @State private var modelMetadata = ModelMetadata()\n\n  var body: some View {\n    Form {\n      Field(key: \"Model Name\", value: $modelMetadata.name)\n      Field(key: \"Description\", value: $modelMetadata.description)\n      Field(key: \"Author\", value: $modelMetadata.author)\n      Field(key: \"Version\", value: $modelMetadata.version)\n      Field(key: \"License\", value: $modelMetadata.license)\n    }\n    .navigationBarTitle(Text(\"New Model\"), displayMode: .inline)\n    .toolbar {\n      ToolbarItem(placement: .primaryAction) {\n        NavigationLink(\n          \"Next\", destination: AlbumSelector(modelMetadata: modelMetadata, completion: completion)\n        )\n        .accessibilityHint(\n          \"Proceeds to the next model creation step. Dimmed until all info is filled.\"\n        )\n        .disabled(!modelMetadata.isValid)\n      }\n      ToolbarItem(placement: .cancellationAction) {\n        Button(\"Cancel\") {\n          completion(nil)\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/ModelData.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Foundation\n\n/// Manages the persistence of the list of models to a file on disk.\nclass ModelData: ObservableObject {\n  /// The list of loaded models.\n  @Published var models: [Model] = []\n  /// The URL on disk where the models are persisted.\n  static var fileURL: URL {\n    FileManager.documentDirectory.appendingPathComponent(\"Models.data\")\n  }\n\n  /// Loads synchronously the list of models in memory.\n  func load() throws {\n    guard FileManager.default.fileExists(atPath: Self.fileURL.path) else { return }\n    let data = try Data(contentsOf: Self.fileURL)\n    models = try JSONDecoder().decode([Model].self, from: data)\n  }\n\n  /// Saves synchronously the list of models to disk.\n  func save() throws {\n    try JSONEncoder().encode(models).write(to: Self.fileURL)\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/ModelInfoView.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport SwiftUI\n\n/// Displays the info located in the metadata of a given model.\nstruct ModelInfoView: View {\n  @Environment(\\.presentationMode) var presentationMode\n\n  /// The model metadata.\n  let metadata: ModelMetadata\n\n  /// The labels from the label map.\n  let labels: [String]\n\n  var body: some View {\n    NavigationView {\n      Form {\n        KeyValueRow(key: \"Name\", value: metadata.name)\n        KeyValueRow(key: \"Description\", value: metadata.description)\n        KeyValueRow(key: \"Author\", value: metadata.author)\n        KeyValueRow(key: \"Version\", value: metadata.version)\n        KeyValueRow(key: \"License\", value: metadata.license)\n        Section(header: Text(\"Labels\")) {\n          ForEach(labels) { label in\n            Text(label)\n          }\n        }\n      }\n      .navigationBarTitle(Text(\"Model Info\"), displayMode: .inline)\n      .toolbar {\n        ToolbarItem(placement: .confirmationAction) {\n          Button(\"OK\") {\n            presentationMode.wrappedValue.dismiss()\n          }\n        }\n      }\n    }\n  }\n\n  struct KeyValueRow: View {\n    let key: String\n    let value: String\n\n    var body: some View {\n      HStack {\n        Text(key)\n        Spacer()\n        Text(value)\n      }\n      .accessibilityElement(children: .combine)\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/ModelList.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport SwiftUI\nimport ios_ImageClassifierBuilderLibObjC\n\n/// Displays the list of models available to the app.\nstruct ModelList: View {\n  /// The manager of the list of models.\n  @StateObject private var modelData = ModelData()\n  /// Whether the model creation UI is currently presented.\n  @State private var showModelCreationUI = false\n  /// Whether the model sharing UI is currently presented.\n  @State private var showModelSharingUI = false\n  /// The error message to display in an alert.\n  @State private var errorAlertMessage: String?\n  /// Whether the delete confirmation sheet it is currently presented.\n  @State private var showDeleteConfirmation = false\n\n  var body: some View {\n    NavigationView {\n      VStack {\n        if modelData.models.isEmpty {\n          Text(\"To create your first model, tap the \\\"+\\\" icon in the toolbar.\")\n            .padding()\n        }\n        List {\n          Section(header: Text(\"Models\")) {\n            ForEach(modelData.models.indices, id: \\.self) { index in\n              let model = modelData.models[index]\n              NavigationLink(destination: ModelVisualizer(model: model)) {\n                Row(model: model)\n                  .contextMenu {\n                    makeShareButton()\n                    makeDeleteButton(index: index)\n                  }\n                  .sheet(isPresented: $showModelSharingUI) {\n                    ShareSheet(modelURL: model.url) {\n                      showModelSharingUI = false\n                    }\n                  }\n              }\n              .swipeActions {\n                makeDeleteButton(index: index)\n                makeShareButton()\n              }\n              .confirmationDialog(\n                \"Are you sure you want to delete \\\"\\(model.name)\\\"?\",\n                isPresented: $showDeleteConfirmation,\n                titleVisibility: .visible\n              ) {\n                Button(\"Delete\", role: .destructive) { removeModel(at: index) }\n              }\n            }\n            .accessibilityAction(named: Text(\"Share\")) {\n              showModelSharingUI.toggle()\n            }\n          }\n        }\n        .listStyle(GroupedListStyle())\n      }\n      .navigationBarTitle(Text(\"Image Classifier Builder\"), displayMode: .inline)\n      .alert(item: $errorAlertMessage) { message in\n        Alert(title: Text(\"Error\"), message: Text(message), dismissButton: .default(Text(\"OK\")))\n      }\n      .sheet(isPresented: $showModelCreationUI) {\n        NavigationView {\n          ModelCreationView { model in\n            add(model)\n            showModelCreationUI = false\n          }\n        }\n      }\n      .toolbar {\n        ToolbarItem(placement: .primaryAction) {\n          Button {\n            showModelCreationUI.toggle()\n          } label: {\n            Image(systemName: \"plus\")\n          }\n          .accessibilityLabel(\"Create a model\")\n          .accessibilityHint(\"Opens the model creation flow.\")\n        }\n        ToolbarItem(placement: .navigationBarLeading) {\n          Link(\n            destination: URL(\n              string:\n                \"https://github.com/tensorflow/examples/blob/master/lite/examples/classification_by_retrieval/README.md\"\n            )!\n          ) {\n            Image(systemName: \"questionmark.circle\")\n              .accessibilityLabel(\"Help\")\n              .accessibilityHint(\"Opens the help page.\")\n          }\n        }\n      }\n    }\n    .navigationViewStyle(.stack)\n    .onAppear {\n      loadModelData()\n    }\n    .onOpenURL { url in\n      do {\n        try importModel(at: url)\n      } catch {\n        errorAlertMessage = error.localizedDescription\n      }\n    }\n  }\n\n  /// Loads the models from disk.\n  private func loadModelData() {\n    do {\n      try modelData.load()\n    } catch {\n      fatalError(\"Failed to load the model data: \\(error)\")\n    }\n  }\n\n  /// Saves the models to disk.\n  private func saveModelData() {\n    do {\n      try modelData.save()\n    } catch {\n      fatalError(\"Failed to save the model data: \\(error)\")\n    }\n  }\n\n  /// Adds a model to the list.\n  ///\n  /// It is inserted in the first position, then the model data is saved immediately.\n  private func add(_ model: Model?) {\n    if let model = model {\n      modelData.models.insert(model, at: 0)\n      saveModelData()\n    }\n  }\n\n  /// Removes a model from the list.\n  ///\n  /// The model data is saved immediately.\n  private func removeModel(at index: Int) {\n    let model = modelData.models[index]\n    do {\n      try FileManager.default.removeItem(at: model.url)\n    } catch {\n      errorAlertMessage = \"Couldn't delete \\(model.name): \\(error.localizedDescription)\"\n      return\n    }\n    modelData.models.remove(at: index)\n    saveModelData()\n  }\n\n  /// Imports a model from a file URL.\n  private func importModel(at url: URL) throws {\n    guard url.pathExtension == \"tflite\" else {\n      throw RuntimeError(\"File extension is not `tflite`.\")\n    }\n\n    // Compose the new URL on disk.\n    let name = url.deletingPathExtension().lastPathComponent\n    let modelURL = Model.makeURLForNewModel(named: name)\n\n    // Copy the file to the location within the app.\n    guard url.startAccessingSecurityScopedResource() else {\n      throw RuntimeError(\"Couldn't access the security-scoped file at \\(url)\")\n    }\n    defer {\n      url.stopAccessingSecurityScopedResource()\n    }\n    do {\n      try FileManager.default.copyItem(at: url, to: modelURL)\n    } catch {\n      throw RuntimeError(\n        \"Couldn't make a local copy of the security-scoped file at \\(url): \\(error)\")\n    }\n\n    // Add a new Model instance.\n    let labelsCount = Classifier(modelURL: modelURL).labels().count\n    let size = (try? url.resourceValues(forKeys: [.fileSizeKey]))?.fileSize.map(Int64.init)\n    add(Model(name: name, labelsCount: labelsCount, size: size))\n  }\n\n  private func makeShareButton() -> some View {\n    Button {\n      showModelSharingUI.toggle()\n    } label: {\n      Label(\"Share\", systemImage: \"square.and.arrow.up\")\n    }\n  }\n\n  private func makeDeleteButton(index: Int) -> some View {\n    Button(role: .destructive) {\n      showDeleteConfirmation = true\n    } label: {\n      Label(\"Delete\", systemImage: \"trash\")\n    }\n  }\n\n  /// Displays a model in the list.\n  struct Row: View {\n    /// The represented model.\n    let model: Model\n    /// The model size formatter.\n    static private var formatter: ByteCountFormatter = {\n      let formatter = ByteCountFormatter()\n      formatter.allowedUnits = [.useMB]\n      formatter.countStyle = .memory\n      return formatter\n    }()\n\n    var body: some View {\n      HStack {\n        HStack(alignment: .lastTextBaseline) {\n          Text(model.name)\n          Text(\"\\(model.labelsCount) labels\")\n            .font(.caption)\n            .foregroundColor(.secondary)\n          if let modelSize = model.size {\n            Text(Row.formatter.string(fromByteCount: modelSize))\n              .font(.caption)\n              .foregroundColor(.secondary)\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/ModelMetadata.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Foundation\n\n/// The metadata to bake into a TFLite model file.\nstruct ModelMetadata {\n  /// The name of the model.\n  var name = \"\"\n  /// The description of the model.\n  var description = \"\"\n  /// The author of the model.\n  var author = \"\"\n  /// The version of the model.\n  var version = \"1.0\"\n  /// The license under which the model is released.\n  var license = \"Apache\"\n\n  /// Whether the metadata are complete enough to be baked into a TFLite model file.\n  var isValid: Bool {\n    !name.isEmpty && !description.isEmpty && !author.isEmpty && !version.isEmpty && !license.isEmpty\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/ModelTrainer.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Foundation\nimport Photos\nimport ios_ImageClassifierBuilderLibObjC\n\nstruct ModelTrainer {\n  /// Trains a new classification model based on user-selected Photos Library albums. Every image is\n  /// labeled after its album name.\n  ///\n  /// - Parameters:\n  ///   - metadata: The metadata of the model to create.\n  ///   - albums: The user-selected Photos Library albums.\n  /// - Returns: The Model object associated to the newly trained TFLite model.\n  func trainModel(metadata: ModelMetadata, on albums: [Album]) -> Model {\n    precondition(metadata.isValid)\n    precondition(!albums.isEmpty)\n\n    // Copy all images to a reasonable format in tmp.\n    let (labels, imagePaths) = prepareImages(from: albums)\n\n    // Create the file URL where the new model will be saved.\n    let modelURL = Model.makeURLForNewModel(named: metadata.name)\n\n    // Train the model.\n    ModelTrainingUtils.trainModel(\n      name: metadata.name, description: metadata.description, author: metadata.author,\n      version: metadata.version, license: metadata.license, labels: labels, imagePaths: imagePaths,\n      outputModelPath: modelURL.path)\n\n    // Get the number of distinct labels in the labelmap.\n    let labelsCount = Classifier(modelURL: modelURL).labels().count\n    let size = (try? modelURL.resourceValues(forKeys: [.fileSizeKey]))?.fileSize.map(Int64.init)\n    return Model(name: metadata.name, labelsCount: labelsCount, size: size)\n  }\n\n  /// Converts albums to a list of labels (the albums names, repeated as many times as there are\n  /// images in it) and an equally long list of image paths on disk (typically prepared copies\n  /// stored in the tmp directory).\n  ///\n  /// - Parameter albums: The user-selected Photos Library albums.\n  /// - Returns: The pair of lists of labels and image paths on disk.\n  private func prepareImages(from albums: [Album]) -> (labels: [String], imagePaths: [String]) {\n    var labels: [String] = []\n    var imagePaths: [String] = []\n    let dispatchGroup = DispatchGroup()\n    for album in albums {\n      // Iterate over the images.\n      let fetchResult = PHAsset.fetchAssets(in: album.collection, options: nil)\n      fetchResult.enumerateObjects { (asset, index, stop) in\n        guard asset.mediaType == .image else { return }\n\n        // Get the image URL.\n        dispatchGroup.enter()\n        asset.requestContentEditingInput(with: PHContentEditingInputRequestOptions()) {\n          (input, _) in\n          defer { dispatchGroup.leave() }\n          guard let input = input,\n            let imageURLInLibrary = input.fullSizeImageURL,\n            let label = album.collection.localizedTitle\n          else {\n            return\n          }\n\n          // Copy the image as resized JPEG to the temporary directory.\n          let tmpDirectory = URL(fileURLWithPath: NSTemporaryDirectory())\n          let options = PHImageRequestOptions()\n          options.isSynchronous = true\n          dispatchGroup.enter()\n          PHImageManager.default().requestImage(\n            for: asset, targetSize: CGSize(width: 640, height: 480), contentMode: .aspectFill,\n            options: options\n          ) { (image, _) in\n            defer { dispatchGroup.leave() }\n            if let data = image?.jpegData(compressionQuality: 1) {\n              var imageURL =\n                tmpDirectory.appendingPathComponent(imageURLInLibrary.lastPathComponent)\n              imageURL.deletePathExtension()\n              imageURL.appendPathExtension(for: .jpeg)\n              do {\n                try data.write(to: imageURL, options: .atomic)\n              } catch {\n                fatalError(\"Couldn't save training image to \\(imageURL): \\(error)\")\n              }\n              labels.append(label)\n              imagePaths.append(imageURL.path)\n            }\n          }\n        }\n      }\n    }\n    dispatchGroup.wait()\n    return (labels, imagePaths)\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/ModelTrainerView.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport SwiftUI\n\n/// Displays a progress view while the model is being trained, and the result once trained.\n///\n/// This screen is part of the model creation UX.\nstruct ModelTrainerView: View {\n  /// The metadata of the model to create.\n  let modelMetadata: ModelMetadata\n  /// The list of selected albums from the Photos Library.\n  let albums: [Album]\n  /// The closure to call once the Model object will be created.\n  let completion: (Model?) -> Void\n  /// The Model object associated to the model trained in this step.\n  @State private var model: Model?\n\n  var body: some View {\n    VStack {\n      if model == nil {\n        Text(\"The model is being trained…\")\n          .padding()\n        ProgressView()\n      } else {\n        Text(\"The model has been trained.\")\n          .padding()\n        Image(systemName: \"checkmark\")\n          .accessibility(hidden: true)\n      }\n    }\n    .onAppear {\n      // onAppear will strangely be called if the back button is tapped [1], on a new\n      // ModelTrainerView whose albums are not set.\n      // [1]: https://developer.apple.com/forums/thread/655338\n      guard !albums.isEmpty else { return }\n      DispatchQueue.global(qos: .userInitiated).async {\n        let model = ModelTrainer().trainModel(metadata: modelMetadata, on: albums)\n        DispatchQueue.main.async {\n          self.model = model\n        }\n      }\n    }\n    .navigationBarTitle(Text(\"Training\"), displayMode: .inline)\n    .toolbar {\n      ToolbarItem(placement: .primaryAction) {\n        if let model = model {\n          Button(\"Save\") {\n            completion(model)\n          }\n          .accessibilityHint(\"Saves the created model locally.\")\n        } else {\n          Button(\"Save\") {}\n            .disabled(true)\n            .accessibilityHint(\n              \"Saves the created model locally. Dimmed until the model has been created.\")\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/ModelTrainingUtils.h",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#import <Foundation/Foundation.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@class ModelMetadata;\n\n@interface ModelTrainingUtils : NSObject\n\n/// Trains a new classification model based on the labeled images.\n/// @param name The name of the model. This must be not empty.\n/// @param description The description of the model. This must be not empty.\n/// @param author The author of the model. This must be not empty.\n/// @param version The version of the model. This must be not empty.\n/// @param license The license of the model. This must be not empty.\n/// @param labels The list of labels. The count must match imagePaths'.\n/// @param imagePaths The list of image paths. The count must match labels'.\n/// @param outputModelPath The path on disk where to store the newly created model.\n+ (void)trainModelWithName:(NSString *)name\n               description:(NSString *)description\n                    author:(NSString *)author\n                   version:(NSString *)version\n                   license:(NSString *)license\n                    labels:(NSArray<NSString *> *)labels\n                imagePaths:(NSArray<NSString *> *)imagePaths\n           outputModelPath:(NSString *)outputModelPath\n    NS_SWIFT_NAME(trainModel(name:description:author:version:license:labels:imagePaths:outputModelPath:));\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/ModelTrainingUtils.mm",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#import \"ios/ImageClassifierBuilder/ModelTrainingUtils.h\"\n\n#import <CoreVideo/CoreVideo.h>\n#import <UIKit/UIKit.h>\n\n#include <cstdint>\n#include \"absl/status/status.h\"\n#include \"flatbuffers/util.h\"\n#import \"ios/ImageClassifierBuilder/NSData+PixelBuffer.h\"\n#import \"ios/ImageClassifierBuilder/NSString+AbseilStringView.h\"\n#import \"ios/ImageClassifierBuilder/UIImage+CoreVideo.h\"\n#include \"lib/model_builder.h\"\n#include \"tensorflow_lite_support/cc/port/statusor.h\"\n#include \"tensorflow_lite_support/cc/task/core/proto/external_file_proto_inc.h\"\n#include \"tensorflow_lite_support/cc/task/vision/core/frame_buffer.h\"\n#include \"tensorflow_lite_support/cc/task/vision/proto/image_embedder_options_proto_inc.h\"\n#include \"tensorflow_lite_support/cc/task/vision/utils/frame_buffer_common_utils.h\"\n\nusing ::tflite::task::core::ExternalFile;\nusing ::tflite::task::vision::FrameBuffer;\nusing ::tflite::task::vision::CreateFromRgbaRawBuffer;\nusing ::tflite::task::vision::ImageEmbedderOptions;\nusing ::tflite::examples::cbr::ModelBuilder;\n\nstatic NSString *const kEmbeddingModelName = @\"imagenet-mobilenet_v3_small_100_224-feature_vector\";\nstatic NSString *const kEmbeddingModelExtension = @\"tflite\";\n\n@implementation ModelTrainingUtils\n\n+ (void)trainModelWithName:(NSString *)name\n               description:(NSString *)description\n                    author:(NSString *)author\n                   version:(NSString *)version\n                   license:(NSString *)license\n                    labels:(NSArray<NSString *> *)labels\n                imagePaths:(NSArray<NSString *> *)imagePaths\n           outputModelPath:(NSString *)outputModelPath {\n  NSParameterAssert(name.length > 0);\n  NSParameterAssert(description.length > 0);\n  NSParameterAssert(author.length > 0);\n  NSParameterAssert(version.length > 0);\n  NSParameterAssert(license.length > 0);\n  NSParameterAssert(labels.count == imagePaths.count);\n  NSParameterAssert(labels.count >= 2);\n\n  // Create ModelBuilder.\n  ImageEmbedderOptions image_embedder_options;\n  NSString *embeddingModelPath =\n      [NSBundle.mainBundle pathForResource:kEmbeddingModelName ofType:kEmbeddingModelExtension];\n  NSAssert(embeddingModelPath, @\"Unable to locate image embedder model file.\");\n  image_embedder_options.mutable_model_file_with_metadata()->set_file_name(\n      embeddingModelPath.UTF8String);\n  std::unique_ptr<ModelBuilder> model_builder =\n      *ModelBuilder::CreateFromImageEmbedderOptions(image_embedder_options);\n\n  // Set the metadata.\n  model_builder->SetMetadata(name.UTF8String, description.UTF8String, author.UTF8String,\n                             version.UTF8String, license.UTF8String,\n                             {{\"model_type.txt\", \"IMAGE_CLASSIFIER\"}});\n\n  // Loop on images.\n  for (NSInteger i = 0; i < imagePaths.count; i++) {\n    // Decode image and load into a FrameBuffer.\n    UIImage *image = [[UIImage alloc] initWithContentsOfFile:imagePaths[i]];\n    NSAssert(image, @\"The image couldn't be loaded: %@\", imagePaths[i]);\n    CVPixelBufferRef pixelBuffer = [image cbr_asNewPixelBuffer];\n    NSData *data = [NSData cbr_RGBA8888DataFromPixelBuffer:pixelBuffer];\n    std::unique_ptr<FrameBuffer> frame_buffer =\n        CreateFromRgbaRawBuffer(static_cast<const uint8 *>(data.bytes),\n                                {static_cast<int>(CVPixelBufferGetWidth(pixelBuffer)),\n                                 static_cast<int>(CVPixelBufferGetHeight(pixelBuffer))});\n\n    // Add to model builder.\n    auto status = model_builder->AddLabeledImage(labels[i].UTF8String, *frame_buffer);\n    NSAssert(status.ok(), @\"Could not add labeled image to the model builder: %@\",\n             [NSString cbr_stringWithStringView:status.message()]);\n  }\n\n  // Finalize model building and write to file.\n  ExternalFile output_external_file = *model_builder->BuildModel();\n  BOOL fileSaved =\n      flatbuffers::SaveFile(outputModelPath.UTF8String, output_external_file.file_content(),\n                            /*binary=*/true);\n  NSAssert(fileSaved, @\"Could not save the trained model file to %@.\", outputModelPath);\n}\n\n@end\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/ModelVisualizer.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport SwiftUI\nimport ios_ImageClassifierBuilderLibObjC\n\n/// Displays the live feed from the camera and overlays classification results.\nstruct ModelVisualizer: View {\n  /// The model to run.\n  let model: Model\n  /// The queue on which classification is performed.\n  private let queue = DispatchQueue(\n    label: \"com.tensorflow.lite.swift.ClassificationByRetrieval.visualizer\")\n  /// The classifier running the `model`.\n  @State private var classifier: Classifier?\n  /// The potential current classification result.\n  @State private var result: [Classifier.Label]?\n  /// Whether to present the model info UI.\n  @State private var showModelInfo = false\n  /// The model metadata. This is stored once in order to avoid querying the classifier multiple\n  /// times.\n  @State private var modelMetadata = ModelMetadata()\n  /// The labels from the label map. This is stored once in order to avoid querying the classifier\n  /// multiple times.\n  @State private var labels: [String] = []\n  /// The presentation mode, to programmatically dismiss the visualizer.\n  @Environment(\\.presentationMode) var presentation\n\n  var body: some View {\n    ZStack(alignment: .bottom) {\n      ZStack(alignment: .topTrailing) {\n        CameraView(queue: queue) { imageBuffer in\n          let result = classifier?.classify(pixelBuffer: imageBuffer)\n          DispatchQueue.main.async {\n            if self.result?.first?.name != result?.first?.name {\n              UIAccessibility.post(notification: .announcement, argument: result?.first?.name)\n            }\n            self.result = result\n          }\n        }\n        .ignoresSafeArea()\n        Button {\n          showModelInfo = true\n        } label: {\n          Image(systemName: \"info.circle\")\n            .resizable()\n            .frame(width: 24, height: 24)\n            .foregroundColor(.primary)\n        }\n        .buttonStyle(RoundButton())\n        .sheet(isPresented: $showModelInfo) {\n          ModelInfoView(metadata: modelMetadata, labels: labels)\n        }\n      }\n      if let result = result {\n        ResultView(result: result)\n          .modifier(BottomPane())\n      } else {\n        Text(\"No result\")\n          .padding()\n          .modifier(BottomPane())\n      }\n    }\n    .accessibilityAction(.escape) {\n      presentation.wrappedValue.dismiss()\n    }\n    .navigationBarTitle(\"\", displayMode: .inline)\n    .navigationBarHidden(true)\n    .onAppear {\n      classifier = Classifier(modelURL: model.url)\n      if let classifier = classifier {\n        labels = classifier.labels()\n        modelMetadata = ModelMetadata(\n          name: classifier.name, description: classifier.modelDescription,\n          author: classifier.author, version: classifier.version, license: classifier.license)\n      }\n    }\n  }\n\n  /// Displays a classification result.\n  struct ResultView: View {\n    /// The best result, provided that it met a certain threshold.\n    private let bestLabels: [Classifier.Label]\n\n    init(result: [Classifier.Label]) {\n      bestLabels = result.prefix(Constants.maxLabels).filter { $0.score > Constants.threshold }\n    }\n\n    var body: some View {\n      VStack {\n        ForEach(bestLabels, id: \\.self) { label in\n          HStack {\n            Text(label.name)\n            Spacer()\n            Text(String(format: \"%.2f\", label.score))\n          }\n          ProgressView(value: label.score)\n            .progressViewStyle(LinearProgressViewStyle())\n        }\n      }\n      .padding()\n    }\n\n    private enum Constants {\n      /// The max number of labels to display.\n      static let maxLabels = 3\n      /// The score threshold for showing labels.\n      static let threshold = Float(0.2)\n    }\n  }\n\n  /// Common styling for views placed as bottom panes.\n  struct BottomPane: ViewModifier {\n    func body(content: Content) -> some View {\n      content\n        .frame(maxWidth: .infinity)\n        .background(\n          VisualEffectView(effect: UIBlurEffect(style: .systemThinMaterial))\n            .ignoresSafeArea())\n    }\n  }\n\n  /// Common styling for buttons.\n  struct RoundButton: ButtonStyle {\n    func makeBody(configuration: Configuration) -> some View {\n      configuration.label.background(\n        VisualEffectView(effect: UIBlurEffect(style: .systemThinMaterial))\n          .frame(width: 48, height: 48)\n          .clipShape(Circle())\n      )\n      .offset(x: -24, y: 24)\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/NSData+PixelBuffer.h",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#import <CoreVideo/CoreVideo.h>\n#import <Foundation/Foundation.h>\n\n@interface NSData (PixelBuffer)\n\n// Converts a pixel buffer to RGBA8888 (a.k.a. 32RGBA) data. It uses vImage internally. Supported\n// pixel formats are:\n// -   kCVPixelFormatType_32BGRA;\n// -   kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;\n// -   kCVPixelFormatType_420YpCbCr8BiPlanarFullRange.\n+ (instancetype)cbr_RGBA8888DataFromPixelBuffer:(CVPixelBufferRef)pixelBuffer;\n\n@end\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/NSData+PixelBuffer.m",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#import \"ios/ImageClassifierBuilder/NSData+PixelBuffer.h\"\n\n#import <Accelerate/Accelerate.h>\n\n@implementation NSData (PixelBuffer)\n\n+ (instancetype)cbr_RGBA8888DataFromPixelBuffer:(CVPixelBufferRef)pixelBuffer {\n  switch (CVPixelBufferGetPixelFormatType(pixelBuffer)) {\n    case kCVPixelFormatType_32BGRA:\n      return [self cbr_RGBA8888DataFromBGRAPixelBuffer:pixelBuffer];\n    case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:\n    case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:\n      return [self cbr_RGBA8888DataFromYUVPixelBuffer:pixelBuffer];\n    default:\n      NSLog(@\"Unsupported pixel buffer format. Supported format types are BGRA, 420v and 420f\");\n      return nil;\n  }\n}\n\n#pragma mark - Private\n\n+ (instancetype)cbr_RGBA8888DataFromBGRAPixelBuffer:(CVPixelBufferRef)pixelBuffer {\n  NSAssert(kCVPixelFormatType_32BGRA == CVPixelBufferGetPixelFormatType(pixelBuffer),\n           @\"Expected BGRA format.\");\n\n  // Lock the buffer address.\n  CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);\n\n  vImage_Buffer src = {\n      .data = CVPixelBufferGetBaseAddress(pixelBuffer),\n      .height = CVPixelBufferGetHeight(pixelBuffer),\n      .width = CVPixelBufferGetWidth(pixelBuffer),\n      .rowBytes = CVPixelBufferGetBytesPerRow(pixelBuffer),\n  };\n\n  // The input buffer might be 16-byte aligned (bytes per row != 4 x width). Since not all APIs\n  // support padding bytes, the destination buffer is explicitly allocated without such padding.\n  vImage_Buffer dest = {\n      .data = malloc(4 * src.width * src.height),\n      .height = src.height,\n      .width = src.width,\n      .rowBytes = 4 * src.width,\n  };\n  if (dest.data == NULL) {\n    NSLog(@\"Error initializing destination pixel buffer: OOM\");\n    return nil;\n  }\n\n  // Source is BGRA so B = 0, G = 1, R = 2, A = 3.\n  // Destination is RGBA, hence a permute map of 2, 1, 0, 3.\n  const uint8_t permuteMap[4] = {2, 1, 0, 3};\n\n  vImage_Error error = vImagePermuteChannels_ARGB8888(&src, &dest, permuteMap, kvImageNoFlags);\n  if (error != kvImageNoError) {\n    NSLog(@\"Error permuting pixel buffer channels: %ld\", error);\n    return nil;\n  }\n\n  // Unlock the buffer address.\n  CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);\n\n  // The NSData takes ownership of the buffer and will free it when done.\n  return [NSData dataWithBytesNoCopy:dest.data length:dest.height * dest.rowBytes];\n}\n\n+ (instancetype)cbr_RGBA8888DataFromYUVPixelBuffer:(CVPixelBufferRef)pixelBuffer {\n  OSType formatType = CVPixelBufferGetPixelFormatType(pixelBuffer);\n  if (kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange != formatType &&\n      kCVPixelFormatType_420YpCbCr8BiPlanarFullRange != formatType) {\n    NSLog(@\"Expected 420v or 420f format.\");\n    return nil;\n  }\n  vImage_Error error = kvImageNoError;\n\n  // Lock the buffer address.\n  CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);\n\n  vImage_Buffer srcYp = {\n      .data = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0),\n      .height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0),\n      .width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0),\n      .rowBytes = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0),\n  };\n  vImage_Buffer srcCbCr = {\n      .data = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1),\n      .height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1),\n      .width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1),\n      .rowBytes = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1),\n  };\n\n  vImage_Buffer dest;\n  error = vImageBuffer_Init(&dest, srcYp.height, srcYp.width, 32 /* pixelBits */, kvImageNoFlags);\n  if (error != kvImageNoError) {\n    NSLog(@\"Error initializing destination pixel buffer: %ld\", error);\n    return nil;\n  }\n\n  // Default output is ARGB so A = 0, R = 1, G = 2, B = 3.\n  // Destination is RGBA, hence a permute map of 1, 2, 3, 0.\n  const uint8_t permuteMap[4] = {1, 2, 3, 0};\n\n  vImage_YpCbCrToARGB *conversionMatrix =\n      (formatType == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)\n          ? [self cbr_YpCbCrFullRangeToARGBConversionMatrix]\n          : [self cbr_YpCbCrVideoRangeToARGBConversionMatrix];\n  error = vImageConvert_420Yp8_CbCr8ToARGB8888(&srcYp, &srcCbCr, &dest, conversionMatrix,\n                                               permuteMap, 255 /* alpha */, kvImageNoFlags);\n  if (error != kvImageNoError) {\n    NSLog(@\"Error converting YUV to RGBA: %ld\", error);\n    return nil;\n  }\n\n  // Unlock the buffer address.\n  CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);\n\n  // The NSData takes ownership of the buffer and will free it when done.\n  return [NSData dataWithBytesNoCopy:dest.data length:dest.height * dest.rowBytes];\n}\n\n+ (vImage_YpCbCrToARGB *)cbr_YpCbCrFullRangeToARGBConversionMatrix {\n  static vImage_YpCbCrToARGB fullRangeConversionMatrix;\n  static dispatch_once_t onceToken;\n  dispatch_once(&onceToken, ^{\n    // Full range 8-bit, clamped to full range.\n    // https://developer.apple.com/documentation/accelerate/vimage_ypcbcrpixelrange\n    vImage_YpCbCrPixelRange pixelRange = {\n        .Yp_bias = 0,\n        .CbCr_bias = 128,\n        .YpRangeMax = 255,\n        .CbCrRangeMax = 255,\n        .YpMax = 255,\n        .YpMin = 1,  // Wonder why not 0 in Apple doc.\n        .CbCrMax = 255,\n        .CbCrMin = 0,\n    };\n    vImage_Error error = vImageConvert_YpCbCrToARGB_GenerateConversion(\n        kvImage_YpCbCrToARGBMatrix_ITU_R_709_2, &pixelRange, &fullRangeConversionMatrix,\n        kvImage420Yp8_CbCr8, kvImageARGB8888, kvImageNoFlags);\n    NSAssert(error == kvImageNoError, @\"Error creating YUV to ARGB conversion matrix: %ld\", error);\n  });\n  return &fullRangeConversionMatrix;\n}\n\n+ (vImage_YpCbCrToARGB *)cbr_YpCbCrVideoRangeToARGBConversionMatrix {\n  static vImage_YpCbCrToARGB videoRangeConversionMatrix;\n  static dispatch_once_t onceToken;\n  dispatch_once(&onceToken, ^{\n    // Video range 8-bit, clamped to video range.\n    // https://developer.apple.com/documentation/accelerate/vimage_ypcbcrpixelrange\n    vImage_YpCbCrPixelRange pixelRange = {\n        .Yp_bias = 16,\n        .CbCr_bias = 128,\n        .YpRangeMax = 265,  // Wonder why not 235 in Apple doc.\n        .CbCrRangeMax = 240,\n        .YpMax = 235,\n        .YpMin = 16,\n        .CbCrMax = 240,\n        .CbCrMin = 0,\n    };\n    vImage_Error error = vImageConvert_YpCbCrToARGB_GenerateConversion(\n        kvImage_YpCbCrToARGBMatrix_ITU_R_709_2, &pixelRange, &videoRangeConversionMatrix,\n        kvImage420Yp8_CbCr8, kvImageARGB8888, kvImageNoFlags);\n    NSAssert(error == kvImageNoError, @\"Error creating YUV to ARGB conversion matrix: %ld\", error);\n  });\n  return &videoRangeConversionMatrix;\n}\n\n@end\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/NSString+AbseilStringView.h",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#import <Foundation/Foundation.h>\n\n#include \"absl/strings/string_view.h\"\n\n@interface NSString (AbseilStringView)\n\n// Returns an NSString with the contents of the string view.\n+ (instancetype)cbr_stringWithStringView:(const absl::string_view &)stringView;\n\n@end\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/NSString+AbseilStringView.mm",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#import \"ios/ImageClassifierBuilder/NSString+AbseilStringView.h\"\n\n@implementation NSString (AbseilStringView)\n\n+ (instancetype)cbr_stringWithStringView:(const absl::string_view &)stringView {\n  // Printing a string_view requires some adjustments. See tip at the bottom of\n  // https://abseil.io/tips/1\n  return [self stringWithFormat:@\"%.*s\", static_cast<int>(stringView.size()), stringView.data()];\n}\n\n@end\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/OnBoardingView.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport SwiftUI\n\nstruct OnBoardingView: View {\n  /// The closure to call once the Get Started button is tapped.\n  let completion: () -> Void\n\n  var body: some View {\n    VStack {\n      Text(\"Image Classifier Builder\")\n        .font(.largeTitle)\n        .padding()\n      Text(\n        \"\"\"\n        This app is designed for easy object classifier prototyping, not for any application \\\n        that requires a high level of accuracy. It is not designed and will not work well for \\\n        person classification. Please visit our \\\n        [source code page](https://github.com/tensorflow/examples/blob/master/lite/examples/classification_by_retrieval/README.md) \\\n        for technical details and responsible model building.\n        \"\"\"\n      )\n      .multilineTextAlignment(.center)\n      .padding()\n      Button {\n        completion()\n      } label: {\n        Text(\"Get Started\")\n      }\n      .buttonStyle(.borderedProminent)\n      .padding()\n    }\n  }\n}\n\n@available(iOS 13.0, *)\nstruct OnBoardingView_Previews: PreviewProvider {\n  static var previews: some View {\n    OnBoardingView {}\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/RuntimeError.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Foundation\n\n/// User-facing errors when managing the model list.\nstruct RuntimeError: Error {\n  /// The message to display\n  let localizedDescription: String\n\n  init(_ localizedDescription: String) {\n    self.localizedDescription = localizedDescription\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/ShareSheet.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport SwiftUI\nimport UIKit\n\n/// Adds support for Share (UIActivityViewController) in Swift UI.\nstruct ShareSheet: UIViewControllerRepresentable {\n  /// The model URL to share.\n  let modelURL: URL\n  /// The closure to call once the Share sheet did finish.\n  let completion: () -> Void\n\n  func makeUIViewController(context: Context) -> UIActivityViewController {\n    let activityViewController =\n      UIActivityViewController(activityItems: [modelURL], applicationActivities: nil)\n    activityViewController.completionWithItemsHandler = {\n      (_: UIActivity.ActivityType?, _: Bool, _: [Any]?, _: Error?) in\n      completion()\n    }\n    return activityViewController\n  }\n\n  func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/String+Identifiable.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Foundation\n\n/// Conforms String to Identifiable to be used as SwiftUI state for showing an alert.\nextension String: Identifiable {\n  public var id: Self { self }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/UIImage+CoreVideo.h",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#import <CoreVideo/CoreVideo.h>\n#import <UIKit/UIKit.h>\n\n@interface UIImage (CoreVideo)\n\n// Converts the image to a 32BGRA pixel buffer. The caller is responsible for releasing the pixel\n// buffer with CVPixelBufferRelease.\n- (CVPixelBufferRef)cbr_asNewPixelBuffer;\n\n@end\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/UIImage+CoreVideo.mm",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#import \"ios/ImageClassifierBuilder/UIImage+CoreVideo.h\"\n\n#import <CoreGraphics/CoreGraphics.h>\n\n@implementation UIImage (CoreVideo)\n\n- (CVPixelBufferRef)cbr_asNewPixelBuffer {\n  CGImageRef CGImage = self.CGImage;\n\n  // Create a pixel buffer.\n  CGSize frameSize = CGSizeMake(CGImageGetWidth(CGImage), CGImageGetHeight(CGImage));\n  NSDictionary<NSString *, NSValue *> *options = @{\n    (__bridge NSString *)kCVPixelBufferCGImageCompatibilityKey : @YES,\n    (__bridge NSString *)kCVPixelBufferCGBitmapContextCompatibilityKey : @YES,\n  };\n  CVPixelBufferRef pixelBuffer = NULL;\n  CVPixelBufferCreate(kCFAllocatorDefault, frameSize.width, frameSize.height,\n                      kCVPixelFormatType_32BGRA, (__bridge CFDictionaryRef)options, &pixelBuffer);\n\n  // Access the buffer.\n  CVPixelBufferLockBaseAddress(pixelBuffer, 0);\n  void *pixelData = CVPixelBufferGetBaseAddress(pixelBuffer);\n\n  // Create a context to draw into.\n  CGColorSpaceRef RGBColorSpace = CGColorSpaceCreateDeviceRGB();\n  CGContextRef context = CGBitmapContextCreate(\n      pixelData, frameSize.width, frameSize.height, 8, CVPixelBufferGetBytesPerRow(pixelBuffer),\n      RGBColorSpace, (CGBitmapInfo)kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);\n\n  // Draw the image.\n  CGContextDrawImage(context, CGRectMake(0, 0, frameSize.width, frameSize.height), CGImage);\n\n  CGColorSpaceRelease(RGBColorSpace);\n  CGContextRelease(context);\n  CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);\n  return pixelBuffer;\n}\n\n@end\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/UINavigationController+Additions.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n/// Enables swipe-to-go-back, even when the navigation bar is hidden.\nextension UINavigationController: UIGestureRecognizerDelegate {\n  override open func viewDidLoad() {\n    super.viewDidLoad()\n    interactivePopGestureRecognizer?.delegate = self\n  }\n\n  public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {\n    // Allow swipe-to-go-back as long as there are more than 2 view controllers on the stack.\n    return viewControllers.count > 1\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/ImageClassifierBuilder/VisualEffectView.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport SwiftUI\nimport UIKit\n\n/// Adds support for visual effect views in Swift UI.\nstruct VisualEffectView: UIViewRepresentable {\n  var effect: UIVisualEffect?\n\n  func makeUIView(context: UIViewRepresentableContext<Self>) -> UIVisualEffectView {\n    UIVisualEffectView()\n  }\n\n  func updateUIView(_ uiView: UIVisualEffectView, context: UIViewRepresentableContext<Self>) {\n    uiView.effect = effect\n  }\n}\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/ios/README.md",
    "content": "# TensorFlow Lite classification by retrieval training iOS example application\n\n## Overview\n\nThis is an example application for\n[TensorFlow Lite](https://tensorflow.org/lite) on iOS. It uses\n[Classification-by-Retrieval](../README.md) to train classification models out\nof a small sample of user-provided images.\n\nThese instructions walk you through building and running the demo on an iOS\ndevice.\n\n| ![Demo of the iOS app](https://user-images.githubusercontent.com/197492/142282646-679b960c-e10d-4570-85e3-ba479225ee29.gif) |\n| ------ |\n\n## Requirements\n\n*   Device with iOS 14.0 or above\n\n*   Xcode 12.5 or above\n\n*   Valid Apple Developer ID\n\n*   Xcode command-line tools (run `xcode-select --install`)\n\nIf this is a new install, you will need to run the Xcode application once to\nagree to the license before continuing.\n\nNote: The demo app requires a camera and must be executed on a real iOS device.\nYou can build it and run with the iPhone Simulator, but the app will raise a\n`Camera not found` exception.\n\n## Build and run\n\n1.  Clone this GitHub repository to your workstation.\n\n    ```\n    $ git clone https://github.com/tensorflow/examples.git\n    ```\n\n1.  Change directory to the Classification-by-Retrieval example\n\n    ```\n    $ cd examples/lite/examples/classification_by_retrieval\n    ```\n\n1.  Build for a simulator with the following command:\n\n    ```\n    $ bazel build -c opt --config=ios_x86_64 ios:ImageClassifierBuilder\n    ```\n\n1.  To build for a device:\n    1.  You'll first need to obtain a mobile provisioning profile from Apple\n        ([documentation]).\n    1.  Make a symlink to your profile like so:\n\n        ```\n        $ ln -s <path/to/your/profile.mobileprovision> ProvisioningProfile.mobileprovision\n        ```\n\n    1.  Uncomment all occurrences of `ProvisioningProfile.mobileprovision` in\n        `ios/BUILD`.\n    1.  Finally, build for a device with the following command:\n\n        ```\n        $ bazel build -c opt --config=ios_arm64 ios:ImageClassifierBuilder\n        ```\n\nYou'll have to grant permissions for the app to use the device's camera and\nPhoto Library.\n\nFirst you'll create a model out of albums of your Photo Library, then you'll be\nable to see how it performs live: point the camera at things you trained your\nmodel with and enjoy seeing how it classifies them!\n\n[documentation]: https://developer.apple.com/documentation/appstoreconnectapi/profiles\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/lib/BUILD",
    "content": "# Description:\n#    Classification-by-retrieval model builder library (C++).\n\nload(\"//third_party/bazel_rules/rules_cc/cc:cc_library.bzl\", \"cc_library\")\n\npackage(\n    default_applicable_licenses = [\"//third_party/py/tensorflow_examples:license\"],\n    default_visibility = [\"//visibility:public\"],\n)\n\nlicenses([\"notice\"])\n\ncc_library(\n    name = \"tflite_builder\",\n    srcs = [\"tflite_builder.cc\"],\n    hdrs = [\"tflite_builder.h\"],\n    deps = [\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_absl//absl/memory\",\n        \"@com_google_absl//absl/status\",\n        \"@com_google_absl//absl/types:optional\",\n        \"@flatbuffers\",\n        \"@org_tensorflow//tensorflow/lite:framework\",\n        \"@org_tensorflow//tensorflow/lite/schema:schema_fbs\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/port:status_macros\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/port:statusor\",\n    ],\n)\n\ncc_library(\n    name = \"tflite_cbr_builder\",\n    srcs = [\"tflite_cbr_builder.cc\"],\n    hdrs = [\"tflite_cbr_builder.h\"],\n    deps = [\n        \":tflite_builder\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_absl//absl/memory\",\n        \"@com_google_absl//absl/status\",\n        \"@com_google_absl//absl/strings:str_format\",\n        \"@flatbuffers\",\n        \"@org_tensorflow//tensorflow/lite:framework\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/port:status_macros\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/port:statusor\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/task/vision/proto:embeddings_proto_inc\",\n    ],\n)\n\ncc_library(\n    name = \"model_builder\",\n    srcs = [\"model_builder.cc\"],\n    hdrs = [\"model_builder.h\"],\n    deps = [\n        \":tflite_cbr_builder\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_absl//absl/status\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_google_absl//absl/strings:str_format\",\n        \"@flatbuffers//:runtime_cc\",\n        \"@org_tensorflow//tensorflow/lite:framework\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/port:status_macros\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/port:statusor\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/task/core/proto:external_file_proto_inc\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/task/vision:image_embedder\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/task/vision/core:frame_buffer\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/task/vision/proto:embeddings_proto_inc\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/task/vision/proto:image_embedder_options_proto_inc\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/metadata:metadata_schema_cc\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/metadata/cc:metadata_populator\",\n    ],\n)\n\ncc_library(\n    name = \"labeled_image_helper\",\n    srcs = [\"labeled_image_helper.cc\"],\n    hdrs = [\"labeled_image_helper.h\"],\n    deps = [\n        \":model_builder\",\n        \"@com_google_absl//absl/status\",\n        \"@com_google_absl//absl/strings\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/port:status_macros\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/port:statusor\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/task/vision/core:frame_buffer\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/task/vision/utils:frame_buffer_common_utils\",\n        \"@org_tensorflow_lite_support//tensorflow_lite_support/cc/task/vision/utils:image_utils\",\n    ],\n)\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/lib/labeled_image_helper.cc",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"lib/labeled_image_helper.h\"\n\n#include <string>\n\n#include \"absl/status/status.h\"\n#include \"absl/strings/str_cat.h\"\n#include \"lib/model_builder.h\"\n#include \"tensorflow_lite_support/cc/port/status_macros.h\"\n#include \"tensorflow_lite_support/cc/port/statusor.h\"\n#include \"tensorflow_lite_support/cc/task/vision/core/frame_buffer.h\"\n#include \"tensorflow_lite_support/cc/task/vision/utils/frame_buffer_common_utils.h\"\n#include \"tensorflow_lite_support/cc/task/vision/utils/image_utils.h\"\n\nnamespace tflite {\nnamespace examples {\nnamespace cbr {\n\nnamespace {\n\nusing ::tflite::task::vision::CreateFromRgbaRawBuffer;\nusing ::tflite::task::vision::CreateFromRgbRawBuffer;\nusing ::tflite::task::vision::DecodeImageFromFile;\nusing ::tflite::task::vision::FrameBuffer;\nusing ::tflite::task::vision::ImageData;\nusing ::tflite::task::vision::ImageDataFree;\n\n}  // namespace\n\ntflite::support::StatusOr<std::unique_ptr<FrameBuffer>>\nBuildFrameBufferFromImageData(const ImageData& image) {\n  std::unique_ptr<FrameBuffer> frame_buffer;\n  if (image.channels == 3) {\n    return CreateFromRgbRawBuffer(image.pixel_data,\n                                  {image.width, image.height});\n  } else if (image.channels == 4) {\n    return CreateFromRgbaRawBuffer(image.pixel_data,\n                                   {image.width, image.height});\n  }\n  return absl::InvalidArgumentError(\n      absl::StrCat(\"Expected image with 3 (RGB) or 4 (RGBA) channels, found \",\n                   image.channels));\n}\n\nabsl::Status AddLabeledImageFromPath(ModelBuilder* model_builder,\n                                     const std::string& label,\n                                     const std::string& image_file_path) {\n  // Decode image and load into a FrameBuffer.\n  ASSIGN_OR_RETURN(ImageData image_data, DecodeImageFromFile(image_file_path));\n  ASSIGN_OR_RETURN(std::unique_ptr<FrameBuffer> frame_buffer,\n                   BuildFrameBufferFromImageData(image_data));\n  // Add to model builder.\n  RETURN_IF_ERROR(model_builder->AddLabeledImage(label, *frame_buffer));\n  // Cleanup.\n  ImageDataFree(&image_data);\n  return absl::OkStatus();\n}\n\n}  // namespace cbr\n}  // namespace examples\n}  // namespace tflite\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/lib/labeled_image_helper.h",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef TENSORFLOW_LITE_EXAMPLES_CLASSIFICATION_BY_RETRIEVAL_LIB_TESTS_LABELED_IMAGE_HELPER_H_\n#define TENSORFLOW_LITE_EXAMPLES_CLASSIFICATION_BY_RETRIEVAL_LIB_TESTS_LABELED_IMAGE_HELPER_H_\n\n#include <string>\n\n#include \"absl/status/status.h\"\n#include \"lib/model_builder.h\"\n#include \"tensorflow_lite_support/cc/task/vision/core/frame_buffer.h\"\n#include \"tensorflow_lite_support/cc/task/vision/utils/image_utils.h\"\n\nnamespace tflite {\nnamespace examples {\nnamespace cbr {\n\n// Returns a FrambeBuffer view of `image`.\ntflite::support::StatusOr<std::unique_ptr<::tflite::task::vision::FrameBuffer>>\nBuildFrameBufferFromImageData(const ::tflite::task::vision::ImageData& image);\n\n// A helper function that reads an image from the given path, converts it to a\n// frame buffer and adds it to the model builder with the provided label.\nabsl::Status AddLabeledImageFromPath(ModelBuilder* model_builder,\n                                     const std::string& label,\n                                     const std::string& image_file_path);\n\n}  // namespace cbr\n}  // namespace examples\n}  // namespace tflite\n\n#endif  // TENSORFLOW_LITE_EXAMPLES_CLASSIFICATION_BY_RETRIEVAL_LIB_TESTS_LABELED_IMAGE_HELPER_H_\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/lib/model_builder.cc",
    "content": "#include \"lib/model_builder.h\"\n\n#include <memory>\n\n#include \"absl/status/status.h\"\n#include \"absl/strings/str_format.h\"\n#include \"absl/strings/str_join.h\"\n#include \"flatbuffers/flatbuffers.h\"\n#include \"tensorflow_lite_support/cc/port/status_macros.h\"\n#include \"tensorflow_lite_support/cc/task/vision/proto/embeddings_proto_inc.h\"\n#include \"tensorflow_lite_support/metadata/cc/metadata_populator.h\"\n#include \"tensorflow_lite_support/metadata/metadata_schema_generated.h\"\n\nnamespace tflite {\nnamespace examples {\nnamespace cbr {\n\nnamespace {\n\nconstexpr char kLabelMapFilename[] = \"labelmap.txt\";\n\nusing ::flatbuffers::FlatBufferBuilder;\nusing ::tflite::FlatBufferModel;\nusing ::tflite::metadata::ModelMetadataPopulator;\nusing ::tflite::task::core::ExternalFile;\nusing ::tflite::task::vision::EmbeddingResult;\nusing ::tflite::task::vision::FeatureVector;\nusing ::tflite::task::vision::FrameBuffer;\nusing ::tflite::task::vision::ImageEmbedder;\nusing ::tflite::task::vision::ImageEmbedderOptions;\n\n}  // namespace\n\nModelBuilder::ModelBuilder(std::unique_ptr<ImageEmbedder> image_embedder,\n                           std::unique_ptr<FlatBufferModel> model,\n                           std::unique_ptr<TfLiteCbRBuilder> tflite_cbr_builder)\n    : image_embedder_(std::move(image_embedder)),\n      model_(std::move(model)),\n      tflite_cbr_builder_(std::move(tflite_cbr_builder)) {}\n\n/* static */\ntflite::support::StatusOr<std::unique_ptr<ModelBuilder>>\nModelBuilder::CreateFromImageEmbedderOptions(\n    const ImageEmbedderOptions& options) {\n  // Quantization is not supported.\n  if (options.quantize()) {\n    return absl::UnimplementedError(\n        \"Scalar quantization through the `quantize` option is not supported.\");\n  }\n  // Build ImageEmbedder.\n  ASSIGN_OR_RETURN(std::unique_ptr<ImageEmbedder> image_embedder,\n                   ImageEmbedder::CreateFromOptions(options));\n  // Multi-head classifiers are not supported either.\n  if (image_embedder->GetNumberOfOutputLayers() != 1) {\n    return absl::UnimplementedError(\n        \"Multi-head ImageEmbedder-s are not supported\");\n  }\n  const std::string& model_file =\n      options.model_file_with_metadata().file_name();\n  std::unique_ptr<FlatBufferModel> model =\n      FlatBufferModel::BuildFromFile(model_file.c_str());\n  if (model == nullptr) {\n    return absl::InvalidArgumentError(\"Failed to build model from file: \" +\n                                      model_file);\n  }\n  return std::make_unique<ModelBuilder>(std::move(image_embedder),\n                                        std::move(model));\n}\n\nvoid ModelBuilder::SetMetadata(\n    const std::string& name, const std::string& description,\n    const std::string& author, const std::string& version,\n    const std::string& license,\n    const absl::flat_hash_map<std::string, std::string>& associated_files) {\n  name_ = name;\n  description_ = description;\n  author_ = author;\n  version_ = version;\n  license_ = license;\n  associated_files_ = associated_files;\n}\n\nabsl::Status ModelBuilder::AddLabeledImage(\n    const std::string& label,\n    const ::tflite::task::vision::FrameBuffer& frame_buffer) {\n  ASSIGN_OR_RETURN(const EmbeddingResult& embedding_result,\n                   image_embedder_->Embed(frame_buffer));\n  feature_vectors_.emplace_back(\n      image_embedder_->GetEmbeddingByIndex(embedding_result, 0)\n          .feature_vector());\n  labels_.emplace_back(label);\n  return absl::OkStatus();\n}\n\ntflite::support::StatusOr<std::string> ModelBuilder::PopulateMetadata(\n    const char* buffer_data, size_t buffer_size) {\n  // Copy metadata from original model.\n  tflite::ModelMetadataT model_metadata_t;\n  image_embedder_->GetMetadataExtractor()->GetModelMetadata()->UnPackTo(\n      &model_metadata_t);\n  // Sanity checks.\n  if (model_metadata_t.subgraph_metadata.size() != 1) {\n    return absl::InternalError(\n        absl::StrFormat(\"Expected exactly one subgraph metadata, found %d.\",\n                        model_metadata_t.subgraph_metadata.size()));\n  }\n  if (model_metadata_t.subgraph_metadata[0]->output_tensor_metadata.size() !=\n      1) {\n    return absl::InternalError(absl::StrFormat(\n        \"Expected exactly one output tensor metadata, found %d.\",\n        model_metadata_t.subgraph_metadata[0]->output_tensor_metadata.size()));\n  }\n\n  // Set the relevant fields for the new model.\n  model_metadata_t.name = name_;\n  model_metadata_t.description = description_;\n  model_metadata_t.author = author_;\n  model_metadata_t.version = version_;\n  model_metadata_t.license = license_;\n\n  // Add the associated files.\n  for (auto it = associated_files_.begin(); it != associated_files_.end();\n       it++) {\n    auto associated_file_t = std::make_unique<tflite::AssociatedFileT>();\n    associated_file_t->name = it->first;\n    associated_file_t->type = tflite::AssociatedFileType_DESCRIPTIONS;\n    model_metadata_t.associated_files.push_back(std::move(associated_file_t));\n  }\n\n  // Build minimalistic output tensor metadata.\n  auto tensor_metadata_t = std::make_unique<tflite::TensorMetadataT>();\n\n  // Add the labelmap file.\n  auto associated_file_t = std::make_unique<tflite::AssociatedFileT>();\n  associated_file_t->name = kLabelMapFilename;\n  associated_file_t->type = tflite::AssociatedFileType_TENSOR_AXIS_LABELS;\n  tensor_metadata_t->associated_files.push_back(std::move(associated_file_t));\n\n  // Replace output tensor metadata.\n  model_metadata_t.subgraph_metadata[0]->output_tensor_metadata[0] =\n      std::move(tensor_metadata_t);\n\n  // Pack metadata.\n  FlatBufferBuilder fbb;\n  fbb.Finish(tflite::ModelMetadata::Pack(fbb, &model_metadata_t),\n             tflite::ModelMetadataIdentifier());\n\n  // Populate metadata.\n  ASSIGN_OR_RETURN(\n      auto metadata_populator,\n      ModelMetadataPopulator::CreateFromModelBuffer(buffer_data, buffer_size));\n  metadata_populator->LoadMetadata(\n      reinterpret_cast<char*>(fbb.GetBufferPointer()), fbb.GetSize());\n\n  // Add the labelmap file to the map of associated files and load them in the\n  // metadata populator.\n  std::string labelmap = absl::StrJoin(labels_, \"\\n\");\n  associated_files_.insert({{kLabelMapFilename, labelmap}});\n  metadata_populator->LoadAssociatedFiles(associated_files_);\n  return metadata_populator->Populate();\n}\n\ntflite::support::StatusOr<ExternalFile> ModelBuilder::BuildModel() {\n  // Sanity checks.\n  if (feature_vectors_.size() < 2) {\n    return absl::FailedPreconditionError(\n        absl::StrFormat(\"Expected at least two labeled images to have been \"\n                        \"provided through `AddLabeledImage`, found %d.\",\n                        feature_vectors_.size()));\n  }\n  if (labels_.size() != feature_vectors_.size()) {\n    return absl::InternalError(\n        \"Expected same number of labels and feature vectors.\");\n  }\n  FlatBufferBuilder fbb;\n  ASSIGN_OR_RETURN(labels_,\n                   tflite_cbr_builder_->BuildCbRModel(\n                       *model_->GetModel(), feature_vectors_, labels_, &fbb));\n\n  // Populate metadata.\n  ExternalFile model_external_file;\n  ASSIGN_OR_RETURN(\n      *model_external_file.mutable_file_content(),\n      PopulateMetadata(reinterpret_cast<char*>(fbb.GetBufferPointer()),\n                       fbb.GetSize()));\n\n  // Reset state and return.\n  name_ = \"\";\n  description_ = \"\";\n  author_ = \"\";\n  version_ = \"\";\n  license_ = \"\";\n  associated_files_ = {};\n  labels_.clear();\n  feature_vectors_.clear();\n  return model_external_file;\n}\n\n}  // namespace cbr\n}  // namespace examples\n}  // namespace tflite\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/lib/model_builder.h",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef TENSORFLOW_LITE_EXAMPLES_CLASSIFICATION_BY_RETRIEVAL_LIB_MODEL_BUILDER_H_\n#define TENSORFLOW_LITE_EXAMPLES_CLASSIFICATION_BY_RETRIEVAL_LIB_MODEL_BUILDER_H_\n\n#include <memory>\n#include <vector>\n\n#include \"absl/container/flat_hash_map.h\"\n#include \"absl/status/status.h\"\n#include \"lib/tflite_cbr_builder.h\"\n#include \"tensorflow/lite/model.h\"\n#include \"tensorflow_lite_support/cc/port/statusor.h\"\n#include \"tensorflow_lite_support/cc/task/core/proto/external_file_proto_inc.h\"\n#include \"tensorflow_lite_support/cc/task/vision/core/frame_buffer.h\"\n#include \"tensorflow_lite_support/cc/task/vision/image_embedder.h\"\n#include \"tensorflow_lite_support/cc/task/vision/proto/embeddings_proto_inc.h\"\n#include \"tensorflow_lite_support/cc/task/vision/proto/image_embedder_options_proto_inc.h\"\n\nnamespace tflite {\nnamespace examples {\nnamespace cbr {\n\n// TODO(b/183007585): add tests for this class once complete.\n// Encapsulates the logic required to transform an embedder model into a\n// classification-by-retrieval model.\nclass ModelBuilder {\n public:\n  // For testing purposes; prefer using CreateFromImageEmbedderOptions.\n  explicit ModelBuilder(\n      std::unique_ptr<::tflite::task::vision::ImageEmbedder> image_embedder,\n      std::unique_ptr<::tflite::FlatBufferModel> model,\n      std::unique_ptr<TfLiteCbRBuilder> tflite_cbr_builder =\n          std::make_unique<TfLiteCbRBuilder>());\n\n  // Initializes the ModelBuilder from the provided ImageEmbedderOptions.\n  static tflite::support::StatusOr<std::unique_ptr<ModelBuilder>>\n  CreateFromImageEmbedderOptions(\n      const ::tflite::task::vision::ImageEmbedderOptions& options);\n\n  // Sets the metadata fields associated with the model. This is flushed when\n  // `BuildModel()` returns and needs to be called again as needed.\n  //\n  // `associated_files` is a map of {filename, file contents}.\n  // The files are added with the DESCRIPTIONS type.\n  void SetMetadata(\n      const std::string& name, const std::string& description,\n      const std::string& author, const std::string& version,\n      const std::string& license,\n      const absl::flat_hash_map<std::string, std::string>& associated_files);\n\n  // Extracts the embedding on the provided FrameBuffer and stores it along\n  // with the provided label. This method is meant to be called multiple times\n  // on each labeled image used to create the classification-by-retrieval model\n  // before a final call to `BuildModel()` actually creates and returns the\n  // final model.\n  absl::Status AddLabeledImage(\n      const std::string& label,\n      const ::tflite::task::vision::FrameBuffer& frame_buffer);\n\n  // Finalizes the classification-by-retrieval model construction using the\n  // feature vectors extracted along the successive (at least two) calls to\n  // `AddLabeledImage()`. If less than two labeled images have been added, this\n  // function returns an absl::FailedPreconditionError.\n  //\n  // The returned model includes model metadata (i.e. it's ready for use in the\n  // ImageClassifier Task API). It is returned as an ExternalFile proto with the\n  // `file_content` field set. It can either be used directly to build an\n  // ImageClassifierOption proto and initialize an ImageClassifier, or written\n  // to disk as a .tflite file.\n  //\n  // Except if an error is returned, calling this function also resets the\n  // ModelBuilder, so that the process of adding new labeled images and building\n  // another model can be repeated.\n  tflite::support::StatusOr<::tflite::task::core::ExternalFile> BuildModel();\n\n private:\n  // Populates metadata on the provided model buffer, returns the model with\n  // metadata as a string.\n  tflite::support::StatusOr<std::string> PopulateMetadata(\n      const char* buffer_data, size_t buffer_size);\n\n  // The ImageEmbedder built from options provided at initialization time.\n  std::unique_ptr<::tflite::task::vision::ImageEmbedder> image_embedder_;\n  // The TfLite embedding model built from options provided at initialization\n  // time. This is the identical model that is the used by `image_embedder_`.\n  std::unique_ptr<::tflite::FlatBufferModel> model_;\n  // The TfLiteCbrBuilder to use for creating the classification-by-retrieval\n  // TFLite model.\n  std::unique_ptr<TfLiteCbRBuilder> tflite_cbr_builder_;\n\n  // Model details, filled by the last call to `SetMetadata()` and flushed on\n  // final call to `BuildModel()`.\n  std::string name_;\n  std::string description_;\n  std::string author_;\n  std::string version_;\n  std::string license_;\n  absl::flat_hash_map<std::string, std::string> associated_files_;\n  // The list of currently added labels, filled along successive calls to\n  // `AddLabeledImage()` and flushed on final call to `BuildModel()`.\n  std::vector<std::string> labels_;\n  // The list of currently extracted feature vectors, filled along successive\n  // calls to `AddLabeledImage()` and flushed on final call to `BuildModel()`.\n  std::vector<::tflite::task::vision::FeatureVector> feature_vectors_;\n};\n\n}  // namespace cbr\n}  // namespace examples\n}  // namespace tflite\n\n#endif  // TENSORFLOW_LITE_EXAMPLES_CLASSIFICATION_BY_RETRIEVAL_LIB_MODEL_BUILDER_H_\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/lib/tflite_builder.cc",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"lib/tflite_builder.h\"\n\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"absl/container/flat_hash_map.h\"\n#include \"absl/memory/memory.h\"\n#include \"absl/status/status.h\"\n#include \"absl/types/optional.h\"\n#include \"flatbuffers/flatbuffers.h\"\n#include \"tensorflow/lite/model.h\"\n#include \"tensorflow/lite/schema/schema_generated.h\"\n#include \"tensorflow_lite_support/cc/port/status_macros.h\"\n#include \"tensorflow_lite_support/cc/port/statusor.h\"\n\nnamespace tflite {\nnamespace examples {\nnamespace cbr {\n\nnamespace {\n\nusing ::flatbuffers::FlatBufferBuilder;\nusing ::flatbuffers::Offset;\nusing ::flatbuffers::Vector;\n\n// Re-implementation of tflite::GetBuiltinCode.\nBuiltinOperator GetBuiltinCode(const OperatorCodeT& op_code) {\n  return std::max(op_code.builtin_code, static_cast<BuiltinOperator>(\n                                            op_code.deprecated_builtin_code));\n}\n\nOffset<QuantizationParameters> CreateQuantizationParameters(\n    FlatBufferBuilder* fbb, const QuantizationParametersT& params) {\n  return ::tflite::CreateQuantizationParameters(\n      *fbb, params.min.empty() ? 0 : fbb->CreateVector(params.min),\n      params.max.empty() ? 0 : fbb->CreateVector(params.max),\n      params.scale.empty() ? 0 : fbb->CreateVector(params.scale),\n      params.zero_point.empty() ? 0 : fbb->CreateVector(params.zero_point));\n}\n\n// A convenience class for adding layers to a TF Lite model.\nclass TfLiteBuilderImpl : public TfLiteBuilder {\n public:\n  // Creates a new instance of TfLiteBuilderImpl.\n  // The caller keeps the ownership of `fbb`.\n  static tflite::support::StatusOr<std::unique_ptr<TfLiteBuilderImpl>> New(\n      FlatBufferBuilder* fbb);\n\n  // Creates a new instance of TfLiteBuilderImpl and initializes it by copying\n  // all the data in `model`. The caller keeps the ownership of `fbb`.\n  static tflite::support::StatusOr<std::unique_ptr<TfLiteBuilderImpl>> New(\n      const tflite::Model& model, FlatBufferBuilder* fbb);\n\n  int AddTensor(const std::string& name, TensorType type,\n                const std::vector<int32_t>& shape) override {\n    return AddTensor(name, type, shape, absl::nullopt);\n  }\n\n  int AddQuantizedTensor(\n      const std::string& name, TensorType type,\n      const std::vector<int32_t>& shape,\n      const QuantizationParametersT& quantization_param) override {\n    return AddTensor(name, type, shape, quantization_param);\n  }\n\n  int AddConstTensor(const std::string& name, TensorType type,\n                     const std::vector<int32_t>& shape, const uint8_t* data,\n                     size_t size) override {\n    return AddConstTensor(name, type, shape, data, size, absl::nullopt);\n  }\n\n  int AddQuantizedConstTensor(\n      const std::string& name, TensorType type,\n      const std::vector<int32_t>& shape, const uint8_t* data, size_t size,\n      const QuantizationParametersT& quantization_param) override {\n    return AddConstTensor(name, type, shape, data, size, quantization_param);\n  }\n\n  void AddOperator(BuiltinOperator op_code,\n                   const std::vector<int>& input_tensor_indices,\n                   int output_tensor_index, BuiltinOptions option_code,\n                   const flatbuffers::Offset<void>& options) override;\n\n  void Build(const std::vector<int32_t>& inputs, int32_t output_tensor_index,\n             const std::string& name, uint32_t version,\n             const std::string& description) override;\n\n private:\n  // Caller keeps the ownership of `fbb`.\n  explicit TfLiteBuilderImpl(FlatBufferBuilder* fbb) : fbb_(fbb) {}\n\n  // Clones all model buffers.\n  absl::Status CloneBuffers(const Model& model);\n  // Clones all model tensors, assuming the model has only one subgraph. It also\n  // assumes that the buffers were cloned (only once) using the above\n  // CloneBuffers().\n  absl::Status CloneTensors(const Model& model);\n  // Clones all model operator codes. No deduping.\n  absl::Status CloneOperatorCodes(const Model& model);\n  // Clones all model operators. It assumes that the operator codes were cloned\n  // (only once) using the above CloneOperatorCodes().\n  absl::Status CloneOperators(const Model& model);\n  // Clones all metadata. In fact, most metadata (model name, description, etc)\n  // needs to be modified but for now, we clone everything for easiness of\n  // implementation.\n  absl::Status CloneMetadata(const Model& model);\n\n  int AddTensor(\n      const std::string& name, TensorType type,\n      const std::vector<int32_t>& shape,\n      const absl::optional<QuantizationParametersT>& quantization_opt);\n\n  int AddConstTensor(\n      const std::string& name, TensorType type,\n      const std::vector<int32_t>& shape, const uint8_t* data, size_t size,\n      const absl::optional<QuantizationParametersT>& quantization_opt);\n\n  FlatBufferBuilder* fbb_;\n  std::vector<Offset<Buffer>> buffer_vector_;\n  std::vector<Offset<Tensor>> tensor_vector_;\n  std::vector<Offset<OperatorCode>> opcode_vector_;\n  std::vector<Offset<Operator>> op_vector_;\n  std::vector<Offset<Metadata>> metadata_;\n  absl::flat_hash_map<BuiltinOperator, int> op_index_;\n};\n\n/*static*/\ntflite::support::StatusOr<std::unique_ptr<TfLiteBuilderImpl>>\nTfLiteBuilderImpl::New(FlatBufferBuilder* fbb) {\n  if (!fbb) return absl::InvalidArgumentError(\"fbb is required\");\n  return absl::WrapUnique(new TfLiteBuilderImpl(fbb));\n}\n\n/*static*/\ntflite::support::StatusOr<std::unique_ptr<TfLiteBuilderImpl>>\nTfLiteBuilderImpl::New(const Model& model, FlatBufferBuilder* fbb) {\n  ASSIGN_OR_RETURN(auto tflite_builder, TfLiteBuilderImpl::New(fbb));\n  RETURN_IF_ERROR(tflite_builder->CloneBuffers(model));\n  RETURN_IF_ERROR(tflite_builder->CloneTensors(model));\n  RETURN_IF_ERROR(tflite_builder->CloneOperatorCodes(model));\n  RETURN_IF_ERROR(tflite_builder->CloneOperators(model));\n  RETURN_IF_ERROR(tflite_builder->CloneMetadata(model));\n  return std::move(tflite_builder);\n}\n\nabsl::Status TfLiteBuilderImpl::CloneBuffers(const Model& model) {\n  for (int i = 0; i < model.buffers()->size(); ++i) {\n    auto* buffer = model.buffers()->Get(i);\n    if (buffer->data() == nullptr) {\n      // Many transient tensors don't have data in the flatbuffer. Their\n      // buffers will be allocated by the interpreter at run-time.\n      buffer_vector_.push_back(CreateBuffer(*fbb_));\n    } else {\n      BufferT buffer_t;\n      buffer->UnPackTo(&buffer_t);\n      Offset<Vector<uint8_t>> data_buffer =\n          fbb_->CreateVector(buffer_t.data.data(), buffer_t.data.size());\n      buffer_vector_.push_back(CreateBuffer(*fbb_, data_buffer));\n    }\n  }\n  return absl::OkStatus();\n}\n\nabsl::Status TfLiteBuilderImpl::CloneTensors(const Model& model) {\n  const auto* subgraphs = model.subgraphs();\n  const auto* tensors = subgraphs->Get(0)->tensors();\n  for (int i = 0; i < tensors->size(); ++i) {\n    const auto* tensor = tensors->Get(i);\n    if (!tensor) return absl::InvalidArgumentError(\"null tensor provided\");\n    TensorT tensor_t;\n    tensor->UnPackTo(&tensor_t);\n    tensor_vector_.push_back(CreateTensor(\n        *fbb_, fbb_->CreateVector(tensor_t.shape), tensor_t.type,\n        tensor_t.buffer, fbb_->CreateString(tensor_t.name),\n        tensor_t.quantization\n            ? CreateQuantizationParameters(fbb_, *tensor_t.quantization)\n            : 0));\n  }\n  return absl::OkStatus();\n}\n\nabsl::Status TfLiteBuilderImpl::CloneOperatorCodes(const Model& model) {\n  for (int i = 0; i < model.operator_codes()->size(); ++i) {\n    const auto* opcode = model.operator_codes()->Get(i);\n    OperatorCodeT opcode_t;\n    opcode->UnPackTo(&opcode_t);\n    BuiltinOperator builtin_code = GetBuiltinCode(opcode_t);\n    op_index_[builtin_code] = opcode_vector_.size();\n    opcode_vector_.push_back(CreateOperatorCode(\n        *fbb_, builtin_code,\n        opcode->custom_code() ? fbb_->CreateString(opcode_t.custom_code) : 0,\n        opcode_t.version));\n  }\n  return absl::OkStatus();\n}\n\nabsl::Status TfLiteBuilderImpl::CloneOperators(const Model& model) {\n  const auto* ops = model.subgraphs()->Get(0)->operators();\n  for (int i = 0; i < ops->size(); ++i) {\n    const auto* op = ops->Get(i);\n    if (!op->inputs()) return absl::InvalidArgumentError(\"empty op inputs\");\n    if (!op->outputs()) return absl::InvalidArgumentError(\"empty op outputs\");\n\n    OperatorT op_t;\n    op->UnPackTo(&op_t);\n\n    // Recalculate input and output indices of this operator.\n    Offset<Vector<int32_t>> input_index_vector =\n        fbb_->CreateVector<int32_t>(op_t.inputs);\n    Offset<Vector<int32_t>> output_index_vector =\n        fbb_->CreateVector<int32_t>(op_t.outputs);\n\n    const auto builtin_options_type = op_t.builtin_options.type;\n\n    const auto custom_options_format = op_t.custom_options_format;\n\n    op_vector_.push_back(CreateOperator(\n        *fbb_, op_t.opcode_index, input_index_vector, output_index_vector,\n        builtin_options_type,\n        op->builtin_options() ? op_t.builtin_options.Pack(*fbb_) : 0,\n        op->custom_options() ? fbb_->CreateVector(op_t.custom_options.data(),\n                                                  op_t.custom_options.size())\n                             : 0,\n        custom_options_format));\n  }\n  return absl::OkStatus();\n}\n\nint TfLiteBuilderImpl::AddTensor(\n    const std::string& name, TensorType type, const std::vector<int32_t>& shape,\n    const absl::optional<QuantizationParametersT>& quantization_opt) {\n  const int buffer_index = buffer_vector_.size();\n  buffer_vector_.push_back(CreateBuffer(*fbb_));\n  const int tensor_index = tensor_vector_.size();\n  tensor_vector_.push_back(CreateTensor(\n      *fbb_, fbb_->CreateVector(shape), type, buffer_index,\n      fbb_->CreateString(name),\n      quantization_opt.has_value()\n          ? CreateQuantizationParameters(fbb_, quantization_opt.value())\n          : 0));\n  return tensor_index;\n}\n\nint TfLiteBuilderImpl::AddConstTensor(\n    const std::string& name, TensorType type, const std::vector<int32_t>& shape,\n    const uint8_t* data, size_t size,\n    const absl::optional<QuantizationParametersT>& quantization_opt) {\n  const int buffer_index = buffer_vector_.size();\n  buffer_vector_.push_back(CreateBuffer(*fbb_, fbb_->CreateVector(data, size)));\n  const int tensor_index = tensor_vector_.size();\n  tensor_vector_.push_back(CreateTensor(\n      *fbb_, fbb_->CreateVector(shape), type, buffer_index,\n      fbb_->CreateString(name),\n      quantization_opt.has_value()\n          ? CreateQuantizationParameters(fbb_, quantization_opt.value())\n          : 0));\n  return tensor_index;\n}\n\n// Appends clones of model metadata to the output metadata vector.\nabsl::Status TfLiteBuilderImpl::CloneMetadata(const Model& model) {\n  const auto* original_metadata_list = model.metadata();\n  for (int i = 0; i < original_metadata_list->size(); ++i) {\n    const auto* original_metadata = original_metadata_list->Get(i);\n    MetadataT original_metadata_t;\n    original_metadata->UnPackTo(&original_metadata_t);\n    // To do this properly, we need to look at the TFLITE_METADATA field\n    // (name = TFLITE_METADATA), parse it, and modify it accordingly. For now,\n    // we just clone all the metadata.\n    metadata_.push_back(\n        CreateMetadata(*fbb_, fbb_->CreateString(original_metadata_t.name),\n                       original_metadata_t.buffer));\n  }\n  return absl::OkStatus();\n}\n\nvoid TfLiteBuilderImpl::AddOperator(\n    BuiltinOperator op_code, const std::vector<int32_t>& input_tensor_indices,\n    int output_tensor_index, BuiltinOptions option_code,\n    const flatbuffers::Offset<void>& options) {\n  auto iter = op_index_.find(op_code);\n  int index;\n  if (iter != op_index_.end()) {\n    index = iter->second;\n  } else {\n    index = opcode_vector_.size();\n    opcode_vector_.push_back(CreateOperatorCode(*fbb_, op_code));\n  }\n\n  Offset<Vector<int32_t>> input_vector =\n      fbb_->CreateVector<int32_t>(input_tensor_indices);\n  Offset<Vector<int32_t>> output_vector =\n      fbb_->CreateVector<int32_t>({output_tensor_index});\n  op_vector_.push_back(CreateOperator(*fbb_, index, input_vector, output_vector,\n                                      option_code, options));\n}\n\nvoid TfLiteBuilderImpl::Build(const std::vector<int32_t>& inputs,\n                              int32_t output_tensor_index,\n                              const std::string& name, uint32_t version,\n                              const std::string& description) {\n  Offset<Vector<int32_t>> input_vector = fbb_->CreateVector<int32_t>(inputs);\n  Offset<Vector<int32_t>> output_vector =\n      fbb_->CreateVector<int32_t>({output_tensor_index});\n  Offset<Vector<Offset<Tensor>>> tensors = fbb_->CreateVector(tensor_vector_);\n  Offset<Vector<Offset<Operator>>> ops = fbb_->CreateVector(op_vector_);\n  Offset<SubGraph> subgraph =\n      CreateSubGraph(*fbb_, tensors, input_vector, output_vector, ops,\n                     name.empty() ? 0 : fbb_->CreateString(name));\n  Offset<Vector<Offset<SubGraph>>> subgraphs =\n      fbb_->CreateVector<Offset<SubGraph>>({subgraph});\n\n  Offset<Vector<Offset<Buffer>>> buffers = fbb_->CreateVector(buffer_vector_);\n  Offset<Vector<Offset<OperatorCode>>> opcodes =\n      fbb_->CreateVector(opcode_vector_);\n\n  auto merged_model = tflite::CreateModel(\n      *fbb_, version, opcodes, subgraphs,\n      description.empty() ? 0 : fbb_->CreateString(description), buffers, 0,\n      fbb_->CreateVector(metadata_));\n\n  tflite::FinishModelBuffer(*fbb_, merged_model);\n}\n\n}  // namespace\n\n/*static*/\ntflite::support::StatusOr<std::unique_ptr<TfLiteBuilder>> TfLiteBuilder::New(\n    FlatBufferBuilder* fbb) {\n  return TfLiteBuilderImpl::New(fbb);\n}\n\n/*static*/\ntflite::support::StatusOr<std::unique_ptr<TfLiteBuilder>> TfLiteBuilder::New(\n    const Model& model, ::flatbuffers::FlatBufferBuilder* fbb) {\n  return TfLiteBuilderImpl::New(model, fbb);\n}\n\n}  // namespace cbr\n}  // namespace examples\n}  // namespace tflite\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/lib/tflite_builder.h",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef TENSORFLOW_LITE_EXAMPLES_CLASSIFICATION_BY_RETRIEVAL_LIB_TFLITE_BUILDER_H_\n#define TENSORFLOW_LITE_EXAMPLES_CLASSIFICATION_BY_RETRIEVAL_LIB_TFLITE_BUILDER_H_\n\n#include <memory>\n#include <string>\n\n#include \"absl/types/optional.h\"\n#include \"flatbuffers/flatbuffers.h\"\n#include \"tensorflow/lite/model.h\"\n#include \"tensorflow_lite_support/cc/port/statusor.h\"\n\nnamespace tflite {\nnamespace examples {\nnamespace cbr {\n\nclass TfLiteBuilder {\n public:\n  // Creates a new instance of TfLiteBuilderImpl.\n  // The caller keeps the ownership of `builder`.\n  static tflite::support::StatusOr<std::unique_ptr<TfLiteBuilder>> New(\n      ::flatbuffers::FlatBufferBuilder* fbb);\n\n  // Creates a new instance of TfLiteBuilder and initializes it by copying\n  // all the data in `model`. The caller keeps the ownership of `builder`.\n  static tflite::support::StatusOr<std::unique_ptr<TfLiteBuilder>> New(\n      const Model& model, ::flatbuffers::FlatBufferBuilder* fbb);\n\n  virtual ~TfLiteBuilder() = default;\n\n  // Add a tensor to the model and returns the index.\n  virtual int AddTensor(const std::string& name, TensorType type,\n                        const std::vector<int32_t>& shape) = 0;\n\n  // AddTensor with a quantization parameter.\n  virtual int AddQuantizedTensor(\n      const std::string& name, TensorType type,\n      const std::vector<int32_t>& shape,\n      const QuantizationParametersT& quantization_param) = 0;\n\n  // Add a tensor with a constant value and returns the index.\n  virtual int AddConstTensor(const std::string& name, TensorType type,\n                             const std::vector<int32_t>& shape,\n                             const uint8_t* data, size_t size) = 0;\n\n  // AddConstTensor with a quantization parameter.\n  virtual int AddQuantizedConstTensor(\n      const std::string& name, TensorType type,\n      const std::vector<int32_t>& shape, const uint8_t* data, size_t size,\n      const QuantizationParametersT& quantization_opt) = 0;\n\n  // Add an operator to the model.\n  virtual void AddOperator(BuiltinOperator op_code,\n                           const std::vector<int>& input_tensor_indices,\n                           int output_tensor_index, BuiltinOptions option_code,\n                           const flatbuffers::Offset<void>& options) = 0;\n\n  virtual void Build(const std::vector<int32_t>& inputs,\n                     int32_t output_tensor_index, const std::string& name,\n                     uint32_t schema_version,\n                     const std::string& description) = 0;\n};\n\n}  // namespace cbr\n}  // namespace examples\n}  // namespace tflite\n\n#endif  // TENSORFLOW_LITE_EXAMPLES_CLASSIFICATION_BY_RETRIEVAL_LIB_TFLITE_BUILDER_H_\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/lib/tflite_cbr_builder.cc",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"lib/tflite_cbr_builder.h\"\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"absl/container/flat_hash_map.h\"\n#include \"absl/memory/memory.h\"\n#include \"absl/status/status.h\"\n#include \"absl/strings/str_format.h\"\n#include \"lib/tflite_builder.h\"\n#include \"tensorflow_lite_support/cc/port/status_macros.h\"\n\nnamespace tflite {\nnamespace examples {\nnamespace cbr {\n\nnamespace {\n\nusing ::flatbuffers::FlatBufferBuilder;\nusing ::tflite::task::vision::FeatureVector;\n\nFeatureVector Normalize(const FeatureVector& fv) {\n  float norm = 0.0f;\n  for (float val : fv.value_float()) norm += val * val;\n  if (norm == 0.0f) return fv;  // Can't normalize 0 vector.\n  FeatureVector normalized;\n  float inv_norm = 1.0f / std::sqrt(norm);\n  for (float val : fv.value_float()) {\n    normalized.add_value_float(val * inv_norm);\n  }\n  return normalized;\n}\n\nint AddRetrievalBlock(flatbuffers::FlatBufferBuilder* fb_builder,\n                      TfLiteBuilder* tflite_builder,\n                      int32_t embedding_output_index,\n                      const std::vector<FeatureVector>& embeddings,\n                      const std::string& output_tensor_name) {\n  const int32_t embedding_dim = embeddings[0].value_float_size();\n  const int32_t num_instances = embeddings.size();\n\n  // Add a normalization layer.\n  const int32_t norm_tensor_index = tflite_builder->AddTensor(\n      \"normalization\", tflite::TensorType_FLOAT32, {1, embedding_dim});\n\n  // Add a weights tensor for the fully-connected retrieval layer.\n  // NOTE: We only support a float embedding layer.\n  std::vector<float> weights_vec;\n  weights_vec.reserve(num_instances * embedding_dim);\n  for (const FeatureVector& embedding : embeddings) {\n    FeatureVector normalized_embedding = Normalize(embedding);\n    for (float val : normalized_embedding.value_float()) {\n      weights_vec.push_back(val);\n    }\n  }\n  const int32_t weight_tensor_index = tflite_builder->AddConstTensor(\n      \"retrieval\", tflite::TensorType_FLOAT32, {num_instances, embedding_dim},\n      reinterpret_cast<const uint8_t*>(weights_vec.data()),\n      sizeof(float) * weights_vec.size());\n\n  // Add a retrieval result tensor.\n  const int32_t instances_tensor_index = tflite_builder->AddTensor(\n      output_tensor_name, tflite::TensorType_FLOAT32, {1, num_instances});\n\n  // Add a normalization operation. This operation is necessary if we want the\n  // final score be meaningful.\n  tflite_builder->AddOperator(\n      tflite::BuiltinOperator_L2_NORMALIZATION, {embedding_output_index},\n      norm_tensor_index, tflite::BuiltinOptions_L2NormOptions,\n      tflite::CreateL2NormOptions(*fb_builder,\n                                  tflite::ActivationFunctionType_NONE)\n          .Union());\n\n  // Add a retrieval operation.\n  tflite_builder->AddOperator(\n      tflite::BuiltinOperator_FULLY_CONNECTED,\n      {norm_tensor_index, weight_tensor_index}, instances_tensor_index,\n      tflite::BuiltinOptions_FullyConnectedOptions,\n      tflite::CreateFullyConnectedOptions(*fb_builder,\n                                          tflite::ActivationFunctionType_NONE)\n          .Union());\n\n  return instances_tensor_index;\n}\n\nint AddQuantizedRetrievalBlock(flatbuffers::FlatBufferBuilder* fb_builder,\n                               TfLiteBuilder* tflite_builder,\n                               int32_t embedding_output_index,\n                               const std::vector<FeatureVector>& embeddings,\n                               const std::string& output_tensor_name) {\n  const int32_t embedding_dim = embeddings[0].value_float_size();\n  const int32_t num_instances = embeddings.size();\n\n  const int32_t float_embedding_tensor_index = tflite_builder->AddTensor(\n      \"dequantized_embedding\", tflite::TensorType_FLOAT32, {1, embedding_dim});\n  const int32_t normalized_embedding_tensor_index = tflite_builder->AddTensor(\n      \"normalized_embedding\", tflite::TensorType_FLOAT32, {1, embedding_dim});\n  tflite::QuantizationParametersT embedding_qparams;\n  embedding_qparams.min.push_back(-1.0f);\n  embedding_qparams.max.push_back(1.0f);\n  embedding_qparams.zero_point.push_back(0);\n  // See the retrieval layer's quantization parameter setting below.\n  embedding_qparams.scale.push_back(1.0f / 128.0f);\n  const int32_t nq_embedding_tensor_index = tflite_builder->AddQuantizedTensor(\n      \"normalized_quantized_embd\", tflite::TensorType_INT8, {1, embedding_dim},\n      embedding_qparams);\n\n  // Add a dequantization operation.\n  // Note that the normalization and matmul operations don't honor the\n  // zero-point in a UINT8 tensor (i.e., it doesn't deal with negative values).\n  // Therefore, we do: UINT8 --dequantize--> FLOAT32 --normalize-->\n  //                   FLOAT32 --quantize--> INT8 --matmul(retrieval)-->\n  //                   INT8 --dequantize--> FLOAT32\n  tflite_builder->AddOperator(\n      tflite::BuiltinOperator_DEQUANTIZE, {embedding_output_index},\n      float_embedding_tensor_index, tflite::BuiltinOptions_NONE, 0);\n\n  // Add a normalization operation. This operation is necessary if we want the\n  // final score be meaningful.\n  tflite_builder->AddOperator(\n      tflite::BuiltinOperator_L2_NORMALIZATION, {float_embedding_tensor_index},\n      normalized_embedding_tensor_index, tflite::BuiltinOptions_L2NormOptions,\n      tflite::CreateL2NormOptions(*fb_builder,\n                                  tflite::ActivationFunctionType_NONE)\n          .Union());\n\n  // Add a (re-)quantization operation.\n  tflite_builder->AddOperator(\n      tflite::BuiltinOperator_QUANTIZE, {normalized_embedding_tensor_index},\n      nq_embedding_tensor_index, tflite::BuiltinOptions_NONE, 0);\n\n  // Add a weights tensor for the fully-connected retrieval layer.\n  // NOTE: We only support a float embedding layer.\n  std::vector<int8_t> weights_vec;\n  weights_vec.reserve(num_instances * embedding_dim);\n\n  // Most efficient quantization scale would be something like 1/(sqrt(d)*64.0)\n  // (calculated with average norm) when d is the embedding dimension. However,\n  // if the scale is too big, the matmul (fully-connected) operation would\n  // overflow (become larger than 128) and the result would be less accurate\n  // for similar embeddings. Therefore, we use the standard scale which is\n  // 1/128 for now.\n  float scale = 1.0f / 128.0f;\n  float scale_inv = 1.0f / scale;\n  float zero_point = 0;\n  for (const FeatureVector& embedding : embeddings) {\n    FeatureVector normalized_embedding = Normalize(embedding);\n    for (float val : normalized_embedding.value_float()) {\n      float quantized_val = std::round(val * scale_inv) + zero_point;\n      if (quantized_val < -128) quantized_val = -128;\n      if (quantized_val > 127) quantized_val = 127;\n      weights_vec.push_back(static_cast<int8_t>(quantized_val));\n    }\n  }\n  tflite::QuantizationParametersT weight_qparams;\n  weight_qparams.min.push_back(-1.0f);\n  weight_qparams.max.push_back(1.0f);\n  weight_qparams.zero_point.push_back(zero_point);\n  weight_qparams.scale.push_back(scale);\n  const int32_t weight_tensor_index = tflite_builder->AddQuantizedConstTensor(\n      \"retrieval\", tflite::TensorType_INT8, {num_instances, embedding_dim},\n      reinterpret_cast<const uint8_t*>(weights_vec.data()), weights_vec.size(),\n      weight_qparams);\n\n  // Add a quantized retrieval result tensor.\n  tflite::QuantizationParametersT instances_qparams;\n  instances_qparams.min.push_back(-1.0f);\n  instances_qparams.max.push_back(1.0f);\n  instances_qparams.zero_point.push_back(0);\n  instances_qparams.scale.push_back(scale);\n  const int32_t quantized_instances_tensor_index =\n      tflite_builder->AddQuantizedTensor(\"quantized_instances\",\n                                         tflite::TensorType_INT8,\n                                         {1, num_instances}, instances_qparams);\n\n  // Add a retrieval operation.\n  tflite_builder->AddOperator(\n      tflite::BuiltinOperator_FULLY_CONNECTED,\n      {nq_embedding_tensor_index, weight_tensor_index},\n      quantized_instances_tensor_index,\n      tflite::BuiltinOptions_FullyConnectedOptions,\n      tflite::CreateFullyConnectedOptions(*fb_builder,\n                                          tflite::ActivationFunctionType_NONE)\n          .Union());\n\n  // Add a float retrieval result tensor.\n  // TODO(b/187715553): this is a temporary solution. Converting the result back\n  // to FLOAT32 is not an optimal solution, but we do that because the\n  // aggregation block and the final output does not support INT8. We will need\n  // to find a better way to support INT8 in the later stage.\n  const int32_t instances_tensor_index = tflite_builder->AddTensor(\n      output_tensor_name, tflite::TensorType_FLOAT32, {1, num_instances});\n\n  // Add a dequantization operation.\n  tflite_builder->AddOperator(\n      tflite::BuiltinOperator_DEQUANTIZE, {quantized_instances_tensor_index},\n      instances_tensor_index, tflite::BuiltinOptions_NONE, 0);\n\n  return instances_tensor_index;\n}\n\nint AddAggregationBlock(flatbuffers::FlatBufferBuilder* fb_builder,\n                        TfLiteBuilder* tflite_builder, int num_instances,\n                        int instances_tensor_index,\n                        const std::vector<std::vector<int32_t>>& classes,\n                        const std::string& output_tensor_name) {\n  int32_t num_classes = classes.size();\n\n  // Add a flattened retrieval result tensor.\n  std::vector<int32_t> flat_instances_shape = {num_instances, 1};\n  const int flat_instances_index = tflite_builder->AddTensor(\n      \"flat_instances\", tflite::TensorType_FLOAT32, flat_instances_shape);\n\n  // Add a flattening operation.\n  tflite_builder->AddOperator(\n      tflite::BuiltinOperator_RESHAPE, {instances_tensor_index},\n      flat_instances_index, tflite::BuiltinOptions_ReshapeOptions,\n      tflite::CreateReshapeOptions(\n          *fb_builder, fb_builder->CreateVector(flat_instances_shape))\n          .Union());\n\n  // Add a class aggregation axis tensor.\n  std::vector<int32_t> aggregation_axis_vec = {0};\n  const int aggregation_axis_tensor_index = tflite_builder->AddConstTensor(\n      \"aggregation_axis\", tflite::TensorType_INT32, {1},\n      reinterpret_cast<const uint8_t*>(aggregation_axis_vec.data()),\n      sizeof(int32_t) * aggregation_axis_vec.size());\n\n  // Add an aggregation block per class.\n  std::vector<int32_t> class_aggregation_indices;\n  for (int i = 0; i < classes.size(); ++i) {\n    const std::string index_str = std::to_string(i);\n    const std::vector<int32_t>& instances = classes[i];\n    int32_t num_instances = instances.size();\n\n    // Add a selection tensor.\n    const int selection_tensor_index = tflite_builder->AddConstTensor(\n        \"selection\" + index_str, tflite::TensorType_INT32, {num_instances},\n        reinterpret_cast<const uint8_t*>(instances.data()),\n        sizeof(int32_t) * num_instances);\n\n    // Add a class instances tensor.\n    const int class_instances_tensor_index = tflite_builder->AddTensor(\n        \"class_instances\" + index_str, tflite::TensorType_FLOAT32,\n        {1, num_instances});\n\n    // Add a class aggregation tensor.\n    int class_aggregation_tensor_index = tflite_builder->AddTensor(\n        \"class_aggregation\" + index_str, tflite::TensorType_FLOAT32, {1});\n\n    // Add a class selection operation.\n    tflite_builder->AddOperator(tflite::BuiltinOperator_EMBEDDING_LOOKUP,\n                                {selection_tensor_index, flat_instances_index},\n                                class_instances_tensor_index,\n                                tflite::BuiltinOptions_NONE, 0);\n\n    // Add a class aggregation operation.\n    tflite_builder->AddOperator(\n        tflite::BuiltinOperator_REDUCE_MAX,\n        {class_instances_tensor_index, aggregation_axis_tensor_index},\n        class_aggregation_tensor_index, tflite::BuiltinOptions_NONE, 0);\n\n    class_aggregation_indices.push_back(class_aggregation_tensor_index);\n  }\n\n  // Add the concatenated classes tensor (with dimension {num_classes}).\n  const int flat_classes_index = tflite_builder->AddTensor(\n      \"flat_classes\", tflite::TensorType_FLOAT32, {num_classes});\n\n  // Add the final classes tensor (with dimension {1, num_classes}).\n  std::vector<int32_t> classes_shape = {1, num_classes};\n  const int classes_index = tflite_builder->AddTensor(\n      output_tensor_name, tflite::TensorType_FLOAT32, classes_shape);\n\n  // Add a result concatenation operation.\n  tflite_builder->AddOperator(tflite::BuiltinOperator_CONCATENATION,\n                              class_aggregation_indices, flat_classes_index,\n                              tflite::BuiltinOptions_NONE, 0);\n\n  // Add a flattening operation.\n  tflite_builder->AddOperator(\n      tflite::BuiltinOperator_RESHAPE, {flat_classes_index}, classes_index,\n      tflite::BuiltinOptions_ReshapeOptions,\n      tflite::CreateReshapeOptions(*fb_builder,\n                                   fb_builder->CreateVector(classes_shape))\n          .Union());\n\n  return classes_index;\n}\n\n}  // namespace\n\ntflite::support::StatusOr<std::vector<std::string>>\nTfLiteCbRBuilder::BuildCbRModel(const tflite::Model& model,\n                                const std::vector<FeatureVector>& embeddings,\n                                const std::vector<std::string>& labels,\n                                flatbuffers::FlatBufferBuilder* builder) {\n  // Check input arguments.\n  if (builder == nullptr) {\n    return absl::InvalidArgumentError(\"FlatBufferBuilder not provided\");\n  }\n  if (model.subgraphs() == nullptr) {\n    return absl::InvalidArgumentError(\"Provided model is empty\");\n  }\n  if (model.subgraphs()->size() != 1) {\n    return absl::InvalidArgumentError(\n        absl::StrFormat(\"The model is required to have a single subgraph: %d\",\n                        model.subgraphs()->size()));\n  }\n  if (embeddings.empty()) {\n    return absl::InvalidArgumentError(\"Provided embeddings is empty\");\n  }\n  tflite::SubGraphT subgraph_t;\n  (*model.subgraphs())[0]->UnPackTo(&subgraph_t);\n  if (subgraph_t.outputs.empty()) {\n    return absl::InvalidArgumentError(\"Invalid output size of the model\");\n  }\n  int32_t embedding_output = subgraph_t.outputs[0];\n\n  ASSIGN_OR_RETURN(auto tflite_builder, TfLiteBuilder::New(model, builder));\n\n  const std::string kInstancesTensorName = \"instances\";\n  const std::string kClassesTensorName = \"classes\";\n  int output_index;\n\n  // Check if the embedding is quantized.\n  tflite::TensorT* embedding_tensor_t =\n      subgraph_t.tensors[embedding_output].get();\n  if (!embedding_tensor_t) {\n    return absl::InvalidArgumentError(\"Empty embedding tensor\");\n  }\n  if (embedding_tensor_t->type == tflite::TensorType_FLOAT32) {\n    output_index = AddRetrievalBlock(\n        builder, tflite_builder.get(), embedding_output, embeddings,\n        labels.empty() ? kClassesTensorName : kInstancesTensorName);\n  } else {\n    // If the embedding is quantized, add a quantized retrieval block which is\n    // 4x smaller. Note that the embeddings will be normalized -- which means\n    // that the quantization parameter will change.\n    output_index = AddQuantizedRetrievalBlock(\n        builder, tflite_builder.get(), embedding_output, embeddings,\n        labels.empty() ? kClassesTensorName : kInstancesTensorName);\n  }\n\n  std::vector<std::string> class_labels;\n  if (!labels.empty()) {\n    if (embeddings.size() != labels.size()) {\n      return absl::InvalidArgumentError(absl::StrCat(\n          \"Labels are not consistent with the embeddings: \", embeddings.size(),\n          \" vs \", labels.size()));\n    }\n    absl::flat_hash_map<std::string, int> label_to_class_id;\n    std::vector<std::vector<int32_t>> classes;\n    int num_classes = 0;\n    for (int i = 0; i < labels.size(); ++i) {\n      const std::string& label = labels[i];\n      int class_id;\n      auto itr = label_to_class_id.find(label);\n      if (itr == label_to_class_id.end()) {\n        class_id = num_classes++;\n        label_to_class_id[label] = class_id;\n        class_labels.push_back(label);\n        classes.push_back({});\n      } else {\n        class_id = itr->second;\n      }\n      classes[class_id].push_back(i);\n    }\n\n    // Add aggregation block and update output index\n    output_index =\n        AddAggregationBlock(builder, tflite_builder.get(), embeddings.size(),\n                            output_index, classes, kClassesTensorName);\n  }\n\n  // Finalize.\n  tflite_builder->Build(\n      subgraph_t.inputs, output_index, subgraph_t.name, model.version(),\n      (model.description() ? model.description()->str() : \"\"));\n\n  return class_labels;\n}\n\n}  // namespace cbr\n}  // namespace examples\n}  // namespace tflite\n"
  },
  {
    "path": "lite/examples/classification_by_retrieval/lib/tflite_cbr_builder.h",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef TENSORFLOW_LITE_EXAMPLES_CLASSIFICATION_BY_RETRIEVAL_LIB_TFLITE_CBR_BUILDER_H_\n#define TENSORFLOW_LITE_EXAMPLES_CLASSIFICATION_BY_RETRIEVAL_LIB_TFLITE_CBR_BUILDER_H_\n\n#include <string>\n#include <vector>\n\n#include \"flatbuffers/flatbuffers.h\"\n#include \"tensorflow/lite/model.h\"\n#include \"tensorflow_lite_support/cc/port/statusor.h\"\n#include \"tensorflow_lite_support/cc/task/vision/proto/embeddings_proto_inc.h\"\n\nnamespace tflite {\nnamespace examples {\nnamespace cbr {\n\n// Wrapper class around BuildCbRModel() function for dependency injection.\nclass TfLiteCbRBuilder {\n public:\n  TfLiteCbRBuilder() = default;\n  virtual ~TfLiteCbRBuilder() = default;\n\n  // Performs modifications on the provided embedder model to turn it into a\n  // classification-by-retrieval model based on the provided list of embeddings.\n  // On success, it returns the updated class labels after aggregation or an\n  // empty vector if `labels` is empty.\n  virtual tflite::support::StatusOr<std::vector<std::string>> BuildCbRModel(\n      const ::tflite::Model& model,\n      const std::vector<::tflite::task::vision::FeatureVector>& embeddings,\n      const std::vector<std::string>& labels,\n      ::flatbuffers::FlatBufferBuilder* builder);\n};\n\n}  // namespace cbr\n}  // namespace examples\n}  // namespace tflite\n\n#endif  // TENSORFLOW_LITE_EXAMPLES_CLASSIFICATION_BY_RETRIEVAL_LIB_TFLITE_CBR_BUILDER_H_\n"
  },
  {
    "path": "lite/examples/digit_classifier/README.md",
    "content": "# Digit Classifier\n\n## Overview\nThis directory contains an end-to-end sample of a digit classifier model built\nwith TensorFlow 2.0 (Keras API), and trained on MNIST dataset.\n\n* Python Notebook showing how to train the model and convert to TensorFlow Lite\nformat. <br/>[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/tensorflow/examples/blob/master/lite/examples/digit_classifier/ml/mnist_tflite.ipynb)\n* An [Android app](android/) that uses the TensorFlow Lite model to classify\nyour handwritten digits.\n* An [iOS app](ios/) that uses the TensorFlow Lite model to classify\nyour handwritten digits.\n\n<img src=\"https://storage.googleapis.com/khanhlvg-public.appspot.com/digit-classifier/screenshot_android.png\" />\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/README.md",
    "content": "# TensorFlow Lite Digit Classification Demo Application\n\n### Overview\n\nThis is an end-to-end example of a digit classifier model built with TensorFlow\n2.0 (Keras API), and trained on MNIST dataset. The example app allows users to\ndraw a number and predict which number it will be. These instructions walk you\nthrough building and running the demo on an Android device.\n\nThe model files are downloaded via Gradle scripts when you build and run the\napp. You don't need to do any steps to download TFLite models into the project\nexplicitly.\n\nThis application should be run on a physical Android device.\n\n![App example UI.](minst.gif?raw=true \"DigitCanvas screen\")\n\n## Build the demo using Android Studio\n\n### Prerequisites\n\n* The **[Android Studio](https://developer.android.com/studio/index.html)**\n  IDE (Android Studio 2021.2.1 or newer). This sample has been tested on Android\n  Studio Chipmunk.\n\n* A physical Android device with a minimum OS version of SDK 23 (Android 6.0 -\n  Marshmallow) with developer mode enabled. The process of enabling developer\n  mode may vary by device.\n\n### Building\n\n* Open Android Studio. From the Welcome screen, select Open an existing Android\n  Studio project.\n\n* From the Open File or Project window that appears, navigate to and select the\n  tensorflow-lite/examples/digit_classifier/android directory. Click OK.\n\n* If it asks you to do a Gradle Sync, click OK.\n\n* With your Android device connected to your computer and developer mode\n  enabled, click on the green Run arrow in Android Studio.\n\n### Models used\n\nDownloading, extraction, and placing the models into the assets folder is\nmanaged automatically by the download.gradle file.\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/build.gradle",
    "content": "plugins {\n    id 'com.android.application'\n    id 'org.jetbrains.kotlin.android'\n    id 'kotlin-kapt'\n    id 'de.undercouch.download'\n}\n\nandroid {\n    compileSdk 32\n\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.digitclassification\"\n        minSdk 23\n        targetSdk 32\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n    kotlinOptions {\n        jvmTarget = '1.8'\n    }\n    buildFeatures {\n        viewBinding true\n    }\n    androidResources {\n        noCompress 'tflite'\n    }\n}\n\n// import DownloadModels task\nproject.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'\nproject.ext.TEST_ASSETS_DIR = projectDir.toString() + '/src/androidTest/assets'\n\n// Download default models; if you wish to use your own models then\n// place them in the \"assets\" directory and comment out this line.\napply from: 'download_models.gradle'\n\ndependencies {\n\n    // Kotlin lang\n    implementation 'androidx.core:core-ktx:1.15.0'\n\n    // App compat and UI things\n    implementation 'androidx.appcompat:appcompat:1.5.0'\n    implementation 'com.google.android.material:material:1.6.1'\n\n    // Navigation library\n    def nav_version = \"2.5.1\"\n    implementation \"androidx.navigation:navigation-fragment-ktx:$nav_version\"\n    implementation \"androidx.navigation:navigation-ui-ktx:$nav_version\"\n\n    // AndroidDraw Library\n    implementation 'com.github.divyanshub024:AndroidDraw:v0.1'\n\n    // Unit testing\n    testImplementation 'junit:junit:4.13.2'\n\n    // Instrumented testing\n    androidTestImplementation 'androidx.test.ext:junit:1.1.3'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n\n    // Import the Task Vision Library dependency (NNAPI is included)\n    implementation 'org.tensorflow:tensorflow-lite-task-vision:0.4.0'\n    // Import the GPU delegate plugin Library for GPU inference\n    implementation 'org.tensorflow:tensorflow-lite-gpu:2.9.0'\n}\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/download_models.gradle",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ntask downloadModelFile(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/digit_classifier/android/mnist_metadata.tflite'\n    dest project.ext.ASSET_DIR + '/mnist.tflite'\n    overwrite false\n}\n\ntask copyTestModel(type: Copy, dependsOn: downloadModelFile) {\n    from project.ext.ASSET_DIR + '/mnist.tflite'\n    into project.ext.TEST_ASSETS_DIR\n}\n\npreBuild.dependsOn downloadModelFile, copyTestModel\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/src/androidTest/java/org/tensorflow/lite/examples/digitclassification/DigitClassificationTest.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage org.tensorflow.lite.examples.digitclassification\n\nimport android.content.res.AssetManager\nimport android.graphics.Bitmap\nimport android.graphics.BitmapFactory\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport org.junit.Assert.assertNotNull\nimport org.junit.Assert.assertEquals\n\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\nimport org.tensorflow.lite.support.label.Category\nimport org.tensorflow.lite.task.vision.classifier.Classifications\nimport java.io.InputStream\nimport java.lang.Exception\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass DigitClassificationTest {\n    val controlCategories = listOf<Category>(\n        Category.create(\"0\", \"\", 0.999565f)\n    )\n\n    @Test\n    fun classificationResultsShouldNotChange() {\n        val digitClassifierHelper = DigitClassifierHelper(\n            context = InstrumentationRegistry.getInstrumentation().context,\n            digitClassifierListener = object :\n                DigitClassifierHelper.DigitClassifierListener {\n                override fun onError(error: String) {\n                    // no op\n                }\n\n                override fun onResults(\n                    results: List<Classifications>?,\n                    inferenceTime: Long\n                ) {\n                    assertNotNull(results)\n\n                    // Verify that the classified data and control\n                    // data have the same number of categories\n                    assertEquals(\n                        controlCategories.size,\n                        results!![0].categories.size\n                    )\n\n                    // Loop through the categories\n                    for (i in controlCategories.indices) {\n                        // Verify that the labels are consistent\n                        assertEquals(\n                            controlCategories[i].label,\n                            results[0].categories[i].label\n                        )\n                    }\n                }\n            }, threshold = 0.9f\n        )\n\n        // Create Bitmap\n        val bitmap = loadImage(\"zero.png\")\n        // Run the image classifier on the sample image\n        digitClassifierHelper.classify(bitmap!!)\n    }\n\n    @Throws(Exception::class)\n    private fun loadImage(fileName: String): Bitmap? {\n        val assetManager: AssetManager =\n            InstrumentationRegistry.getInstrumentation().context.assets\n        val inputStream: InputStream = assetManager.open(fileName)\n        return BitmapFactory.decodeStream(inputStream)\n    }\n}\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"org.tensorflow.lite.examples.digitclassification\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:taskAffinity=\"\"\n        android:supportsRtl=\"true\">\n\n        <activity\n            android:name=\".MainActivity\"\n            android:clearTaskOnLaunch=\"true\"\n            android:configChanges=\"orientation|screenLayout|screenSize|smallestScreenSize\"\n            android:exported=\"true\"\n            android:resizeableActivity=\"true\"\n            android:rotationAnimation=\"seamless\"\n            android:theme=\"@style/AppTheme\"\n            tools:targetApi=\"O\">\n\n            <!-- Main app intent filter -->\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n\n            <!-- Declare notch support -->\n            <meta-data\n                android:name=\"android.notch_support\"\n                android:value=\"true\" />\n        </activity>\n\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/src/main/java/org/tensorflow/lite/examples/digitclassification/DigitClassifierHelper.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.digitclassification\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.os.SystemClock\nimport android.util.Log\nimport org.tensorflow.lite.gpu.CompatibilityList\nimport org.tensorflow.lite.support.image.TensorImage\nimport org.tensorflow.lite.task.core.BaseOptions\nimport org.tensorflow.lite.task.vision.classifier.Classifications\nimport org.tensorflow.lite.task.vision.classifier.ImageClassifier\n\nclass DigitClassifierHelper(\n    var threshold: Float = 0.5f,\n    var numThreads: Int = 2,\n    var maxResults: Int = 3,\n    var currentDelegate: Int = 0,\n    val context: Context,\n    val digitClassifierListener: DigitClassifierListener?\n) {\n\n    private var digitClassifier: ImageClassifier? = null\n\n    init {\n        setupDigitClassifier()\n    }\n\n    fun clearDigitClassifier() {\n        digitClassifier = null\n    }\n\n    private fun setupDigitClassifier() {\n        val optionsBuilder = ImageClassifier.ImageClassifierOptions.builder()\n            .setScoreThreshold(threshold)\n            .setMaxResults(maxResults)\n\n        val baseOptionsBuilder = BaseOptions.builder().setNumThreads(numThreads)\n\n        when (currentDelegate) {\n            DELEGATE_CPU -> {\n                // Default\n            }\n            DELEGATE_GPU -> {\n                if (CompatibilityList().isDelegateSupportedOnThisDevice) {\n                    baseOptionsBuilder.useGpu()\n                } else {\n                    digitClassifierListener?.onError(\n                        \"GPU is not supported on this device\"\n                    )\n                }\n            }\n            DELEGATE_NNAPI -> {\n                baseOptionsBuilder.useNnapi()\n            }\n        }\n\n        optionsBuilder.setBaseOptions(baseOptionsBuilder.build())\n\n        try {\n            digitClassifier =\n                ImageClassifier.createFromFileAndOptions(\n                    context,\n                    \"mnist.tflite\",\n                    optionsBuilder.build()\n                )\n        } catch (e: IllegalStateException) {\n            digitClassifierListener?.onError(\n                \"Image classifier failed to initialize. See error logs for \" +\n                        \"details\"\n            )\n            Log.e(TAG, \"TFLite failed to load model with error: \" + e.message)\n        }\n    }\n\n    fun classify(image: Bitmap) {\n        if (digitClassifier == null) {\n            setupDigitClassifier()\n        }\n\n        // Inference time is the difference between the system time at the\n        // start and finish of the process\n        var inferenceTime = SystemClock.uptimeMillis()\n\n        // Preprocess the image and convert it into a TensorImage for\n        // classification.\n        val tensorImage = TensorImage.fromBitmap(image)\n\n        val results = digitClassifier?.classify(tensorImage)\n        inferenceTime = SystemClock.uptimeMillis() - inferenceTime\n        digitClassifierListener?.onResults(results, inferenceTime)\n    }\n\n    interface DigitClassifierListener {\n        fun onError(error: String)\n        fun onResults(\n            results: List<Classifications>?,\n            inferenceTime: Long\n        )\n    }\n\n    companion object {\n        const val DELEGATE_CPU = 0\n        const val DELEGATE_GPU = 1\n        const val DELEGATE_NNAPI = 2\n\n        private const val TAG = \"DigitClassifierHelper\"\n    }\n}\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/src/main/java/org/tensorflow/lite/examples/digitclassification/MainActivity.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.digitclassification\n\nimport android.os.Build\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport org.tensorflow.lite.examples.digitclassification.databinding.ActivityMainBinding\n\nclass MainActivity : AppCompatActivity() {\n    private lateinit var activityMainBinding: ActivityMainBinding\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)\n        setContentView(activityMainBinding.root)\n    }\n\n    override fun onBackPressed() {\n        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {\n            // Workaround for Android Q memory leak issue in IRequestFinishCallback$Stub.\n            // (https://issuetracker.google.com/issues/139738913)\n            finishAfterTransition()\n        } else {\n            super.onBackPressed()\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/src/main/java/org/tensorflow/lite/examples/digitclassification/fragments/ClassificationResultsAdapter.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.digitclassification.fragments\n\nimport android.annotation.SuppressLint\nimport android.view.LayoutInflater\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport org.tensorflow.lite.examples.digitclassification.databinding.ItemClassificationResultBinding\nimport org.tensorflow.lite.support.label.Category\nimport org.tensorflow.lite.task.vision.classifier.Classifications\nimport java.util.Locale\n\nclass ClassificationResultsAdapter :\n    RecyclerView.Adapter<ClassificationResultsAdapter.ViewHolder>() {\n\n    private var categories: MutableList<Category?> = mutableListOf()\n\n    @SuppressLint(\"NotifyDataSetChanged\")\n    fun updateResults(listClassifications: List<Classifications>?) {\n        listClassifications?.get(0)?.categories?.let {\n            categories = it\n            notifyDataSetChanged()\n        }\n    }\n\n    @SuppressLint(\"NotifyDataSetChanged\")\n    fun reset() {\n        categories = mutableListOf()\n        notifyDataSetChanged()\n    }\n\n    override fun onCreateViewHolder(\n        parent: ViewGroup,\n        viewType: Int\n    ): ViewHolder {\n        val binding = ItemClassificationResultBinding.inflate(\n            LayoutInflater.from(parent.context),\n            parent,\n            false\n        )\n        return ViewHolder(binding)\n    }\n\n    override fun onBindViewHolder(holder: ViewHolder, position: Int) {\n        categories[position]?.let { category ->\n            holder.bind(category.label, category.score)\n        }\n    }\n\n    override fun getItemCount(): Int = categories.size\n\n    inner class ViewHolder(private val binding: ItemClassificationResultBinding) :\n        RecyclerView.ViewHolder(binding.root) {\n\n        fun bind(label: String, score: Float) {\n            with(binding) {\n                tvLabel.text = label\n                tvScore.text = String.format(\n                    Locale.US,\n                    \"%.4f\",\n                    score\n                )\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/src/main/java/org/tensorflow/lite/examples/digitclassification/fragments/DigitCanvasFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.digitclassification.fragments\n\nimport android.annotation.SuppressLint\nimport android.graphics.Color\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.MotionEvent\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.AdapterView\nimport android.widget.Toast\nimport androidx.fragment.app.Fragment\nimport androidx.recyclerview.widget.DividerItemDecoration\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport org.tensorflow.lite.examples.digitclassification.DigitClassifierHelper\nimport org.tensorflow.lite.examples.digitclassification.R\nimport org.tensorflow.lite.examples.digitclassification.databinding.FragmentDigitCanvasBinding\nimport org.tensorflow.lite.task.vision.classifier.Classifications\nimport java.util.Locale\n\nclass DigitCanvasFragment : Fragment(),\n    DigitClassifierHelper.DigitClassifierListener {\n    private var _fragmentDigitCanvasBinding: FragmentDigitCanvasBinding? = null\n    private val fragmentDigitCanvasBinding get() = _fragmentDigitCanvasBinding!!\n    private lateinit var digitClassifierHelper: DigitClassifierHelper\n    private lateinit var classificationResultAdapter:\n            ClassificationResultsAdapter\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View {\n        _fragmentDigitCanvasBinding = FragmentDigitCanvasBinding.inflate(\n            inflater,\n            container, false\n        )\n        return fragmentDigitCanvasBinding.root\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n        digitClassifierHelper = DigitClassifierHelper(\n            context = requireContext(), digitClassifierListener = this\n        )\n        setupDigitCanvas()\n        setupClassificationResultAdapter()\n        // Attach listeners to UI control widgets\n        initBottomSheetControls()\n\n        fragmentDigitCanvasBinding.btnClear.setOnClickListener {\n            fragmentDigitCanvasBinding.digitCanvas.clearCanvas()\n            classificationResultAdapter.reset()\n        }\n    }\n\n    override fun onDestroyView() {\n        _fragmentDigitCanvasBinding = null\n        super.onDestroyView()\n    }\n\n    @SuppressLint(\"ClickableViewAccessibility\")\n    private fun setupDigitCanvas() {\n        with(fragmentDigitCanvasBinding.digitCanvas) {\n            setStrokeWidth(70f)\n            setColor(Color.WHITE)\n            setBackgroundColor(Color.BLACK)\n            setOnTouchListener { _, event ->\n                // As we have interrupted DrawView's touch event, we first\n                // need to pass touch events through to the instance for the drawing to show up\n                onTouchEvent(event)\n\n                // Then if user finished a touch event, run classification\n                if (event.action == MotionEvent.ACTION_UP) {\n                    classifyDrawing()\n                }\n                true\n            }\n        }\n    }\n\n    private fun setupClassificationResultAdapter() {\n        classificationResultAdapter = ClassificationResultsAdapter()\n        with(fragmentDigitCanvasBinding.recyclerViewResults) {\n            adapter = classificationResultAdapter\n            layoutManager = LinearLayoutManager(requireContext())\n            addItemDecoration(\n                DividerItemDecoration(\n                    requireContext(),\n                    DividerItemDecoration.VERTICAL\n                )\n            )\n        }\n    }\n\n    private fun initBottomSheetControls() {\n        // When clicked, lower classification score threshold floor\n        fragmentDigitCanvasBinding.bottomSheetLayout.thresholdMinus.setOnClickListener {\n            if (digitClassifierHelper.threshold >= 0.1) {\n                digitClassifierHelper.threshold -= 0.1f\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, raise classification score threshold floor\n        fragmentDigitCanvasBinding.bottomSheetLayout.thresholdPlus.setOnClickListener {\n            if (digitClassifierHelper.threshold < 0.9) {\n                digitClassifierHelper.threshold += 0.1f\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, reduce the number of objects that can be classified at a time\n        fragmentDigitCanvasBinding.bottomSheetLayout.maxResultsMinus.setOnClickListener {\n            if (digitClassifierHelper.maxResults > 1) {\n                digitClassifierHelper.maxResults--\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, increase the number of objects that can be classified at a time\n        fragmentDigitCanvasBinding.bottomSheetLayout.maxResultsPlus.setOnClickListener {\n            if (digitClassifierHelper.maxResults < 3) {\n                digitClassifierHelper.maxResults++\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, decrease the number of threads used for classification\n        fragmentDigitCanvasBinding.bottomSheetLayout.threadsMinus.setOnClickListener {\n            if (digitClassifierHelper.numThreads > 1) {\n                digitClassifierHelper.numThreads--\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, increase the number of threads used for classification\n        fragmentDigitCanvasBinding.bottomSheetLayout.threadsPlus.setOnClickListener {\n            if (digitClassifierHelper.numThreads < 4) {\n                digitClassifierHelper.numThreads++\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, change the underlying hardware used for inference. Current options are CPU\n        // GPU, and NNAPI\n        fragmentDigitCanvasBinding.bottomSheetLayout.spinnerDelegate.setSelection(\n            0,\n            false\n        )\n        fragmentDigitCanvasBinding.bottomSheetLayout.spinnerDelegate.onItemSelectedListener =\n            object : AdapterView.OnItemSelectedListener {\n                override fun onItemSelected(\n                    parent: AdapterView<*>?,\n                    view: View?,\n                    position: Int,\n                    id: Long\n                ) {\n                    digitClassifierHelper.currentDelegate = position\n                    updateControlsUi()\n                }\n\n                override fun onNothingSelected(parent: AdapterView<*>?) {\n                    /* no op */\n                }\n            }\n    }\n\n    // Update the values displayed in the bottom sheet. Reset classifier.\n    private fun updateControlsUi() {\n        fragmentDigitCanvasBinding.bottomSheetLayout.maxResultsValue.text =\n            digitClassifierHelper.maxResults.toString()\n\n        fragmentDigitCanvasBinding.bottomSheetLayout.thresholdValue.text =\n            String.format(Locale.US, \"%.2f\", digitClassifierHelper.threshold)\n        fragmentDigitCanvasBinding.bottomSheetLayout.threadsValue.text =\n            digitClassifierHelper.numThreads.toString()\n        // Needs to be cleared instead of reinitialized because the GPU\n        // delegate needs to be initialized on the thread using it when applicable\n        digitClassifierHelper.clearDigitClassifier()\n    }\n\n    private fun classifyDrawing() {\n        val bitmap = fragmentDigitCanvasBinding.digitCanvas.getBitmap()\n        digitClassifierHelper.classify(bitmap)\n    }\n\n    override fun onError(error: String) {\n        activity?.runOnUiThread {\n            Toast.makeText(requireActivity(), error, Toast.LENGTH_SHORT).show()\n            classificationResultAdapter.reset()\n        }\n    }\n\n    override fun onResults(\n        results: List<Classifications>?,\n        inferenceTime: Long\n    ) {\n        activity?.runOnUiThread {\n            classificationResultAdapter.updateResults(results)\n            fragmentDigitCanvasBinding.tvInferenceTime.text = requireActivity()\n                .getString(R.string.inference_time, inferenceTime.toString())\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/src/main/res/drawable/ic_minus.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13H5v-2h14v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/src/main/res/drawable/ic_plus.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path android:pathData=\"M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"85.84757\"\n                android:endY=\"92.4963\"\n                android:startX=\"42.9492\"\n                android:startY=\"49.59793\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <androidx.fragment.app.FragmentContainerView\n            android:id=\"@+id/fragment_container\"\n            android:name=\"androidx.navigation.fragment.NavHostFragment\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginTop=\"?android:attr/actionBarSize\"\n            android:background=\"@android:color/transparent\"\n            android:keepScreenOn=\"true\"\n            app:defaultNavHost=\"true\"\n            app:navGraph=\"@navigation/nav_graph\"\n            tools:context=\".MainActivity\" />\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:layout_alignParentTop=\"true\"\n            android:background=\"@color/toolbar_background\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/tfl_logo\" />\n        </androidx.appcompat.widget.Toolbar>\n\n    </RelativeLayout>\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/src/main/res/layout/fragment_digit_canvas.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginBottom=\"@dimen/bottom_sheet_peek_height\">\n\n        <com.divyanshu.draw.widget.DrawView\n            android:id=\"@+id/digitCanvas\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"0dp\"\n            app:layout_constraintDimensionRatio=\"1:1\"\n            app:layout_constraintTop_toTopOf=\"parent\" />\n\n        <TextView\n            android:id=\"@+id/tvPredictedResults\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/result_content_padding_top\"\n            android:text=\"@string/tv_predicted_results\"\n            app:layout_constraintTop_toBottomOf=\"@id/digitCanvas\" />\n\n        <androidx.recyclerview.widget.RecyclerView\n            android:id=\"@+id/recyclerViewResults\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"0dp\"\n            android:padding=\"@dimen/result_content_padding\"\n            app:layout_constraintBottom_toTopOf=\"@id/tvInferenceTime\"\n            app:layout_constraintTop_toBottomOf=\"@id/tvPredictedResults\" />\n\n        <TextView\n            android:id=\"@+id/tvInferenceTime\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:padding=\"@dimen/result_content_padding\"\n            app:layout_constraintBottom_toTopOf=\"@id/btnClear\" />\n\n        <Button\n            android:id=\"@+id/btnClear\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/btn_clear\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintLeft_toLeftOf=\"parent\"\n            app:layout_constraintRight_toRightOf=\"parent\" />\n    </androidx.constraintlayout.widget.ConstraintLayout>\n\n    <include\n        android:id=\"@+id/bottom_sheet_layout\"\n        layout=\"@layout/info_bottom_sheet\" />\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/src/main/res/layout/info_bottom_sheet.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.core.widget.NestedScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/bottom_sheet_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_gravity=\"center_horizontal\"\n    android:background=\"@color/bottom_sheet_background\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:padding=\"@dimen/bottom_sheet_padding\"\n    app:behavior_hideable=\"false\"\n    app:behavior_peekHeight=\"@dimen/bottom_sheet_peek_height\"\n    app:layout_behavior=\"com.google.android.material.bottomsheet.BottomSheetBehavior\">\n\n    <androidx.appcompat.widget.LinearLayoutCompat\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <!-- Chevron indicating that the bottom sheet is expandable -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerHorizontal=\"true\"\n                android:layout_centerVertical=\"true\"\n                android:contentDescription=\"@string/alt_bottom_sheet_chevron\"\n                android:src=\"@drawable/icn_chevron_up\" />\n        </RelativeLayout>\n\n        <!-- ML confidence threshold adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:text=\"@string/label_confidence_threshold\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\">\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threshold_minus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_threshold_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/threshold_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:gravity=\"center\"\n                    android:minEms=\"@integer/bottom_sheet_control_text_min_ems\"\n                    android:text=\"@string/default_threshold\"\n                    android:textColor=\"@color/bottom_sheet_text_color\"\n                    android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threshold_plus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_threshold_button_plus\"\n                    android:src=\"@drawable/ic_plus\" />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- ML max results adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:text=\"@string/label_max_results\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center\"\n                android:orientation=\"horizontal\">\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/max_results_minus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_max_results_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/max_results_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:gravity=\"center\"\n                    android:minEms=\"@integer/bottom_sheet_control_text_min_ems\"\n                    android:text=\"@string/default_max_results\"\n                    android:textColor=\"@color/bottom_sheet_text_color\"\n                    android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/max_results_plus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_max_results_button_plus\"\n                    android:src=\"@drawable/ic_plus\" />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- Number of threads adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:text=\"@string/label_threads\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\">\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threads_minus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_thread_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/threads_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:gravity=\"center\"\n                    android:minEms=\"@integer/bottom_sheet_control_text_min_ems\"\n                    android:text=\"2\"\n                    android:textColor=\"@color/bottom_sheet_text_color\"\n                    android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threads_plus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_thread_button_plus\"\n                    android:src=\"@drawable/ic_plus\" />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- Delegate selection row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/label_delegate\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <androidx.appcompat.widget.AppCompatSpinner\n                android:id=\"@+id/spinner_delegate\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:entries=\"@array/delegate_spinner_titles\"\n                android:minWidth=\"@dimen/bottom_sheet_spinner_delegate_min_width\"\n                android:spinnerMode=\"dropdown\"\n                android:theme=\"@style/BottomSheetSpinnerItemStyle\" />\n\n        </RelativeLayout>\n    </androidx.appcompat.widget.LinearLayoutCompat>\n</androidx.core.widget.NestedScrollView>\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/src/main/res/layout/item_classification_result.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <TextView\n        android:id=\"@+id/tvLabel\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:textColor=\"@color/bottom_sheet_text_color\"\n        android:textSize=\"@dimen/bottom_sheet_text_size\"\n        app:layout_constraintEnd_toStartOf=\"@+id/tvScore\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <TextView\n        android:id=\"@+id/tvScore\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:textSize=\"@dimen/bottom_sheet_text_size\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/src/main/res/navigation/nav_graph.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/nav_graph\"\n    app:startDestination=\"@id/digitCanvasFragment\">\n\n    <fragment\n        android:id=\"@+id/digitCanvasFragment\"\n        android:name=\"org.tensorflow.lite.examples.digitclassification.fragments.DigitCanvasFragment\"\n        android:label=\"DigitCanvasFragment\" />\n</navigation>\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n\n<resources>\n    <!-- TF Branding Orange -->\n    <color name=\"toolbar_background\">#EEEEEE</color>\n    <color name=\"ic_launcher_background\">#FFFFFF</color>\n    <color name=\"bottom_sheet_background\">#EEEEEE</color>\n    <color name=\"bottom_sheet_text_color\">@android:color/black</color>\n    <color name=\"icActive\">#FFFFFFFF</color>\n    <color name=\"icFocused\">#DDFFFFFF</color>\n    <color name=\"icPressed\">#AAFFFFFF</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n\n    <!-- Bottom Sheet -->\n    <dimen name=\"bottom_sheet_text_size\">20sp</dimen>\n    <dimen name=\"bottom_sheet_padding\">16dp</dimen>\n    <dimen name=\"bottom_sheet_peek_height\">50dp</dimen>\n    <dimen name=\"bottom_sheet_default_row_margin\">16dp</dimen>\n    <dimen name=\"bottom_sheet_control_btn_size\">48dp</dimen>\n    <dimen name=\"bottom_sheet_control_text_side_margin\">10dp</dimen>\n    <dimen name=\"bottom_sheet_spinner_delegate_min_width\">160dp</dimen>\n    <dimen name=\"result_content_padding\">5dp</dimen>\n    <dimen name=\"result_content_padding_top\">10dp</dimen>\n    <integer name=\"bottom_sheet_control_text_min_ems\">3</integer>\n</resources>\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n    <string name=\"app_name\">TFLite Digit Classification Demo</string>\n\n    <string name=\"alt_bottom_sheet_chevron\">Bottom sheet expandable\n        indicator</string>\n\n    <string name=\"alt_bottom_sheet_max_results_button_minus\">Decreasing\n        maximum classified results button</string>\n    <string name=\"alt_bottom_sheet_max_results_button_plus\">Increasing\n        maximum classified results button</string>\n    <string name=\"alt_bottom_sheet_threshold_button_minus\">Decreasing\n        threshold of classified image results button</string>\n    <string name=\"alt_bottom_sheet_threshold_button_plus\">Increasing\n        threshold of classified image results button</string>\n    <string name=\"alt_bottom_sheet_thread_button_minus\">Decrease the number\n        of threads used</string>\n    <string name=\"alt_bottom_sheet_thread_button_plus\">Increase the number of\n        threads used</string>\n\n    <string name=\"inference_time\">Inference Time: %1$s ms</string>\n    <string name=\"label_confidence_threshold\">Threshold</string>\n    <string name=\"label_max_results\">Max Results</string>\n    <string name=\"label_threads\">Number of Threads</string>\n    <string name=\"label_delegate\">Delegate</string>\n    <string name=\"default_threshold\">0.50</string>\n    <string name=\"default_max_results\">3</string>\n    <string name=\"tv_predicted_results\">Predicted results:</string>\n    <string name=\"btn_clear\">Clear</string>\n\n    <string-array name=\"delegate_spinner_titles\">\n        <item>CPU</item>\n        <item>GPU</item>\n        <item>NNAPI</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n    </style>\n\n    <style name=\"BottomSheetSpinnerItemStyle\" parent=\"Widget.AppCompat.Light.DropDownItem.Spinner\">\n        <item name=\"android:textSize\">@dimen/bottom_sheet_text_size</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n    dependencies {\n        classpath 'de.undercouch:gradle-download-task:4.1.2'\n    }\n}\nplugins {\n    id 'com.android.application' version '7.2.0' apply false\n    id 'com.android.library' version '7.2.0' apply false\n    id 'org.jetbrains.kotlin.android' version '1.6.21' apply false\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/gradle.properties",
    "content": "## For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n#\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\n# Default value: -Xmx1024m -XX:MaxPermSize=256m\n# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\n#\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n#Wed Aug 10 15:39:44 ICT 2022\nandroid.nonTransitiveRClass=true\nkotlin.code.style=official\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding\\=UTF-8\nandroid.useAndroidX=true\nandroid.enableJetifier=true\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/digit_classifier/android/settings.gradle",
    "content": "pluginManagement {\n    repositories {\n        gradlePluginPortal()\n        google()\n        mavenCentral()\n    }\n}\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        google()\n        mavenCentral()\n        maven { url 'https://jitpack.io' }\n    }\n}\nrootProject.name = \"TFLite Digit Classification Demo\"\ninclude ':app'\n"
  },
  {
    "path": "lite/examples/digit_classifier/ios/DigitClassifier/AppDelegate.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n  var window: UIWindow?\n\n\n  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {\n    // Override point for customization after application launch.\n    return true\n  }\n\n  func applicationWillResignActive(_ application: UIApplication) {\n    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.\n    // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.\n  }\n\n  func applicationDidEnterBackground(_ application: UIApplication) {\n    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.\n    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.\n  }\n\n  func applicationWillEnterForeground(_ application: UIApplication) {\n    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.\n  }\n\n  func applicationDidBecomeActive(_ application: UIApplication) {\n    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.\n  }\n\n  func applicationWillTerminate(_ application: UIApplication) {\n    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.\n  }\n\n}\n\n"
  },
  {
    "path": "lite/examples/digit_classifier/ios/DigitClassifier/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\"images\":[{\"size\":\"60x60\",\"expected-size\":\"180\",\"filename\":\"180.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"40x40\",\"expected-size\":\"80\",\"filename\":\"80.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"40x40\",\"expected-size\":\"120\",\"filename\":\"120.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"60x60\",\"expected-size\":\"120\",\"filename\":\"120.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"57x57\",\"expected-size\":\"57\",\"filename\":\"57.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"58\",\"filename\":\"58.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"29x29\",\"expected-size\":\"29\",\"filename\":\"29.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"87\",\"filename\":\"87.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"57x57\",\"expected-size\":\"114\",\"filename\":\"114.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"60\",\"filename\":\"60.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"1024x1024\",\"filename\":\"1024.png\",\"expected-size\":\"1024\",\"idiom\":\"ios-marketing\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"scale\":\"1x\"},{\"size\":\"40x40\",\"expected-size\":\"80\",\"filename\":\"80.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"72x72\",\"expected-size\":\"72\",\"filename\":\"72.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"76x76\",\"expected-size\":\"152\",\"filename\":\"152.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"50x50\",\"expected-size\":\"100\",\"filename\":\"100.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"29x29\",\"expected-size\":\"58\",\"filename\":\"58.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"76x76\",\"expected-size\":\"76\",\"filename\":\"76.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"29\",\"filename\":\"29.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"50x50\",\"expected-size\":\"50\",\"filename\":\"50.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"72x72\",\"expected-size\":\"144\",\"filename\":\"144.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"40x40\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"83.5x83.5\",\"expected-size\":\"167\",\"filename\":\"167.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"20\",\"filename\":\"20.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"20x20\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"}]}"
  },
  {
    "path": "lite/examples/digit_classifier/ios/DigitClassifier/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/digit_classifier/ios/DigitClassifier/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"13122.16\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"13104.12\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "lite/examples/digit_classifier/ios/DigitClassifier/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"14490.70\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <device id=\"retina6_1\" orientation=\"portrait\">\n        <adaptation id=\"fullscreen\"/>\n    </device>\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"14490.49\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"ViewController\" customModule=\"DigitClassifier\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"896\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"jLj-qe-jmK\" customClass=\"SketchView\" customModule=\"Sketch\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"44\" width=\"414\" height=\"414\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"width\" secondItem=\"jLj-qe-jmK\" secondAttribute=\"height\" multiplier=\"1:1\" id=\"099-hi-oGZ\"/>\n                                </constraints>\n                            </view>\n                            <button opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" buttonType=\"roundedRect\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"pb7-DZ-81v\">\n                                <rect key=\"frame\" x=\"177\" y=\"818\" width=\"60\" height=\"44\"/>\n                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"26\"/>\n                                <state key=\"normal\" title=\"Clear\"/>\n                                <connections>\n                                    <action selector=\"tapClear:\" destination=\"BYZ-38-t0r\" eventType=\"touchUpInside\" id=\"zaU-EF-XxE\"/>\n                                </connections>\n                            </button>\n                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Please draw a digit.\" textAlignment=\"center\" lineBreakMode=\"tailTruncation\" numberOfLines=\"0\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"DQi-js-SKf\">\n                                <rect key=\"frame\" x=\"107\" y=\"538\" width=\"200\" height=\"29\"/>\n                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"24\"/>\n                                <nil key=\"textColor\"/>\n                                <nil key=\"highlightedColor\"/>\n                            </label>\n                        </subviews>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <constraints>\n                            <constraint firstItem=\"jLj-qe-jmK\" firstAttribute=\"trailing\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"trailing\" id=\"ADj-4I-xHF\"/>\n                            <constraint firstItem=\"DQi-js-SKf\" firstAttribute=\"centerX\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"centerX\" id=\"L1t-L7-n4f\"/>\n                            <constraint firstItem=\"jLj-qe-jmK\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" id=\"N2S-Ig-442\"/>\n                            <constraint firstItem=\"DQi-js-SKf\" firstAttribute=\"top\" secondItem=\"jLj-qe-jmK\" secondAttribute=\"bottom\" constant=\"80\" id=\"aS9-iV-XND\"/>\n                            <constraint firstAttribute=\"bottomMargin\" secondItem=\"pb7-DZ-81v\" secondAttribute=\"bottom\" id=\"bzb-f0-dXI\"/>\n                            <constraint firstItem=\"jLj-qe-jmK\" firstAttribute=\"top\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"top\" id=\"hRm-i1-giw\"/>\n                            <constraint firstItem=\"pb7-DZ-81v\" firstAttribute=\"centerX\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"centerX\" id=\"yQ5-us-mWo\"/>\n                        </constraints>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                    <connections>\n                        <outlet property=\"resultLabel\" destination=\"DQi-js-SKf\" id=\"AKw-Rv-l64\"/>\n                        <outlet property=\"sketchView\" destination=\"jLj-qe-jmK\" id=\"gng-L9-9NS\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"131.8840579710145\" y=\"138.61607142857142\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "lite/examples/digit_classifier/ios/DigitClassifier/DigitClassifier.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport CoreImage\nimport UIKit\nimport TensorFlowLite\n\nclass DigitClassifier {\n\n  /// TensorFlow Lite `Interpreter` object for performing inference on a given model.\n  private var interpreter: Interpreter\n  private var inputImageWidth: Int\n  private var inputImageHeight: Int\n\n  static func newInstance(completion: @escaping ((Result<DigitClassifier>) -> ())) {\n    // Run initialization in background thread to avoid UI freeze\n    DispatchQueue.global(qos: .background).async {\n\n      // Construct the path to the model file.\n      guard let modelPath = Bundle.main.path(\n        forResource: Constant.modelFilename,\n        ofType: Constant.modelFileExtension\n        ) else {\n          print(\"Failed to load the model file with name: \\(Constant.modelFilename).\")\n          DispatchQueue.main.async {\n            completion(.error(InitializationError.invalidModel(\"\\(Constant.modelFilename).\\(Constant.modelFileExtension)\")))\n          }\n          return\n      }\n\n      // Specify the options for the `Interpreter`.\n      var options = InterpreterOptions()\n      options.threadCount = 2\n\n      do {\n        // Create the `Interpreter`.\n        let interpreter = try Interpreter(modelPath: modelPath, options: options)\n\n        // Allocate memory for the model's input `Tensor`s.\n        try interpreter.allocateTensors()\n\n        // Read TF Lite model input dimension\n        let inputShape = try interpreter.input(at: 0).shape\n        let inputImageWidth = inputShape.dimensions[1]\n        let inputImageHeight = inputShape.dimensions[2]\n\n        // Create DigitClassifier instance and return\n        let classifier = DigitClassifier(\n          interpreter: interpreter,\n          inputImageWidth: inputImageWidth,\n          inputImageHeight: inputImageHeight\n        )\n        DispatchQueue.main.async {\n          completion(.success(classifier))\n        }\n      } catch let error {\n        print(\"Failed to create the interpreter with error: \\(error.localizedDescription)\")\n        DispatchQueue.main.async {\n          completion(.error(InitializationError.internalError(error)))\n        }\n        return\n      }\n    }\n  }\n\n  /// Initialize Digit Classifer instance\n  fileprivate init(interpreter: Interpreter, inputImageWidth: Int, inputImageHeight: Int) {\n    self.interpreter = interpreter\n    self.inputImageWidth = inputImageWidth\n    self.inputImageHeight = inputImageHeight\n  }\n\n  /// Run image classification on the input image.\n  ///\n  /// - Parameters\n  ///   - image: an UIImage instance to classify.\n  ///   - completion: callback to receive the classification result.\n  func classify(image: UIImage, completion: @escaping ((Result<String>) -> ())) {\n    DispatchQueue.global(qos: .background).async {\n      let outputTensor: Tensor\n      do {\n        // Preprocessing: Convert the input UIImage to (28 x 28) grayscale image to feed to TF Lite model.\n        guard let rgbData = image.scaledData(with: CGSize(width: self.inputImageWidth, height: self.inputImageHeight))\n          else {\n            DispatchQueue.main.async {\n              completion(.error(ClassificationError.invalidImage))\n            }\n            print(\"Failed to convert the image buffer to RGB data.\")\n            return\n        }\n\n        // Copy the RGB data to the input `Tensor`.\n        try self.interpreter.copy(rgbData, toInputAt: 0)\n\n        // Run inference by invoking the `Interpreter`.\n        try self.interpreter.invoke()\n\n        // Get the output `Tensor` to process the inference results.\n        outputTensor = try self.interpreter.output(at: 0)\n      } catch let error {\n        print(\"Failed to invoke the interpreter with error: \\(error.localizedDescription)\")\n        DispatchQueue.main.async {\n          completion(.error(ClassificationError.internalError(error)))\n        }\n        return\n      }\n\n      // Postprocessing: Find the label with highest confidence and return as human readable text.\n      let results = outputTensor.data.toArray(type: Float32.self)\n      let maxConfidence = results.max() ?? -1\n      let maxIndex = results.firstIndex(of: maxConfidence) ?? -1\n      let humanReadableResult = \"Predicted: \\(maxIndex)\\nConfidence: \\(maxConfidence)\"\n\n      // Return the classification result\n      DispatchQueue.main.async {\n        completion(.success(humanReadableResult))\n      }\n    }\n  }\n}\n\n/// Convenient enum to return result with a callback\nenum Result<T> {\n  case success(T)\n  case error(Error)\n}\n\n/// Define errors that could happen in the initialization of this class\nenum InitializationError: Error {\n  // Invalid TF Lite model\n  case invalidModel(String)\n  // TF Lite Internal Error when initializing\n  case internalError(Error)\n}\n\n/// Define errors that could happen in when doing image clasification\nenum ClassificationError: Error {\n  // Invalid input image\n  case invalidImage\n  // TF Lite Internal Error when initializing\n  case internalError(Error)\n}\n\n// MARK: - Constants\nprivate enum Constant {\n  /// Specify the TF Lite model file\n  static let modelFilename = \"mnist\"\n  static let modelFileExtension = \"tflite\"\n}\n"
  },
  {
    "path": "lite/examples/digit_classifier/ios/DigitClassifier/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>UIUserInterfaceStyle</key>\n\t<string>Light</string>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "lite/examples/digit_classifier/ios/DigitClassifier/TFLiteExtensions.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport CoreGraphics\nimport Foundation\nimport UIKit\n\n// MARK: - UIImage\nextension UIImage {\n\n  /// Returns the data representation of the image after scaling to the given `size` and converting\n  /// to grayscale.\n  ///\n  /// - Parameters\n  ///   - size: Size to scale the image to (i.e. image size used while training the model).\n  /// - Returns: The scaled image as data or `nil` if the image could not be scaled.\n  public func scaledData(with size: CGSize) -> Data? {\n    guard let cgImage = self.cgImage, cgImage.width > 0, cgImage.height > 0 else { return nil }\n\n    let bitmapInfo = CGBitmapInfo(\n      rawValue: CGImageAlphaInfo.none.rawValue\n    )\n    let width = Int(size.width)\n    guard let context = CGContext(\n      data: nil,\n      width: width,\n      height: Int(size.height),\n      bitsPerComponent: cgImage.bitsPerComponent,\n      bytesPerRow: width * 1,\n      space: CGColorSpaceCreateDeviceGray(),\n      bitmapInfo: bitmapInfo.rawValue)\n      else {\n        return nil\n    }\n    context.draw(cgImage, in: CGRect(origin: .zero, size: size))\n    guard let scaledBytes = context.makeImage()?.dataProvider?.data as Data? else { return nil }\n    let scaledFloats = scaledBytes.map { Float32($0) / Constant.maxRGBValue }\n\n    return Data(copyingBufferOf: scaledFloats)\n  }\n\n}\n\n// MARK: - Data\nextension Data {\n  /// Creates a new buffer by copying the buffer pointer of the given array.\n  ///\n  /// - Warning: The given array's element type `T` must be trivial in that it can be copied bit\n  ///     for bit with no indirection or reference-counting operations; otherwise, reinterpreting\n  ///     data from the resulting buffer has undefined behavior.\n  /// - Parameter array: An array with elements of type `T`.\n  init<T>(copyingBufferOf array: [T]) {\n    self = array.withUnsafeBufferPointer(Data.init)\n  }\n\n  func toArray<T>(type: T.Type) -> [T] where T: ExpressibleByIntegerLiteral {\n    var array = Array<T>(repeating: 0, count: self.count/MemoryLayout<T>.stride)\n    _ = array.withUnsafeMutableBytes { copyBytes(to: $0) }\n    return array\n  }\n}\n\n// MARK: - Constants\nprivate enum Constant {\n  static let maxRGBValue: Float32 = 255.0\n}\n"
  },
  {
    "path": "lite/examples/digit_classifier/ios/DigitClassifier/ViewController.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\nimport Sketch\n\nclass ViewController: UIViewController, SketchViewDelegate {\n\n  @IBOutlet weak var resultLabel: UILabel!\n  @IBOutlet weak var sketchView: SketchView!\n  private var classifier: DigitClassifier?\n\n  override func viewDidLoad() {\n    super.viewDidLoad()\n\n    // Setup sketch view.\n    sketchView.lineWidth = 30\n    sketchView.backgroundColor = UIColor.black\n    sketchView.lineColor = UIColor.white\n    sketchView.sketchViewDelegate = self\n\n    // Initialize a DigitClassifier instance\n    DigitClassifier.newInstance { result in\n      switch result {\n      case let .success(classifier):\n        self.classifier = classifier\n      case .error(_):\n        self.resultLabel.text = \"Failed to initialize.\"\n      }\n    }\n  }\n\n  /// Clear drawing canvas and result text when tapping Clear button.\n  @IBAction func tapClear(_ sender: Any) {\n    sketchView.clear()\n    resultLabel.text = \"Please draw a digit.\"\n  }\n\n  /// Callback executed every time there is a new drawing\n  func drawView(_ view: SketchView, didEndDrawUsingTool tool: AnyObject) {\n    classifyDrawing()\n  }\n\n  /// Classify the drawing currently on the canvas and display result.\n  private func classifyDrawing() {\n    guard let classifier = self.classifier else { return }\n\n    // Capture drawing to RGB file.\n    UIGraphicsBeginImageContext(sketchView.frame.size)\n    sketchView.layer.render(in: UIGraphicsGetCurrentContext()!)\n    let drawing = UIGraphicsGetImageFromCurrentImageContext()\n    UIGraphicsEndImageContext();\n\n    guard drawing != nil else {\n      resultLabel.text = \"Invalid drawing.\"\n      return\n    }\n\n    // Run digit classifier.\n    classifier.classify(image: drawing!) { result in\n      // Show the classification result on screen.\n      switch result {\n      case let .success(classificationResult):\n        self.resultLabel.text = classificationResult\n      case .error(_):\n        self.resultLabel.text = \"Failed to classify drawing.\"\n      }\n    }\n  }\n\n}\n\n"
  },
  {
    "path": "lite/examples/digit_classifier/ios/DigitClassifier.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t78B462C45A26DFD66CF04F72 /* Pods_DigitClassifier.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 35BBB99DAF5672B05BF4D890 /* Pods_DigitClassifier.framework */; };\n\t\t7F2AAA5122FAB86900C66F75 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F2AAA5022FAB86900C66F75 /* AppDelegate.swift */; };\n\t\t7F2AAA5322FAB86900C66F75 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F2AAA5222FAB86900C66F75 /* ViewController.swift */; };\n\t\t7F2AAA5622FAB86900C66F75 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7F2AAA5422FAB86900C66F75 /* Main.storyboard */; };\n\t\t7F2AAA5822FAB86C00C66F75 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7F2AAA5722FAB86C00C66F75 /* Assets.xcassets */; };\n\t\t7F2AAA5B22FAB86C00C66F75 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7F2AAA5922FAB86C00C66F75 /* LaunchScreen.storyboard */; };\n\t\t7F2AAA6322FAC5F700C66F75 /* DigitClassifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F2AAA6222FAC5F700C66F75 /* DigitClassifier.swift */; };\n\t\t7F2AAA6522FAC78500C66F75 /* mnist.tflite in Resources */ = {isa = PBXBuildFile; fileRef = 7F2AAA6422FAC78500C66F75 /* mnist.tflite */; };\n\t\t7F2AAA6722FAE69A00C66F75 /* TFLiteExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F2AAA6622FAE69A00C66F75 /* TFLiteExtensions.swift */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\t0E02F6AC415C851ECEEC3E6C /* Pods-DigitClassifier.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-DigitClassifier.release.xcconfig\"; path = \"Pods/Target Support Files/Pods-DigitClassifier/Pods-DigitClassifier.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t23653868A9D4D98AFCB0433E /* Pods-DigitClassifier.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-DigitClassifier.debug.xcconfig\"; path = \"Pods/Target Support Files/Pods-DigitClassifier/Pods-DigitClassifier.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t35BBB99DAF5672B05BF4D890 /* Pods_DigitClassifier.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DigitClassifier.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t7F2AAA4D22FAB86900C66F75 /* TFL DigitClassifier.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = \"TFL DigitClassifier.app\"; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t7F2AAA5022FAB86900C66F75 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t7F2AAA5222FAB86900C66F75 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = \"<group>\"; };\n\t\t7F2AAA5522FAB86900C66F75 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t7F2AAA5722FAB86C00C66F75 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t7F2AAA5A22FAB86C00C66F75 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t7F2AAA5C22FAB86C00C66F75 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t7F2AAA6222FAC5F700C66F75 /* DigitClassifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DigitClassifier.swift; sourceTree = \"<group>\"; };\n\t\t7F2AAA6422FAC78500C66F75 /* mnist.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = mnist.tflite; sourceTree = \"<group>\"; };\n\t\t7F2AAA6622FAE69A00C66F75 /* TFLiteExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TFLiteExtensions.swift; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t7F2AAA4A22FAB86900C66F75 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t78B462C45A26DFD66CF04F72 /* Pods_DigitClassifier.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t054A4559A6F6171CAD5DB505 /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t23653868A9D4D98AFCB0433E /* Pods-DigitClassifier.debug.xcconfig */,\n\t\t\t\t0E02F6AC415C851ECEEC3E6C /* Pods-DigitClassifier.release.xcconfig */,\n\t\t\t);\n\t\t\tname = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t292F58C19DCE0840527E39B6 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t35BBB99DAF5672B05BF4D890 /* Pods_DigitClassifier.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7F2AAA4422FAB86900C66F75 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t7F2AAA4F22FAB86900C66F75 /* DigitClassifier */,\n\t\t\t\t7F2AAA4E22FAB86900C66F75 /* Products */,\n\t\t\t\t054A4559A6F6171CAD5DB505 /* Pods */,\n\t\t\t\t292F58C19DCE0840527E39B6 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7F2AAA4E22FAB86900C66F75 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t7F2AAA4D22FAB86900C66F75 /* TFL DigitClassifier.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7F2AAA4F22FAB86900C66F75 /* DigitClassifier */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t7F2AAA5022FAB86900C66F75 /* AppDelegate.swift */,\n\t\t\t\t7F2AAA5222FAB86900C66F75 /* ViewController.swift */,\n\t\t\t\t7F2AAA6222FAC5F700C66F75 /* DigitClassifier.swift */,\n\t\t\t\t7F2AAA6622FAE69A00C66F75 /* TFLiteExtensions.swift */,\n\t\t\t\t7F2AAA6422FAC78500C66F75 /* mnist.tflite */,\n\t\t\t\t7F2AAA5422FAB86900C66F75 /* Main.storyboard */,\n\t\t\t\t7F2AAA5722FAB86C00C66F75 /* Assets.xcassets */,\n\t\t\t\t7F2AAA5922FAB86C00C66F75 /* LaunchScreen.storyboard */,\n\t\t\t\t7F2AAA5C22FAB86C00C66F75 /* Info.plist */,\n\t\t\t);\n\t\t\tpath = DigitClassifier;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t7F2AAA4C22FAB86900C66F75 /* DigitClassifier */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 7F2AAA5F22FAB86C00C66F75 /* Build configuration list for PBXNativeTarget \"DigitClassifier\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tD72900AFF43DC7B4B5CD1959 /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t7F5EEAE82333630A00A72F0E /* Download TF Lite model */,\n\t\t\t\t7F2AAA4922FAB86900C66F75 /* Sources */,\n\t\t\t\t7F2AAA4A22FAB86900C66F75 /* Frameworks */,\n\t\t\t\t7F2AAA4B22FAB86900C66F75 /* Resources */,\n\t\t\t\t4571ED1E975E7C5FA0035739 /* [CP] Embed Pods Frameworks */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = DigitClassifier;\n\t\t\tproductName = DigitClassifier;\n\t\t\tproductReference = 7F2AAA4D22FAB86900C66F75 /* TFL DigitClassifier.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t7F2AAA4522FAB86900C66F75 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 1030;\n\t\t\t\tLastUpgradeCheck = 1030;\n\t\t\t\tORGANIZATIONNAME = \"Google Inc.\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t7F2AAA4C22FAB86900C66F75 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 10.3;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 7F2AAA4822FAB86900C66F75 /* Build configuration list for PBXProject \"DigitClassifier\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 7F2AAA4422FAB86900C66F75;\n\t\t\tproductRefGroup = 7F2AAA4E22FAB86900C66F75 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t7F2AAA4C22FAB86900C66F75 /* DigitClassifier */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t7F2AAA4B22FAB86900C66F75 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t7F2AAA5B22FAB86C00C66F75 /* LaunchScreen.storyboard in Resources */,\n\t\t\t\t7F2AAA5822FAB86C00C66F75 /* Assets.xcassets in Resources */,\n\t\t\t\t7F2AAA5622FAB86900C66F75 /* Main.storyboard in Resources */,\n\t\t\t\t7F2AAA6522FAC78500C66F75 /* mnist.tflite in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t4571ED1E975E7C5FA0035739 /* [CP] Embed Pods Frameworks */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${SRCROOT}/Pods/Target Support Files/Pods-DigitClassifier/Pods-DigitClassifier-frameworks.sh\",\n\t\t\t\t\"${BUILT_PRODUCTS_DIR}/Sketch/Sketch.framework\",\n\t\t\t);\n\t\t\tname = \"[CP] Embed Pods Frameworks\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sketch.framework\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"${SRCROOT}/Pods/Target Support Files/Pods-DigitClassifier/Pods-DigitClassifier-frameworks.sh\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t7F5EEAE82333630A00A72F0E /* Download TF Lite model */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Download TF Lite model\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"# Download mnist.tflite from the internet if it's not exist.\\nTFLITE_FILE=${SRCROOT}/DigitClassifier/mnist.tflite\\nif test -f \\\"$TFLITE_FILE\\\"; then\\n    echo \\\"INFO: mnist.tflite existed. Skip downloading and use the local model.\\\"\\nelse\\n    curl -o ${TFLITE_FILE} https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/digit_classifier/ios/mnist.tflite\\n    echo \\\"INFO: Downloaded mnist.tflite to $TFLITE_FILE .\\\"\\nfi\\n\\n\";\n\t\t};\n\t\tD72900AFF43DC7B4B5CD1959 /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-DigitClassifier-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t7F2AAA4922FAB86900C66F75 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t7F2AAA5322FAB86900C66F75 /* ViewController.swift in Sources */,\n\t\t\t\t7F2AAA6722FAE69A00C66F75 /* TFLiteExtensions.swift in Sources */,\n\t\t\t\t7F2AAA6322FAC5F700C66F75 /* DigitClassifier.swift in Sources */,\n\t\t\t\t7F2AAA5122FAB86900C66F75 /* AppDelegate.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\t7F2AAA5422FAB86900C66F75 /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t7F2AAA5522FAB86900C66F75 /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7F2AAA5922FAB86C00C66F75 /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t7F2AAA5A22FAB86C00C66F75 /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t7F2AAA5D22FAB86C00C66F75 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.4;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t7F2AAA5E22FAB86C00C66F75 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.4;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t7F2AAA6022FAB86C00C66F75 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 23653868A9D4D98AFCB0433E /* Pods-DigitClassifier.debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tDEVELOPMENT_TEAM = PNR9X9TVFG;\n\t\t\t\tINFOPLIST_FILE = DigitClassifier/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.google.examples.DigitClassifier;\n\t\t\t\tPRODUCT_NAME = \"TFL DigitClassifier\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = Wildcard;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t7F2AAA6122FAB86C00C66F75 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 0E02F6AC415C851ECEEC3E6C /* Pods-DigitClassifier.release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tDEVELOPMENT_TEAM = PNR9X9TVFG;\n\t\t\t\tINFOPLIST_FILE = DigitClassifier/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.google.examples.DigitClassifier;\n\t\t\t\tPRODUCT_NAME = \"TFL DigitClassifier\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = Wildcard;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t7F2AAA4822FAB86900C66F75 /* Build configuration list for PBXProject \"DigitClassifier\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t7F2AAA5D22FAB86C00C66F75 /* Debug */,\n\t\t\t\t7F2AAA5E22FAB86C00C66F75 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t7F2AAA5F22FAB86C00C66F75 /* Build configuration list for PBXNativeTarget \"DigitClassifier\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t7F2AAA6022FAB86C00C66F75 /* Debug */,\n\t\t\t\t7F2AAA6122FAB86C00C66F75 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 7F2AAA4522FAB86900C66F75 /* Project object */;\n}\n"
  },
  {
    "path": "lite/examples/digit_classifier/ios/Podfile",
    "content": "platform :ios, '12.0'\n\ntarget 'DigitClassifier' do\n  use_frameworks!\n  \n  pod 'Sketch'\n  pod 'TensorFlowLiteSwift' \n\nend\n"
  },
  {
    "path": "lite/examples/digit_classifier/ios/README.md",
    "content": "# Digit Classifier iOS sample\n\n<img src=\"https://storage.googleapis.com/khanhlvg-public.appspot.com/digit-classifier/screenshot_ios.png\" />\n\n## Requirements\n\n*  Xcode 10.3 (installed on a Mac machine)\n*  An iOS Simuiator running iOS 12 or above\n*  Xcode command-line tools (run ```xcode-select --install```)\n*  CocoaPods (run ```sudo gem install cocoapods```)\n\n## Build and run\n\n1. Clone the TensorFlow examples GitHub repository to your computer to get the\ndemo\napplication.<br/>\n```git clone https://github.com/tensorflow/examples```\n1. Install the pod to generate the workspace file:<br/>\n```cd examples/lite/examples/digit_classifier/ios && pod install```<br/>\nNote: If you have installed this pod before and that command doesn't work, try ```pod update```.<br/>\nAt the end of this step you should have a directory called ```DigitClassifier.xcworkspace```.\n1. Open the project in Xcode with the following command:<br/>\n```open DigitClassifier.xcworkspace```<br/>\nThis launches Xcode and opens the ```DigitClassifier``` project.\n1. Select `Product -> Run` to install the app on an iOS Simulator or a physical\ndevice.\n"
  },
  {
    "path": "lite/examples/digit_classifier/ml/mnist_tflite.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Tce3stUlHN0L\"\n      },\n      \"source\": [\n        \"##### Copyright 2019 The TensorFlow Authors.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"tuOe1ymfHZPu\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"MfBg1C5NB3X0\"\n      },\n      \"source\": [\n        \"# Build a digit classifier app with TensorFlow Lite\\n\",\n        \"\\n\",\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/lite/examples/digit_classifier/ml/mnist_tflite.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/lite/examples/digit_classifier/ml/mnist_tflite.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"xHxb-dlhMIzW\"\n      },\n      \"source\": [\n        \"## Overview\\n\",\n        \"\\n\",\n        \"This notebook shows an end-to-end example of training a TensorFlow model using Keras and Python, then export it to TensorFlow Lite format to use in mobile apps. Here we will train a handwritten digit classifier using MNIST dataset.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"MUXex9ctTuDB\"\n      },\n      \"source\": [\n        \"## Setup\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"kS_mq4yAlXHZ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# TensorFlow and tf.keras\\n\",\n        \"import tensorflow as tf\\n\",\n        \"from tensorflow import keras\\n\",\n        \"\\n\",\n        \"# Helper libraries\\n\",\n        \"import numpy as np\\n\",\n        \"import matplotlib.pyplot as plt\\n\",\n        \"import math\\n\",\n        \"\\n\",\n        \"print(tf.__version__)\\n\",\n        \"\\n\",\n        \"# Helper function to display digit images\\n\",\n        \"def show_sample(images, labels, sample_count=25):\\n\",\n        \"  # Create a square with can fit {sample_count} images\\n\",\n        \"  grid_count = math.ceil(math.ceil(math.sqrt(sample_count)))\\n\",\n        \"  grid_count = min(grid_count, len(images), len(labels))\\n\",\n        \"  \\n\",\n        \"  plt.figure(figsize=(2*grid_count, 2*grid_count))\\n\",\n        \"  for i in range(sample_count):\\n\",\n        \"    plt.subplot(grid_count, grid_count, i+1)\\n\",\n        \"    plt.xticks([])\\n\",\n        \"    plt.yticks([])\\n\",\n        \"    plt.grid(False)\\n\",\n        \"    plt.imshow(images[i], cmap=plt.cm.gray)\\n\",\n        \"    plt.xlabel(labels[i])\\n\",\n        \"  plt.show()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"r0WlLrJcnwWp\"\n      },\n      \"source\": [\n        \"## Download and explore the MNIST dataset\\n\",\n        \"The MNIST database contains 60,000 training images and 10,000 testing images of handwritten digits. We will use the dataset to demonstrate how to train a image classification model and convert it to TensorFlow Lite format.\\n\",\n        \"\\n\",\n        \"Each image in the MNIST dataset is a 28x28 grayscale image containing a digit.\\n\",\n        \"![MNIST sample](https://storage.googleapis.com/khanhlvg-public.appspot.com/digit-classifier/mnist.png)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"B5REuMrblewK\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Download MNIST dataset.\\n\",\n        \"mnist = keras.datasets.mnist\\n\",\n        \"(train_images, train_labels), (test_images, test_labels) = mnist.load_data()\\n\",\n        \"\\n\",\n        \"# If you can't download the MNIST dataset from Keras, please try again with an alternative method below\\n\",\n        \"# path = keras.utils.get_file('mnist.npz',\\n\",\n        \"#                             origin='https://s3.amazonaws.com/img-datasets/mnist.npz',\\n\",\n        \"#                             file_hash='8a61469f7ea1b51cbae51d4f78837e45')\\n\",\n        \"# with np.load(path, allow_pickle=True) as f:\\n\",\n        \"#   train_images, train_labels = f['x_train'], f['y_train']\\n\",\n        \"#   test_images, test_labels = f['x_test'], f['y_test']\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"REY5lDDylpFh\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Normalize the input image so that each pixel value is between 0 to 1.\\n\",\n        \"train_images = train_images / 255.0\\n\",\n        \"test_images = test_images / 255.0\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"SAOE84IplyWR\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Show the first 25 images in the training dataset.\\n\",\n        \"show_sample(train_images, \\n\",\n        \"            ['Label: %s' % label for label in train_labels])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"9v-Wp3TxpLX6\"\n      },\n      \"source\": [\n        \"## Train a TensorFlow model to classify digit images\\n\",\n        \"We use Keras API to build a TensorFlow model that can classify the digit images. Please see this [tutorial](https://www.tensorflow.org/beta/tutorials/keras/basic_classification) if you are interested to learn more about how to build machine learning model with Keras and TensorFlow.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"IWgBGmaplzcp\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Define the model architecture\\n\",\n        \"model = keras.Sequential([\\n\",\n        \"    keras.layers.Flatten(input_shape=(28, 28)),\\n\",\n        \"    keras.layers.Dense(128, activation=tf.nn.relu),\\n\",\n        \"\\n\",\n        \"# Optional: You can replace the dense layer above with the convolution layers below to get higher accuracy.\\n\",\n        \"    # keras.layers.Reshape(target_shape=(28, 28, 1)),\\n\",\n        \"    # keras.layers.Conv2D(filters=32, kernel_size=(3, 3), activation=tf.nn.relu),\\n\",\n        \"    # keras.layers.Conv2D(filters=64, kernel_size=(3, 3), activation=tf.nn.relu),\\n\",\n        \"    # keras.layers.MaxPooling2D(pool_size=(2, 2)),\\n\",\n        \"    # keras.layers.Dropout(0.25),\\n\",\n        \"    # keras.layers.Flatten(input_shape=(28, 28)),\\n\",\n        \"    # keras.layers.Dense(128, activation=tf.nn.relu),\\n\",\n        \"    # keras.layers.Dropout(0.5),\\n\",\n        \"\\n\",\n        \"    keras.layers.Dense(10)\\n\",\n        \"])\\n\",\n        \"\\n\",\n        \"model.compile(optimizer='adam',\\n\",\n        \"              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\\n\",\n        \"              metrics=['accuracy'])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"V6SOZuLRmEzS\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Train the digit classification model\\n\",\n        \"model.fit(train_images, train_labels, epochs=5)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"za35DFJ5uFkX\"\n      },\n      \"source\": [\n        \"## Evaluate our model\\n\",\n        \"We run our digit classification model against our test dataset that the model hasn't seen during its training process. We want to confirm that the model didn't just remember the digits it saw but also generalize well to new images.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"sJI8nqFWmMwC\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Evaluate the model using test dataset.\\n\",\n        \"test_loss, test_acc = model.evaluate(test_images, test_labels)\\n\",\n        \"\\n\",\n        \"print('Test accuracy:', test_acc)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"PdlXEyUImeXC\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Predict the labels of digit images in our test dataset.\\n\",\n        \"predictions = model.predict(test_images)\\n\",\n        \"\\n\",\n        \"# Then plot the first 25 test images and their predicted labels.\\n\",\n        \"show_sample(test_images, \\n\",\n        \"            ['Predicted: %d' % np.argmax(result) for result in predictions])\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"AWROBI4iv9fY\"\n      },\n      \"source\": [\n        \"## Convert the Keras model to TensorFlow Lite\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"2fXStjR4mzkR\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Convert Keras model to TF Lite format.\\n\",\n        \"converter = tf.lite.TFLiteConverter.from_keras_model(model)\\n\",\n        \"tflite_model = converter.convert()\\n\",\n        \"\\n\",\n        \"# Save the TF Lite model as file\\n\",\n        \"f = open('mnist.tflite', \\\"wb\\\")\\n\",\n        \"f.write(tflite_model)\\n\",\n        \"f.close()\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Q_Z5yLxrwbpI\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Download the digit classification model if you're using Colab, \\n\",\n        \"# or print the model's local path if you're not using Colab.\\n\",\n        \"try:\\n\",\n        \"  from google.colab import files\\n\",\n        \"  files.download('mnist.tflite')\\n\",\n        \"except ImportError:\\n\",\n        \"  import os\\n\",\n        \"  print('TF Lite model:', os.path.join(os.getcwd(), 'mnist.tflite'))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"3TvDxaYU2ui7\"\n      },\n      \"source\": [\n        \"## Verify the TensorFlow Lite model\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"fFtIESwrx7cR\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Download a test image\\n\",\n        \"zero_img_path = keras.utils.get_file(\\n\",\n        \"    'zero.png', \\n\",\n        \"    'https://storage.googleapis.com/khanhlvg-public.appspot.com/digit-classifier/zero.png'\\n\",\n        \")\\n\",\n        \"image = keras.preprocessing.image.load_img(\\n\",\n        \"    zero_img_path,\\n\",\n        \"    color_mode = 'grayscale',\\n\",\n        \"    target_size=(28, 28),\\n\",\n        \"    interpolation='bilinear'\\n\",\n        \")\\n\",\n        \"\\n\",\n        \"# Pre-process the image: Adding batch dimension and normalize the pixel value to [0..1]\\n\",\n        \"# In training, we feed images in a batch to the model to improve training speed, making the model input shape to be (BATCH_SIZE, 28, 28).\\n\",\n        \"# For inference, we still need to match the input shape with training, so we expand the input dimensions to (1, 28, 28) using np.expand_dims\\n\",\n        \"input_image = np.expand_dims(np.array(image, dtype=np.float32) / 255.0, 0)\\n\",\n        \"\\n\",\n        \"# Show the pre-processed input image\\n\",\n        \"show_sample(input_image, ['Input Image'], 1)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"xPtbtEJ2uacB\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Run inference with TensorFlow Lite\\n\",\n        \"interpreter = tf.lite.Interpreter(model_content=tflite_model)\\n\",\n        \"interpreter.allocate_tensors()\\n\",\n        \"interpreter.set_tensor(interpreter.get_input_details()[0][\\\"index\\\"], input_image)\\n\",\n        \"interpreter.invoke()\\n\",\n        \"output = interpreter.tensor(interpreter.get_output_details()[0][\\\"index\\\"])()[0]\\n\",\n        \"\\n\",\n        \"# Print the model's classification result\\n\",\n        \"digit = np.argmax(output)\\n\",\n        \"print('Predicted Digit: %d\\\\nConfidence: %f' % (digit, output[digit]))\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"mnist_tflite.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "lite/examples/generative_ai/android/README.md",
    "content": "# Generative AI\n\n## Introduction\nLarge language models (LLMs) are types of machine learning models that are created based on large bodies of text data to generate various outputs for natural language processing (NLP) tasks, including text generation, question answering, and machine translation. They are based on Transformer architecture and are trained on massive amounts of text data, often involving billions of words. Even LLMs of a smaller scale, such as GPT-2, can perform impressively. Converting TensorFlow models to a lighter, faster, and low-power model allows for us to run generative AI models on-device, with benefits of better user security because data will never leave your device.\n\n This example shows you how to build an Android app with TensorFlow Lite to run a Keras  LLM  and provides suggestions for model optimization using quantizing techniques, which otherwise would require a much larger amount of memory and greater computational power to run.\n\nThis example open sourced an Android app framework that any compatible TFLite LLMs can plug into. Here are two demos: \n*   In Figure 1, we used a Keras GPT-2 model to perform text completion tasks on device.\n*   In Figure 2, we converted a version of instruction-tuned [PaLM model](https://ai.googleblog.com/2022/04/pathways-language-model-palm-scaling-to.html) (1.5 billion parameters) to TFLite and executed through TFLite runtime.\n\n<p align=\"center\">\n  <img src=\"figures/fig1.gif\" width=\"300\">\n</p>\nFigure 1: Example of running the Keras GPT-2 model (converted from this Codelab) on device to perform text completion on Pixel 7. Demo shows the real latency with no speedup.\n<p align=\"center\">\n  <img src=\"figures/fig2.gif\" width=\"300\">\n</p>\nFigure 2: Example of running a version of [PaLM model](https://ai.googleblog.com/2022/04/pathways-language-model-palm-scaling-to.html) with 1.5 billion parameters. Demo is recorded on Pixel 7 Pro without playback speedup.\n\n\n## Guides\n### Step 1. Train a language model using Keras\n\nFor this demonstration, we will use KerasNLP to get the GPT-2 model. KerasNLP is a library that contains state-of-the-art pretrained models for natural language processing tasks, and can support users through their entire development cycle. You can see the list of models available in the [KerasNLP repository](https://github.com/keras-team/keras-nlp/tree/master/keras_nlp/models). The workflows are built from modular components that have state-of-the-art preset weights and architectures when used out-of-the-box and are easily customizable when more control is needed. Creating the GPT-2 model can be done with the following steps:\n\n```python\ngpt2_tokenizer = keras_nlp.models.GPT2Tokenizer.from_preset(\"gpt2_base_en\")\n\ngpt2_preprocessor = keras_nlp.models.GPT2CausalLMPreprocessor.from_preset(\n  \"gpt2_base_en\",\n  sequence_length=256,\n  add_end_token=True,\n)\n\ngpt2_lm = keras_nlp.models.GPT2CausalLM.from_preset(\n  \"gpt2_base_en\", \n  preprocessor=gpt2_preprocessor,\n)\n```\n\nYou can check out the full GPT-2 model implementation [on GitHub](https://github.com/keras-team/keras-nlp/tree/master/keras_nlp/models/gpt2).\n\n\n### Step 2. Convert a Keras model to a TFLite model\n\nStart with the `generate()` function from GPT2CausalLM that performs the conversion. Wrap the `generate()` function to create a concrete TensorFlow function:\n\n```python\n@tf.function\ndef generate(prompt, max_length):\n  # prompt: input prompt to the LLM in string format\n  # max_length: the max length of the generated tokens \n  return gpt2_lm.generate(prompt, max_length)\nconcrete_func = generate.get_concrete_function(tf.TensorSpec([], tf.string), 100)\n```\n\nNow define a helper function that will run inference with an input and a TFLite model. TensorFlow text ops are not built-in ops in the TFLite runtime, so you will need to add these custom ops in order for the interpreter to make inference on this model. This helper function accepts an input and a function that performs the conversion, namely the `generator()` function defined above. \n\n```python\ndef run_inference(input, generate_tflite):\n  interp = interpreter.InterpreterWithCustomOps(\n    model_content=generate_tflite,\n    custom_op_registerers=tf_text.tflite_registrar.SELECT_TFTEXT_OPS)\n  interp.get_signature_list()\n\n  generator = interp.get_signature_runner('serving_default')\n  output = generator(prompt=np.array([input]))\n```\n\nYou can convert the model now:\n\n```python\ngpt2_lm.jit_compile = False\nconverter = tf.lite.TFLiteConverter.from_concrete_functions(\n  [concrete_func],\n  gpt2_lm)\n\nconverter.target_spec.supported_ops = [\n  tf.lite.OpsSet.TFLITE_BUILTINS, # enable TFLite ops\n  tf.lite.OpsSet.SELECT_TF_OPS, # enable TF ops\n]\nconverter.allow_custom_ops = True\nconverter.target_spec.experimental_select_user_tf_ops = [\n  \"UnsortedSegmentJoin\",\n  \"UpperBound\"\n]\nconverter._experimental_guarantee_all_funcs_one_use = True\ngenerate_tflite = converter.convert()\nrun_inference(\"I'm enjoying a\", generate_tflite)\n```\n\n### Step 3. Quantization\nTensorFlow Lite has implemented an optimization technique called quantization which can  reduce model size and accelerate inference. Through the quantization process, 32-bit floats are mapped to smaller 8-bit integers, therefore reducing the model size by a factor of 4 for more efficient execution on modern hardwares. There are several ways to do quantization in TensorFlow. You can visit the [TFLite Model optimization](https://www.tensorflow.org/lite/performance/model_optimization) and [TensorFlow Model Optimization Toolkit](https://www.tensorflow.org/model_optimization) pages for more information. The types of quantizations are explained briefly below.\n\nHere, you will use the post-training dynamic range quantization on the GPT-2 model by setting the converter optimization flag to tf.lite.Optimize.DEFAULT, and the rest of the conversion process is the same as detailed before. We tested that with this quantization technique the latency is around 6.7 seconds on Pixel 7 with max output length set to 100.\n\n```python\ngpt2_lm.jit_compile = False\nconverter = tf.lite.TFLiteConverter.from_concrete_functions(\n  [concrete_func],\n  gpt2_lm)\n\nconverter.target_spec.supported_ops = [\n  tf.lite.OpsSet.TFLITE_BUILTINS, # enable TFLite ops\n  tf.lite.OpsSet.SELECT_TF_OPS, # enable TF ops\n]\nconverter.allow_custom_ops = True\nconverter.optimizations = [tf.lite.Optimize.DEFAULT]\nconverter.target_spec.experimental_select_user_tf_ops = [\n  \"UnsortedSegmentJoin\",\n  \"UpperBound\"\n]\nconverter._experimental_guarantee_all_funcs_one_use = True\nquant_generate_tflite = converter.convert()\nrun_inference(\"I'm enjoying a\", quant_generate_tflite)\n\nwith open('quantized_gpt2.tflite', 'wb') as f:\n  f.write(quant_generate_tflite)\n```\n\n\n\n### Step 4. Android App integration\n\nYou can clone this repo and substitute `android/app/src/main/assets/autocomplete.tflite` with your converted `quant_generate_tflite` file. Please refer to [how-to-build.md](https://github.com/tensorflow/examples/blob/master/lite/examples/generative_ai/android/how-to-build.md) to build this Android App. \n\n## Safety and Responsible AI\nAs noted in the original [OpenAI GPT-2 announcement](https://openai.com/research/better-language-models), there are [notable caveats and limitations](https://github.com/openai/gpt-2#some-caveats) with the GPT-2 model. In fact, LLMs today generally have some well-known challenges such as hallucinations, fairness, and bias; this is because these models are trained on real-world data, which make them reflect real world issues.\nThis codelab is created only to demonstrate how to create an app powered by LLMs with TensorFlow tooling. The model produced in this codelab is for educational purposes only and not intended for production usage.\nLLM production usage requires thoughtful selection of training datasets and comprehensive safety mitigations. One such functionality offered in this Android app is the profanity filter, which rejects bad user inputs or model outputs. If any inappropriate language is detected, the app will in return reject that action. To learn more about Responsible AI in the context of LLMs, make sure to watch the Safe and Responsible Development with Generative Language Models technical session at Google I/O 2023 and check out the [Responsible AI Toolkit](https://www.tensorflow.org/responsible_ai).\n"
  },
  {
    "path": "lite/examples/generative_ai/android/app/build.gradle.kts",
    "content": "@file:Suppress(\"UnstableApiUsage\")\n\nplugins {\n    kotlin(\"android\")\n    id(\"com.android.application\")\n    id(\"de.undercouch.download\")\n}\n\next {\n    set(\"AAR_URL\", \"https://storage.googleapis.com/download.tensorflow.org/models/tflite/generativeai/tensorflow-lite-select-tf-ops.aar\")\n    set(\"AAR_PATH\", \"$projectDir/libs/tensorflow-lite-select-tf-ops.aar\")\n}\n\napply {\n    from(\"download.gradle\")\n}\n\nandroid {\n    namespace = \"com.google.tensorflowdemo\"\n    compileSdk = 33\n\n    defaultConfig {\n        applicationId = \"com.google.tensorflowdemo\"\n        minSdk = 24\n        targetSdk = 33\n        versionCode = 1\n        versionName = \"1.0\"\n    }\n    buildFeatures {\n        compose = true\n        buildConfig = true\n        viewBinding = true\n    }\n    composeOptions {\n        kotlinCompilerExtensionVersion = \"1.3.2\"\n    }\n    packagingOptions {\n        resources {\n            excludes += \"/META-INF/{AL2.0,LGPL2.1}\"\n        }\n    }\n    buildTypes {\n        getByName(\"release\") {\n            isMinifyEnabled = true\n            proguardFiles(getDefaultProguardFile(\"proguard-android-optimize.txt\"), \"proguard-rules.pro\")\n            isDebuggable = false\n        }\n        getByName(\"debug\") {\n            applicationIdSuffix = \".debug\"\n        }\n    }\n    compileOptions {\n        sourceCompatibility = JavaVersion.VERSION_1_8\n        targetCompatibility = JavaVersion.VERSION_1_8\n    }\n    kotlinOptions {\n        jvmTarget = \"1.8\"\n        freeCompilerArgs = listOf(\n            \"-P\",\n            \"plugin:androidx.compose.compiler.plugins.kotlin:suppressKotlinVersionCompatibilityCheck=1.8.10\"\n        )\n    }\n}\n\ndependencies {\n    implementation(fileTree(mapOf(\"dir\" to \"libs\", \"include\" to listOf(\"*.aar\"))))\n\n    // Compose\n    implementation(libraries.compose.ui)\n    implementation(libraries.compose.ui.tooling)\n    implementation(libraries.compose.ui.tooling.preview)\n    implementation(libraries.compose.foundation)\n    implementation(libraries.compose.material)\n    implementation(libraries.compose.material.icons)\n    implementation(libraries.compose.activity)\n\n    // Accompanist for Compose\n    implementation(libraries.accompanist.systemuicontroller)\n\n    // Koin\n    implementation(libraries.koin.core)\n    implementation(libraries.koin.android)\n    implementation(libraries.koin.compose)\n\n    // Lifecycle\n    implementation(libraries.lifecycle.viewmodel)\n    implementation(libraries.lifecycle.viewmodel.compose)\n    implementation(libraries.lifecycle.viewmodel.ktx)\n    implementation(libraries.lifecycle.runtime.compose)\n\n    // Logging\n    implementation(libraries.napier)\n\n    // Profanity filter\n    implementation(libraries.wordfilter)\n\n    // TensorFlow Lite\n    implementation(libraries.tflite)\n\n    // Unit tests\n    testImplementation(libraries.junit)\n}\n"
  },
  {
    "path": "lite/examples/generative_ai/android/app/download.gradle",
    "content": "task downloadAAR {\n    download {\n        src project.ext.AAR_URL\n        dest project.ext.AAR_PATH\n        overwrite false\n    }\n}"
  },
  {
    "path": "lite/examples/generative_ai/android/app/libs/.gitignore",
    "content": "tensorflow-lite-select-tf-ops.aar"
  },
  {
    "path": "lite/examples/generative_ai/android/app/libs/build_aar/README.md",
    "content": "# Build your own aar\n\nBy default the app automatically downloads the needed aar files. But if you want\nto build your own, just go ahead and run `./build_aar.sh`. This script will pull\nin the necessary ops from [TensorFlow Text](https://www.tensorflow.org/text) and\nbuild the aar for [Select TF operators](https://www.tensorflow.org/lite/guide/ops_select).\n\nAfter compilation, a new file `tftext_tflite_flex.aar` is generated. Replace the\none in app/libs/ folder and re-build the app.\n\nBy default, the script builds only for `android_x86_64`. You can change it to\n`android_x86`, `android_arm` or `android_arm64`.\n\nNote that you still need to include the standard `tensorflow-lite` aar in your\ngradle file.\n\n"
  },
  {
    "path": "lite/examples/generative_ai/android/app/libs/build_aar/build_aar.sh",
    "content": "#! /bin/bash\n\nset -e\n\n# Clone TensorFlow Text repo\ngit clone https://github.com/tensorflow/text.git tensorflow_text\n\ncd tensorflow_text/\necho 'exports_files([\"LICENSE\"])' > BUILD\n\n# Checkout 2.12 branch \ngit checkout 2.12\n\n# Apply tftext-2.12.patch\ngit apply ../tftext-2.12.patch\n\n# Run config\n./oss_scripts/configure.sh\n\n# Run bazel build \nbazel build -c opt --cxxopt='--std=c++14' --config=monolithic --config=android_x86_64  --experimental_repo_remote_exec //tensorflow_text:tftext_tflite_flex\n\nif [ $? -eq 0 ]; then\n    # Print a message\n    echo \"Please find the aar file: tensorflow_text/bazel-bin/tensorflow_text/tftext_tflite_flex.aar\"\nelse\n    echo \"build_aar.sh has failed. Please find the error message above and address it before proceeding.\"\nfi"
  },
  {
    "path": "lite/examples/generative_ai/android/app/libs/build_aar/tftext-2.12.patch",
    "content": "diff --git a/WORKSPACE b/WORKSPACE\nindex 28b7ee5..5ad0b55 100644\n--- a/WORKSPACE\n+++ b/WORKSPACE\n@@ -116,3 +116,10 @@ load(\"@org_tensorflow//third_party/android:android_configure.bzl\", \"android_conf\n android_configure(name=\"local_config_android\")\n load(\"@local_config_android//:android.bzl\", \"android_workspace\")\n android_workspace()\n+\n+android_sdk_repository(name = \"androidsdk\")\n+\n+android_ndk_repository(\n+    name = \"androidndk\",\n+    api_level = 21,\n+)\ndiff --git a/tensorflow_text/BUILD b/tensorflow_text/BUILD\nindex 9b5ee5b..880c7c5 100644\n--- a/tensorflow_text/BUILD\n+++ b/tensorflow_text/BUILD\n@@ -2,6 +2,8 @@ load(\"//tensorflow_text:tftext.bzl\", \"py_tf_text_library\")\n \n # [internal] load build_test.bzl\n load(\"@org_tensorflow//tensorflow/lite:build_def.bzl\", \"tflite_cc_shared_object\")\n+load(\"@org_tensorflow//tensorflow/lite/delegates/flex:build_def.bzl\", \"tflite_flex_android_library\")\n+load(\"@org_tensorflow//tensorflow/lite/java:aar_with_jni.bzl\", \"aar_with_jni\")\n \n # Visibility rules\n package(\n@@ -61,6 +63,20 @@ tflite_cc_shared_object(\n     deps = [\":ops_lib\"],\n )\n \n+tflite_flex_android_library(\n+    name = \"tftext_ops\",\n+    additional_deps = [\n+        \"@org_tensorflow//tensorflow/lite/delegates/flex:delegate\",\n+        \":ops_lib\",\n+    ],\n+    visibility = [\"//visibility:public\"],\n+)\n+\n+aar_with_jni(\n+    name = \"tftext_tflite_flex\",\n+    android_library = \":tftext_ops\",\n+)\n+\n py_library(\n     name = \"ops\",\n     srcs = [\ndiff --git a/tensorflow_text/tftext.bzl b/tensorflow_text/tftext.bzl\nindex 65430ca..04f68d8 100644\n--- a/tensorflow_text/tftext.bzl\n+++ b/tensorflow_text/tftext.bzl\n@@ -140,6 +140,7 @@ def tf_cc_library(\n     deps += select({\n         \"@org_tensorflow//tensorflow:mobile\": [\n             \"@org_tensorflow//tensorflow/core:portable_tensorflow_lib_lite\",\n+            \"@org_tensorflow//tensorflow/lite/kernels/shim:tf_op_shim\",\n         ],\n         \"//conditions:default\": [\n             \"@local_config_tf//:libtensorflow_framework\",\n"
  },
  {
    "path": "lite/examples/generative_ai/android/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <application\n        android:name=\".DemoApplication\"\n        android:allowBackup=\"true\"\n        android:dataExtractionRules=\"@xml/data_extraction_rules\"\n        android:fullBackupContent=\"@xml/backup_rules\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/Theme.TensorFlowDemo\"\n        tools:targetApi=\"31\">\n        <activity\n            android:name=\".ui.MainActivity\"\n            android:exported=\"true\"\n            android:screenOrientation=\"portrait\"\n            tools:ignore=\"LockedOrientationActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/java/com/google/tensorflowdemo/DemoApplication.kt",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage com.google.tensorflowdemo\n\nimport android.app.Application\nimport com.google.tensorflowdemo.di.appModule\nimport com.google.tensorflowdemo.di.viewmodelModule\nimport io.github.aakira.napier.DebugAntilog\nimport io.github.aakira.napier.Napier\nimport org.koin.android.ext.koin.androidContext\nimport org.koin.android.ext.koin.androidLogger\nimport org.koin.core.context.startKoin\n\nclass DemoApplication : Application() {\n\n    override fun onCreate() {\n        super.onCreate()\n\n        if (BuildConfig.DEBUG) {\n            Napier.base(DebugAntilog())\n        }\n\n        startKoin {\n            androidLogger()\n            androidContext(this@DemoApplication)\n            modules(\n                appModule,\n                viewmodelModule\n            )\n        }\n    }\n}"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/java/com/google/tensorflowdemo/data/autocomplete/AutoCompleteService.kt",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage com.google.tensorflowdemo.data.autocomplete\n\nimport android.content.Context\nimport androidx.annotation.WorkerThread\nimport com.google.tensorflowdemo.data.autocomplete.AutoCompleteService.AutoCompleteInputConfiguration\nimport com.google.tensorflowdemo.data.autocomplete.AutoCompleteService.AutoCompleteResult\nimport com.google.tensorflowdemo.data.autocomplete.AutoCompleteService.AutoCompleteServiceError\nimport com.google.tensorflowdemo.data.autocomplete.AutoCompleteService.InitModelResult\nimport com.google.tensorflowdemo.util.splitToWords\nimport com.google.tensorflowdemo.util.trimToMaxWordCount\nimport com.mediamonks.wordfilter.LanguageChecker\nimport io.github.aakira.napier.Napier\nimport kotlinx.coroutines.CoroutineDispatcher\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport org.tensorflow.lite.Interpreter\nimport java.io.FileInputStream\nimport java.io.FileNotFoundException\nimport java.nio.ByteBuffer\nimport java.nio.MappedByteBuffer\nimport java.nio.channels.FileChannel\nimport kotlin.math.min\n\ninterface AutoCompleteService {\n\n    /**\n     * Configuration of input for model\n     */\n    val inputConfiguration: AutoCompleteInputConfiguration\n\n    /**\n     * Boolean indicating whether the model has been initialized successfully\n     */\n    val isInitialized: Boolean\n\n    /**\n     * Initialize TensorFlow-Lite with app provided model\n     * @return [InitModelResult.Success] if TFLite was initialized properly, otherwise [InitModelResult.Error]\n     */\n    suspend fun initModel(): InitModelResult\n\n    /**\n     * Get autocomplete suggestion split into words for the provided [input].\n     * If [applyWindow] is true, the last [windowSize] words are taken from [input] and fed into the interpreter.\n     * @return an instance of [AutoCompleteResult.Error] if something went wrong, or\n     * an instance of [AutoCompleteResult.Success] with the suggested text, split into words\n     */\n    suspend fun getSuggestion(input: String, applyWindow: Boolean = false, windowSize: Int = 50): AutoCompleteResult\n\n    /**\n     * Possible errors from [AutoCompleteService]\n     */\n    enum class AutoCompleteServiceError {\n        MODEL_FILE_NOT_FOUND,\n        MODEL_NOT_INITIALIZED,\n        NO_SUGGESTIONS,\n        BAD_LANGUAGE,\n    }\n\n    /**\n     * Result from [AutoCompleteService.getSuggestion] method call\n     */\n    sealed interface AutoCompleteResult {\n        data class Success(val words: List<String>) : AutoCompleteResult\n        data class Error(val error: AutoCompleteServiceError) : AutoCompleteResult\n    }\n\n    /**\n     * Result from [AutoCompleteService.initModel] method call\n     */\n    sealed interface InitModelResult {\n        object Success : InitModelResult\n        data class Error(val error: AutoCompleteServiceError) : InitModelResult\n    }\n\n    data class AutoCompleteInputConfiguration(\n        // Minimum number of words to be taken from the end of the input text\n        val minWordCount: Int = 5,\n        // Maximum number of words to be taken from the end of the input text\n        val maxWordCount: Int = 50,\n        // Initially selected value for number of words to be taken from the end of the input text\n        val initialWordCount: Int = 20,\n    )\n}\n\nclass AutoCompleteServiceImpl(\n    private val context: Context,\n    private val languageChecker: LanguageChecker,\n    private val dispatcher: CoroutineDispatcher = Dispatchers.IO\n) : AutoCompleteService, AutoCloseable {\n\n    /**\n     * The values below are used to configure the slider that allows a user to select the number of words to take from the current text\n     * and use as input for the model to generate new text from.\n     */\n    override val inputConfiguration = AutoCompleteInputConfiguration(\n        // Minimum number of words to be taken from the end of the input text\n        minWordCount = 5,\n        // Maximum number of words to be taken from the end of the input text, limited by what the model allows\n        maxWordCount = min(50, MAX_INPUT_WORD_COUNT),\n        // Initially selected value for number of words to be taken from the end of the input text\n        initialWordCount = 20\n    )\n\n    override var isInitialized: Boolean = false\n        private set\n\n    private lateinit var interpreter: Interpreter\n    private val outputBuffer = ByteBuffer.allocateDirect(OUTPUT_BUFFER_SIZE)\n\n\n    /**\n     * Initialize TensorFlow Lite with app provided model\n     * @return [InitModelResult.Success] if TFLite was initialized properly, otherwise [InitModelResult.Error]\n     */\n    override suspend fun initModel(): InitModelResult {\n        return withContext(dispatcher) {\n            // Load model file\n            val loadResult = loadModelFile(context)\n\n            // Determine if load was successful\n            if (loadResult.isFailure) {\n                val exc = loadResult.exceptionOrNull()\n                return@withContext if (exc is FileNotFoundException) {\n                    InitModelResult.Error(AutoCompleteServiceError.MODEL_FILE_NOT_FOUND)\n                } else {\n                    InitModelResult.Error(AutoCompleteServiceError.MODEL_NOT_INITIALIZED)\n                }\n            }\n\n            // Instantiate interpreter with loaded model\n            val model = loadResult.getOrNull()\n            isInitialized = model?.let {\n                interpreter = Interpreter(it)\n                true\n            } ?: false\n\n            if (isInitialized) InitModelResult.Success\n            else InitModelResult.Error(AutoCompleteServiceError.MODEL_NOT_INITIALIZED)\n        }\n    }\n\n    /**\n     * Get autocomplete suggestion split into words for the provided [input].\n     * If [applyWindow] is true, the last [windowSize] words are taken from [input] and fed into the interpreter.\n     * @return an instance of [AutoCompleteResult.Error] if something went wrong, or\n     * an instance of [AutoCompleteResult.Success] with the suggested text, split into words\n     */\n    override suspend fun getSuggestion(input: String, applyWindow: Boolean, windowSize: Int) = withContext(dispatcher) {\n        Napier.d { \"[0] Start interpretation\" }\n        Napier.d { \"[1] Input text: (${input.length} chars) '$input'\" }\n\n        // Check input for bad language\n        if (languageChecker.containsBadLanguage(input)) {\n            Napier.w { \"[2] Input contains bad language, refused!\" }\n            return@withContext AutoCompleteResult.Error(AutoCompleteServiceError.BAD_LANGUAGE)\n        }\n\n        // Initialize interpreter if necessary\n        if (!::interpreter.isInitialized) {\n            val result = initModel()\n            if (result is InitModelResult.Error) {\n                return@withContext AutoCompleteResult.Error(result.error)\n            }\n        }\n\n        // Determine maximum number of words to take from input as model input\n        val maxInputWordCount = if (applyWindow) windowSize else MAX_INPUT_WORD_COUNT\n        Napier.d { \"[2] Trimming input to max $maxInputWordCount words\" }\n\n        // Trim input text to  maximum number of words\n        val trimmedInput = input.trimToMaxWordCount(maxInputWordCount)\n        Napier.d { \"[3] Model input: (${trimmedInput.length} chars) '$trimmedInput'\" }\n\n        var retryCount = 0\n        var containsBadLanguage: Boolean\n        lateinit var output: String\n\n        // Run generation until it no longer contains bad language or a max number of tries has been exceeded\n        do {\n            // Let model generate new text based on windowed input\n            output = runInterpreterOn(trimmedInput)\n\n            // Check output for bad language\n            containsBadLanguage = languageChecker.containsBadLanguage(output)\n\n            retryCount++\n        } while (containsBadLanguage && retryCount < RETRY_COUNT_ON_BAD_LANGUAGE)\n\n        // Return error if output still contains bad language\n        if (containsBadLanguage) {\n            Napier.w { \"[4] Output still contains bad language after 3 attempts, refused!\" }\n\n            return@withContext AutoCompleteResult.Error(AutoCompleteServiceError.NO_SUGGESTIONS)\n        }\n\n        Napier.d { \"[4] Model output: (${output.length} chars) '$output'\" }\n\n        // Check if output size is actually longer than original input text, if not that's an error\n        if (output.length < trimmedInput.length) {\n            Napier.w { \"[5] NO SUGGESTION: Output length is shorter than trimmed input length, so there was no new text suggested\" }\n            AutoCompleteResult.Error(AutoCompleteServiceError.NO_SUGGESTIONS)\n        } else {\n            // Output = input + new text, determine new text by subtracting input\n            val newText = output.substring(output.indexOf(trimmedInput) + trimmedInput.length)\n            Napier.d { \"[5] New text from interpreter: (${newText.length} chars) '$newText'\" }\n\n            // Split new text into words. If there are no words, that's an error, otherwise return the words\n            val words = newText.splitToWords()\n            if (words.isEmpty()) {\n                Napier.w { \"[6] NO SUGGESTION: No words found after splitting new text into words\" }\n                AutoCompleteResult.Error(AutoCompleteServiceError.NO_SUGGESTIONS)\n            } else {\n                Napier.d { \"[6] New text split in words: (${words.size} words) [${words.joinToString()}]\" }\n                AutoCompleteResult.Success(words)\n            }\n        }\n    }\n\n    /**\n     * Run the previously created [interpreter] on the provided input, which will return with appended generated text\n     * Note that this method may take quite some time to finish, so call this from a background thread\n     */\n    @WorkerThread\n    private fun runInterpreterOn(input: String): String {\n        outputBuffer.clear()\n\n        // Run interpreter, which will generate text into outputBuffer\n        interpreter.run(input, outputBuffer)\n\n        // Set output buffer limit to current position & position to 0\n        outputBuffer.flip()\n\n        // Get bytes from output buffer\n        val bytes = ByteArray(outputBuffer.remaining())\n        outputBuffer.get(bytes)\n\n        outputBuffer.clear()\n\n        // Return bytes converted to String\n        return String(bytes, Charsets.UTF_8)\n    }\n\n    /**\n     * Load TF Lite model file into memory.\n     * The model file is expected in the `src/main/assets` folder, with name configured in [TFLITE_MODEL]\n     */\n    private fun loadModelFile(context: Context): Result<MappedByteBuffer?> {\n        try {\n            val descriptor = context.assets.openFd(TFLITE_MODEL)\n\n            FileInputStream(descriptor.fileDescriptor).use { stream ->\n                return Result.success(\n                    stream.channel.map(\n                        /* mode = */ FileChannel.MapMode.READ_ONLY,\n                        /* position = */ descriptor.startOffset,\n                        /* size = */ descriptor.declaredLength\n                    )\n                )\n            }\n        } catch (e: Exception) {\n            Napier.e { \"Failed to load model: ${e.localizedMessage}\" }\n\n            return Result.failure(e)\n        }\n    }\n\n    override fun close() {\n        interpreter.close()\n    }\n\n    companion object {\n        // File name of TF Lite model as expected in the assets folder\n        private const val TFLITE_MODEL = \"autocomplete.tflite\"\n\n        // Size of output buffer for the model to generate text into\n        private const val OUTPUT_BUFFER_SIZE = 800\n\n        // Maximum number of words that can be fed into the model\n        private const val MAX_INPUT_WORD_COUNT = 1024\n\n        // Maximum number of attempts to generate text that does not contain bad language\n        private const val RETRY_COUNT_ON_BAD_LANGUAGE = 3\n    }\n}"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/java/com/google/tensorflowdemo/di/appModule.kt",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage com.google.tensorflowdemo.di\n\nimport com.google.tensorflowdemo.data.autocomplete.AutoCompleteService\nimport com.google.tensorflowdemo.data.autocomplete.AutoCompleteServiceImpl\nimport com.mediamonks.wordfilter.LanguageChecker\nimport com.mediamonks.wordfilter.LanguageCheckerImpl\nimport org.koin.android.ext.koin.androidContext\nimport org.koin.core.module.dsl.singleOf\nimport org.koin.dsl.module\n\nval appModule = module {\n    single<AutoCompleteService> {\n        AutoCompleteServiceImpl(\n            context = androidContext(),\n            languageChecker = get()\n        )\n    }\n\n    singleOf<LanguageChecker>(::LanguageCheckerImpl)\n}\n"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/java/com/google/tensorflowdemo/di/viewmodelModule.kt",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage com.google.tensorflowdemo.di\n\nimport com.google.tensorflowdemo.ui.screens.autocomplete.AutoCompleteViewModel\nimport org.koin.androidx.viewmodel.dsl.viewModelOf\nimport org.koin.dsl.module\n\nval viewmodelModule = module {\n    viewModelOf(::AutoCompleteViewModel)\n}"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/java/com/google/tensorflowdemo/ui/MainActivity.kt",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage com.google.tensorflowdemo.ui\n\nimport android.annotation.SuppressLint\nimport android.os.Bundle\nimport android.widget.Toast\nimport androidx.activity.ComponentActivity\nimport androidx.activity.compose.setContent\nimport androidx.compose.foundation.layout.WindowInsets\nimport androidx.compose.foundation.layout.asPaddingValues\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.statusBars\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.Scaffold\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.DisposableEffect\nimport androidx.compose.ui.ExperimentalComposeUiApi\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.Preview\nimport androidx.compose.ui.unit.dp\nimport androidx.core.view.WindowCompat\nimport com.google.accompanist.systemuicontroller.rememberSystemUiController\nimport com.google.tensorflowdemo.R\nimport com.google.tensorflowdemo.ui.components.HeaderBar\nimport com.google.tensorflowdemo.ui.screens.autocomplete.AutoCompleteScreen\nimport com.google.tensorflowdemo.ui.theme.TensorFlowDemoTheme\nimport com.google.tensorflowdemo.ui.theme.VeryLightGrey\n\n@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)\nclass MainActivity : ComponentActivity() {\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        WindowCompat.setDecorFitsSystemWindows(window, false)\n\n        setContent {\n\n            val systemUiController = rememberSystemUiController()\n            DisposableEffect(systemUiController) {\n                // Update all of the system bar colors to be transparent, and use\n                // dark icons if we're in light theme\n                systemUiController.setSystemBarsColor(\n                    color = Color.Transparent,\n                    darkIcons = true\n                )\n                systemUiController.setNavigationBarColor(Color.White)\n                onDispose {}\n            }\n\n            TensorFlowDemoTheme {\n                val insets = WindowInsets.statusBars.asPaddingValues()\n                val barHeight = 66.dp\n\n                Scaffold(\n                    topBar = {\n                        HeaderBar(\n                            label = stringResource(R.string.header_autocomplete),\n                            textOffset = (insets.calculateTopPadding() / 4),\n                            modifier = Modifier.height(barHeight + insets.calculateTopPadding() / 2)\n                        )\n                    }\n                ) { paddings ->\n                    AutoCompleteScreen(\n                        onShowToast = { id -> Toast.makeText(this, id, Toast.LENGTH_SHORT).show() },\n                        modifier = Modifier.padding(\n                            top = barHeight - 20.dp,\n                            bottom = paddings.calculateBottomPadding()\n                        )\n                    )\n                }\n            }\n        }\n    }\n}\n\n@SuppressLint(\"UnusedMaterial3ScaffoldPaddingParameter\")\n@OptIn(ExperimentalMaterial3Api::class)\n@Preview\n@Composable\nfun PreviewMain() {\n    TensorFlowDemoTheme {\n        Scaffold {\n            AutoCompleteScreen(onShowToast = {}, modifier = Modifier.padding(top = 50.dp))\n        }\n    }\n}"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/java/com/google/tensorflowdemo/ui/components/HeaderBar.kt",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage com.google.tensorflowdemo.ui.components\n\nimport androidx.compose.foundation.Image\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.offset\nimport androidx.compose.material3.Divider\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.layout.FixedScale\nimport androidx.compose.ui.res.painterResource\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.Preview\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\nimport com.google.tensorflowdemo.R\nimport com.google.tensorflowdemo.ui.theme.TensorFlowDemoTheme\n\n@Composable\nfun HeaderBar(\n    label: String,\n    textOffset: Dp,\n    modifier:Modifier = Modifier\n) {\n    Box(\n        modifier = modifier\n            .fillMaxWidth()\n            .background(Color.White)\n    ) {\n        Image(\n            painter = painterResource(id = R.drawable.header_background),\n            contentDescription = stringResource(R.string.header_background_desc),\n            contentScale = FixedScale(.47f),\n        )\n        Text(\n            text = label,\n            color = MaterialTheme.colorScheme.secondary,\n            style = MaterialTheme.typography.displayLarge,\n            modifier = Modifier\n                .align(Alignment.Center)\n                .offset(y = textOffset)\n        )\n        Divider(\n            modifier = Modifier\n                .fillMaxWidth()\n                .height(1.dp)\n                .align(Alignment.BottomStart)\n        )\n\n    }\n}\n\n@Preview\n@Composable\nfun PreviewHeaderBar() {\n    TensorFlowDemoTheme {\n        HeaderBar(\n            label = \"Autocomplete\",\n            textOffset = 20.dp\n        )\n    }\n}"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/java/com/google/tensorflowdemo/ui/screens/autocomplete/AutoCompleteScreen.kt",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage com.google.tensorflowdemo.ui.screens.autocomplete\n\nimport android.content.res.Configuration\nimport androidx.annotation.StringRes\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.fillMaxHeight\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.ColorScheme\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.LaunchedEffect\nimport androidx.compose.runtime.MutableState\nimport androidx.compose.runtime.collectAsState\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.saveable.rememberSaveable\nimport androidx.compose.ui.ExperimentalComposeUiApi\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.platform.ClipboardManager\nimport androidx.compose.ui.platform.LocalClipboardManager\nimport androidx.compose.ui.platform.LocalLifecycleOwner\nimport androidx.compose.ui.platform.LocalSoftwareKeyboardController\nimport androidx.compose.ui.platform.LocalUriHandler\nimport androidx.compose.ui.text.AnnotatedString\nimport androidx.compose.ui.text.SpanStyle\nimport androidx.compose.ui.text.TextRange\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.text.input.TextFieldValue\nimport androidx.compose.ui.tooling.preview.Preview\nimport androidx.compose.ui.unit.dp\nimport androidx.lifecycle.Lifecycle\nimport androidx.lifecycle.compose.collectAsStateWithLifecycle\nimport androidx.lifecycle.repeatOnLifecycle\nimport com.google.tensorflowdemo.R\nimport com.google.tensorflowdemo.data.autocomplete.AutoCompleteService.AutoCompleteInputConfiguration\nimport com.google.tensorflowdemo.data.autocomplete.AutoCompleteService.AutoCompleteServiceError\nimport com.google.tensorflowdemo.ui.screens.autocomplete.components.AutoCompleteInfo\nimport com.google.tensorflowdemo.ui.screens.autocomplete.components.AutoCompleteTextField\nimport com.google.tensorflowdemo.ui.screens.autocomplete.components.TextControlBar\nimport com.google.tensorflowdemo.ui.screens.autocomplete.components.WindowSizeSelection\nimport com.google.tensorflowdemo.ui.theme.TensorFlowDemoTheme\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.flow.collectLatest\nimport kotlinx.coroutines.launch\nimport org.koin.androidx.compose.getViewModel\n\n@OptIn(ExperimentalComposeUiApi::class)\n@Composable\nfun AutoCompleteScreen(\n    onShowToast: (Int) -> Unit,\n    modifier: Modifier = Modifier\n) {\n    val viewmodel = getViewModel<AutoCompleteViewModel>()\n\n    val textValue = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue(annotatedString = AnnotatedString(\"\"))) }\n    val barState by viewmodel.textBarState.collectAsState()\n    val inputFieldEnabled by viewmodel.inputFieldEnabled.collectAsStateWithLifecycle()\n    val windowSizeConfiguration by remember { mutableStateOf(viewmodel.windowSizeConfiguration) }\n    val clipboardManager: ClipboardManager = LocalClipboardManager.current\n    val keyboardController = LocalSoftwareKeyboardController.current\n    val uriHandler = LocalUriHandler.current\n\n    fun showToast(@StringRes id: Int) {\n        keyboardController?.hide()\n\n        onShowToast(id)\n    }\n\n    AutoCompleteScreenContent(\n        inputValue = textValue.value,\n        inputEnabled = inputFieldEnabled,\n        onInputValueChange = { value ->\n            textValue.value = value\n            viewmodel.isTextEmpty = value.text.isEmpty()\n        },\n        barState = barState,\n        inputConfiguration = windowSizeConfiguration,\n        previousSuggestions = viewmodel.previousSuggestions,\n        onClear = {\n            textValue.value = TextFieldValue(AnnotatedString(\"\"))\n            viewmodel.onClearInput()\n        },\n        onCopy = {\n            clipboardManager.setText(textValue.value.annotatedString)\n\n            onShowToast(R.string.text_copied)\n        },\n        onGenerate = { viewmodel.onGenerateAutoComplete(textValue.value.text) },\n        onRetry = viewmodel::onRetryGenerateAutoComplete,\n        onAccept = {\n            viewmodel.onAcceptSuggestion()\n            textValue.value = TextFieldValue(\n                text = textValue.value.text,\n                selection = TextRange(textValue.value.text.length)\n            )\n        },\n        onWindowSizeChange = viewmodel::onWindowSizeChange,\n        onSuggestionsRemoved = viewmodel::removeMissingSuggestions,\n        onLinkoutSelect = { uriHandler.openUri(\"https://github.com/keras-team/keras-nlp\") },\n        modifier = modifier\n    )\n\n    val lifecycle = LocalLifecycleOwner.current.lifecycle\n    val colorScheme = MaterialTheme.colorScheme\n\n    LaunchedEffect(key1 = Unit) {\n        lifecycle.repeatOnLifecycle(state = Lifecycle.State.STARTED) {\n            launch {\n                viewmodel.suggestion.collectLatest { words ->\n                    words?.let {\n                        animateSuggestion(textValue, words, colorScheme) {\n                            viewmodel.onSuggestionReceived()\n                        }\n                    }\n                }\n            }\n            launch {\n                viewmodel.resetInputText.collectLatest { resetText ->\n                    resetText?.let { text ->\n                        textValue.value = TextFieldValue(\n                            annotatedString = AnnotatedString(text),\n                            selection = TextRange(text.length)\n                        )\n\n                        viewmodel.onResetReceived()\n                    }\n                }\n            }\n            launch {\n                viewmodel.error.collectLatest { error ->\n                    showToast(getErrorMessage(error))\n                }\n            }\n        }\n    }\n}\n\nsuspend fun animateSuggestion(\n    textValueState: MutableState<TextFieldValue>,\n    words: List<String>,\n    colorScheme: ColorScheme,\n    onAnimationComplete: () -> Unit\n) {\n    val builder = AnnotatedString.Builder(textValueState.value.annotatedString)\n\n    val stylePos = builder.pushStyle(\n        SpanStyle(\n            color = colorScheme.primary,\n            fontWeight = FontWeight.Bold\n        )\n    )\n\n    for (word in words) {\n        builder.append(word)\n\n        val annotatedString = builder.toAnnotatedString()\n        textValueState.value = TextFieldValue(\n            annotatedString = annotatedString,\n            selection = TextRange(annotatedString.length)\n        )\n        delay(100)\n    }\n\n    builder.pop(stylePos)\n\n    onAnimationComplete()\n}\n\n@Composable\nfun AutoCompleteScreenContent(\n    inputValue: TextFieldValue,\n    inputEnabled: Boolean,\n    onInputValueChange: (TextFieldValue) -> Unit,\n    barState: TextEditBarState,\n    previousSuggestions: List<Suggestion>,\n    inputConfiguration: AutoCompleteInputConfiguration,\n    onClear: () -> Unit,\n    onCopy: () -> Unit,\n    onGenerate: () -> Unit,\n    onRetry: () -> Unit,\n    onAccept: () -> Unit,\n    onWindowSizeChange: (Int) -> Unit,\n    onSuggestionsRemoved: (List<Int>) -> Unit,\n    onLinkoutSelect: () -> Unit,\n    modifier: Modifier = Modifier\n) {\n    Box(\n        modifier = modifier\n            .background(Color.White)\n            .fillMaxHeight()\n    ) {\n        Column(modifier = Modifier.fillMaxHeight()) {\n            Column(\n                modifier = modifier\n                    .background(Color.White)\n                    .padding(start = 16.dp, end = 16.dp, top = 20.dp)\n            ) {\n                AutoCompleteTextField(\n                    inputValue = inputValue,\n                    inputEnabled = inputEnabled,\n                    previousSuggestions = previousSuggestions,\n                    onInputValueChange = onInputValueChange,\n                    onSuggestionsRemoved = onSuggestionsRemoved,\n                    modifier = Modifier\n                        .fillMaxWidth()\n                        .height(250.dp)\n                        .padding(bottom = 16.dp),\n                )\n                TextControlBar(\n                    state = barState,\n                    onClearClick = onClear,\n                    onGenerateClick = onGenerate,\n                    onCopyClick = onCopy,\n                    onAccept = onAccept,\n                    onRetry = onRetry\n                )\n            }\n            Spacer(modifier = Modifier.weight(1f))\n            WindowSizeSelection(\n                inputConfiguration = inputConfiguration,\n                onWindowValueChange = onWindowSizeChange,\n                modifier = Modifier.padding(bottom = 32.dp, start = 16.dp, end = 16.dp)\n            )\n            AutoCompleteInfo(\n                onLinkoutSelect = onLinkoutSelect,\n                modifier = Modifier\n                    .padding(horizontal = 8.dp, vertical = 8.dp)\n            )\n        }\n    }\n}\n\nprivate fun getErrorMessage(error: AutoCompleteServiceError) = when (error) {\n    AutoCompleteServiceError.MODEL_NOT_INITIALIZED -> R.string.error_model_not_initialized\n    AutoCompleteServiceError.NO_SUGGESTIONS -> R.string.error_no_suggestion_found\n    AutoCompleteServiceError.MODEL_FILE_NOT_FOUND -> R.string.error_model_not_found\n    AutoCompleteServiceError.BAD_LANGUAGE -> R.string.error_input_contains_bad_language\n}\n\n@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, showBackground = true)\n@Composable\nfun PreviewAutoCompleteScreen() {\n    TensorFlowDemoTheme {\n        val inputValue by remember { mutableStateOf(TextFieldValue()) }\n        AutoCompleteScreenContent(\n            inputValue = inputValue,\n            onInputValueChange = {},\n            inputEnabled = true,\n            inputConfiguration = AutoCompleteInputConfiguration(),\n            previousSuggestions = listOf(),\n            onClear = {},\n            onCopy = {},\n            onGenerate = {},\n            onRetry = {},\n            onAccept = {},\n            onWindowSizeChange = {},\n            onSuggestionsRemoved = {},\n            onLinkoutSelect = {},\n            barState = initialControlBarState,\n        )\n    }\n}"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/java/com/google/tensorflowdemo/ui/screens/autocomplete/AutoCompleteViewModel.kt",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage com.google.tensorflowdemo.ui.screens.autocomplete\n\nimport androidx.compose.runtime.mutableStateListOf\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport com.google.tensorflowdemo.data.autocomplete.AutoCompleteService\nimport com.google.tensorflowdemo.data.autocomplete.AutoCompleteService.AutoCompleteResult\nimport com.google.tensorflowdemo.data.autocomplete.AutoCompleteService.AutoCompleteServiceError\nimport com.google.tensorflowdemo.data.autocomplete.AutoCompleteService.InitModelResult\nimport kotlinx.coroutines.flow.MutableSharedFlow\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.SharedFlow\nimport kotlinx.coroutines.flow.SharingStarted\nimport kotlinx.coroutines.flow.StateFlow\nimport kotlinx.coroutines.flow.combine\nimport kotlinx.coroutines.flow.stateIn\nimport kotlinx.coroutines.launch\n\nclass AutoCompleteViewModel(\n    private val autoCompleteService: AutoCompleteService\n) : ViewModel() {\n\n    private var currentSuggestionInputText: String = \"\"\n    private var currentSuggestionText: String? = null\n    private var windowSize = 0\n    private var initModelError: InitModelResult.Error? = null\n    private var previousSuggestionsIndex = 0\n    private val isGenerating = MutableStateFlow(false)\n    private val hasGenerated = MutableStateFlow(false)\n    private val isSuggesting = MutableStateFlow(false)\n    private val isModelInitialized = MutableStateFlow(false)\n\n    init {\n        if (!autoCompleteService.isInitialized) {\n            viewModelScope.launch {\n                val result = autoCompleteService.initModel()\n                if (result is InitModelResult.Error) {\n                    initModelError = result\n                }\n                isModelInitialized.value = true\n            }\n        } else {\n            isModelInitialized.value = true\n        }\n    }\n\n    private val _isTextEmpty = MutableStateFlow(true)\n    var isTextEmpty: Boolean = true\n        set(value) {\n            field = value\n            _isTextEmpty.value = value\n\n            // Clear previous suggestions if the input text is empty\n            if (value) {\n                _previousSuggestions.clear()\n            }\n        }\n\n    val windowSizeConfiguration = autoCompleteService.inputConfiguration\n\n    /**\n     * State flow to reset text to previous value.\n     * Needs to be acknowledged by calling [onResetReceived] since the previous value can be the same\n     */\n    private val _resetInputText = MutableStateFlow<String?>(null)\n    val resetInputText: StateFlow<String?>\n        get() = _resetInputText\n\n    fun onResetReceived() {\n        _resetInputText.value = null\n    }\n\n    /**\n     * State flow containing most recent suggestion from model, as list of words\n     * Needs to be acknowledged by calling [onSuggestionReceived]\n     */\n    private val _suggestion = MutableStateFlow<List<String>?>(null)\n    val suggestion: StateFlow<List<String>?>\n        get() = _suggestion\n\n    /**\n     * Shared flow exposing errors from autocomplete service\n     */\n    private val _error = MutableSharedFlow<AutoCompleteServiceError>()\n    val error: SharedFlow<AutoCompleteServiceError>\n        get() = _error\n\n    /**\n     * State flow exposing previously made suggestions\n     */\n    private val _previousSuggestions = mutableStateListOf<Suggestion>()\n    val previousSuggestions: List<Suggestion>\n        get() = _previousSuggestions\n\n    /**\n     * State flow exposing whether Clear CTA should be enabled\n     */\n    private val clearEnabled = combine(isGenerating, _isTextEmpty) { isGenerating, isEmpty ->\n        !isGenerating && !isEmpty\n    }\n\n    /**\n     * State flow exposing whether Generate CTA should be enabled\n     */\n    private val generateEnabled = combine(isGenerating, _isTextEmpty) { isGenerating, isEmpty ->\n        !isGenerating && !isEmpty\n    }\n\n    /**\n     * State flow exposing whether Copy CTA should be enabled\n     */\n    private val copyEnabled = combine(isGenerating, hasGenerated) { isGenerating, hasGenerated ->\n        !isGenerating && hasGenerated\n    }\n\n    /**\n     * State flow exposing edit bar state for Clear, Generate & Copy CTAs & generation process state\n     */\n    private val editingBarState = combine(clearEnabled, generateEnabled, copyEnabled, isGenerating) { clear, generate, copy, generating ->\n        TextEditBarState.Editing(\n            clearEnabled = clear,\n            generateEnabled = generate,\n            copyEnabled = copy,\n            generating = generating,\n        )\n    }\n\n    /**\n     * State flow exposing edit bar state & whether a suggestion is active\n     */\n    val textBarState = combine(editingBarState, isSuggesting) { editState, suggesting ->\n        if (suggesting) TextEditBarState.Suggesting\n        else editState\n    }.stateIn(\n        scope = viewModelScope,\n        started = SharingStarted.Lazily,\n        initialValue = initialControlBarState\n    )\n\n    /**\n     * State flow exposing whether input by user should be possible\n     */\n    val inputFieldEnabled = combine(isModelInitialized, isGenerating, isSuggesting) { initialized, generating, suggesting ->\n        initialized && !generating && !suggesting\n    }.stateIn(\n        scope = viewModelScope,\n        started = SharingStarted.Lazily,\n        initialValue = true\n    )\n\n    fun onClearInput() {\n        isGenerating.value = false\n        hasGenerated.value = false\n        _isTextEmpty.value = true\n        currentSuggestionInputText = \"\"\n\n        _previousSuggestions.clear()\n    }\n\n    fun onWindowSizeChange(size: Int) {\n        windowSize = size\n    }\n\n    fun onRetryGenerateAutoComplete() {\n        _resetInputText.value = currentSuggestionInputText\n\n        isSuggesting.value = false\n        currentSuggestionText = null\n\n        onGenerateAutoComplete(currentSuggestionInputText)\n    }\n\n    fun onAcceptSuggestion() {\n        currentSuggestionText?.let { text ->\n            _previousSuggestions += Suggestion(\n                text = text,\n                id = previousSuggestionsIndex++\n            )\n        }\n\n        isSuggesting.value = false\n        currentSuggestionText = null\n    }\n\n    fun removeMissingSuggestions(ids: List<Int>) {\n        for (id in ids) {\n            _previousSuggestions.removeIf { suggestion -> suggestion.id == id }\n        }\n    }\n\n    fun onGenerateAutoComplete(text: String) {\n        initModelError?.let { error ->\n            viewModelScope.launch {\n                _error.emit(error.error)\n            }\n            return\n        }\n\n        currentSuggestionInputText = text\n\n        isGenerating.value = true\n\n        viewModelScope.launch {\n            when (val result = autoCompleteService.getSuggestion(text, applyWindow = true, windowSize = windowSize)) {\n                is AutoCompleteResult.Success -> {\n                    _suggestion.value = result.words\n                    currentSuggestionText = result.words.joinToString(separator = \"\")\n                }\n\n                is AutoCompleteResult.Error -> {\n                    _error.emit(result.error)\n\n                    isGenerating.value = false\n                }\n            }\n        }\n    }\n\n    fun onSuggestionReceived() {\n        _suggestion.value = null\n\n        isGenerating.value = false\n        hasGenerated.value = true\n        isSuggesting.value = true\n    }\n}\n\nsealed class TextEditBarState {\n    data class Editing(\n        val clearEnabled: Boolean,\n        val generateEnabled: Boolean,\n        val copyEnabled: Boolean,\n        val generating: Boolean,\n    ) : TextEditBarState()\n\n    object Suggesting : TextEditBarState()\n}\n\nval initialControlBarState: TextEditBarState = TextEditBarState.Editing(\n    clearEnabled = false,\n    generateEnabled = false,\n    copyEnabled = false,\n    generating = false\n)\n\ndata class Suggestion(\n    val text: String,\n    val id: Int\n)"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/java/com/google/tensorflowdemo/ui/screens/autocomplete/components/AutoCompleteInfo.kt",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage com.google.tensorflowdemo.ui.screens.autocomplete.components\n\nimport android.content.res.Configuration\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TextButton\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.Preview\nimport com.google.tensorflowdemo.R\nimport com.google.tensorflowdemo.ui.theme.TensorFlowDemoTheme\n\n@Composable\nfun AutoCompleteInfo(\n    onLinkoutSelect: () -> Unit,\n    modifier: Modifier = Modifier\n) {\n    TextButton(\n        onClick = onLinkoutSelect,\n        modifier = modifier.fillMaxWidth()\n    ) {\n        Spacer(modifier = Modifier.weight(1f))\n        Text(\n            text = stringResource(R.string.about_title),\n            style = MaterialTheme.typography.titleSmall,\n            color = MaterialTheme.colorScheme.tertiary,\n        )\n        Spacer(modifier = Modifier.weight(1f))\n    }\n}\n\n@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, showBackground = true)\n@Composable\nfun PreviewAutCompleteInfo() {\n    TensorFlowDemoTheme {\n        AutoCompleteInfo({})\n    }\n}"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/java/com/google/tensorflowdemo/ui/screens/autocomplete/components/AutoCompleteTextField.kt",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage com.google.tensorflowdemo.ui.screens.autocomplete.components\n\nimport androidx.compose.foundation.text.KeyboardOptions\nimport androidx.compose.material3.ColorScheme\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.OutlinedTextField\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TextFieldDefaults\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.SideEffect\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.rememberCoroutineScope\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.alpha\nimport androidx.compose.ui.focus.FocusRequester\nimport androidx.compose.ui.focus.focusRequester\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.text.AnnotatedString\nimport androidx.compose.ui.text.SpanStyle\nimport androidx.compose.ui.text.buildAnnotatedString\nimport androidx.compose.ui.text.input.KeyboardCapitalization\nimport androidx.compose.ui.text.input.TextFieldValue\nimport com.google.tensorflowdemo.R\nimport com.google.tensorflowdemo.ui.screens.autocomplete.Suggestion\nimport com.google.tensorflowdemo.ui.theme.InactiveOutlinedTextFieldBorder\nimport com.google.tensorflowdemo.ui.theme.DarkBlue\nimport com.google.tensorflowdemo.ui.theme.ActiveOutlinedTextFieldBackground\nimport com.google.tensorflowdemo.ui.theme.InactiveOutlinedTextFieldBackground\nimport kotlinx.coroutines.launch\n\n@OptIn(ExperimentalMaterial3Api::class)\n@Composable\nfun AutoCompleteTextField(\n    inputValue: TextFieldValue,\n    inputEnabled: Boolean,\n    previousSuggestions: List<Suggestion>,\n    onInputValueChange: (TextFieldValue) -> Unit,\n    onSuggestionsRemoved: (List<Int>) -> Unit,\n    modifier: Modifier = Modifier\n) {\n    val focusRequester = remember { FocusRequester() }\n    val scope = rememberCoroutineScope()\n    val colorScheme = MaterialTheme.colorScheme\n\n    SideEffect {\n        if (inputEnabled) {\n            scope.launch {\n                focusRequester.requestFocus()\n            }\n        }\n    }\n\n    OutlinedTextField(\n        value = inputValue,\n        onValueChange = onInputValueChange,\n        enabled = inputEnabled,\n        textStyle = MaterialTheme.typography.bodySmall,\n        shape = MaterialTheme.shapes.medium,\n        placeholder = {\n            Text(\n                text = stringResource(R.string.input_hint),\n                color = MaterialTheme.colorScheme.tertiary,\n                modifier = Modifier.alpha(.7f)\n            )\n        },\n        colors = TextFieldDefaults.outlinedTextFieldColors(\n            disabledTextColor = MaterialTheme.colorScheme.onSurface,\n            unfocusedBorderColor = MaterialTheme.colorScheme.tertiary.copy(alpha = .7f),\n            disabledBorderColor = InactiveOutlinedTextFieldBorder,\n            focusedBorderColor = DarkBlue,\n            containerColor = when {\n                inputValue.text.isEmpty() -> InactiveOutlinedTextFieldBackground\n                inputEnabled -> ActiveOutlinedTextFieldBackground\n                else -> InactiveOutlinedTextFieldBackground\n            }\n        ),\n        keyboardOptions = KeyboardOptions(\n            capitalization = KeyboardCapitalization.Sentences,\n        ),\n        modifier = modifier\n            .focusRequester(focusRequester)\n    )\n}\n\nprivate fun annotatePreviousSuggestions(\n    text: AnnotatedString,\n    suggestions: List<Suggestion>,\n    colorScheme: ColorScheme,\n    onSuggestionsRemoved: (List<Int>) -> Unit\n): AnnotatedString {\n    val removedSuggestionIds = mutableListOf<Int>()\n\n    val string = buildAnnotatedString {\n        append(text)\n\n        for (suggestion in suggestions) {\n            val index = text.indexOf(suggestion.text)\n            if (index == -1) {\n                removedSuggestionIds += suggestion.id\n            } else {\n                addStyle(\n                    style = SpanStyle(color = colorScheme.primary),\n                    start = index,\n                    end = index + suggestion.text.length\n                )\n            }\n        }\n    }\n\n    if (removedSuggestionIds.isNotEmpty()) {\n        onSuggestionsRemoved(removedSuggestionIds)\n    }\n\n    return string\n}\n"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/java/com/google/tensorflowdemo/ui/screens/autocomplete/components/TextControlBar.kt",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage com.google.tensorflowdemo.ui.screens.autocomplete.components\n\nimport android.content.res.Configuration.UI_MODE_NIGHT_NO\nimport androidx.compose.foundation.BorderStroke\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.offset\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.width\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.filled.Check\nimport androidx.compose.material.icons.outlined.ContentCopy\nimport androidx.compose.material.icons.outlined.RestartAlt\nimport androidx.compose.material3.ButtonDefaults\nimport androidx.compose.material3.CircularProgressIndicator\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.IconButton\nimport androidx.compose.material3.IconButtonDefaults\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.OutlinedButton\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TextButton\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.draw.scale\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.Preview\nimport androidx.compose.ui.unit.dp\nimport com.google.tensorflowdemo.R\nimport com.google.tensorflowdemo.ui.screens.autocomplete.TextEditBarState\nimport com.google.tensorflowdemo.ui.theme.TensorFlowDemoTheme\n\n@Composable\nfun TextControlBar(\n    state: TextEditBarState,\n    onClearClick: () -> Unit,\n    onGenerateClick: () -> Unit,\n    onCopyClick: () -> Unit,\n    onRetry: () -> Unit,\n    onAccept: () -> Unit,\n    modifier: Modifier = Modifier\n) {\n    when (state) {\n        is TextEditBarState.Editing ->\n            TextEditBar(\n                onClearClick = onClearClick,\n                onGenerateClick = onGenerateClick,\n                onCopyClick = onCopyClick,\n                barState = state,\n                modifier = modifier\n            )\n\n        is TextEditBarState.Suggesting ->\n            SuggestionControlBar(\n                onRetry = onRetry,\n                onAccept = onAccept,\n                modifier = modifier\n            )\n    }\n}\n\n@Composable\nfun TextEditBar(\n    onClearClick: () -> Unit,\n    onGenerateClick: () -> Unit,\n    onCopyClick: () -> Unit,\n    barState: TextEditBarState.Editing,\n    modifier: Modifier = Modifier\n) {\n    Row(\n        modifier = modifier.fillMaxWidth(),\n        verticalAlignment = Alignment.CenterVertically\n    ) {\n        IconButton(\n            onClick = onClearClick,\n            enabled = barState.clearEnabled,\n            colors = IconButtonDefaults.iconButtonColors(\n                contentColor = MaterialTheme.colorScheme.tertiary\n            ),\n            modifier = Modifier.padding(start = 12.dp)\n        ) {\n            Icon(\n                imageVector = Icons.Outlined.RestartAlt,\n                contentDescription = stringResource(R.string.clear_cta)\n            )\n        }\n        Spacer(modifier = Modifier.weight(1f))\n        OutlinedButton(\n            onClick = onGenerateClick,\n            enabled = barState.generateEnabled,\n            border = if (barState.generateEnabled) {\n                BorderStroke(1.dp, MaterialTheme.colorScheme.primary)\n            } else {\n                BorderStroke(1.dp, MaterialTheme.colorScheme.outline.copy(alpha = .38f))\n            },\n            colors = ButtonDefaults.outlinedButtonColors(\n                contentColor = MaterialTheme.colorScheme.tertiary\n            ),\n            modifier = Modifier\n                .height(40.dp)\n                .width(180.dp)\n        ) {\n            if (barState.generating) {\n                CircularProgressIndicator(\n                    strokeWidth = 2.dp,\n                    modifier = Modifier\n                        .scale(.5f)\n                        .offset(0.dp, (-8).dp)\n                )\n            } else {\n                Text(\n                    text = stringResource(R.string.generate_cta),\n                    modifier = Modifier.padding(horizontal = 32.dp)\n                )\n            }\n        }\n        Spacer(modifier = Modifier.weight(1f))\n        IconButton(\n            onClick = onCopyClick,\n            enabled = barState.copyEnabled,\n            colors = IconButtonDefaults.iconButtonColors(\n                contentColor = MaterialTheme.colorScheme.tertiary\n            ),\n            modifier = Modifier.padding(end = 12.dp)\n        ) {\n            Icon(\n                imageVector = Icons.Outlined.ContentCopy,\n                contentDescription = stringResource(R.string.copy_cta)\n            )\n        }\n    }\n}\n\n@Composable\nfun SuggestionControlBar(\n    onRetry: () -> Unit,\n    onAccept: () -> Unit,\n    modifier: Modifier = Modifier\n) {\n    Row(modifier = modifier.fillMaxWidth()) {\n        TextButton(\n            onClick = onRetry,\n            modifier = Modifier.padding(start = 16.dp)\n        ) {\n            Text(\n                text = stringResource(R.string.reject_suggestion_cta),\n                color = MaterialTheme.colorScheme.tertiary\n            )\n        }\n        Spacer(modifier = Modifier.weight(1f))\n        OutlinedButton(\n            onClick = onAccept,\n            border = BorderStroke(1.dp, MaterialTheme.colorScheme.primary),\n            modifier = Modifier.padding(end = 12.dp)\n        ) {\n            Row {\n                Icon(\n                    imageVector = Icons.Default.Check,\n                    tint = MaterialTheme.colorScheme.tertiary,\n                    contentDescription = null,\n                    modifier = Modifier\n                        .scale(.8f)\n                        .padding(start = 32.dp)\n                )\n                Text(\n                    text = stringResource(R.string.accept_suggestion_cta),\n                    color = MaterialTheme.colorScheme.tertiary,\n                    modifier = Modifier.padding(start = 8.dp, end = 32.dp, top = 2.dp)\n                )\n            }\n\n        }\n    }\n}\n\n@Preview(uiMode = UI_MODE_NIGHT_NO, showBackground = true)\n@Composable\nfun PreviewTextControlBar() {\n    TensorFlowDemoTheme {\n        Column {\n            TextControlBar(\n                state = TextEditBarState.Editing(\n                    clearEnabled = false,\n                    generateEnabled = true,\n                    copyEnabled = false,\n                    generating = false\n                ),\n                onClearClick = {},\n                onGenerateClick = {},\n                onCopyClick = {},\n                onRetry = {},\n                onAccept = {}\n            )\n            Spacer(modifier = Modifier.height(8.dp))\n            TextControlBar(\n                state = TextEditBarState.Suggesting,\n                onClearClick = { },\n                onGenerateClick = { },\n                onCopyClick = {},\n                onRetry = {},\n                onAccept = {}\n            )\n        }\n    }\n}"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/java/com/google/tensorflowdemo/ui/screens/autocomplete/components/WindowSizeSelection.kt",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage com.google.tensorflowdemo.ui.screens.autocomplete.components\n\nimport android.content.res.Configuration\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Slider\nimport androidx.compose.material3.SliderDefaults\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.LaunchedEffect\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.tooling.preview.Preview\nimport androidx.compose.ui.unit.dp\nimport com.google.tensorflowdemo.R\nimport com.google.tensorflowdemo.data.autocomplete.AutoCompleteService.AutoCompleteInputConfiguration\nimport com.google.tensorflowdemo.ui.theme.LightGrey\nimport com.google.tensorflowdemo.ui.theme.TensorFlowDemoTheme\n\n@Composable\nfun WindowSizeSelection(\n    inputConfiguration: AutoCompleteInputConfiguration,\n    onWindowValueChange: (Int) -> Unit,\n    modifier: Modifier = Modifier\n) {\n    var sliderValue by remember { mutableStateOf(inputConfiguration.initialWordCount) }\n\n    LaunchedEffect(key1 = Unit) {\n        onWindowValueChange(inputConfiguration.initialWordCount)\n    }\n\n    Column(modifier = modifier.fillMaxWidth()) {\n        Text(\n            text = stringResource(R.string.window_size_slider_label),\n            style = MaterialTheme.typography.bodySmall,\n            color = MaterialTheme.colorScheme.tertiary,\n            modifier = Modifier\n        )\n        Row(\n            verticalAlignment = Alignment.CenterVertically,\n            modifier = Modifier\n                .fillMaxWidth()\n                .height(50.dp)\n        ) {\n            Slider(\n                value = sliderValue.toFloat(),\n                onValueChange = { value ->\n                    sliderValue = value.toInt()\n\n                    onWindowValueChange(sliderValue)\n                },\n                valueRange = inputConfiguration.minWordCount.toFloat()..inputConfiguration.maxWordCount.toFloat(),\n                steps = 45,\n                colors = SliderDefaults.colors(\n                    activeTickColor = MaterialTheme.colorScheme.primary,\n                    inactiveTrackColor = LightGrey,\n                    inactiveTickColor = LightGrey\n                ),\n                modifier = Modifier.weight(1f)\n            )\n            Text(\n                text = stringResource(R.string.window_size_wordcount).replace(\"{count}\", sliderValue.toString()),\n                style = MaterialTheme.typography.bodySmall,\n                color = MaterialTheme.colorScheme.tertiary,\n                modifier = Modifier.padding(start = 16.dp)\n            )\n        }\n    }\n}\n\n@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, showBackground = true)\n@Composable\nfun PreviewWindowSizeSelection() {\n    TensorFlowDemoTheme {\n        WindowSizeSelection(\n            inputConfiguration = AutoCompleteInputConfiguration(),\n            onWindowValueChange = {}\n        )\n    }\n}\n"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/java/com/google/tensorflowdemo/ui/theme/Color.kt",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage com.google.tensorflowdemo.ui.theme\n\nimport androidx.compose.ui.graphics.Color\n\nval Orange = Color(0xFFFF6F00)\nval DarkBlue = Color(0xFF425066)\nval DarkGrey = Color(0xFF616161)\nval MediumGrey = Color(0xFFCCCCCC)\nval LightGrey = Color(0xFFE6E6E6)\nval VeryLightGrey = Color(0xFFF8F8F8)\nval DarkRed = Color(0xFFBF281B)\n\nval InactiveOutlinedTextFieldBorder = Color(0xFFD4D7DC)\nval InactiveOutlinedTextFieldBackground = Color(0xFFF9F9F9)\nval ActiveOutlinedTextFieldBackground = Color(0xFFFFFFFF)"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/java/com/google/tensorflowdemo/ui/theme/Shape.kt",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage com.google.tensorflowdemo.ui.theme\n\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.material3.Shapes\nimport androidx.compose.ui.unit.dp\n\nval TfDemoShapes = Shapes(\n    small = RoundedCornerShape(size = 12.dp),\n    medium = RoundedCornerShape(size = 12.dp)\n)"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/java/com/google/tensorflowdemo/ui/theme/Theme.kt",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage com.google.tensorflowdemo.ui.theme\n\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.lightColorScheme\nimport androidx.compose.runtime.Composable\n\nprivate val TfDemoColorScheme = lightColorScheme(\n    primary = Orange,\n    secondary = DarkBlue,\n    tertiary = DarkGrey,\n    surface = VeryLightGrey,\n    surfaceVariant = LightGrey,\n    outline = MediumGrey,\n    error = DarkRed\n)\n\n@Composable\nfun TensorFlowDemoTheme(\n    content: @Composable () -> Unit\n) {\n    MaterialTheme(\n        colorScheme = TfDemoColorScheme,\n        typography = TfDemoTypography,\n        shapes = TfDemoShapes,\n        content = content\n    )\n}\n"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/java/com/google/tensorflowdemo/ui/theme/Type.kt",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage com.google.tensorflowdemo.ui.theme\n\nimport androidx.compose.material3.Typography\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.text.TextStyle\nimport androidx.compose.ui.text.font.Font\nimport androidx.compose.ui.text.font.FontFamily\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.unit.TextUnit\nimport androidx.compose.ui.unit.TextUnitType\nimport androidx.compose.ui.unit.sp\nimport com.google.tensorflowdemo.R\n\nval TfDemoTypography = Typography(\n    displayLarge = TextStyle(\n        fontFamily = FontFamily(Font(R.font.roboto_regular)),\n        fontSize = 20.sp,\n        fontWeight = FontWeight.W800,\n        letterSpacing = TextUnit(.05f, TextUnitType.Em),\n        background = Color.White\n    ),\n    titleSmall = TextStyle(\n        fontFamily = FontFamily(Font(R.font.roboto_regular)),\n        fontSize = 14.sp,\n        fontWeight = FontWeight.W500,\n        letterSpacing = TextUnit(.1f, TextUnitType.Em),\n    ),\n    bodySmall = TextStyle(\n        fontFamily = FontFamily(Font(R.font.roboto_regular)),\n        fontSize = 14.sp,\n        fontWeight = FontWeight.W200,\n        letterSpacing = TextUnit(.1f, TextUnitType.Em),\n        lineHeight = 20.sp\n    )\n)"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/java/com/google/tensorflowdemo/util/StringExt.kt",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage com.google.tensorflowdemo.util\n\nfun String.trimToMaxWordCount(count: Int): String {\n    val allWords = allWords()\n    val wordCount = allWords.size\n    if (wordCount < count) return this\n\n    val lastWords = allWords.toMutableList().subList(allWords.size - count, allWords.size)\n    val lastText = lastWords.joinToString(separator = \"\")\n\n    var inputIndex = this.length\n    for (trimmedTextIndex in lastText.length - 1 downTo 0) {\n        inputIndex--\n        val trimmedChar = lastText[trimmedTextIndex]\n        while (inputIndex >= 0 && this[inputIndex] != trimmedChar) {\n            inputIndex--\n        }\n    }\n    return this.substring(inputIndex)\n}\n\nfun String.splitToWords(): List<String> {\n    val allWords = allWords()\n\n    var index = 0\n    val indexList = allWords.mapIndexed { wordIndex, word ->\n        if (wordIndex == 0) {\n            index += word.length\n            0\n        } else {\n            val ch = word[0]\n            while (index < length && this[index] != ch) index++\n            val outputIndex = index\n            index += word.length\n            outputIndex\n        }\n    }\n\n    return indexList.mapIndexed { i, wordStartIndex ->\n        if (i < indexList.size - 1) {\n            val wordEndIndex = indexList[i + 1]\n            this.substring(wordStartIndex, wordEndIndex)\n        } else this.substring(wordStartIndex)\n    }\n}\n\nprivate val wordsRegex = \"\"\"(\\b\\S+\\b)\"\"\".toRegex()\nfun String.allWords() = wordsRegex.findAll(this).toList().map { it.groupValues.first() }\n"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/res/drawable/header_background.xml",
    "content": "<vector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:width=\"1269dp\"\n    android:height=\"564dp\"\n    android:viewportWidth=\"1269\"\n    android:viewportHeight=\"564\"\n    tools:ignore=\"VectorRaster\"\n    >\n    <group\n        android:translateX=\"-223\"\n        android:translateY=\"-78\"\n        >\n        <!--    <clip-path-->\n        <!--        android:pathData=\"M0,0h1269v564h-1269z\"/>-->\n        <!--    <path-->\n        <!--        android:pathData=\"M0,0h1269v564h-1269z\"-->\n        <!--        android:fillColor=\"#ffffff\"/>-->\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M1043.6,-25.6L1107.3,11.1C1114.4,15.3 1114.5,22 1107.3,26.1L1097.3,31.9C1090.1,36 1090.2,42.7 1097.3,46.8L1125.7,63.2C1132.8,67.3 1144.4,67.3 1151.6,63.2L1159.9,58.4C1171.7,51.6 1190.8,51.7 1202.6,58.5L1293.4,110.9\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#E6E6E6\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M400.6,459.4L464.2,496.1C471.4,500.3 471.4,507 464.3,511.1L454.2,516.9C447.1,521 447.1,527.7 454.3,531.8L482.6,548.2C489.8,552.3 501.4,552.3 508.5,548.2L516.9,543.4C528.6,536.6 547.7,536.7 559.6,543.5L650.3,595.9\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#E6E6E6\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M875.1,-14.3L933.9,19.6C941.1,23.7 941.1,30.4 934,34.5L924.5,40C917.4,44.1 905.8,44.1 898.6,39.9L793,-21C785.9,-25.2 774.3,-25.2 767.1,-21.1L730.7,-0C723.6,4.1 723.6,10.8 730.8,14.9L769,37\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#FF6F00\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M232.1,470.6L290.9,504.6C298,508.7 298.1,515.4 290.9,519.5L281.5,525C274.3,529.1 262.7,529.1 255.6,524.9L150,464C142.8,459.8 131.2,459.8 124.1,463.9L87.6,485C80.5,489.1 80.6,495.8 87.7,499.9L125.9,522\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#FF6F00\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M381.2,65.1L440,99C447.2,103.1 447.2,109.8 440.1,113.9L430.6,119.4C423.5,123.5 411.9,123.5 404.7,119.3L299.1,58.4C292,54.2 280.4,54.2 273.2,58.3L236.8,79.4C229.7,83.5 229.7,90.2 236.9,94.3L275.1,116.4\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#FF6F00\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:pathData=\"M227.3,463.4C229.8,463.4 231.8,465.4 231.8,467.9C231.8,469.1 231.4,470.2 230.5,471.1C229.6,471.9 228.5,472.4 227.3,472.4C224.8,472.4 222.8,470.4 222.8,467.9C222.8,466.7 223.3,465.6 224.1,464.7C225,463.9 226.1,463.4 227.3,463.4ZM227.3,462.4C224.3,462.4 221.8,464.9 221.8,467.9C221.8,470.9 224.3,473.4 227.3,473.4C230.4,473.4 232.8,470.9 232.8,467.9C232.8,464.9 230.4,462.4 227.3,462.4Z\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:pathData=\"M279,115.2C281.5,115.2 283.5,117.2 283.5,119.7C283.5,120.8 283,122 282.2,122.8C281.3,123.7 280.2,124.2 279,124.2C276.5,124.2 274.5,122.1 274.5,119.7C274.5,117.2 276.5,115.2 279,115.2ZM279,114.2C275.9,114.2 273.5,116.6 273.5,119.7C273.5,122.7 275.9,125.2 279,125.2C282,125.2 284.5,122.7 284.5,119.7C284.5,116.6 282,114.2 279,114.2Z\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:pathData=\"M773.7,44.7C776.5,44.7 778.7,42.5 778.7,39.7C778.7,37 776.5,34.7 773.7,34.7C771,34.7 768.7,37 768.7,39.7C768.7,42.5 771,44.7 773.7,44.7Z\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:pathData=\"M773.7,35.2C776.2,35.2 778.2,37.3 778.2,39.7C778.2,42.2 776.2,44.2 773.7,44.2C771.3,44.2 769.2,42.2 769.2,39.7C769.2,38.5 769.7,37.4 770.6,36.6C771.4,35.7 772.5,35.2 773.7,35.2ZM773.7,34.2C770.7,34.2 768.2,36.7 768.2,39.7C768.2,42.8 770.7,45.2 773.7,45.2C776.8,45.2 779.2,42.8 779.2,39.7C779.2,36.7 776.8,34.2 773.7,34.2Z\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:pathData=\"M130.7,529.7C133.5,529.7 135.7,527.5 135.7,524.7C135.7,522 133.5,519.7 130.7,519.7C127.9,519.7 125.7,522 125.7,524.7C125.7,527.5 127.9,529.7 130.7,529.7Z\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:pathData=\"M130.7,520.2C133.2,520.2 135.2,522.3 135.2,524.7C135.2,527.2 133.2,529.2 130.7,529.2C128.2,529.2 126.2,527.2 126.2,524.7C126.2,523.5 126.7,522.4 127.5,521.6C128.4,520.7 129.5,520.2 130.7,520.2ZM130.7,519.2C127.7,519.2 125.2,521.7 125.2,524.7C125.2,527.8 127.7,530.2 130.7,530.2C133.7,530.2 136.2,527.8 136.2,524.7C136.2,521.7 133.7,519.2 130.7,519.2Z\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:pathData=\"M383.7,70.6C386.4,70.6 388.7,68.3 388.7,65.6C388.7,62.8 386.4,60.6 383.7,60.6C380.9,60.6 378.7,62.8 378.7,65.6C378.7,68.3 380.9,70.6 383.7,70.6Z\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:pathData=\"M383.7,61.1C386.1,61.1 388.2,63.1 388.2,65.6C388.2,68 386.1,70.1 383.7,70.1C381.2,70.1 379.2,68 379.2,65.6C379.2,63.1 381.2,61.1 383.7,61.1ZM383.7,60.1C380.6,60.1 378.2,62.5 378.2,65.6C378.2,68.6 380.6,71.1 383.7,71.1C386.7,71.1 389.2,68.6 389.2,65.6C389.2,62.5 386.7,60.1 383.7,60.1Z\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:pathData=\"M477.5,418C480,418 482,420 482,422.5C482,423.7 481.5,424.8 480.7,425.7C479.8,426.5 478.7,427 477.5,427C475,427 473,425 473,422.5C473,420 475,418 477.5,418ZM477.5,417C474.5,417 472,419.5 472,422.5C472,425.5 474.5,428 477.5,428C480.5,428 483,425.5 483,422.5C483,419.5 480.5,417 477.5,417Z\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M1286.9,-35L1111.4,67.1C1097.2,75.3 1074,75.3 1059.7,67L956,7.2C941.7,-1.1 918.5,-1.1 904.2,7.1L801.2,66.6C787,74.8 763.8,74.7 749.4,66.5L625,-5.3\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#425066\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M643.9,450L468.4,552.1C454.1,560.3 431,560.3 416.6,552L313,492.2C298.6,483.9 275.4,483.9 261.2,492.1L158.2,551.5C143.9,559.8 120.7,559.7 106.4,551.5L-18,479.6\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#425066\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M471.9,46.4L296.4,148.5C282.1,156.8 259,156.7 244.6,148.4L141,88.6C126.6,80.3 103.4,80.3 89.2,88.5L-13.8,148\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#425066\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M480,88.3L513.8,107.8C521,111.9 521,118.6 513.9,122.7L493,134.8C485.9,138.9 485.9,145.6 493.1,149.7L545.4,179.9C552.6,184.1 552.7,190.8 545.5,194.9L512.3,214.1\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#425066\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M472.2,16.6L577.6,77.5C584,81.2 584,87.1 577.7,90.7C571.4,94.4 571.4,100.3 577.8,103.9L604.9,119.6\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#E6E6E6\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M24.7,60.5L130.1,121.4C136.5,125.1 136.5,131 130.2,134.6C123.9,138.3 123.9,144.2 130.3,147.9L157.4,163.5\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#E6E6E6\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M599.3,61.4L563.1,40.4C555.9,36.3 555.9,29.6 563,25.5L594.3,7.4C601.4,3.3 601.3,-3.4 594.2,-7.5L564.8,-24.5C557.6,-28.6 546,-28.6 538.9,-24.5L494.8,1\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#E6E6E6\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M650.3,36.9L705.5,68.8C712.7,73 712.6,79.7 705.5,83.8L690.7,92.3C683.5,96.4 683.5,103.2 690.6,107.3L739.3,135.4\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#CCCCCC\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M696.9,88.7L643.3,119.7\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#CCCCCC\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#ffffff\"\n            android:fillType=\"evenOdd\"\n            android:pathData=\"M643.3,124.7C640.5,124.7 638.3,122.5 638.3,119.7C638.3,116.9 640.5,114.7 643.3,114.7C646,114.7 648.3,116.9 648.3,119.7C648.3,122.5 646,124.7 643.3,124.7Z\"\n            />\n        <path\n            android:fillColor=\"#CCCCCC\"\n            android:pathData=\"M643.3,115.2C644.5,115.2 645.6,115.7 646.4,116.5C647.3,117.4 647.8,118.5 647.8,119.7C647.8,122.2 645.7,124.2 643.3,124.2C640.8,124.2 638.8,122.2 638.8,119.7C638.8,117.2 640.8,115.2 643.3,115.2ZM643.3,114.2C640.2,114.2 637.8,116.7 637.8,119.7C637.8,122.7 640.2,125.2 643.3,125.2C646.3,125.2 648.8,122.7 648.8,119.7C648.8,116.7 646.3,114.2 643.3,114.2Z\"\n            />\n        <path\n            android:fillColor=\"#CCCCCC\"\n            android:pathData=\"M650.3,41.9C653,41.9 655.3,39.7 655.3,36.9C655.3,34.2 653,31.9 650.3,31.9C647.5,31.9 645.3,34.2 645.3,36.9C645.3,39.7 647.5,41.9 650.3,41.9Z\"\n            />\n        <path\n            android:fillColor=\"#CCCCCC\"\n            android:pathData=\"M650.3,32.4C652.7,32.4 654.8,34.5 654.8,36.9C654.8,39.4 652.7,41.4 650.3,41.4C649,41.4 647.9,41 647.1,40.1C646.2,39.3 645.8,38.1 645.8,36.9C645.8,34.5 647.8,32.4 650.3,32.4ZM650.3,31.4C647.2,31.4 644.8,33.9 644.8,36.9C644.8,40 647.2,42.4 650.3,42.4C653.3,42.4 655.8,40 655.8,36.9C655.8,33.9 653.3,31.4 650.3,31.4Z\"\n            />\n        <path\n            android:fillColor=\"#CCCCCC\"\n            android:pathData=\"M744.1,143.1C746.9,143.1 749.1,140.9 749.1,138.1C749.1,135.4 746.9,133.1 744.1,133.1C741.3,133.1 739.1,135.4 739.1,138.1C739.1,140.9 741.3,143.1 744.1,143.1Z\"\n            />\n        <path\n            android:fillColor=\"#CCCCCC\"\n            android:pathData=\"M744.1,133.6C745.3,133.6 746.4,134.1 747.3,134.9C748.1,135.8 748.6,136.9 748.6,138.1C748.6,140.6 746.6,142.6 744.1,142.6C742.9,142.6 741.8,142.2 740.9,141.3C740.1,140.5 739.6,139.3 739.6,138.1C739.6,135.6 741.6,133.6 744.1,133.6ZM744.1,132.6C741.1,132.6 738.6,135.1 738.6,138.1C738.6,141.2 741.1,143.6 744.1,143.6C747.1,143.6 749.6,141.2 749.6,138.1C749.6,135.1 747.1,132.6 744.1,132.6Z\"\n            />\n        <path\n            android:fillColor=\"#ffffff\"\n            android:pathData=\"M494.8,6C497.5,6 499.8,3.7 499.8,1C499.8,-1.8 497.5,-4 494.8,-4C492,-4 489.8,-1.8 489.8,1C489.8,3.7 492,6 494.8,6Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M494.8,-3.5C497.2,-3.5 499.3,-1.5 499.3,1C499.3,2.2 498.8,3.3 497.9,4.1C497.1,5 496,5.5 494.8,5.5C492.3,5.5 490.3,3.4 490.3,1C490.3,-0.2 490.7,-1.4 491.6,-2.2C492.4,-3.1 493.5,-3.5 494.8,-3.5ZM494.8,-4.5C491.7,-4.5 489.3,-2.1 489.3,1C489.3,4 491.7,6.5 494.8,6.5C497.8,6.5 500.3,4 500.3,1C500.3,-2.1 497.8,-4.5 494.8,-4.5Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:fillType=\"evenOdd\"\n            android:pathData=\"M603.7,68.9C601,68.9 598.7,66.7 598.7,63.9C598.7,61.1 601,58.9 603.7,58.9C606.5,58.9 608.7,61.1 608.7,63.9C608.7,66.7 606.5,68.9 603.7,68.9Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:fillType=\"evenOdd\"\n            android:pathData=\"M608.5,127.5C605.7,127.5 603.5,125.3 603.5,122.5C603.5,119.7 605.7,117.5 608.5,117.5C611.3,117.5 613.5,119.7 613.5,122.5C613.5,125.3 611.3,127.5 608.5,127.5Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M603.7,59.4C606.2,59.4 608.2,61.4 608.2,63.9C608.2,65.1 607.8,66.2 606.9,67.1C606.1,67.9 604.9,68.4 603.7,68.4C601.3,68.4 599.2,66.4 599.2,63.9C599.2,61.4 601.3,59.4 603.7,59.4ZM603.7,58.4C600.7,58.4 598.2,60.9 598.2,63.9C598.2,66.9 600.7,69.4 603.7,69.4C606.8,69.4 609.2,66.9 609.2,63.9C609.2,60.9 606.8,58.4 603.7,58.4Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M608.5,118C611,118 613,120 613,122.5C613,123.7 612.5,124.8 611.7,125.7C610.8,126.5 609.7,127 608.5,127C606,127 604,125 604,122.5C604,120 606,118 608.5,118ZM608.5,117C605.5,117 603,119.5 603,122.5C603,125.5 605.5,128 608.5,128C611.5,128 614,125.5 614,122.5C614,119.5 611.5,117 608.5,117Z\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M466.5,226.4L502.8,247.4C510,251.5 510,258.2 502.9,262.4L464.1,284.7\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#E6E6E6\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M144,-15.6L227.1,32.4C234.3,36.6 234.3,43.3 227.2,47.4L188.4,69.8\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#E6E6E6\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M459.9,282.7C462.3,282.7 464.4,284.7 464.4,287.2C464.4,289.7 462.3,291.7 459.9,291.7C457.4,291.7 455.4,289.7 455.4,287.2C455.4,286 455.8,284.9 456.7,284C457.5,283.2 458.7,282.7 459.9,282.7ZM459.9,281.7C456.8,281.7 454.4,284.2 454.4,287.2C454.4,290.2 456.8,292.7 459.9,292.7C462.9,292.7 465.4,290.2 465.4,287.2C465.4,284.2 462.9,281.7 459.9,281.7Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:fillType=\"evenOdd\"\n            android:pathData=\"M466.6,231.4C463.8,231.4 461.6,229.2 461.6,226.4C461.6,223.7 463.8,221.4 466.6,221.4C469.3,221.4 471.6,223.7 471.6,226.4C471.6,229.2 469.3,231.4 466.6,231.4Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M466.6,221.9C469,221.9 471.1,224 471.1,226.4C471.1,227.6 470.6,228.8 469.7,229.6C468.9,230.5 467.8,230.9 466.6,230.9C464.1,230.9 462.1,228.9 462.1,226.4C462.1,224 464.1,221.9 466.6,221.9ZM466.6,220.9C463.5,220.9 461.1,223.4 461.1,226.4C461.1,229.5 463.5,231.9 466.6,231.9C469.6,231.9 472.1,229.5 472.1,226.4C472.1,223.4 469.6,220.9 466.6,220.9Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:fillType=\"evenOdd\"\n            android:pathData=\"M394.4,169.6C391.7,169.6 389.4,167.3 389.4,164.6C389.4,161.8 391.7,159.6 394.4,159.6C397.2,159.6 399.4,161.8 399.4,164.6C399.4,167.3 397.2,169.6 394.4,169.6Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M394.4,160.1C396.9,160.1 398.9,162.1 398.9,164.6C398.9,167.1 396.9,169.1 394.4,169.1C391.9,169.1 389.9,167.1 389.9,164.6C389.9,163.4 390.4,162.2 391.2,161.4C392.1,160.5 393.2,160.1 394.4,160.1ZM394.4,159.1C391.4,159.1 388.9,161.5 388.9,164.6C388.9,167.6 391.4,170.1 394.4,170.1C397.5,170.1 399.9,167.6 399.9,164.6C399.9,161.5 397.5,159.1 394.4,159.1Z\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M476.5,251.4L512.8,272.4C520,276.5 520,283.2 512.9,287.4L470.5,311.9\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#E6E6E6\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M469.9,317.2C472.6,317.2 474.9,315 474.9,312.2C474.9,309.4 472.6,307.2 469.9,307.2C467.1,307.2 464.9,309.4 464.9,312.2C464.9,315 467.1,317.2 469.9,317.2Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M469.9,307.7C472.3,307.7 474.4,309.7 474.4,312.2C474.4,314.7 472.3,316.7 469.9,316.7C467.4,316.7 465.4,314.7 465.4,312.2C465.4,311 465.8,309.9 466.7,309C467.5,308.2 468.7,307.7 469.9,307.7ZM469.9,306.7C466.8,306.7 464.4,309.2 464.4,312.2C464.4,315.2 466.8,317.7 469.9,317.7C472.9,317.7 475.4,315.2 475.4,312.2C475.4,309.2 472.9,306.7 469.9,306.7Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M156.5,169.2C159.2,169.2 161.5,167 161.5,164.2C161.5,161.5 159.2,159.2 156.5,159.2C153.7,159.2 151.5,161.5 151.5,164.2C151.5,167 153.7,169.2 156.5,169.2Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M156.5,159.7C158.9,159.7 161,161.8 161,164.2C161,166.7 158.9,168.7 156.5,168.7C154,168.7 152,166.7 152,164.2C152,163 152.4,161.9 153.3,161.1C154.1,160.2 155.3,159.7 156.5,159.7ZM156.5,158.7C153.4,158.7 151,161.2 151,164.2C151,167.3 153.4,169.7 156.5,169.7C159.5,169.7 162,167.3 162,164.2C162,161.2 159.5,158.7 156.5,158.7Z\"\n            />\n        <path\n            android:fillColor=\"#ffffff\"\n            android:fillType=\"evenOdd\"\n            android:pathData=\"M476.6,256.5C473.8,256.5 471.6,254.2 471.6,251.4C471.6,248.7 473.8,246.4 476.6,246.4C479.3,246.4 481.6,248.7 481.6,251.4C481.6,254.2 479.3,256.5 476.6,256.5Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M476.6,246.9C479,246.9 481.1,249 481.1,251.4C481.1,252.6 480.6,253.8 479.7,254.6C478.9,255.5 477.8,255.9 476.6,255.9C474.1,255.9 472.1,253.9 472.1,251.4C472.1,249 474.1,246.9 476.6,246.9ZM476.6,245.9C473.5,245.9 471.1,248.4 471.1,251.4C471.1,254.5 473.5,257 476.6,257C479.6,257 482.1,254.5 482.1,251.4C482.1,248.4 479.6,245.9 476.6,245.9Z\"\n            />\n        <path\n            android:fillColor=\"#ffffff\"\n            android:pathData=\"M24.7,65.5C27.5,65.5 29.7,63.3 29.7,60.5C29.7,57.8 27.5,55.5 24.7,55.5C21.9,55.5 19.7,57.8 19.7,60.5C19.7,63.3 21.9,65.5 24.7,65.5Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M24.7,56C27.2,56 29.2,58 29.2,60.5C29.2,63 27.2,65 24.7,65C22.2,65 20.2,63 20.2,60.5C20.2,58 22.2,56 24.7,56ZM24.7,55C21.6,55 19.2,57.5 19.2,60.5C19.2,63.6 21.6,66 24.7,66C27.7,66 30.2,63.6 30.2,60.5C30.2,57.5 27.7,55 24.7,55Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M186.5,76.2C189.2,76.2 191.5,74 191.5,71.2C191.5,68.5 189.2,66.2 186.5,66.2C183.7,66.2 181.5,68.5 181.5,71.2C181.5,74 183.7,76.2 186.5,76.2Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M186.5,66.7C189,66.7 191,68.7 191,71.2C191,72.4 190.5,73.6 189.7,74.4C188.8,75.3 187.7,75.7 186.5,75.7C184,75.7 182,73.7 182,71.2C182,68.7 184,66.7 186.5,66.7ZM186.5,65.7C183.4,65.7 181,68.2 181,71.2C181,74.3 183.4,76.7 186.5,76.7C189.5,76.7 192,74.3 192,71.2C192,68.2 189.5,65.7 186.5,65.7Z\"\n            />\n        <path\n            android:fillColor=\"#425066\"\n            android:pathData=\"M625.1,-9.8C627.5,-9.8 629.6,-7.8 629.6,-5.3C629.6,-4.1 629.1,-3 628.3,-2.2C627.4,-1.3 626.3,-0.8 625.1,-0.8C622.6,-0.8 620.6,-2.9 620.6,-5.3C620.6,-6.5 621,-7.7 621.9,-8.5C622.7,-9.4 623.9,-9.8 625.1,-9.8ZM625.1,-10.8C622,-10.8 619.6,-8.4 619.6,-5.3C619.6,-2.3 622,0.2 625.1,0.2C628.1,0.2 630.6,-2.3 630.6,-5.3C630.6,-8.4 628.1,-10.8 625.1,-10.8Z\"\n            />\n        <path\n            android:fillColor=\"#ffffff\"\n            android:fillType=\"evenOdd\"\n            android:pathData=\"M472.2,21.6C469.4,21.6 467.2,19.4 467.2,16.6C467.2,13.9 469.4,11.6 472.2,11.6C474.9,11.6 477.2,13.9 477.2,16.6C477.2,19.4 474.9,21.6 472.2,21.6Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M472.2,12.1C474.6,12.1 476.7,14.1 476.7,16.6C476.7,19.1 474.6,21.1 472.2,21.1C469.7,21.1 467.7,19.1 467.7,16.6C467.7,15.4 468.1,14.3 469,13.4C469.8,12.6 471,12.1 472.2,12.1ZM472.2,11.1C469.1,11.1 466.7,13.6 466.7,16.6C466.7,19.7 469.1,22.1 472.2,22.1C475.2,22.1 477.7,19.7 477.7,16.6C477.7,13.6 475.2,11.1 472.2,11.1Z\"\n            />\n        <path\n            android:fillColor=\"#425066\"\n            android:pathData=\"M475.3,81C477.8,81 479.8,83 479.8,85.5C479.8,86.7 479.3,87.8 478.5,88.7C477.6,89.5 476.5,90 475.3,90C472.8,90 470.8,88 470.8,85.5C470.8,83 472.8,81 475.3,81ZM475.3,80C472.2,80 469.8,82.5 469.8,85.5C469.8,88.6 472.2,91 475.3,91C478.3,91 480.8,88.6 480.8,85.5C480.8,82.5 478.3,80 475.3,80Z\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M-30.8,188.8L254.7,354.8C269.1,363.1 292.3,363.1 306.5,354.9L308.3,353.9C322.5,345.6 345.7,345.6 360.1,353.9L473.6,419.3\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#FF6F00\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M574.1,154.2L604.5,171.7C611.7,175.9 611.7,182.6 604.6,186.7L587.9,196.4C580.8,200.5 580.8,207.2 588,211.3L613.7,226.2C620.9,230.3 620.9,237 613.8,241.1L600.2,249C593,253.1 593.1,259.8 600.3,263.9L653,294.4\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#E6E6E6\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M657.5,293C660,293 662,295 662,297.5C662,298.7 661.5,299.8 660.7,300.7C659.8,301.5 658.7,302 657.5,302C655,302 653,300 653,297.5C653,295 655,293 657.5,293ZM657.5,292C654.5,292 652,294.5 652,297.5C652,300.5 654.5,303 657.5,303C660.5,303 663,300.5 663,297.5C663,294.5 660.5,292 657.5,292Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M571.9,159.2C574.7,159.2 576.9,157 576.9,154.2C576.9,151.4 574.7,149.2 571.9,149.2C569.1,149.2 566.9,151.4 566.9,154.2C566.9,157 569.1,159.2 571.9,159.2Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M571.9,149.7C574.4,149.7 576.4,151.7 576.4,154.2C576.4,155.4 575.9,156.5 575.1,157.4C574.2,158.2 573.1,158.7 571.9,158.7C569.4,158.7 567.4,156.7 567.4,154.2C567.4,151.7 569.4,149.7 571.9,149.7ZM571.9,148.7C568.9,148.7 566.4,151.1 566.4,154.2C566.4,157.2 568.9,159.7 571.9,159.7C574.9,159.7 577.4,157.2 577.4,154.2C577.4,151.1 574.9,148.7 571.9,148.7Z\"\n            />\n        <path\n            android:fillColor=\"#425066\"\n            android:fillType=\"evenOdd\"\n            android:pathData=\"M253.2,394.9C252.9,394.9 252.6,394.9 252.2,394.8C249.5,394.2 247.8,391.6 248.3,388.9C248.8,386.6 250.9,384.9 253.2,384.9C253.6,384.9 253.9,384.9 254.2,385C255.5,385.3 256.7,386 257.4,387.1C258.1,388.2 258.4,389.6 258.1,390.9C257.7,393.2 255.6,394.9 253.2,394.9V394.9Z\"\n            />\n        <path\n            android:fillColor=\"#425066\"\n            android:pathData=\"M253.2,385.4C253.5,385.4 253.8,385.4 254.1,385.5C255.3,385.7 256.3,386.4 257,387.4C257.6,388.4 257.9,389.6 257.6,390.8C257.2,392.9 255.4,394.4 253.2,394.4C252.9,394.4 252.6,394.4 252.3,394.3C249.9,393.8 248.3,391.4 248.8,389C249.3,386.9 251.1,385.4 253.2,385.4M253.2,384.4C250.7,384.4 248.4,386.2 247.9,388.8C247.2,391.8 249.2,394.7 252.1,395.3C252.5,395.4 252.9,395.4 253.2,395.4C255.8,395.4 258.1,393.6 258.6,391C259.2,388 257.3,385.1 254.3,384.5C254,384.4 253.6,384.4 253.2,384.4Z\"\n            />\n        <path\n            android:fillColor=\"#425066\"\n            android:pathData=\"M391.1,296.1C393.9,296.1 396.1,293.8 396.1,291.1C396.1,288.3 393.9,286.1 391.1,286.1C388.4,286.1 386.1,288.3 386.1,291.1C386.1,293.8 388.4,296.1 391.1,296.1Z\"\n            />\n        <path\n            android:fillColor=\"#425066\"\n            android:pathData=\"M508.5,221.5C511.3,221.5 513.5,219.3 513.5,216.5C513.5,213.7 511.3,211.5 508.5,211.5C505.7,211.5 503.5,213.7 503.5,216.5C503.5,219.3 505.7,221.5 508.5,221.5Z\"\n            />\n        <path\n            android:fillColor=\"#425066\"\n            android:pathData=\"M391.1,286.6C393.6,286.6 395.6,288.6 395.6,291.1C395.6,293.5 393.6,295.6 391.1,295.6C388.6,295.6 386.6,293.5 386.6,291.1C386.6,288.6 388.6,286.6 391.1,286.6ZM391.1,285.6C388.1,285.6 385.6,288 385.6,291.1C385.6,294.1 388.1,296.6 391.1,296.6C394.2,296.6 396.6,294.1 396.6,291.1C396.6,288 394.2,285.6 391.1,285.6Z\"\n            />\n        <path\n            android:fillColor=\"#425066\"\n            android:pathData=\"M508.5,212C511,212 513,214 513,216.5C513,219 511,221 508.5,221C506,221 504,219 504,216.5C504,214 506,212 508.5,212ZM508.5,211C505.5,211 503,213.5 503,216.5C503,219.5 505.5,222 508.5,222C511.5,222 514,219.5 514,216.5C514,213.5 511.5,211 508.5,211Z\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M392.1,291L339.3,260.1C334,257 327.5,257.1 322.3,260.3L309.9,267.8C303.9,271.5 296.3,271.5 290.2,267.8L237.8,236.1C228.2,230.3 216.2,230.2 206.6,235.9L107,294.7C101.4,298 94.6,298.1 89,295L61.8,279.9C56.4,276.9 56.3,269.1 61.7,266L119.4,232.8C137.5,222.5 137.5,196.4 119.4,186L-12.7,109.9\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#425066\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M393.8,164.7L309.4,213.8C303.6,217.1 296.5,217 290.8,213.6C286.9,211.3 282,211.2 278.1,213.5L226.6,243.4C217.7,248.6 217.8,261.5 226.7,266.6L283.1,299C290.4,303.1 290.4,313.5 283.3,317.7L233.1,347.4C226.4,351.4 218.1,351.4 211.4,347.5L171.8,324.4C164.1,320 154.6,320 147,324.5L65.9,372C56.6,377.5 45.1,377.6 35.7,372.4L0.1,353C-7,348.8 -7.1,342.1 0,338L13.7,330.1C20.8,326 20.7,319.3 13.6,315.2L-12.2,300.3C-19.3,296.2 -19.4,289.5 -12.2,285.4L4.5,275.7C11.6,271.6 11.6,264.9 4.4,260.8L-26,243.2\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#E6E6E6\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M239.3,-15.5L287.3,13.4C295.5,18.3 305.9,18.3 314.1,13.3L362.8,-16.2\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#425066\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M286.8,-7.2L334.7,21.7C343,26.7 353.4,26.7 361.6,21.7L410.3,-7.9\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#E6E6E6\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M409.4,227.8L331.8,272.6C324.6,276.8 324.6,287.1 331.8,291.2L403.6,333C413.1,338.5 413.1,352.2 403.6,357.6L285.4,426\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#E6E6E6\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M408.7,232.8C411.4,232.8 413.7,230.6 413.7,227.8C413.7,225 411.4,222.8 408.7,222.8C405.9,222.8 403.7,225 403.7,227.8C403.7,230.6 405.9,232.8 408.7,232.8Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M408.7,223.3C411.1,223.3 413.2,225.3 413.2,227.8C413.2,229 412.7,230.1 411.8,231C411,231.8 409.9,232.3 408.7,232.3C406.2,232.3 404.2,230.3 404.2,227.8C404.2,225.3 406.2,223.3 408.7,223.3ZM408.7,222.3C405.6,222.3 403.2,224.8 403.2,227.8C403.2,230.8 405.6,233.3 408.7,233.3C411.7,233.3 414.2,230.8 414.2,227.8C414.2,224.8 411.7,222.3 408.7,222.3Z\"\n            />\n        <path\n            android:fillColor=\"#ffffff\"\n            android:fillType=\"evenOdd\"\n            android:pathData=\"M283.3,431.8C280.5,431.8 278.3,429.5 278.3,426.8C278.3,425.4 278.8,424.2 279.7,423.2C280.7,422.3 281.9,421.8 283.3,421.8C286,421.8 288.3,424 288.3,426.8C288.3,429.5 286,431.8 283.3,431.8Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M283.3,422.3C285.7,422.3 287.8,424.3 287.8,426.8C287.8,429.2 285.7,431.3 283.3,431.3C280.8,431.3 278.8,429.2 278.8,426.8C278.8,424.3 280.8,422.3 283.3,422.3ZM283.3,421.3C280.2,421.3 277.8,423.7 277.8,426.8C277.8,429.8 280.2,432.3 283.3,432.3C286.3,432.3 288.8,429.8 288.8,426.8C288.8,423.7 286.3,421.3 283.3,421.3Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M396.5,452C399,452 401,454 401,456.5C401,459 399,461 396.5,461C394,461 392,459 392,456.5C392,454 394,452 396.5,452ZM396.5,451C393.5,451 391,453.5 391,456.5C391,459.5 393.5,462 396.5,462C399.5,462 402,459.5 402,456.5C402,453.5 399.5,451 396.5,451Z\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M253.3,390.1L186.2,428.8C179.4,432.7 171,432.7 164.2,428.8L112.8,399.1\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#425066\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#ffffff\"\n            android:pathData=\"M112.7,404.3C115.5,404.3 117.7,402.1 117.7,399.3C117.7,396.5 115.5,394.3 112.7,394.3C109.9,394.3 107.7,396.5 107.7,399.3C107.7,402.1 109.9,404.3 112.7,404.3Z\"\n            />\n        <path\n            android:fillColor=\"#425066\"\n            android:pathData=\"M112.7,394.8C115.2,394.8 117.2,396.8 117.2,399.3C117.2,401.8 115.2,403.8 112.7,403.8C110.2,403.8 108.2,401.8 108.2,399.3C108.2,396.8 110.2,394.8 112.7,394.8ZM112.7,393.8C109.7,393.8 107.2,396.3 107.2,399.3C107.2,402.4 109.7,404.8 112.7,404.8C115.7,404.8 118.2,402.4 118.2,399.3C118.2,396.3 115.7,393.8 112.7,393.8Z\"\n            />\n        <path\n            android:fillColor=\"#425066\"\n            android:pathData=\"M476.5,39C479,39 481,41 481,43.5C481,46 479,48 476.5,48C474,48 472,46 472,43.5C472,41 474,39 476.5,39ZM476.5,38C473.5,38 471,40.5 471,43.5C471,46.5 473.5,49 476.5,49C479.5,49 482,46.5 482,43.5C482,40.5 479.5,38 476.5,38Z\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M1161.7,389.5L1117.2,415.1C1106.1,421.6 1092.3,421.6 1081.2,415.1L1065.6,406.1C1047.7,395.8 1025.8,395.8 1007.9,406.1L818.2,515.7\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#FF6F00\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M1098.9,552.5L1041.3,518.1C1028.6,510.5 1012.8,510.5 1000.1,518.1L942.5,552.5\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#425066\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M1083.6,470.6L960.5,398.6C949.8,392.4 936.5,392.3 925.7,398.5L882.8,424C874.8,428.7 874.9,440.2 882.9,444.9L1036.3,533.3\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#E6E6E6\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M1114.8,380.4L935.3,276.6C921.3,268.5 904.1,268.5 890,276.4L804,325.2C793.3,331.2 780.3,331.2 769.6,325.1L689.5,278.8C676.8,271.5 676.8,253.1 689.6,245.8L973.1,83.1\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#425066\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:fillType=\"evenOdd\"\n            android:pathData=\"M1162.5,383.9C1165.2,383.9 1167.5,386.1 1167.5,388.9C1167.5,390.2 1167,391.5 1166,392.4C1165.1,393.4 1163.8,393.9 1162.5,393.9C1159.7,393.9 1157.5,391.6 1157.5,388.9C1157.5,386.1 1159.7,383.9 1162.5,383.9Z\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:pathData=\"M1162.5,393.4C1160,393.4 1158,391.4 1158,388.9C1158,387.7 1158.4,386.6 1159.3,385.7C1160.2,384.9 1161.3,384.4 1162.5,384.4C1165,384.4 1167,386.4 1167,388.9C1167,391.4 1165,393.4 1162.5,393.4ZM1162.5,394.4C1165.5,394.4 1168,391.9 1168,388.9C1168,385.9 1165.5,383.4 1162.5,383.4C1159.4,383.4 1157,385.9 1157,388.9C1157,391.9 1159.4,394.4 1162.5,394.4Z\"\n            />\n        <path\n            android:fillColor=\"#ffffff\"\n            android:fillType=\"evenOdd\"\n            android:pathData=\"M1213.5,138.5C1216.3,138.5 1218.5,140.7 1218.5,143.5C1218.5,146.3 1216.3,148.5 1213.5,148.5C1210.7,148.5 1208.5,146.3 1208.5,143.5C1208.5,140.7 1210.7,138.5 1213.5,138.5Z\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:pathData=\"M1213.5,148C1211,148 1209,146 1209,143.5C1209,142.3 1209.5,141.2 1210.3,140.3C1211.2,139.5 1212.3,139 1213.5,139C1216,139 1218,141 1218,143.5C1218,144.7 1217.5,145.8 1216.7,146.7C1215.8,147.5 1214.7,148 1213.5,148ZM1213.5,149C1216.5,149 1219,146.5 1219,143.5C1219,140.5 1216.5,138 1213.5,138C1210.5,138 1208,140.5 1208,143.5C1208,146.5 1210.5,149 1213.5,149Z\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M663.5,374.5C663.5,377.3 665.7,379.5 668.5,379.5C665.7,379.5 663.5,377.3 663.5,374.5ZM665,371C664,371.9 663.5,373.1 663.5,374.4C663.6,371.7 665.7,369.5 668.4,369.5C667.1,369.5 665.9,370 665,371ZM673.5,374.5C673.5,371.7 671.3,369.5 668.5,369.5C671.3,369.5 673.5,371.7 673.5,374.5ZM672,378C673,377.1 673.5,375.9 673.5,374.6C673.4,377.3 671.3,379.5 668.6,379.5C669.9,379.5 671.1,379 672,378Z\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#FF6F00\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:pathData=\"M1053.6,123.9C1050.8,123.9 1048.6,126.1 1048.6,128.9C1048.6,131.7 1050.8,133.9 1053.6,133.9C1056.3,133.9 1058.6,131.7 1058.6,128.9C1058.6,126.1 1056.3,123.9 1053.6,123.9Z\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:pathData=\"M1053.6,133.4C1051.1,133.4 1049.1,131.4 1049.1,128.9C1049.1,126.4 1051.1,124.4 1053.6,124.4C1056,124.4 1058.1,126.4 1058.1,128.9C1058.1,131.4 1056,133.4 1053.6,133.4ZM1053.6,134.4C1056.6,134.4 1059.1,131.9 1059.1,128.9C1059.1,125.8 1056.6,123.4 1053.6,123.4C1050.5,123.4 1048.1,125.8 1048.1,128.9C1048.1,131.9 1050.5,134.4 1053.6,134.4Z\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:pathData=\"M508.5,355.4C505.7,355.4 503.5,357.6 503.5,360.4C503.5,363.2 505.7,365.4 508.5,365.4C511.3,365.4 513.5,363.2 513.5,360.4C513.5,357.6 511.3,355.4 508.5,355.4Z\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:pathData=\"M508.5,364.9C506,364.9 504,362.9 504,360.4C504,357.9 506,355.9 508.5,355.9C511,355.9 513,357.9 513,360.4C513,362.9 511,364.9 508.5,364.9ZM508.5,365.9C511.5,365.9 514,363.4 514,360.4C514,357.4 511.5,354.9 508.5,354.9C505.5,354.9 503,357.4 503,360.4C503,363.4 505.5,365.9 508.5,365.9Z\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:pathData=\"M813.7,522.5C811.2,522.5 809.2,520.5 809.2,518C809.2,515.5 811.2,513.5 813.7,513.5C816.2,513.5 818.2,515.5 818.2,518C818.2,520.5 816.2,522.5 813.7,522.5ZM813.7,523.5C816.7,523.5 819.2,521 819.2,518C819.2,514.9 816.7,512.5 813.7,512.5C810.6,512.5 808.2,514.9 808.2,518C808.2,521 810.6,523.5 813.7,523.5Z\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M1168.1,290.1L1218.6,261C1225.7,256.8 1225.7,250.1 1218.5,246L1203.8,237.5C1196.6,233.4 1196.5,226.7 1203.7,222.6L1252.4,194.4\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#425066\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M1158.9,492L1223.3,454.8C1237.6,446.5 1260.8,446.6 1275.1,454.9L1324.3,483.3\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#CCCCCC\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M1210,241.1L1161.1,212.9\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#425066\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#ffffff\"\n            android:pathData=\"M1159.8,207.4C1157.1,207.4 1154.8,209.6 1154.8,212.4C1154.8,215.1 1157.1,217.4 1159.8,217.4C1162.6,217.4 1164.8,215.1 1164.8,212.4C1164.8,209.6 1162.6,207.4 1159.8,207.4Z\"\n            />\n        <path\n            android:fillColor=\"#425066\"\n            android:pathData=\"M1159.8,216.9C1157.4,216.9 1155.3,214.9 1155.3,212.4C1155.3,209.9 1157.4,207.9 1159.8,207.9C1162.3,207.9 1164.3,209.9 1164.3,212.4C1164.3,213.6 1163.9,214.7 1163,215.6C1162.2,216.4 1161.1,216.9 1159.8,216.9ZM1159.8,217.9C1162.9,217.9 1165.3,215.4 1165.3,212.4C1165.3,209.3 1162.9,206.9 1159.8,206.9C1156.8,206.9 1154.3,209.3 1154.3,212.4C1154.3,215.4 1156.8,217.9 1159.8,217.9Z\"\n            />\n        <path\n            android:fillColor=\"#425066\"\n            android:pathData=\"M1171.2,284.1C1168.5,284.1 1166.2,286.3 1166.2,289.1C1166.2,291.9 1168.5,294.1 1171.2,294.1C1174,294.1 1176.2,291.9 1176.2,289.1C1176.2,286.3 1174,284.1 1171.2,284.1Z\"\n            />\n        <path\n            android:fillColor=\"#425066\"\n            android:pathData=\"M1171.2,293.6C1168.8,293.6 1166.7,291.6 1166.7,289.1C1166.7,287.9 1167.2,286.8 1168.1,285.9C1168.9,285.1 1170,284.6 1171.2,284.6C1173.7,284.6 1175.7,286.6 1175.7,289.1C1175.7,291.6 1173.7,293.6 1171.2,293.6ZM1171.2,294.6C1174.3,294.6 1176.7,292.1 1176.7,289.1C1176.7,286 1174.3,283.6 1171.2,283.6C1168.2,283.6 1165.7,286 1165.7,289.1C1165.7,292.1 1168.2,294.6 1171.2,294.6Z\"\n            />\n        <path\n            android:fillColor=\"#425066\"\n            android:pathData=\"M974.5,77.5C971.8,77.5 969.5,79.7 969.5,82.5C969.5,85.3 971.8,87.5 974.5,87.5C977.3,87.5 979.5,85.3 979.5,82.5C979.5,79.7 977.3,77.5 974.5,77.5Z\"\n            />\n        <path\n            android:fillColor=\"#425066\"\n            android:pathData=\"M974.5,87C972,87 970,85 970,82.5C970,81.3 970.5,80.2 971.3,79.3C972.2,78.5 973.3,78 974.5,78C977,78 979,80 979,82.5C979,85 977,87 974.5,87ZM974.5,88C977.6,88 980,85.5 980,82.5C980,79.5 977.6,77 974.5,77C971.5,77 969,79.5 969,82.5C969,85.5 971.5,88 974.5,88Z\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M1294.6,329.2L1305.8,322.8C1312.9,318.6 1312.9,311.9 1305.7,307.8L1266.9,285.4C1259.8,281.3 1248.2,281.3 1241,285.4L1147.3,339.5C1140.2,343.6 1140.2,350.3 1147.4,354.5L1259.2,419C1266.4,423.2 1266.4,429.9 1259.3,434L1202.7,466.7\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#CCCCCC\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#CCCCCC\"\n            android:pathData=\"M1006.2,349C1003.4,349 1001.2,351.2 1001.2,354C1001.2,356.7 1003.4,359 1006.2,359C1008.9,359 1011.2,356.7 1011.2,354C1011.2,351.2 1008.9,349 1006.2,349Z\"\n            />\n        <path\n            android:fillColor=\"#CCCCCC\"\n            android:pathData=\"M1006.2,358.5C1003.7,358.5 1001.7,356.5 1001.7,354C1001.7,351.5 1003.7,349.5 1006.2,349.5C1008.7,349.5 1010.7,351.5 1010.7,354C1010.7,356.5 1008.7,358.5 1006.2,358.5ZM1006.2,359.5C1009.2,359.5 1011.7,357 1011.7,354C1011.7,350.9 1009.2,348.5 1006.2,348.5C1003.1,348.5 1000.7,350.9 1000.7,354C1000.7,357 1003.1,359.5 1006.2,359.5Z\"\n            />\n        <path\n            android:fillColor=\"#CCCCCC\"\n            android:pathData=\"M723,511.3C720.3,511.3 718,513.6 718,516.3C718,519.1 720.3,521.3 723,521.3C725.8,521.3 728,519.1 728,516.3C728,513.6 725.8,511.3 723,511.3Z\"\n            />\n        <path\n            android:fillColor=\"#CCCCCC\"\n            android:pathData=\"M723,520.8C720.5,520.8 718.5,518.8 718.5,516.3C718.5,513.8 720.5,511.8 723,511.8C725.5,511.8 727.5,513.8 727.5,516.3C727.5,518.8 725.5,520.8 723,520.8ZM723,521.8C726.1,521.8 728.5,519.4 728.5,516.3C728.5,513.3 726.1,510.8 723,510.8C720,510.8 717.5,513.3 717.5,516.3C717.5,519.4 720,521.8 723,521.8Z\"\n            />\n        <path\n            android:fillColor=\"#ffffff\"\n            android:pathData=\"M828.5,118.5C825.7,118.5 823.5,120.7 823.5,123.5C823.5,126.3 825.7,128.5 828.5,128.5C831.3,128.5 833.5,126.3 833.5,123.5C833.5,120.7 831.3,118.5 828.5,118.5Z\"\n            />\n        <path\n            android:fillColor=\"#CCCCCC\"\n            android:pathData=\"M828.5,128C826,128 824,126 824,123.5C824,121 826,119 828.5,119C831,119 833,121 833,123.5C833,126 831,128 828.5,128ZM828.5,129C831.5,129 834,126.5 834,123.5C834,120.5 831.5,118 828.5,118C825.5,118 823,120.5 823,123.5C823,126.5 825.5,129 828.5,129Z\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M814.4,404.4C812.2,406.5 812.2,410 814.4,412.2C816.5,414.3 820,414.3 822.1,412.2C824.3,410 824.3,406.5 822.1,404.4C820,402.3 816.5,402.3 814.4,404.4Z\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#E6E6E6\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M942.8,204.7C940.1,204.7 937.8,206.9 937.8,209.7C937.8,212.4 940.1,214.7 942.8,214.7C945.6,214.7 947.8,212.4 947.8,209.7C947.8,206.9 945.6,204.7 942.8,204.7Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M942.8,214.2C940.4,214.2 938.3,212.2 938.3,209.7C938.3,208.5 938.8,207.4 939.7,206.5C940.5,205.6 941.6,205.2 942.8,205.2C945.3,205.2 947.3,207.2 947.3,209.7C947.3,212.2 945.3,214.2 942.8,214.2ZM942.8,215.2C945.9,215.2 948.3,212.7 948.3,209.7C948.3,206.6 945.9,204.2 942.8,204.2C939.8,204.2 937.3,206.6 937.3,209.7C937.3,212.7 939.8,215.2 942.8,215.2Z\"\n            />\n        <path\n            android:fillColor=\"#ffffff\"\n            android:pathData=\"M1157.3,488.5C1154.5,488.5 1152.3,490.7 1152.3,493.5C1152.3,496.3 1154.5,498.5 1157.3,498.5C1160.1,498.5 1162.3,496.3 1162.3,493.5C1162.3,490.7 1160.1,488.5 1157.3,488.5Z\"\n            />\n        <path\n            android:fillColor=\"#CCCCCC\"\n            android:pathData=\"M1157.3,498C1154.8,498 1152.8,496 1152.8,493.5C1152.8,491 1154.8,489 1157.3,489C1159.8,489 1161.8,491 1161.8,493.5C1161.8,494.7 1161.3,495.8 1160.5,496.7C1159.6,497.5 1158.5,498 1157.3,498ZM1157.3,499C1160.3,499 1162.8,496.5 1162.8,493.5C1162.8,490.5 1160.3,488 1157.3,488C1154.3,488 1151.8,490.5 1151.8,493.5C1151.8,496.5 1154.3,499 1157.3,499Z\"\n            />\n        <path\n            android:fillColor=\"#425066\"\n            android:pathData=\"M1099,546.8C1096.2,546.8 1094,549 1094,551.8C1094,554.5 1096.2,556.8 1099,556.8C1101.8,556.8 1104,554.5 1104,551.8C1104,549 1101.8,546.8 1099,546.8Z\"\n            />\n        <path\n            android:fillColor=\"#425066\"\n            android:pathData=\"M1099,556.3C1096.5,556.3 1094.5,554.2 1094.5,551.8C1094.5,550.5 1095,549.4 1095.8,548.6C1096.7,547.7 1097.8,547.3 1099,547.3C1101.5,547.3 1103.5,549.3 1103.5,551.8C1103.5,554.2 1101.5,556.3 1099,556.3ZM1099,557.3C1102,557.3 1104.5,554.8 1104.5,551.8C1104.5,548.7 1102,546.3 1099,546.3C1096,546.3 1093.5,548.7 1093.5,551.8C1093.5,554.8 1096,557.3 1099,557.3Z\"\n            />\n        <path\n            android:fillColor=\"#425066\"\n            android:pathData=\"M938.1,560.3C935.6,560.3 933.6,558.2 933.6,555.8C933.6,553.3 935.6,551.3 938.1,551.3C940.5,551.3 942.6,553.3 942.6,555.8C942.6,558.2 940.5,560.3 938.1,560.3ZM938.1,561.3C941.1,561.3 943.6,558.8 943.6,555.8C943.6,552.7 941.1,550.3 938.1,550.3C935,550.3 932.6,552.7 932.6,555.8C932.6,558.8 935,561.3 938.1,561.3Z\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M1178.9,554.3L1147.4,536.1C1140.2,532 1140.2,525.3 1147.3,521.2L1189.7,496.7\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#E6E6E6\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:fillType=\"evenOdd\"\n            android:pathData=\"M1190.3,491.3C1193.1,491.3 1195.3,493.5 1195.3,496.3C1195.3,499 1193.1,501.3 1190.3,501.3C1187.6,501.3 1185.3,499 1185.3,496.3C1185.3,493.5 1187.6,491.3 1190.3,491.3Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M1190.3,500.8C1187.8,500.8 1185.8,498.8 1185.8,496.3C1185.8,495.1 1186.3,494 1187.2,493.1C1188,492.3 1189.1,491.8 1190.3,491.8C1192.8,491.8 1194.8,493.8 1194.8,496.3C1194.8,498.8 1192.8,500.8 1190.3,500.8ZM1190.3,501.8C1193.4,501.8 1195.8,499.3 1195.8,496.3C1195.8,493.3 1193.4,490.8 1190.3,490.8C1187.3,490.8 1184.8,493.3 1184.8,496.3C1184.8,499.3 1187.3,501.8 1190.3,501.8Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M1183.6,561.5C1181.2,561.5 1179.1,559.5 1179.1,557C1179.1,554.6 1181.2,552.5 1183.6,552.5C1186.1,552.5 1188.1,554.6 1188.1,557C1188.1,558.3 1187.7,559.4 1186.8,560.2C1186,561.1 1184.8,561.5 1183.6,561.5ZM1183.6,562.5C1186.7,562.5 1189.1,560.1 1189.1,557C1189.1,554 1186.7,551.5 1183.6,551.5C1180.6,551.5 1178.1,554 1178.1,557C1178.1,560.1 1180.6,562.5 1183.6,562.5Z\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M1329,502L1296.2,483.1C1289,479 1277.4,479 1270.3,483.1L1269.4,483.6C1262.3,487.7 1250.8,487.6 1243.6,483.5L1187,451\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#FF6F00\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#ffffff\"\n            android:pathData=\"M1187.2,445.8C1184.4,445.8 1182.2,448.1 1182.2,450.8C1182.2,453.6 1184.4,455.8 1187.2,455.8C1190,455.8 1192.2,453.6 1192.2,450.8C1192.2,448.1 1190,445.8 1187.2,445.8Z\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:pathData=\"M1187.2,455.3C1184.7,455.3 1182.7,453.3 1182.7,450.8C1182.7,448.3 1184.7,446.3 1187.2,446.3C1189.7,446.3 1191.7,448.3 1191.7,450.8C1191.7,452 1191.2,453.1 1190.4,454C1189.5,454.9 1188.4,455.3 1187.2,455.3ZM1187.2,456.3C1190.2,456.3 1192.7,453.9 1192.7,450.8C1192.7,447.8 1190.2,445.3 1187.2,445.3C1184.2,445.3 1181.7,447.8 1181.7,450.8C1181.7,453.9 1184.2,456.3 1187.2,456.3Z\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M1208.9,146L1158.5,175C1152.1,178.7 1144.2,178.7 1137.7,175.1L1115.4,162.6C1108.9,159 1108.9,149.7 1115.3,146L1136.3,133.9C1141,131.2 1141.1,124.4 1136.5,121.5L1110.4,105.3C1104.4,101.6 1097,101.6 1091,105.2L1054.6,127.2\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#FF6F00\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M663.8,377.5L613.5,406.5C607,410.2 599.1,410.2 592.7,406.6L570.4,394.1C563.9,390.5 563.8,381.2 570.3,377.5L591.3,365.4C596,362.7 596.1,355.9 591.5,353L565.3,336.8C559.4,333.1 551.9,333.1 545.9,336.7L509.5,358.7\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#FF6F00\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M823,404.5L895.8,362.7C902.9,358.5 902.9,348.2 895.8,344.1L823.9,302.3C814.4,296.8 814.4,283.1 823.9,277.6L942.1,209.3\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#E6E6E6\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#ffffff\"\n            android:fillType=\"evenOdd\"\n            android:pathData=\"M1040.4,532.1C1042.6,532.1 1044.5,533.5 1045.2,535.6C1046,538.3 1044.5,541 1041.8,541.8C1041.3,542 1040.9,542.1 1040.4,542.1C1038.2,542.1 1036.2,540.7 1035.6,538.5C1035.2,537.3 1035.3,535.9 1036,534.7C1036.6,533.5 1037.6,532.7 1038.9,532.3C1039.4,532.1 1039.9,532.1 1040.4,532.1Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M1040.4,541.6C1038.4,541.6 1036.7,540.3 1036.1,538.4C1035.7,537.2 1035.8,536 1036.4,535C1037,533.9 1037.9,533.1 1039.1,532.8C1039.5,532.6 1039.9,532.6 1040.4,532.6C1042.4,532.6 1044.1,533.8 1044.7,535.8C1045,536.9 1044.9,538.1 1044.3,539.2C1043.8,540.2 1042.8,541 1041.7,541.4C1041.3,541.5 1040.8,541.6 1040.4,541.6ZM1040.4,542.6C1040.9,542.6 1041.4,542.5 1042,542.3C1044.9,541.4 1046.5,538.4 1045.6,535.5C1044.9,533.1 1042.7,531.6 1040.4,531.6C1039.8,531.6 1039.3,531.7 1038.8,531.8C1035.9,532.7 1034.2,535.8 1035.1,538.7C1035.8,541 1038,542.6 1040.4,542.6Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:fillType=\"evenOdd\"\n            android:pathData=\"M1087.1,469.1C1089.3,469.1 1091.2,470.5 1091.9,472.6C1092.3,473.9 1092.1,475.3 1091.5,476.5C1090.9,477.6 1089.8,478.5 1088.6,478.9C1088.1,479 1087.6,479.1 1087.1,479.1C1084.9,479.1 1083,477.7 1082.3,475.6C1081.5,472.9 1083,470.1 1085.6,469.3C1086.1,469.2 1086.6,469.1 1087.1,469.1Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M1087.1,478.6C1085.1,478.6 1083.4,477.3 1082.8,475.4C1082.4,474.3 1082.6,473 1083.1,472C1083.7,470.9 1084.6,470.1 1085.8,469.8C1086.2,469.7 1086.7,469.6 1087.1,469.6C1089.1,469.6 1090.8,470.9 1091.4,472.8C1092.1,475.2 1090.8,477.7 1088.4,478.4C1088,478.5 1087.5,478.6 1087.1,478.6ZM1087.1,479.6C1087.6,479.6 1088.2,479.5 1088.7,479.4C1091.6,478.5 1093.2,475.4 1092.3,472.5C1091.6,470.1 1089.4,468.6 1087.1,468.6C1086.6,468.6 1086,468.7 1085.5,468.9C1082.6,469.7 1080.9,472.8 1081.8,475.7C1082.6,478.1 1084.7,479.6 1087.1,479.6Z\"\n            />\n        <path\n            android:fillColor=\"#ffffff\"\n            android:pathData=\"M1118.7,378.8C1115.9,378.8 1113.7,381 1113.7,383.8C1113.7,386.5 1115.9,388.8 1118.7,388.8C1121.4,388.8 1123.7,386.5 1123.7,383.8C1123.7,381 1121.4,378.8 1118.7,378.8Z\"\n            />\n        <path\n            android:fillColor=\"#425066\"\n            android:pathData=\"M1118.7,388.3C1116.2,388.3 1114.2,386.2 1114.2,383.8C1114.2,382.5 1114.6,381.4 1115.5,380.6C1116.3,379.7 1117.4,379.3 1118.7,379.3C1121.1,379.3 1123.2,381.3 1123.2,383.8C1123.2,386.2 1121.1,388.3 1118.7,388.3ZM1118.7,389.3C1121.7,389.3 1124.2,386.8 1124.2,383.8C1124.2,380.7 1121.7,378.3 1118.7,378.3C1115.6,378.3 1113.2,380.7 1113.2,383.8C1113.2,386.8 1115.6,389.3 1118.7,389.3Z\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M1006.2,354L1097,300.4C1105.3,295.5 1105.2,283.5 1096.9,278.8L833,126.5\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#CCCCCC\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M724.5,515.7L783.9,481.2C792.4,476.3 792.4,464 783.8,459.1L725.1,426\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#CCCCCC\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#ffffff\"\n            android:pathData=\"M723.4,419.8C720.7,419.8 718.4,422 718.4,424.8C718.4,427.5 720.7,429.8 723.4,429.8C726.2,429.8 728.4,427.5 728.4,424.8C728.4,422 726.2,419.8 723.4,419.8Z\"\n            />\n        <path\n            android:fillColor=\"#CCCCCC\"\n            android:pathData=\"M723.4,429.3C721,429.3 718.9,427.3 718.9,424.8C718.9,422.3 721,420.3 723.4,420.3C725.9,420.3 727.9,422.3 727.9,424.8C727.9,426 727.5,427.1 726.6,428C725.8,428.8 724.6,429.3 723.4,429.3ZM723.4,430.3C726.5,430.3 728.9,427.8 728.9,424.8C728.9,421.7 726.5,419.3 723.4,419.3C720.4,419.3 717.9,421.7 717.9,424.8C717.9,427.8 720.4,430.3 723.4,430.3Z\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M695.9,335L764.9,293.8C774.7,287.9 774.6,273.6 764.7,267.9L695.2,227.7C687.4,223.2 687.5,211.9 695.3,207.6L720.3,193.6C729.2,188.6 729.3,175.8 720.4,170.7L693.8,155.7\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#FF6F00\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:fillType=\"evenOdd\"\n            android:pathData=\"M693.7,330.9C695,330.9 696.3,331.4 697.2,332.3C698.2,333.3 698.7,334.5 698.7,335.9C698.7,337.2 698.2,338.5 697.2,339.4C696.3,340.3 695,340.9 693.7,340.9C692.3,340.9 691.1,340.3 690.1,339.4C688.2,337.5 688.2,334.3 690.1,332.3C691.1,331.4 692.3,330.9 693.7,330.9V330.9Z\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:pathData=\"M693.7,340.4C692.5,340.4 691.3,339.9 690.5,339C688.7,337.3 688.7,334.4 690.5,332.7C691.3,331.8 692.5,331.4 693.7,331.4C694.9,331.4 696,331.8 696.9,332.7C698.6,334.4 698.6,337.3 696.9,339C696,339.9 694.9,340.4 693.7,340.4ZM693.7,341.4C695.1,341.4 696.5,340.8 697.6,339.8C699.7,337.6 699.7,334.1 697.6,332C696.5,330.9 695.1,330.4 693.7,330.4C692.3,330.4 690.9,330.9 689.8,332C687.6,334.1 687.6,337.6 689.8,339.8C690.9,340.8 692.3,341.4 693.7,341.4Z\"\n            />\n        <path\n            android:fillColor=\"#ffffff\"\n            android:fillType=\"evenOdd\"\n            android:pathData=\"M693.9,151C695.2,151 696.5,151.5 697.4,152.4C699.4,154.4 699.4,157.5 697.4,159.5C696.5,160.4 695.2,160.9 693.9,160.9C692.6,160.9 691.3,160.4 690.4,159.5C688.4,157.5 688.4,154.4 690.4,152.4C691.3,151.5 692.6,151 693.9,151Z\"\n            />\n        <path\n            android:fillColor=\"#FF6F00\"\n            android:pathData=\"M693.9,160.5C692.7,160.5 691.6,160 690.7,159.1C689.9,158.3 689.4,157.2 689.4,156C689.4,154.8 689.9,153.6 690.7,152.8C691.6,151.9 692.7,151.5 693.9,151.5C695.1,151.5 696.2,151.9 697.1,152.8C697.9,153.6 698.4,154.8 698.4,156C698.4,157.2 697.9,158.3 697.1,159.1C696.2,160 695.1,160.5 693.9,160.5ZM693.9,161.5C695.3,161.5 696.7,160.9 697.8,159.9C699.9,157.7 699.9,154.2 697.8,152.1C696.7,151 695.3,150.5 693.9,150.5C692.5,150.5 691.1,151 690,152.1C687.9,154.2 687.9,157.7 690,159.9C691.1,160.9 692.5,161.5 693.9,161.5Z\"\n            />\n        <path\n            android:fillColor=\"#00000000\"\n            android:pathData=\"M1068,209.2L976.4,156.3C968.5,151.8 968.5,140.4 976.4,135.8L989.5,128.3C996.6,124.2 996.6,113.9 989.5,109.8L940.1,81.3C932.3,76.7 922.6,76.7 914.8,81.3L896.2,92\"\n            android:strokeWidth=\"1\"\n            android:strokeColor=\"#E6E6E6\"\n            android:strokeLineCap=\"round\"\n            android:strokeLineJoin=\"round\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:fillType=\"evenOdd\"\n            android:pathData=\"M1069.3,205.1C1072.1,205.1 1074.3,207.4 1074.3,210.1C1074.3,212.9 1072.1,215.1 1069.3,215.1C1066.6,215.1 1064.3,212.9 1064.3,210.1C1064.3,208.8 1064.8,207.6 1065.8,206.6C1066.7,205.7 1068,205.1 1069.3,205.1Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M1069.3,214.6C1066.8,214.6 1064.8,212.6 1064.8,210.1C1064.8,207.7 1066.8,205.6 1069.3,205.6C1071.8,205.6 1073.8,207.7 1073.8,210.1C1073.8,212.6 1071.8,214.6 1069.3,214.6ZM1069.3,215.6C1072.4,215.6 1074.8,213.2 1074.8,210.1C1074.8,207.1 1072.4,204.6 1069.3,204.6C1066.3,204.6 1063.8,207.1 1063.8,210.1C1063.8,213.2 1066.3,215.6 1069.3,215.6Z\"\n            />\n        <path\n            android:fillColor=\"#ffffff\"\n            android:fillType=\"evenOdd\"\n            android:pathData=\"M895.1,88.2C897.9,88.2 900.1,90.4 900.1,93.2C900.1,95.9 897.9,98.2 895.1,98.2C892.4,98.2 890.1,95.9 890.1,93.2C890.1,91.8 890.6,90.6 891.6,89.6C892.5,88.7 893.8,88.2 895.1,88.2Z\"\n            />\n        <path\n            android:fillColor=\"#E6E6E6\"\n            android:pathData=\"M895.1,97.7C892.6,97.7 890.6,95.7 890.6,93.2C890.6,90.7 892.6,88.7 895.1,88.7C897.6,88.7 899.6,90.7 899.6,93.2C899.6,95.7 897.6,97.7 895.1,97.7ZM895.1,98.7C898.2,98.7 900.6,96.2 900.6,93.2C900.6,90.1 898.2,87.7 895.1,87.7C892.1,87.7 889.6,90.1 889.6,93.2C889.6,96.2 892.1,98.7 895.1,98.7Z\"\n            />\n    </group>\n</vector>\n"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"1023\"\n    android:viewportHeight=\"1025\">\n  <group android:scaleX=\"0.9980488\"\n      android:translateX=\"0.9980488\">\n    <path\n        android:pathData=\"M0,0h1023v1025h-1023z\">\n      <aapt:attr name=\"android:fillColor\">\n        <gradient \n            android:startX=\"511.5\"\n            android:startY=\"0\"\n            android:endX=\"511.5\"\n            android:endY=\"1025\"\n            android:type=\"linear\">\n          <item android:offset=\"0\" android:color=\"#FFFFA800\"/>\n          <item android:offset=\"1\" android:color=\"#FFFF6F00\"/>\n        </gradient>\n      </aapt:attr>\n    </path>\n  </group>\n</vector>\n"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/res/drawable/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"784\"\n    android:viewportHeight=\"915\">\n  <group android:scaleX=\"0.5455491\"\n      android:scaleY=\"0.6367059\"\n      android:translateX=\"192.0108\"\n      android:translateY=\"179.69284\">\n    <path\n        android:pathData=\"M99.08,626.38L307.3,770.2C337.02,790.73 381.41,772.27 387.75,736.78L432.26,488.35C437.24,460.5 472.39,446.43 495.16,463.17L567.59,516.42C593.46,535.46 633.47,519.24 638.85,487.53L654.81,393.08\"\n        android:strokeLineJoin=\"round\"\n        android:strokeWidth=\"27\"\n        android:fillColor=\"#00000000\"\n        android:strokeColor=\"#425066\"\n        android:strokeLineCap=\"round\"/>\n    <path\n        android:strokeWidth=\"1\"\n        android:pathData=\"M63.63,584.82L64.07,584.58L64.04,584.53C71.28,580.8 79.59,580.03 87.45,582.37C95.42,584.78 102.01,590.2 105.97,597.6C109.93,605.01 110.75,613.44 108.37,621.47C105.96,629.43 100.55,636.03 93.15,639.99C85.75,643.94 77.32,644.77 69.29,642.38C52.77,637.37 43.4,619.86 48.4,603.33C50.81,595.37 56.23,588.77 63.63,584.82Z\"\n        android:fillColor=\"#425066\"\n        android:strokeColor=\"#425066\"/>\n    <path\n        android:strokeWidth=\"1\"\n        android:pathData=\"M69.94,640.24C77.38,642.49 85.27,641.72 92.12,638.06C98.97,634.4 103.98,628.26 106.23,620.82C110.87,605.48 102.14,589.16 86.8,584.51C79.36,582.25 71.47,583.02 64.63,586.68C57.78,590.34 52.76,596.48 50.51,603.93C45.87,619.26 54.61,635.59 69.94,640.24ZM111.39,622.38C108.64,631.46 102.45,638.62 94.65,642.79C86.86,646.96 77.46,648.15 68.38,645.4C50.13,639.86 39.83,620.62 45.35,602.37C48.1,593.28 54.3,586.12 62.1,581.95C69.89,577.78 79.28,576.6 88.36,579.35C106.61,584.88 116.91,604.13 111.39,622.38Z\"\n        android:fillColor=\"#425066\"\n        android:strokeColor=\"#425066\"/>\n    <path\n        android:pathData=\"M657.37,388.99C649.83,387.75 643.31,383.68 638.82,377.48C629.69,364.68 632.65,346.83 645.42,337.68C651.57,333.27 659.12,331.51 666.68,332.76C674.21,334.01 680.73,338.07 685.22,344.28C694.35,357.07 691.4,374.92 678.63,384.08C672.47,388.49 664.93,390.24 657.37,388.99Z\"\n        android:strokeWidth=\"13\"\n        android:fillColor=\"#ffffff\"\n        android:strokeColor=\"#425066\"/>\n    <path\n        android:pathData=\"M549.59,97.98L586.32,134.82C607.82,156.11 607.82,190.77 586.32,212.06L226.36,573.13H112.63V459.05L395.59,174.94L472.59,97.98C493.81,76.69 528.37,76.69 549.59,97.98ZM167.04,518.54L205.4,520.18L472.59,251.9L434.23,213.42L167.04,481.42V518.54Z\"\n        android:fillType=\"evenOdd\">\n      <aapt:attr name=\"android:fillColor\">\n        <gradient \n            android:startX=\"357.53\"\n            android:startY=\"82.01\"\n            android:endX=\"357.53\"\n            android:endY=\"573.13\"\n            android:type=\"linear\">\n          <item android:offset=\"0\" android:color=\"#FFFFA800\"/>\n          <item android:offset=\"1\" android:color=\"#FFFF6F00\"/>\n        </gradient>\n      </aapt:attr>\n    </path>\n  </group>\n</vector>\n"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/res/values/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"ic_launcher_background\">#FFFFFF</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">Keras to TFL</string>\n\n    <string name=\"header_background_desc\">Header background</string>\n    <string name=\"header_autocomplete\">Autocomplete</string>\n\n    <string name=\"text_copied\">Text copied</string>\n    <string name=\"input_hint\">Tap to begin typing your story…</string>\n    <string name=\"clear_cta\">Clear</string>\n    <string name=\"generate_cta\">Generate</string>\n    <string name=\"copy_cta\">Copy</string>\n    <string name=\"reject_suggestion_cta\">New suggestion</string>\n    <string name=\"accept_suggestion_cta\">Accept</string>\n\n    <string name=\"window_size_slider_label\">Apply context window</string>\n    <string name=\"window_size_wordcount\">{count} words</string>\n\n    <string name=\"about_title\">Powered by KerasNLP and TensorFlow Lite</string>\n\n    <!--  Errors  -->\n    <string name=\"error_model_not_initialized\">Model not initialized</string>\n    <string name=\"error_no_suggestion_found\">No suggestions found</string>\n    <string name=\"error_model_not_found\">The autocomplete.tflite model is missing</string>\n    <string name=\"error_input_contains_bad_language\">Mind your language!</string>\n</resources>"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/res/values/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"Theme.TensorFlowDemo\" parent=\"android:Theme.Material.Light.NoActionBar\" />\n</resources>"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/res/xml/backup_rules.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n   Sample backup rules file; uncomment and customize as necessary.\n   See https://developer.android.com/guide/topics/data/autobackup\n   for details.\n   Note: This file is ignored for devices older that API 31\n   See https://developer.android.com/about/versions/12/backup-restore\n-->\n<full-backup-content>\n    <!--\n   <include domain=\"sharedpref\" path=\".\"/>\n   <exclude domain=\"sharedpref\" path=\"device.xml\"/>\n-->\n</full-backup-content>"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/main/res/xml/data_extraction_rules.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n   Sample data extraction rules file; uncomment and customize as necessary.\n   See https://developer.android.com/about/versions/12/backup-restore#xml-changes\n   for details.\n-->\n<data-extraction-rules>\n    <cloud-backup>\n        <!-- TODO: Use <include> and <exclude> to control what is backed up.\n        <include .../>\n        <exclude .../>\n        -->\n    </cloud-backup>\n    <!--\n    <device-transfer>\n        <include .../>\n        <exclude .../>\n    </device-transfer>\n    -->\n</data-extraction-rules>"
  },
  {
    "path": "lite/examples/generative_ai/android/app/src/test/java/com/google/tensorflowdemo/util/StringExtKtTest.kt",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage com.google.tensorflowdemo.util\n\nimport org.junit.Assert.*\nimport org.junit.Test\n\nclass StringExtKtTest {\n    @Test fun TestThatShortTextIsLeftUnharmed() {\n        val input = \"Mary had a little lamb!\"\n        val output = input.trimToMaxWordCount(20)\n        assertEquals(input, output)\n    }\n\n    @Test fun TestThatATextWithMoreWordsThanAllowedIsTrimmedProperly() {\n        val input = \"Mary had a little, lamb\"\n        val output = input.trimToMaxWordCount(2)\n        assertTrue(output.length < input.length)\n    }\n\n    @Test fun TestThatAnEmptyTextIsTrimmedProperly() {\n        val input = \"\"\n        val output = input.trimToMaxWordCount(5)\n        assertTrue(output.isEmpty())\n    }\n\n    @Test fun TestThatASentenceIsSplitIntoWordsProperly() {\n        val input = \"Mary had a little lamb!\"\n        val output = input.splitToWords()\n        assertEquals(5, output.size)\n    }\n\n    @Test fun TestThatAnEmptyStringIsSplitIntoWordsProperly() {\n        val input = \"\"\n        val output = input.splitToWords()\n        assertEquals(0, output.size)\n    }\n\n    @Test fun TestThatNonWordsAtStartAreKeptAsIs() {\n        val input = \". A few of us, a couple of guys\"\n        val output = input.splitToWords()\n        assertEquals(8, output.size)\n        assertTrue(output.first().startsWith(input.substring(0, 2)))\n    }\n}"
  },
  {
    "path": "lite/examples/generative_ai/android/build.gradle.kts",
    "content": "val libs = libraries\nval versionCatalog = extensions.getByType<VersionCatalogsExtension>().named(\"libs\")\n\nplugins {\n    //trick: for the same plugin versions in all sub-modules\n    id(\"com.android.application\").version(\"7.4.2\").apply(false)\n    id(\"com.android.library\").version(\"7.4.2\").apply(false)\n    kotlin(\"android\").version(\"1.8.10\").apply(false)\n    id(\"com.android.test\").version(\"7.4.0\").apply(false)\n    id(\"de.undercouch.download\").version(\"4.0.2\").apply(false)\n}\n\ntasks.register(\"clean\", Delete::class) {\n    delete(rootProject.buildDir)\n}\n"
  },
  {
    "path": "lite/examples/generative_ai/android/gradle/libs.versions.toml",
    "content": "[versions]\ncompose = \"1.3.2\"\nlifecycle = \"2.6.0-beta01\"\nkoin = \"3.4.0\"\n\n[libraries]\ncompose-ui = { module = \"androidx.compose.ui:ui\", version.ref = \"compose\" }\ncompose-ui-tooling = { module = \"androidx.compose.ui:ui-tooling\", version.ref = \"compose\" }\ncompose-ui-tooling-preview = { module = \"androidx.compose.ui:ui-tooling-preview\", version.ref = \"compose\" }\ncompose-foundation = { module = \"androidx.compose.foundation:foundation\", version = \"1.3.1\" }\ncompose-material = { module = \"androidx.compose.material3:material3\", version = \"1.0.1\" }\ncompose-material-icons = { module = \"androidx.compose.material:material-icons-extended\", version = \"1.4.0\" }\ncompose-activity = { module = \"androidx.activity:activity-compose\", version = \"1.6.1\" }\n\naccompanist-systemuicontroller = { module = \"com.google.accompanist:accompanist-systemuicontroller\", version = \"0.30.0\" }\n\nkoin-core = { module = \"io.insert-koin:koin-core\", version.ref = \"koin\" }\nkoin-android = { module = \"io.insert-koin:koin-android\", version.ref = \"koin\" }\nkoin-compose = { module = \"io.insert-koin:koin-androidx-compose\", version.ref = \"koin\" }\n\nlifecycle-viewmodel = { module = \"androidx.lifecycle:lifecycle-viewmodel\", version.ref = \"lifecycle\" }\nlifecycle-viewmodel-compose = { module = \"androidx.lifecycle:lifecycle-viewmodel-compose\", version.ref = \"lifecycle\" }\nlifecycle-viewmodel-ktx = { module = \"androidx.lifecycle:lifecycle-viewmodel-ktx\", version.ref = \"lifecycle\" }\nlifecycle-runtime-compose = { module = \"androidx.lifecycle:lifecycle-runtime-compose\", version.ref = \"lifecycle\" }\n\nnapier = { module = \"io.github.aakira:napier\", version = \"2.6.1\" }\n\nwordfilter = { module = \"com.github.mediamonks:AndroidWordFilter\", version = \"1.0.3\" }\n\ntflite = { module = \"org.tensorflow:tensorflow-lite\", version = \"2.12.0\" }\n\njunit = { module = \"junit:junit\", version = \"4.13.2\" }"
  },
  {
    "path": "lite/examples/generative_ai/android/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app's APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Enables namespacing of each library's R class so that its R class includes only the\n# resources declared in the library itself and none from the library's dependencies,\n# thereby reducing the size of the R class for that library\nandroid.nonTransitiveRClass=true"
  },
  {
    "path": "lite/examples/generative_ai/android/how-to-build.md",
    "content": "## Prerequisites\nIf you haven’t already, install [Android Studio](https://developer.android.com/studio/index.html), following the instructions on the website.\n\n*\tAndroid Studio 2022.2.1 or above\n*\tAn Android device or Android emulator with more than 4G memory\n\n## Building and Running with Android Studio\n*\tOpen Android Studio, and from the Welcome screen, select Open an existing Android Studio project.\n*\tFrom the `Open File or Project` window that appears, navigate to and select the `lite/examples/generative_ai/android` directory from wherever you cloned the TensorFlow Lite sample GitHub repo.\n*\tYou may also need to install various platforms and tools according to error messages.\n*\tRename the converted `.tflite model` to `autocomplete.tflite` and copy it into `app/src/main/assets/` folder.\n*\tSelect menu `Build -> Make Project` to build the app (Ctrl+F9, depending on your version).\n*\tClick menu `Run -> Run 'app'` (Shift+F10, depending on your version).\n\nAlternatively, you can also use the [gradle wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html#gradle_wrapper) to build it in the command line. Please refer to the [Gradle documentation](https://docs.gradle.org/current/userguide/command_line_interface.html) for more information.\n\n## (Optional) Building the .aar file\nBy default the app automatically downloads the needed .aar files. But if you want to build your own, switch to `app/libs/build_aar/` folder run `./build_aar.sh`. This script will pull in the necessary ops from [TensorFlow Text](https://www.tensorflow.org/text) and build the aar for [Select TF operators](https://www.tensorflow.org/lite/guide/ops_select).\n\nAfter compilation, a new file `tftext_tflite_flex.aar` is generated. Replace the `.aar` file in `app/libs/` folder and re-build the app.\n\nNote that you still need to include the standard `tensorflow-lite` aar in your gradle file."
  },
  {
    "path": "lite/examples/generative_ai/android/ml/README.md",
    "content": "To generate the TFLite model, please follow this [notebook](https://github.com/tensorflow/codelabs/blob/main/KerasNLP/io2023_workshop.ipynb).\n"
  },
  {
    "path": "lite/examples/generative_ai/android/settings.gradle.kts",
    "content": "@file:Suppress(\"UnstableApiUsage\")\npluginManagement {\n    repositories {\n        google()\n        gradlePluginPortal()\n        mavenCentral()\n    }\n}\n\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)\n    repositories {\n        google()\n        mavenCentral()\n        maven {\n            url = uri(\"https://jitpack.io\")\n        }\n    }\n    versionCatalogs {\n        create(\"libraries\") {\n            from(files(\"gradle/libs.versions.toml\"))\n        }\n    }\n}\n\nrootProject.name = \"Google TensorFlow Demo\"\ninclude(\":app\")"
  },
  {
    "path": "lite/examples/gesture_classification/README.md",
    "content": "# Gesture Classification\n\n## Overview\nThis directory contains end-to-end samples that perform gesture classification\non live camera feed.\n\n* An [Android app](android/) that uses the TensorFlow Lite model to classify\nthe gesture on camera.\n* An [iOS app](ios/) that uses the TensorFlow Lite model to classify\nthe gesture on camera.\n* A [web app](web/) that uses the TensorFlow JS model to predict\nthe gesture from a webcam.\n* A [guide](ml/) to get TFJS model and TFLite model.\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/README.md",
    "content": "# TensorFlow Lite Gesture Classification\n\n### Overview\n\nThis is a camera app that continuously classifies the gesture in the frames seen\nby your device's front camera. These instructions walk you through building and\nrunning the demo on an Android device.\n\nThe model files are downloaded via Gradle scripts when you build and run the\napp. You don't need to do any steps to download TFLite models into the project\nexplicitly.\n\nWe trained the model with the following hand gesture images.\n![Training images](gesture_training_image.png \"Training images\")\n\nThis application should be run on a physical Android device.\n\n![App example showing UI controls. Result is down.](\ngesture_classification_screen_shot.jpeg?raw=true \"Screenshot with controls\")\n\n## Build the demo using Android Studio\n\n### Prerequisites\n\n* The **[Android Studio](https://developer.android.com/studio/index.html)**\n  IDE (Android Studio 2021.2.1 or newer). This sample has been tested on Android\n  Studio Chipmunk.\n\n* A physical Android device with a minimum OS version of SDK 23 (Android 6.0 -\n  Marshmallow) with developer mode enabled. The process of enabling developer\n  mode may vary by device.\n\n### Building\n\n* Open Android Studio. From the Welcome screen, select Open an existing Android\n  Studio project.\n\n* From the Open File or Project window that appears, navigate to and select the\n  tensorflow-lite/examples/gesture_classification/android directory. Click OK.\n\n* If it asks you to do a Gradle Sync, click OK.\n\n* With your Android device connected to your computer and developer mode\n  enabled, click on the green Run arrow in Android Studio.\n\n### Customize your model\n\n* Read the following **[doc](../ml/README.md)** to generate TFLite model file.\n\n* Add the metadata to the tflite model using this [Colab notebook](\nhttps://colab.research.google.com/github/tensorflow/examples/blob/master/lite/examples/gesture_classification/android/adding_metadata_to_tflite_model_colab_notebook.ipynb)\n\n* Copy the downloaded model into app/src/main/assets folder. Make sure the\n  model is named model_metadata.tflite for this sample.\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/adding_metadata_to_tflite_model_colab_notebook.ipynb",
    "content": "{\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0,\n  \"metadata\": {\n    \"colab\": {\n      \"name\": \"adding_metadata_to_tflite_model_colab_notebook.ipynb\",\n      \"provenance\": [],\n      \"collapsed_sections\": []\n    },\n    \"kernelspec\": {\n      \"name\": \"python3\",\n      \"display_name\": \"Python 3\"\n    },\n    \"language_info\": {\n      \"name\": \"python\"\n    }\n  },\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"source\": [\n        \"##### Copyright 2022 The TensorFlow Authors.\"\n      ],\n      \"metadata\": {\n        \"id\": \"U2F9EQ7PlPgW\"\n      }\n    },\n    {\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ],\n      \"metadata\": {\n        \"id\": \"-UoKBJEqlfR7\"\n      },\n      \"execution_count\": 19,\n      \"outputs\": []\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"source\": [\n        \"# Tensorflow Lite Gesture Classification Example \\bAdding Metadata Script\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"This guide shows how you can go about adding the metadata into Tensorflow Lite model.\\n\",\n        \"\\n\",\n        \"Run all steps in-order. At the end, `model_metadata.tflite` file will be downloaded.\"\n      ],\n      \"metadata\": {\n        \"id\": \"Hh7ruhMFlr_V\"\n      }\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"jVQAmH1w1wu5\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Install the TensorFlow Lite Support Pypi package.\\n\",\n        \"!pip install tflite-support-nightly\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"# Import the required packages.\\n\",\n        \"from tflite_support.metadata_writers import image_classifier\\n\",\n        \"from tflite_support.metadata_writers import metadata_info\\n\",\n        \"from tflite_support.metadata_writers import writer_utils\\n\",\n        \"from tflite_support import metadata_schema_py_generated as _metadata_fb\\n\",\n        \"\\n\",\n        \"from google.colab import files\"\n      ],\n      \"metadata\": {\n        \"id\": \"Qmx4P7Au12oH\"\n      },\n      \"execution_count\": 21,\n      \"outputs\": []\n    },\n    {\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"# Upload your model and labels here.\\n\",\n        \"files.upload()\"\n      ],\n      \"metadata\": {\n        \"id\": \"KUdyCjopkEO0\"\n      },\n      \"execution_count\": null,\n      \"outputs\": []\n    },\n    {\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"# Declare the resource paths\\n\",\n        \"_MODEL_PATH = \\\"model.tflite\\\"\\n\",\n        \"_LABEL_PATH = \\\"labels.txt\\\"\\n\",\n        \"_SAVE_TO_PATH = \\\"model_metadata.tflite\\\"\"\n      ],\n      \"metadata\": {\n        \"id\": \"1rXRefHHIMzK\"\n      },\n      \"execution_count\": 23,\n      \"outputs\": []\n    },\n    {\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"# Format the inline label file to 8 lines label file.\\n\",\n        \"search_text = \\\",\\\"\\n\",\n        \"replace_text = \\\"\\\\n\\\"\\n\",\n        \"  \\n\",\n        \"# Opening our label file in read only\\n\",\n        \"with open(_LABEL_PATH, 'r') as file:\\n\",\n        \"  \\n\",\n        \"    # Reading the content of the file\\n\",\n        \"    data = file.read()\\n\",\n        \"  \\n\",\n        \"    # Searching and replacing the text\\n\",\n        \"    data = data.replace(search_text, replace_text)\\n\",\n        \"  \\n\",\n        \"# Opening our label file in write only\\n\",\n        \"with open(_LABEL_PATH, 'w') as file:\\n\",\n        \"  \\n\",\n        \"    # Writing the replaced data in our label file\\n\",\n        \"    file.write(data)\"\n      ],\n      \"metadata\": {\n        \"id\": \"Za0zcM97HMK_\"\n      },\n      \"execution_count\": 27,\n      \"outputs\": []\n    },\n    {\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"model_buffer = writer_utils.load_file(_MODEL_PATH)\\n\",\n        \"\\n\",\n        \"# Create general model information.\\n\",\n        \"general_md = metadata_info.GeneralMd(\\n\",\n        \"    name=\\\"Guesture Classifier\\\",\\n\",\n        \"    version=\\\"v1\\\",\\n\",\n        \"    description=(\\\"Identify the most prominent gesture in the image from a \\\"\\n\",\n        \"                 \\\"known set of categories.\\\"),\\n\",\n        \"    author=\\\"TensorFlow Lite\\\",\\n\",\n        \"    licenses=\\\"Apache License. Version 2.0\\\")\\n\",\n        \"\\n\",\n        \"# Create input tensor information.\\n\",\n        \"input_md = metadata_info.InputImageTensorMd(\\n\",\n        \"    name=\\\"input image\\\",\\n\",\n        \"    description=(\\\"Input image to be classified. The expected image is \\\"\\n\",\n        \"                 \\\"224 x 224, with three channels (red, blue, and green) per \\\"\\n\",\n        \"                 \\\"pixel. Each element in the tensor is a value between min and \\\"\\n\",\n        \"                 \\\"max, where (per-channel) min is [-1] and max is [1].\\\"),\\n\",\n        \"    norm_mean=[127.5],\\n\",\n        \"    norm_std=[127.5],\\n\",\n        \"    color_space_type=_metadata_fb.ColorSpaceType.RGB,\\n\",\n        \"    tensor_type=writer_utils.get_input_tensor_types(model_buffer)[0])\\n\",\n        \"\\n\",\n        \"# Create output tensor information.\\n\",\n        \"output_md = metadata_info.ClassificationTensorMd(\\n\",\n        \"    name=\\\"probability\\\",\\n\",\n        \"    description=\\\"Probabilities of the 8 labels respectively.\\\",\\n\",\n        \"    label_files=[\\n\",\n        \"        metadata_info.LabelFileMd(file_path=_LABEL_PATH, locale=\\\"en\\\")\\n\",\n        \"    ],\\n\",\n        \"    tensor_type=writer_utils.get_output_tensor_types(model_buffer)[0])\\n\",\n        \"\\n\",\n        \"ImageClassifierWriter = image_classifier.MetadataWriter\\n\",\n        \"# Create the metadata writer.\\n\",\n        \"writer = ImageClassifierWriter.create_from_metadata_info(\\n\",\n        \"    model_buffer, general_md, input_md, output_md)\\n\",\n        \"\\n\",\n        \"# Verify the metadata generated by metadata writer.\\n\",\n        \"print(writer.get_metadata_json())\\n\",\n        \"\\n\",\n        \"# Populate the metadata into the model.\\n\",\n        \"writer_utils.save_file(writer.populate(), _SAVE_TO_PATH)\"\n      ],\n      \"metadata\": {\n        \"id\": \"2gfWkoDF14mA\"\n      },\n      \"execution_count\": null,\n      \"outputs\": []\n    },\n    {\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"# Download the model after add metadata.\\n\",\n        \"files.download(_SAVE_TO_PATH)\"\n      ],\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 17\n        },\n        \"id\": \"OQmXw6MBlhla\",\n        \"outputId\": \"6b1ea079-ee07-4c90-e152-add1844347d5\"\n      },\n      \"execution_count\": 29,\n      \"outputs\": [\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"text/plain\": [\n              \"<IPython.core.display.Javascript object>\"\n            ],\n            \"application/javascript\": [\n              \"\\n\",\n              \"    async function download(id, filename, size) {\\n\",\n              \"      if (!google.colab.kernel.accessAllowed) {\\n\",\n              \"        return;\\n\",\n              \"      }\\n\",\n              \"      const div = document.createElement('div');\\n\",\n              \"      const label = document.createElement('label');\\n\",\n              \"      label.textContent = `Downloading \\\"${filename}\\\": `;\\n\",\n              \"      div.appendChild(label);\\n\",\n              \"      const progress = document.createElement('progress');\\n\",\n              \"      progress.max = size;\\n\",\n              \"      div.appendChild(progress);\\n\",\n              \"      document.body.appendChild(div);\\n\",\n              \"\\n\",\n              \"      const buffers = [];\\n\",\n              \"      let downloaded = 0;\\n\",\n              \"\\n\",\n              \"      const channel = await google.colab.kernel.comms.open(id);\\n\",\n              \"      // Send a message to notify the kernel that we're ready.\\n\",\n              \"      channel.send({})\\n\",\n              \"\\n\",\n              \"      for await (const message of channel.messages) {\\n\",\n              \"        // Send a message to notify the kernel that we're ready.\\n\",\n              \"        channel.send({})\\n\",\n              \"        if (message.buffers) {\\n\",\n              \"          for (const buffer of message.buffers) {\\n\",\n              \"            buffers.push(buffer);\\n\",\n              \"            downloaded += buffer.byteLength;\\n\",\n              \"            progress.value = downloaded;\\n\",\n              \"          }\\n\",\n              \"        }\\n\",\n              \"      }\\n\",\n              \"      const blob = new Blob(buffers, {type: 'application/binary'});\\n\",\n              \"      const a = document.createElement('a');\\n\",\n              \"      a.href = window.URL.createObjectURL(blob);\\n\",\n              \"      a.download = filename;\\n\",\n              \"      div.appendChild(a);\\n\",\n              \"      a.click();\\n\",\n              \"      div.remove();\\n\",\n              \"    }\\n\",\n              \"  \"\n            ]\n          },\n          \"metadata\": {}\n        },\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"text/plain\": [\n              \"<IPython.core.display.Javascript object>\"\n            ],\n            \"application/javascript\": [\n              \"download(\\\"download_0b7c0687-5a92-4369-9646-fc0b5910cc8a\\\", \\\"model_metadata.tflite\\\", 5879897)\"\n            ]\n          },\n          \"metadata\": {}\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/build.gradle",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nplugins {\n    id 'com.android.application'\n    id 'org.jetbrains.kotlin.android'\n    id 'kotlin-kapt'\n    id 'androidx.navigation.safeargs'\n    id 'de.undercouch.download'\n}\n\nandroid {\n    compileSdk 32\n\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.gestureclassification\"\n        minSdk 23\n        targetSdk 32\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n    kotlinOptions {\n        jvmTarget = '1.8'\n    }\n\n    buildFeatures {\n        viewBinding true\n    }\n\n    androidResources {\n        noCompress 'tflite'\n    }\n}\n\n// import DownloadModels task\nproject.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'\nproject.ext.TEST_ASSETS_DIR = projectDir.toString() + '/src/androidTest/assets'\n\n// Download default models; if you wish to use your own models then\n// place them in the \"assets\" directory and comment out this line.\napply from: 'download_models.gradle'\n\ndependencies {\n\n    // Kotlin lang\n    implementation 'androidx.core:core-ktx:1.15.0'\n\n    // App compat and UI things\n    implementation 'androidx.appcompat:appcompat:1.5.0'\n    implementation 'com.google.android.material:material:1.6.1'\n    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'\n\n    // Navigation library\n    def nav_version = \"2.5.1\"\n    implementation \"androidx.navigation:navigation-fragment-ktx:$nav_version\"\n    implementation \"androidx.navigation:navigation-ui-ktx:$nav_version\"\n\n    // CameraX core library\n    def camerax_version = '1.2.0-alpha04'\n    implementation \"androidx.camera:camera-core:$camerax_version\"\n\n    // CameraX Camera2 extensions\n    implementation \"androidx.camera:camera-camera2:$camerax_version\"\n\n    // CameraX Lifecycle library\n    implementation \"androidx.camera:camera-lifecycle:$camerax_version\"\n\n    // CameraX View class\n    implementation \"androidx.camera:camera-view:$camerax_version\"\n\n    //WindowManager\n    implementation 'androidx.window:window:1.1.0-alpha03'\n\n    // Unit testing\n    testImplementation 'junit:junit:4.13.2'\n\n    // Instrumented testing\n    androidTestImplementation 'androidx.test.ext:junit:1.1.3'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n\n    implementation 'org.tensorflow:tensorflow-lite-task-vision:0.4.0'\n    // Import the GPU delegate plugin Library for GPU inference\n    implementation 'org.tensorflow:tensorflow-lite-gpu-delegate-plugin:0.4.0'\n    implementation 'org.tensorflow:tensorflow-lite-gpu:2.9.0'\n}\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/download_models.gradle",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ntask downloadTestFile(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/gesture_classification/android/model_metadata.tflite'\n    dest project.ext.TEST_ASSETS_DIR + '/model_metadata.tflite'\n    overwrite false\n}\n\ntask downloadModelFile(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/gesture_classification/android/model_metadata.tflite'\n    dest project.ext.ASSET_DIR + '/model_metadata.tflite'\n    overwrite false\n}\n\npreBuild.dependsOn downloadModelFile, downloadTestFile\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/androidTest/java/org/tensorflow/lite/examples/gestureclassification/GestureClassificationTest.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage org.tensorflow.lite.examples.gestureclassification\n\nimport android.content.res.AssetManager\nimport android.graphics.Bitmap\nimport android.graphics.BitmapFactory\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.platform.app.InstrumentationRegistry\nimport org.junit.Assert\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.tensorflow.lite.support.label.Category\nimport org.tensorflow.lite.task.vision.classifier.Classifications\nimport java.io.InputStream\nimport java.lang.Exception\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass GestureClassificationTest {\n\n    val controlCategories = listOf<Category>(\n        Category.create(\"down\", \"down\", 0.82084465f),\n        Category.create(\"up\", \"up\", 0.08746685f),\n        Category.create(\"scrolldown\", \"scrolldown\", 0.04518125f)\n    )\n\n    @Test\n    fun classificationResultsShouldNotChange() {\n        val gestureClassifierHelper = GestureClassifierHelper(\n            context = InstrumentationRegistry.getInstrumentation().context,\n            gestureClassifierListener = object :\n                GestureClassifierHelper.ClassifierListener {\n                override fun onError(error: String) {\n                    // no op\n                }\n\n                override fun onResults(\n                    results: List<Classifications>?,\n                    inferenceTime: Long\n                ) {\n                    Assert.assertNotNull(results)\n\n                    // Verify that the classified data and control\n                    // data have the same number of categories\n                    Assert.assertEquals(\n                        controlCategories.size,\n                        results!![0].categories.size\n                    )\n\n                    // Loop through the categories\n                    for (i in controlCategories.indices) {\n                        // Verify that the labels are consistent\n                        Assert.assertEquals(\n                            controlCategories[i].label,\n                            results[0].categories[i].label\n                        )\n                    }\n                }\n            }, threshold = 0.0f\n        )\n\n        // Create Bitmap and convert to TensorImage\n        val bitmap = loadImage(\"test_image.jpg\")\n        // Run the gesture classifier on the sample image\n        gestureClassifierHelper.classify(bitmap!!, 0)\n    }\n\n    @Throws(Exception::class)\n    private fun loadImage(fileName: String): Bitmap? {\n        val assetManager: AssetManager =\n            InstrumentationRegistry.getInstrumentation().context.assets\n        val inputStream: InputStream = assetManager.open(fileName)\n        return BitmapFactory.decodeStream(inputStream)\n    }\n}\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:dist=\"http://schemas.android.com/apk/distribution\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"org.tensorflow.lite.examples.gestureclassification\">\n\n    <!-- Enable instant app support -->\n    <dist:module dist:instant=\"true\" />\n\n    <!-- Declare features -->\n    <uses-feature android:name=\"android.hardware.camera\" />\n\n    <!-- Declare permissions -->\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:taskAffinity=\"\"\n        tools:ignore=\"AllowBackup\">\n\n        <activity\n            android:name=\".MainActivity\"\n            android:clearTaskOnLaunch=\"true\"\n            android:configChanges=\"orientation|screenLayout|screenSize|smallestScreenSize\"\n            android:exported=\"true\"\n            android:resizeableActivity=\"true\"\n            android:rotationAnimation=\"seamless\"\n            android:theme=\"@style/AppTheme\"\n            tools:targetApi=\"O\">\n\n            <!-- Main app intent filter -->\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n\n            <!-- Declare notch support -->\n            <meta-data\n                android:name=\"android.notch_support\"\n                android:value=\"true\" />\n        </activity>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/java/org/tensorflow/lite/examples/gestureclassification/GestureClassifierHelper.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.gestureclassification\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.os.SystemClock\nimport android.util.Log\nimport org.tensorflow.lite.gpu.CompatibilityList\nimport org.tensorflow.lite.support.image.ImageProcessor\nimport org.tensorflow.lite.support.image.TensorImage\nimport org.tensorflow.lite.support.image.ops.Rot90Op\nimport org.tensorflow.lite.task.core.BaseOptions\nimport org.tensorflow.lite.task.vision.classifier.Classifications\nimport org.tensorflow.lite.task.vision.classifier.ImageClassifier\n\nclass GestureClassifierHelper(\n    var threshold: Float = 0.5f,\n    var numThreads: Int = 2,\n    var maxResults: Int = 3,\n    var currentDelegate: Int = 0,\n    val context: Context,\n    val gestureClassifierListener: ClassifierListener?\n) {\n    private var gestureClassifier: ImageClassifier? = null\n\n    init {\n        setupGestureClassifier()\n    }\n\n    fun clearGestureClassifier() {\n        gestureClassifier = null\n    }\n\n    private fun setupGestureClassifier() {\n        val optionsBuilder = ImageClassifier.ImageClassifierOptions.builder()\n            .setScoreThreshold(threshold)\n            .setMaxResults(maxResults)\n\n\n        val baseOptionsBuilder = BaseOptions.builder().setNumThreads(numThreads)\n        when (currentDelegate) {\n            DELEGATE_CPU -> {\n                // Default\n            }\n            DELEGATE_GPU -> {\n                if (CompatibilityList().isDelegateSupportedOnThisDevice) {\n                    baseOptionsBuilder.useGpu()\n                } else {\n                    gestureClassifierListener?.onError(\n                        \"GPU is not supported on \" +\n                                \"this device\"\n                    )\n                }\n            }\n            DELEGATE_NNAPI -> {\n                baseOptionsBuilder.useNnapi()\n            }\n        }\n\n        optionsBuilder.setBaseOptions(baseOptionsBuilder.build())\n\n        try {\n            gestureClassifier =\n                ImageClassifier.createFromFileAndOptions(\n                    context, MODEL_PATH,\n                    optionsBuilder.build()\n                )\n        } catch (e: IllegalStateException) {\n            gestureClassifierListener?.onError(\n                \"Gesture classifier failed to initialize. See error logs for \" +\n                        \"details\"\n            )\n            Log.e(TAG, \"TFLite failed to load model with error: \" + e.message)\n        }\n    }\n\n    fun classify(image: Bitmap, imageRotation: Int) {\n        if (gestureClassifier == null) {\n            setupGestureClassifier()\n        }\n\n        // Inference time is the difference between the system time at the start and finish of the\n        // process\n        var inferenceTime = SystemClock.uptimeMillis()\n\n        // Create preprocessor for the image.\n        // See https://www.tensorflow.org/lite/inference_with_metadata/\n        //            lite_support#imageprocessor_architecture\n        val imageProcessor =\n            ImageProcessor.Builder()\n                .add(Rot90Op(imageRotation / 90))\n                .build()\n\n        // Preprocess the image and convert it into a TensorImage for classification.\n        val tensorImage = imageProcessor.process(TensorImage.fromBitmap(image))\n\n        val results = gestureClassifier?.classify(tensorImage)\n        inferenceTime = SystemClock.uptimeMillis() - inferenceTime\n        gestureClassifierListener?.onResults(results, inferenceTime)\n    }\n\n    interface ClassifierListener {\n        fun onError(error: String)\n        fun onResults(\n            results: List<Classifications>?,\n            inferenceTime: Long\n        )\n    }\n\n    companion object {\n        const val DELEGATE_CPU = 0\n        const val DELEGATE_GPU = 1\n        const val DELEGATE_NNAPI = 2\n\n        private const val MODEL_PATH = \"model_metadata.tflite\"\n        private const val TAG = \"GestureClassifierHelper\"\n    }\n}\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/java/org/tensorflow/lite/examples/gestureclassification/MainActivity.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.gestureclassification\n\nimport android.os.Build\nimport androidx.appcompat.app.AppCompatActivity\nimport android.os.Bundle\nimport org.tensorflow.lite.examples.gestureclassification.databinding.ActivityMainBinding\n\nclass MainActivity : AppCompatActivity() {\n    private lateinit var activityMainBinding: ActivityMainBinding\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)\n        setContentView(activityMainBinding.root)\n    }\n\n    override fun onBackPressed() {\n        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {\n            // Workaround for Android Q memory leak issue in IRequestFinishCallback$Stub.\n            // (https://issuetracker.google.com/issues/139738913)\n            finishAfterTransition()\n        } else {\n            super.onBackPressed()\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/java/org/tensorflow/lite/examples/gestureclassification/fragments/CameraFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.gestureclassification.fragments\n\nimport android.annotation.SuppressLint\nimport android.content.res.Configuration\nimport android.graphics.Bitmap\nimport android.graphics.Matrix\nimport android.os.Bundle\nimport android.util.Log\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.AdapterView\nimport android.widget.Toast\nimport androidx.camera.core.AspectRatio\nimport androidx.camera.core.Camera\nimport androidx.camera.core.CameraSelector\nimport androidx.camera.core.ImageAnalysis\nimport androidx.camera.core.ImageProxy\nimport androidx.camera.core.Preview\nimport androidx.camera.lifecycle.ProcessCameraProvider\nimport androidx.core.content.ContextCompat\nimport androidx.fragment.app.Fragment\nimport androidx.navigation.Navigation\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport org.tensorflow.lite.examples.gestureclassification.GestureClassifierHelper\nimport org.tensorflow.lite.examples.gestureclassification.R\nimport org.tensorflow.lite.examples.gestureclassification.databinding.FragmentCameraBinding\nimport org.tensorflow.lite.task.vision.classifier.Classifications\nimport java.util.Locale\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Executors\n\nclass CameraFragment : Fragment(), GestureClassifierHelper.ClassifierListener {\n\n    companion object {\n        private const val TAG = \"Gesture Classifier\"\n    }\n\n    private var _fragmentCameraBinding: FragmentCameraBinding? = null\n    private val fragmentCameraBinding\n        get() = _fragmentCameraBinding!!\n\n    private lateinit var gestureClassifierHelper: GestureClassifierHelper\n    private lateinit var bitmapBuffer: Bitmap\n    private val classificationResultsAdapter by lazy {\n        ClassificationResultsAdapter().apply {\n            updateAdapterSize(gestureClassifierHelper.maxResults)\n        }\n    }\n    private var preview: Preview? = null\n    private var imageAnalyzer: ImageAnalysis? = null\n    private var camera: Camera? = null\n    private var cameraProvider: ProcessCameraProvider? = null\n\n    /** Blocking camera operations are performed using this executor */\n    private lateinit var cameraExecutor: ExecutorService\n\n    override fun onResume() {\n        super.onResume()\n\n        if (!PermissionsFragment.hasPermissions(requireContext())) {\n            Navigation.findNavController(\n                requireActivity(),\n                R.id.fragment_container\n            )\n                .navigate(CameraFragmentDirections.actionCameraToPermissions())\n        }\n    }\n\n    override fun onDestroyView() {\n        _fragmentCameraBinding = null\n        super.onDestroyView()\n\n        // Shut down our background executor\n        cameraExecutor.shutdown()\n    }\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View {\n        _fragmentCameraBinding =\n            FragmentCameraBinding.inflate(inflater, container, false)\n\n        return fragmentCameraBinding.root\n    }\n\n    @SuppressLint(\"MissingPermission\")\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n\n        gestureClassifierHelper =\n            GestureClassifierHelper(\n                context = requireContext(),\n                gestureClassifierListener = this\n            )\n\n        with(fragmentCameraBinding.recyclerviewResults) {\n            layoutManager = LinearLayoutManager(requireContext())\n            adapter = classificationResultsAdapter\n        }\n\n        cameraExecutor = Executors.newSingleThreadExecutor()\n\n        fragmentCameraBinding.viewFinder.post {\n            // Set up the camera and its use cases\n            setUpCamera()\n        }\n\n        // Attach listeners to UI control widgets\n        initBottomSheetControls()\n    }\n\n    // Initialize CameraX, and prepare to bind the camera use cases\n    private fun setUpCamera() {\n        val cameraProviderFuture =\n            ProcessCameraProvider.getInstance(requireContext())\n        cameraProviderFuture.addListener(\n            {\n                // CameraProvider\n                cameraProvider = cameraProviderFuture.get()\n\n                // Build and bind the camera use cases\n                bindCameraUseCases()\n            },\n            ContextCompat.getMainExecutor(requireContext())\n        )\n    }\n\n    private fun initBottomSheetControls() {\n        // When clicked, lower classification score threshold floor\n        fragmentCameraBinding.bottomSheetLayout.thresholdMinus.setOnClickListener {\n            if (gestureClassifierHelper.threshold >= 0.1) {\n                gestureClassifierHelper.threshold -= 0.1f\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, raise classification score threshold floor\n        fragmentCameraBinding.bottomSheetLayout.thresholdPlus.setOnClickListener {\n            if (gestureClassifierHelper.threshold < 0.9) {\n                gestureClassifierHelper.threshold += 0.1f\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, reduce the number of objects that can be classified at a time\n        fragmentCameraBinding.bottomSheetLayout.maxResultsMinus.setOnClickListener {\n            if (gestureClassifierHelper.maxResults > 1) {\n                gestureClassifierHelper.maxResults--\n                updateControlsUi()\n                classificationResultsAdapter.updateAdapterSize(size = gestureClassifierHelper.maxResults)\n            }\n        }\n\n        // When clicked, increase the number of objects that can be classified at a time\n        fragmentCameraBinding.bottomSheetLayout.maxResultsPlus.setOnClickListener {\n            if (gestureClassifierHelper.maxResults < 3) {\n                gestureClassifierHelper.maxResults++\n                updateControlsUi()\n                classificationResultsAdapter.updateAdapterSize(size = gestureClassifierHelper.maxResults)\n            }\n        }\n\n        // When clicked, decrease the number of threads used for classification\n        fragmentCameraBinding.bottomSheetLayout.threadsMinus.setOnClickListener {\n            if (gestureClassifierHelper.numThreads > 1) {\n                gestureClassifierHelper.numThreads--\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, increase the number of threads used for classification\n        fragmentCameraBinding.bottomSheetLayout.threadsPlus.setOnClickListener {\n            if (gestureClassifierHelper.numThreads < 4) {\n                gestureClassifierHelper.numThreads++\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, change the underlying hardware used for inference. Current options are CPU\n        // GPU, and NNAPI\n        fragmentCameraBinding.bottomSheetLayout.spinnerDelegate.setSelection(\n            0,\n            false\n        )\n        fragmentCameraBinding.bottomSheetLayout.spinnerDelegate.onItemSelectedListener =\n            object : AdapterView.OnItemSelectedListener {\n                override fun onItemSelected(\n                    parent: AdapterView<*>?,\n                    view: View?,\n                    position: Int,\n                    id: Long\n                ) {\n                    gestureClassifierHelper.currentDelegate = position\n                    updateControlsUi()\n                }\n\n                override fun onNothingSelected(parent: AdapterView<*>?) {\n                    /* no op */\n                }\n            }\n    }\n\n    // Update the values displayed in the bottom sheet. Reset classifier.\n    private fun updateControlsUi() {\n        fragmentCameraBinding.bottomSheetLayout.maxResultsValue.text =\n            gestureClassifierHelper.maxResults.toString()\n\n        fragmentCameraBinding.bottomSheetLayout.thresholdValue.text =\n            String.format(Locale.US, \"%.2f\", gestureClassifierHelper.threshold)\n        fragmentCameraBinding.bottomSheetLayout.threadsValue.text =\n            gestureClassifierHelper.numThreads.toString()\n        // Needs to be cleared instead of reinitialized because the GPU\n        // delegate needs to be initialized on the thread using it when applicable\n        gestureClassifierHelper.clearGestureClassifier()\n    }\n\n    override fun onConfigurationChanged(newConfig: Configuration) {\n        super.onConfigurationChanged(newConfig)\n        imageAnalyzer?.targetRotation =\n            fragmentCameraBinding.viewFinder.display.rotation\n    }\n\n    // Declare and bind preview, capture and analysis use cases\n    @SuppressLint(\"UnsafeOptInUsageError\")\n    private fun bindCameraUseCases() {\n\n        // CameraProvider\n        val cameraProvider =\n            cameraProvider\n                ?: throw IllegalStateException(\"Camera initialization failed.\")\n\n        // CameraSelector - makes assumption that we're only using the front\n        // camera\n        val cameraSelector =\n            CameraSelector.Builder()\n                .requireLensFacing(CameraSelector.LENS_FACING_FRONT).build()\n\n        // Preview. Only using the 4:3 ratio because this is the closest to our models\n        preview =\n            Preview.Builder()\n                .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n                .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)\n                .build()\n\n        // ImageAnalysis. Using RGBA 8888 to match how our models work\n        imageAnalyzer =\n            ImageAnalysis.Builder()\n                .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n                .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)\n                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)\n                .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)\n                .build()\n                // The analyzer can then be assigned to the instance\n                .also {\n                    it.setAnalyzer(cameraExecutor) { image ->\n                        if (!::bitmapBuffer.isInitialized) {\n                            // The image rotation and RGB image buffer are initialized only once\n                            // the analyzer has started running\n                            bitmapBuffer = Bitmap.createBitmap(\n                                image.width,\n                                image.height,\n                                Bitmap.Config.ARGB_8888\n                            )\n                        }\n\n                        classifyImage(image)\n                    }\n                }\n\n        // Must unbind the use-cases before rebinding them\n        cameraProvider.unbindAll()\n\n        try {\n            // A variable number of use-cases can be passed here -\n            // camera provides access to CameraControl & CameraInfo\n            camera = cameraProvider.bindToLifecycle(\n                this,\n                cameraSelector,\n                preview,\n                imageAnalyzer\n            )\n\n            // Attach the viewfinder's surface provider to preview use case\n            preview?.setSurfaceProvider(fragmentCameraBinding.viewFinder.surfaceProvider)\n        } catch (exc: Exception) {\n            Log.e(TAG, \"Use case binding failed\", exc)\n        }\n    }\n\n    private fun classifyImage(image: ImageProxy) {\n        // Copy out RGB bits to the shared bitmap buffer\n        image.use { bitmapBuffer.copyPixelsFromBuffer(image.planes[0].buffer) }\n\n        // Because we use front camera so we need flip the bitmap before\n        // classify\n        val flipMatrix = Matrix().apply {\n            postScale(\n                -1f,\n                1f,\n                bitmapBuffer.width / 2f,\n                bitmapBuffer.height / 2f\n            )\n        }\n\n        val flippedBitmap = Bitmap.createBitmap(\n            bitmapBuffer, 0, 0,\n            bitmapBuffer.width, bitmapBuffer.height, flipMatrix, true\n        )\n\n        // calculate rotation degrees after flip image\n        val imageRotation = 180 - image.imageInfo.rotationDegrees\n        // Pass Bitmap and rotation to the gesture classifier helper for\n        // processing and classification\n        gestureClassifierHelper.classify(flippedBitmap, imageRotation)\n    }\n\n    @SuppressLint(\"NotifyDataSetChanged\")\n    override fun onError(error: String) {\n        activity?.runOnUiThread {\n            Toast.makeText(requireContext(), error, Toast.LENGTH_SHORT).show()\n            classificationResultsAdapter.updateResults(null)\n            classificationResultsAdapter.notifyDataSetChanged()\n        }\n    }\n\n    @SuppressLint(\"NotifyDataSetChanged\")\n    override fun onResults(\n        results: List<Classifications>?,\n        inferenceTime: Long\n    ) {\n        activity?.runOnUiThread {\n            // Show result on bottom sheet\n            classificationResultsAdapter.updateResults(results)\n            classificationResultsAdapter.notifyDataSetChanged()\n            fragmentCameraBinding.bottomSheetLayout.inferenceTimeVal.text =\n                String.format(\"%d ms\", inferenceTime)\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/java/org/tensorflow/lite/examples/gestureclassification/fragments/ClassificationResultsAdapter.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.gestureclassification.fragments\n\nimport android.view.LayoutInflater\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport org.tensorflow.lite.examples.gestureclassification.databinding.ItemClassificationResultBinding\nimport org.tensorflow.lite.support.label.Category\nimport org.tensorflow.lite.task.vision.classifier.Classifications\nimport java.util.Locale\nimport kotlin.math.min\n\nclass ClassificationResultsAdapter :\n    RecyclerView.Adapter<ClassificationResultsAdapter.ViewHolder>() {\n    companion object {\n        private const val NO_VALUE = \"--\"\n    }\n\n    private var categories: MutableList<Category?> = mutableListOf()\n    private var adapterSize: Int = 0\n\n    fun updateResults(listClassifications: List<Classifications>?) {\n        categories = MutableList(adapterSize) { null }\n        listClassifications?.let { it ->\n            if (it.isNotEmpty()) {\n                val sortedCategories = it[0].categories.sortedBy { it?.index }\n                val min = min(sortedCategories.size, categories.size)\n                for (i in 0 until min) {\n                    categories[i] = sortedCategories[i]\n                }\n            }\n        }\n    }\n\n    fun updateAdapterSize(size: Int) {\n        adapterSize = size\n    }\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {\n        val binding = ItemClassificationResultBinding.inflate(\n            LayoutInflater.from(parent.context),\n            parent,\n            false\n        )\n        return ViewHolder(binding)\n    }\n\n    override fun onBindViewHolder(holder: ViewHolder, position: Int) {\n        categories[position].let { category ->\n            holder.bind(category?.label, category?.score)\n        }\n    }\n\n    override fun getItemCount(): Int = categories.size\n\n    inner class ViewHolder(private val binding: ItemClassificationResultBinding) :\n        RecyclerView.ViewHolder(binding.root) {\n\n        fun bind(label: String?, score: Float?) {\n            with(binding) {\n                tvLabel.text = label ?: NO_VALUE\n                tvScore.text = if (score != null) String.format(\n                    Locale.US,\n                    \"%.2f\",\n                    score\n                ) else NO_VALUE\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/java/org/tensorflow/lite/examples/gestureclassification/fragments/PermissionsFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.gestureclassification.fragments\n\nimport android.Manifest\nimport android.content.Context\nimport android.content.pm.PackageManager\nimport android.os.Bundle\nimport android.widget.Toast\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.core.content.ContextCompat\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.lifecycleScope\nimport androidx.navigation.Navigation\nimport org.tensorflow.lite.examples.gestureclassification.R\n\nprivate val PERMISSIONS_REQUIRED = arrayOf(Manifest.permission.CAMERA)\n\nclass PermissionsFragment : Fragment() {\n\n    private val requestPermissionLauncher =\n        registerForActivityResult(\n            ActivityResultContracts.RequestPermission()\n        ) { isGranted: Boolean ->\n            if (isGranted) {\n                Toast.makeText(context, \"Permission request granted\", Toast.LENGTH_LONG).show()\n                navigateToCamera()\n            } else {\n                Toast.makeText(context, \"Permission request denied\", Toast.LENGTH_LONG).show()\n            }\n        }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        when {\n            ContextCompat.checkSelfPermission(\n                requireContext(),\n                Manifest.permission.CAMERA\n            ) == PackageManager.PERMISSION_GRANTED -> {\n                navigateToCamera()\n            }\n            else -> {\n                requestPermissionLauncher.launch(\n                    Manifest.permission.CAMERA\n                )\n            }\n        }\n    }\n\n    private fun navigateToCamera() {\n        lifecycleScope.launchWhenStarted {\n            Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(\n                PermissionsFragmentDirections.actionPermissionsToCamera()\n            )\n        }\n    }\n\n    companion object {\n\n        /** Convenience method used to check if all permissions required by this app are granted */\n        fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all {\n            ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/res/color/selector_ic.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"true\" android:color=\"@color/icPressed\" />\n    <item android:state_focused=\"true\" android:color=\"@color/icFocused\" />\n    <item android:color=\"@color/icActive\" />\n</selector>"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/res/drawable/ic_minus.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13H5v-2h14v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/res/drawable/ic_plus.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path android:pathData=\"M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"85.84757\"\n                android:endY=\"92.4963\"\n                android:startX=\"42.9492\"\n                android:startY=\"49.59793\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:background=\"@android:color/transparent\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <androidx.fragment.app.FragmentContainerView\n            android:id=\"@+id/fragment_container\"\n            android:name=\"androidx.navigation.fragment.NavHostFragment\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:background=\"@android:color/transparent\"\n            android:keepScreenOn=\"true\"\n            app:defaultNavHost=\"true\"\n            app:navGraph=\"@navigation/nav_graph\"\n            android:layout_marginTop=\"?android:attr/actionBarSize\"\n            tools:context=\".MainActivity\"/>\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:layout_alignParentTop=\"true\"\n            android:background=\"@color/toolbar_background\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/tfl_logo\" />\n        </androidx.appcompat.widget.Toolbar>\n\n    </RelativeLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/res/layout/fragment_camera.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/camera_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.camera.view.PreviewView\n        android:id=\"@+id/view_finder\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:scaleType=\"fillStart\" />\n\n    <include\n        android:id=\"@+id/bottom_sheet_layout\"\n        layout=\"@layout/info_bottom_sheet\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/recyclerview_results\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"top\"\n        android:layout_marginBottom=\"@dimen/bottom_sheet_peek_height\"\n        android:background=\"@color/bottom_sheet_background\"\n        android:clipToPadding=\"true\"\n        android:padding=\"@dimen/bottom_sheet_padding\"\n        app:layout_anchor=\"@id/bottom_sheet_layout\"\n        app:layout_anchorGravity=\"top\"\n        app:layout_behavior=\"com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior\" />\n\n    <View\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"1dp\"\n        android:background=\"@color/bounding_box_color\"\n        app:layout_anchor=\"@id/recyclerview_results\"\n        app:layout_anchorGravity=\"bottom\" />\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/res/layout/info_bottom_sheet.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.core.widget.NestedScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/bottom_sheet_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_gravity=\"center_horizontal\"\n    android:background=\"@color/bottom_sheet_background\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:padding=\"@dimen/bottom_sheet_padding\"\n    app:behavior_hideable=\"false\"\n    app:behavior_peekHeight=\"@dimen/bottom_sheet_peek_height\"\n    app:layout_behavior=\"com.google.android.material.bottomsheet.BottomSheetBehavior\">\n\n    <androidx.appcompat.widget.LinearLayoutCompat\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <!-- Chevron indicating that the bottom sheet is expandable -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerHorizontal=\"true\"\n                android:layout_centerVertical=\"true\"\n                android:contentDescription=\"@string/alt_bottom_sheet_chevron\"\n                android:src=\"@drawable/icn_chevron_up\" />\n        </RelativeLayout>\n\n        <!-- Inference time row -->\n        <androidx.appcompat.widget.LinearLayoutCompat\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/inference_time_label\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:text=\"@string/label_inference_time\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <TextView\n                android:id=\"@+id/inference_time_val\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:gravity=\"end\"\n                android:text=\"@string/default_inference_time\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n        </androidx.appcompat.widget.LinearLayoutCompat>\n\n        <!-- ML confidence threshold adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:text=\"@string/label_confidence_threshold\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\">\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threshold_minus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_threshold_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/threshold_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:gravity=\"center\"\n                    android:minEms=\"@integer/bottom_sheet_control_text_min_ems\"\n                    android:text=\"@string/default_threshold\"\n                    android:textColor=\"@color/bottom_sheet_text_color\"\n                    android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threshold_plus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_threshold_button_plus\"\n                    android:src=\"@drawable/ic_plus\" />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- ML max results adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:text=\"@string/label_max_results\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center\"\n                android:orientation=\"horizontal\">\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/max_results_minus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_max_results_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/max_results_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:gravity=\"center\"\n                    android:minEms=\"@integer/bottom_sheet_control_text_min_ems\"\n                    android:text=\"@string/default_max_results\"\n                    android:textColor=\"@color/bottom_sheet_text_color\"\n                    android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/max_results_plus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_max_results_button_plus\"\n                    android:src=\"@drawable/ic_plus\" />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- Number of threads adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:text=\"@string/label_threads\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\">\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threads_minus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_thread_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/threads_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:gravity=\"center\"\n                    android:minEms=\"@integer/bottom_sheet_control_text_min_ems\"\n                    android:text=\"2\"\n                    android:textColor=\"@color/bottom_sheet_text_color\"\n                    android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threads_plus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_thread_button_plus\"\n                    android:src=\"@drawable/ic_plus\" />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- Delegate selection row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/label_delegate\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <androidx.appcompat.widget.AppCompatSpinner\n                android:id=\"@+id/spinner_delegate\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:entries=\"@array/delegate_spinner_titles\"\n                android:minWidth=\"@dimen/bottom_sheet_spinner_delegate_min_width\"\n                android:spinnerMode=\"dropdown\"\n                android:theme=\"@style/BottomSheetSpinnerItemStyle\" />\n\n        </RelativeLayout>\n    </androidx.appcompat.widget.LinearLayoutCompat>\n</androidx.core.widget.NestedScrollView>\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/res/layout/item_classification_result.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <TextView\n        android:id=\"@+id/tvLabel\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:textColor=\"@color/bottom_sheet_text_color\"\n        android:textSize=\"@dimen/bottom_sheet_text_size\"\n        app:layout_constraintEnd_toStartOf=\"@+id/tvScore\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <TextView\n        android:id=\"@+id/tvScore\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:textSize=\"@dimen/bottom_sheet_text_size\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/res/navigation/nav_graph.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/nav_graph\"\n    app:startDestination=\"@id/permissions_fragment\">\n\n    <fragment\n        android:id=\"@+id/permissions_fragment\"\n        android:name=\"org.tensorflow.lite.examples.gestureclassification.fragments.PermissionsFragment\"\n        android:label=\"PermissionsFragment\">\n\n        <action\n            android:id=\"@+id/action_permissions_to_camera\"\n            app:destination=\"@id/camera_fragment\"\n            app:popUpTo=\"@id/permissions_fragment\"\n            app:popUpToInclusive=\"true\" />\n\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/camera_fragment\"\n        android:name=\"org.tensorflow.lite.examples.gestureclassification.fragments.CameraFragment\"\n        android:label=\"CameraFragment\">\n\n        <action\n            android:id=\"@+id/action_camera_to_permissions\"\n            app:destination=\"@id/permissions_fragment\"\n            app:popUpTo=\"@id/camera_fragment\"\n            app:popUpToInclusive=\"true\" />\n    </fragment>\n\n</navigation>\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n\n<resources>\n    <!-- TF Branding Orange -->\n    <color name=\"bounding_box_color\">#FF6F00</color>\n    <color name=\"toolbar_background\">#EEEEEE</color>\n    <color name=\"ic_launcher_background\">#FFFFFF</color>\n    <color name=\"bottom_sheet_background\">#EEEEEE</color>\n    <color name=\"bottom_sheet_text_color\">@android:color/black</color>\n    <color name=\"icActive\">#FFFFFFFF</color>\n    <color name=\"icFocused\">#DDFFFFFF</color>\n    <color name=\"icPressed\">#AAFFFFFF</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n\n    <dimen name=\"stroke_small\">4dp</dimen>\n    <dimen name=\"round_button_medium\">64dp</dimen>\n\n    <!-- Bottom Sheet -->\n    <dimen name=\"bottom_sheet_text_size\">20sp</dimen>\n    <dimen name=\"bottom_sheet_padding\">16dp</dimen>\n    <dimen name=\"bottom_sheet_peek_height\">50dp</dimen>\n    <dimen name=\"bottom_sheet_default_row_margin\">16dp</dimen>\n    <dimen name=\"bottom_sheet_control_btn_size\">48dp</dimen>\n    <dimen name=\"bottom_sheet_control_text_side_margin\">10dp</dimen>\n    <dimen name=\"bottom_sheet_spinner_delegate_min_width\">160dp</dimen>\n    <dimen name=\"bottom_sheet_spinner_model_min_width\">240dp</dimen>\n\n    <integer name=\"bottom_sheet_control_text_min_ems\">3</integer>\n</resources>\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n    <string name=\"app_name\">TFLite Gesture Classification</string>\n\n    <string name=\"alt_bottom_sheet_chevron\">Bottom sheet expandable indicator</string>\n\n    <string name=\"alt_bottom_sheet_max_results_button_minus\">Decreasing maximum classified results\n        button</string>\n    <string name=\"alt_bottom_sheet_max_results_button_plus\">Increasing maximum classified results\n        button</string>\n    <string name=\"alt_bottom_sheet_threshold_button_minus\">Decreasing threshold of classified image\n        results button</string>\n    <string name=\"alt_bottom_sheet_threshold_button_plus\">Increasing threshold of classified image\n        results button</string>\n    <string name=\"alt_bottom_sheet_thread_button_minus\">Decrease the number of threads used</string>\n    <string name=\"alt_bottom_sheet_thread_button_plus\">Increase the number of threads used</string>\n\n    <string name=\"label_inference_time\">Inference Time</string>\n    <string name=\"label_fps\">Frames per Second</string>\n    <string name=\"label_confidence_threshold\">Threshold</string>\n    <string name=\"label_max_results\">Max Results</string>\n    <string name=\"label_threads\">Number of Threads</string>\n    <string name=\"label_delegate\">Delegate</string>\n    <string name=\"default_threshold\">0.50</string>\n    <string name=\"default_inference_time\">0ms</string>\n    <string name=\"default_max_results\">3</string>\n\n    <string-array name=\"delegate_spinner_titles\">\n        <item>CPU</item>\n        <item>GPU</item>\n        <item>NNAPI</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n    </style>\n\n    <style name=\"BottomSheetSpinnerItemStyle\" parent=\"Widget.AppCompat.Light.DropDownItem.Spinner\">\n        <item name=\"android:textSize\">@dimen/bottom_sheet_text_size</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n    dependencies {\n        classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.5.1'\n        classpath 'de.undercouch:gradle-download-task:4.1.2'\n    }\n}\nplugins {\n    id 'com.android.application' version '7.2.0' apply false\n    id 'com.android.library' version '7.2.0' apply false\n    id 'org.jetbrains.kotlin.android' version '1.7.10' apply false\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}"
  },
  {
    "path": "lite/examples/gesture_classification/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app\"s APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official\n# Enables namespacing of each library's R class so that its R class includes only the\n# resources declared in the library itself and none from the library's dependencies,\n# thereby reducing the size of the R class for that library\nandroid.nonTransitiveRClass=true"
  },
  {
    "path": "lite/examples/gesture_classification/android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/gesture_classification/android/settings.gradle",
    "content": "pluginManagement {\n    repositories {\n        gradlePluginPortal()\n        google()\n        mavenCentral()\n    }\n}\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\nrootProject.name = \"TFLite Gesture Classification\"\ninclude ':app'\n"
  },
  {
    "path": "lite/examples/gesture_classification/ios/.gitignore",
    "content": "*.tflite\nlabels.txt"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/AppDelegate.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n  var window: UIWindow?\n\n\n  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {\n    // Override point for customization after application launch.\n\n    return true\n  }\n\n  func applicationWillResignActive(_ application: UIApplication) {\n    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.\n    // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.\n  }\n\n  func applicationDidEnterBackground(_ application: UIApplication) {\n    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.\n    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.\n  }\n\n  func applicationWillEnterForeground(_ application: UIApplication) {\n    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.\n  }\n\n  func applicationDidBecomeActive(_ application: UIApplication) {\n    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.\n  }\n\n  func applicationWillTerminate(_ application: UIApplication) {\n    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.\n  }\n\n\n}\n\n"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\"images\":[{\"size\":\"60x60\",\"expected-size\":\"180\",\"filename\":\"180.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"40x40\",\"expected-size\":\"80\",\"filename\":\"80.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"40x40\",\"expected-size\":\"120\",\"filename\":\"120.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"60x60\",\"expected-size\":\"120\",\"filename\":\"120.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"57x57\",\"expected-size\":\"57\",\"filename\":\"57.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"58\",\"filename\":\"58.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"29x29\",\"expected-size\":\"29\",\"filename\":\"29.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"87\",\"filename\":\"87.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"57x57\",\"expected-size\":\"114\",\"filename\":\"114.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"60\",\"filename\":\"60.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"1024x1024\",\"filename\":\"1024.png\",\"expected-size\":\"1024\",\"idiom\":\"ios-marketing\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"scale\":\"1x\"},{\"size\":\"40x40\",\"expected-size\":\"80\",\"filename\":\"80.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"72x72\",\"expected-size\":\"72\",\"filename\":\"72.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"76x76\",\"expected-size\":\"152\",\"filename\":\"152.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"50x50\",\"expected-size\":\"100\",\"filename\":\"100.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"29x29\",\"expected-size\":\"58\",\"filename\":\"58.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"76x76\",\"expected-size\":\"76\",\"filename\":\"76.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"29\",\"filename\":\"29.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"50x50\",\"expected-size\":\"50\",\"filename\":\"50.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"72x72\",\"expected-size\":\"144\",\"filename\":\"144.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"40x40\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"83.5x83.5\",\"expected-size\":\"167\",\"filename\":\"167.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"20\",\"filename\":\"20.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"20x20\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"}]}"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Assets.xcassets/down.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_down.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_down@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_down@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Assets.xcassets/down_icon.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icnChevronDown.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icnChevronDown@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icnChevronDown@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Assets.xcassets/left.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_left.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_left@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_left@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Assets.xcassets/left_click.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_left_click.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_left_click@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_left_click@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Assets.xcassets/right.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_right.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_right@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_right@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Assets.xcassets/right_click.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_right_click.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_right_click@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_right_click@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Assets.xcassets/scroll_down.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_scroll_down.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_scroll_down@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_scroll_down@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Assets.xcassets/scroll_up.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_scroll_up.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_scroll_up@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_scroll_up@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Assets.xcassets/selection_base.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"base.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"base@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"base@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Assets.xcassets/selection_base_default.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"selection_base_default.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"selection_base_default@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"selection_base_default@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Assets.xcassets/tfl_logo.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"tfl_logo.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"tfl_logo@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"tfl_logo@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Assets.xcassets/up.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_up.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_up@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icn_up@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Assets.xcassets/up_icon.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icnChevronUp.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icnChevronUp@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icnChevronUp@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"13122.16\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"13104.12\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"14460.31\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <device id=\"retina6_5\" orientation=\"portrait\">\n        <adaptation id=\"fullscreen\"/>\n    </device>\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"14460.20\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"ViewController\" customModule=\"GestureClassification\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"896\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"m0U-7a-tgu\" customClass=\"PreviewView\" customModule=\"GestureClassification\" customModuleProvider=\"target\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"862\"/>\n                                <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                            </view>\n                            <label hidden=\"YES\" opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Camera Unavailable\" textAlignment=\"center\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"m1k-8C-vx1\">\n                                <rect key=\"frame\" x=\"20\" y=\"400.66666666666669\" width=\"374\" height=\"25\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"height\" constant=\"25\" id=\"cdo-av-jZy\"/>\n                                </constraints>\n                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                <nil key=\"textColor\"/>\n                                <nil key=\"highlightedColor\"/>\n                            </label>\n                            <button hidden=\"YES\" opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" buttonType=\"roundedRect\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"rDb-QD-u4A\">\n                                <rect key=\"frame\" x=\"150.66666666666666\" y=\"430.66666666666669\" width=\"112.99999999999997\" height=\"40\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"height\" constant=\"40\" id=\"FrS-89-8WF\"/>\n                                </constraints>\n                                <state key=\"normal\" title=\"Resume Session\"/>\n                                <connections>\n                                    <action selector=\"onClickResumeButton:\" destination=\"BYZ-38-t0r\" eventType=\"touchUpInside\" id=\"bRp-U5-gK7\"/>\n                                </connections>\n                            </button>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"rUC-RG-P60\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"-1\" width=\"414\" height=\"100\"/>\n                                <subviews>\n                                    <imageView userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" image=\"tfl_logo\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"UUw-Qg-7Ws\">\n                                        <rect key=\"frame\" x=\"16\" y=\"56\" width=\"160\" height=\"24\"/>\n                                        <constraints>\n                                            <constraint firstAttribute=\"height\" constant=\"24\" id=\"e8p-aj-d2u\"/>\n                                            <constraint firstAttribute=\"width\" constant=\"160\" id=\"uXm-jf-MRk\"/>\n                                        </constraints>\n                                    </imageView>\n                                </subviews>\n                                <color key=\"backgroundColor\" red=\"0.0\" green=\"0.0\" blue=\"0.0\" alpha=\"0.50341497315950923\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"height\" constant=\"100\" id=\"JGY-HB-7r9\"/>\n                                    <constraint firstItem=\"UUw-Qg-7Ws\" firstAttribute=\"leading\" secondItem=\"rUC-RG-P60\" secondAttribute=\"leading\" constant=\"16\" id=\"Q2E-In-7bl\"/>\n                                    <constraint firstAttribute=\"bottom\" secondItem=\"UUw-Qg-7Ws\" secondAttribute=\"bottom\" constant=\"20\" id=\"eS7-x6-fBf\"/>\n                                </constraints>\n                            </view>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"E72-2W-mTu\" customClass=\"CurvedView\" customModule=\"GestureClassification\" customModuleProvider=\"target\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"486\" width=\"414\" height=\"410\"/>\n                                <subviews>\n                                    <containerView opaque=\"NO\" clipsSubviews=\"YES\" contentMode=\"scaleToFill\" insetsLayoutMarginsFromSafeArea=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"87J-DJ-wQL\" userLabel=\"Inference View\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"44\" width=\"414\" height=\"366\"/>\n                                        <viewLayoutGuide key=\"safeArea\" id=\"B3x-0r-sJs\"/>\n                                        <connections>\n                                            <segue destination=\"vHs-Vj-CyA\" kind=\"embed\" identifier=\"EMBED\" id=\"8KM-Yz-Ak7\"/>\n                                        </connections>\n                                    </containerView>\n                                    <imageView userInteractionEnabled=\"NO\" contentMode=\"center\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" image=\"up_icon\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"STU-il-QFX\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"44\"/>\n                                        <color key=\"backgroundColor\" white=\"1\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                        <constraints>\n                                            <constraint firstAttribute=\"height\" constant=\"44\" id=\"czm-Qa-yjq\"/>\n                                        </constraints>\n                                    </imageView>\n                                </subviews>\n                                <color key=\"backgroundColor\" white=\"1\" alpha=\"0.90321846242331283\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"STU-il-QFX\" secondAttribute=\"trailing\" id=\"4H5-3x-WnM\"/>\n                                    <constraint firstAttribute=\"height\" constant=\"410\" id=\"Ueb-Jp-7br\"/>\n                                    <constraint firstItem=\"STU-il-QFX\" firstAttribute=\"top\" secondItem=\"E72-2W-mTu\" secondAttribute=\"top\" id=\"WB0-VI-5ye\"/>\n                                    <constraint firstItem=\"STU-il-QFX\" firstAttribute=\"leading\" secondItem=\"E72-2W-mTu\" secondAttribute=\"leading\" id=\"blb-LA-WO6\"/>\n                                    <constraint firstItem=\"87J-DJ-wQL\" firstAttribute=\"leading\" secondItem=\"E72-2W-mTu\" secondAttribute=\"leading\" id=\"enA-MI-nd5\"/>\n                                    <constraint firstItem=\"87J-DJ-wQL\" firstAttribute=\"top\" secondItem=\"STU-il-QFX\" secondAttribute=\"bottom\" id=\"iTz-Jz-nfY\"/>\n                                    <constraint firstAttribute=\"bottom\" secondItem=\"87J-DJ-wQL\" secondAttribute=\"bottom\" id=\"oxi-Iy-EuZ\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"87J-DJ-wQL\" secondAttribute=\"trailing\" id=\"uQm-Wx-6Wr\"/>\n                                </constraints>\n                            </view>\n                        </subviews>\n                        <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                        <constraints>\n                            <constraint firstItem=\"rDb-QD-u4A\" firstAttribute=\"top\" secondItem=\"m1k-8C-vx1\" secondAttribute=\"bottom\" constant=\"5\" id=\"0Lq-WP-AFv\"/>\n                            <constraint firstItem=\"6Tk-OE-BBY\" firstAttribute=\"trailing\" secondItem=\"m1k-8C-vx1\" secondAttribute=\"trailing\" constant=\"20\" id=\"1ax-ig-wVQ\"/>\n                            <constraint firstAttribute=\"trailing\" secondItem=\"E72-2W-mTu\" secondAttribute=\"trailing\" id=\"3GS-5z-NaA\"/>\n                            <constraint firstItem=\"m0U-7a-tgu\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" id=\"3bY-yN-e3e\"/>\n                            <constraint firstItem=\"E72-2W-mTu\" firstAttribute=\"leading\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"leading\" id=\"51V-Bk-Hp5\"/>\n                            <constraint firstItem=\"6Tk-OE-BBY\" firstAttribute=\"trailing\" secondItem=\"rUC-RG-P60\" secondAttribute=\"trailing\" id=\"79T-lX-PhV\"/>\n                            <constraint firstItem=\"rDb-QD-u4A\" firstAttribute=\"centerX\" secondItem=\"m1k-8C-vx1\" secondAttribute=\"centerX\" id=\"Bji-Gq-fG8\"/>\n                            <constraint firstItem=\"m0U-7a-tgu\" firstAttribute=\"top\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"top\" id=\"OXp-Cr-YF0\"/>\n                            <constraint firstItem=\"m0U-7a-tgu\" firstAttribute=\"trailing\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"trailing\" id=\"XqL-lO-XsC\"/>\n                            <constraint firstAttribute=\"bottom\" secondItem=\"E72-2W-mTu\" secondAttribute=\"bottom\" id=\"gCI-4i-vnP\"/>\n                            <constraint firstItem=\"m0U-7a-tgu\" firstAttribute=\"bottom\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"bottom\" id=\"j5w-nF-4oj\"/>\n                            <constraint firstItem=\"m1k-8C-vx1\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" constant=\"20\" id=\"lVV-Ah-8ns\"/>\n                            <constraint firstItem=\"m1k-8C-vx1\" firstAttribute=\"centerY\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"centerY\" constant=\"-40\" id=\"rtE-Bo-aHP\"/>\n                            <constraint firstItem=\"6Tk-OE-BBY\" firstAttribute=\"top\" secondItem=\"rUC-RG-P60\" secondAttribute=\"top\" constant=\"45\" id=\"wmm-Ve-LI0\"/>\n                            <constraint firstItem=\"rUC-RG-P60\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" id=\"yOf-ji-g8Z\"/>\n                        </constraints>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                    <extendedEdge key=\"edgesForExtendedLayout\" top=\"YES\"/>\n                    <connections>\n                        <outlet property=\"bottomSheetView\" destination=\"E72-2W-mTu\" id=\"F1b-ap-5Uh\"/>\n                        <outlet property=\"cameraUnavailableLabel\" destination=\"m1k-8C-vx1\" id=\"rYI-1d-am9\"/>\n                        <outlet property=\"containerViewBottomSpace\" destination=\"gCI-4i-vnP\" id=\"sjs-oT-bhQ\"/>\n                        <outlet property=\"containerViewHeight\" destination=\"Ueb-Jp-7br\" id=\"uDl-M3-QOn\"/>\n                        <outlet property=\"previewView\" destination=\"m0U-7a-tgu\" id=\"WKt-RT-fec\"/>\n                        <outlet property=\"resumeButton\" destination=\"rDb-QD-u4A\" id=\"pSW-nO-uw9\"/>\n                        <outlet property=\"upImageView\" destination=\"STU-il-QFX\" id=\"arU-sb-CNp\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"140\" y=\"127.28635682158921\"/>\n        </scene>\n        <!--Inference View Controller-->\n        <scene sceneID=\"L7K-Mf-Eiv\">\n            <objects>\n                <viewController id=\"vHs-Vj-CyA\" customClass=\"InferenceViewController\" customModule=\"GestureClassification\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"jRk-pV-rwc\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"366\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <tableView clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" alwaysBounceVertical=\"YES\" scrollEnabled=\"NO\" dataMode=\"prototypes\" style=\"plain\" separatorStyle=\"none\" allowsSelection=\"NO\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"28\" sectionFooterHeight=\"28\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"xsq-YM-pZQ\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"192\" width=\"414\" height=\"121\"/>\n                                <color key=\"backgroundColor\" white=\"1\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"height\" constant=\"121\" id=\"5GW-wy-yVw\"/>\n                                </constraints>\n                                <prototypes>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" reuseIdentifier=\"INFO_CELL\" rowHeight=\"161\" id=\"8cp-WY-GhK\" customClass=\"InfoCell\" customModule=\"GestureClassification\" customModuleProvider=\"target\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"28\" width=\"414\" height=\"161\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"8cp-WY-GhK\" id=\"F7n-7p-Qm1\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"161\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Label\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" numberOfLines=\"0\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"rrm-Jd-QZm\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"5\" width=\"36\" height=\"17\"/>\n                                                    <constraints>\n                                                        <constraint firstAttribute=\"height\" constant=\"17\" id=\"Fvd-Jh-6pX\"/>\n                                                    </constraints>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" weight=\"medium\" pointSize=\"14\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                                <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Label\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"6Dv-QG-oaN\">\n                                                    <rect key=\"frame\" x=\"362\" y=\"5\" width=\"36\" height=\"17\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" weight=\"medium\" pointSize=\"14\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                                <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"rLH-Ou-AVa\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"42\" width=\"382\" height=\"1\"/>\n                                                    <color key=\"backgroundColor\" red=\"0.45882352939999999\" green=\"0.45882352939999999\" blue=\"0.45882352939999999\" alpha=\"0.19554016490000001\" colorSpace=\"custom\" customColorSpace=\"calibratedRGB\"/>\n                                                    <constraints>\n                                                        <constraint firstAttribute=\"height\" constant=\"1\" id=\"PgS-v8-cw4\"/>\n                                                    </constraints>\n                                                </view>\n                                            </subviews>\n                                            <constraints>\n                                                <constraint firstAttribute=\"trailing\" secondItem=\"rLH-Ou-AVa\" secondAttribute=\"trailing\" constant=\"16\" id=\"Ba7-77-bmp\"/>\n                                                <constraint firstAttribute=\"trailing\" secondItem=\"6Dv-QG-oaN\" secondAttribute=\"trailing\" constant=\"16\" id=\"J7k-zd-lhq\"/>\n                                                <constraint firstItem=\"rrm-Jd-QZm\" firstAttribute=\"top\" secondItem=\"F7n-7p-Qm1\" secondAttribute=\"top\" constant=\"5\" id=\"Vg0-dc-4sL\"/>\n                                                <constraint firstItem=\"rLH-Ou-AVa\" firstAttribute=\"leading\" secondItem=\"F7n-7p-Qm1\" secondAttribute=\"leading\" constant=\"16\" id=\"WRA-cw-jVl\"/>\n                                                <constraint firstItem=\"rLH-Ou-AVa\" firstAttribute=\"top\" secondItem=\"rrm-Jd-QZm\" secondAttribute=\"bottom\" constant=\"20\" id=\"WjB-zt-b64\"/>\n                                                <constraint firstItem=\"rrm-Jd-QZm\" firstAttribute=\"leading\" secondItem=\"F7n-7p-Qm1\" secondAttribute=\"leading\" constant=\"16\" id=\"vzI-iF-oba\"/>\n                                                <constraint firstItem=\"6Dv-QG-oaN\" firstAttribute=\"centerY\" secondItem=\"rrm-Jd-QZm\" secondAttribute=\"centerY\" id=\"wzW-ur-1og\"/>\n                                            </constraints>\n                                        </tableViewCellContentView>\n                                        <color key=\"backgroundColor\" white=\"1\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                        <connections>\n                                            <outlet property=\"fieldNameLabel\" destination=\"rrm-Jd-QZm\" id=\"jOn-Io-l0I\"/>\n                                            <outlet property=\"infoLabel\" destination=\"6Dv-QG-oaN\" id=\"kOP-CQ-fsj\"/>\n                                        </connections>\n                                    </tableViewCell>\n                                </prototypes>\n                                <connections>\n                                    <outlet property=\"dataSource\" destination=\"vHs-Vj-CyA\" id=\"2Mf-yR-L9L\"/>\n                                    <outlet property=\"delegate\" destination=\"vHs-Vj-CyA\" id=\"zKk-Rw-Lxr\"/>\n                                </connections>\n                            </tableView>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"sSo-3R-jLB\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"313\" width=\"414\" height=\"53\"/>\n                                <subviews>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Threads\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"M7n-AC-mQc\">\n                                        <rect key=\"frame\" x=\"16\" y=\"10\" width=\"53\" height=\"17\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"1\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" numberOfLines=\"0\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"XVR-0O-Ncu\">\n                                        <rect key=\"frame\" x=\"289.33333333333331\" y=\"10\" width=\"6.6666666666666856\" height=\"17\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                </subviews>\n                                <color key=\"backgroundColor\" white=\"1\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                <constraints>\n                                    <constraint firstItem=\"M7n-AC-mQc\" firstAttribute=\"leading\" secondItem=\"sSo-3R-jLB\" secondAttribute=\"leading\" constant=\"16\" id=\"ANy-mW-APh\"/>\n                                    <constraint firstItem=\"M7n-AC-mQc\" firstAttribute=\"top\" secondItem=\"sSo-3R-jLB\" secondAttribute=\"top\" constant=\"10\" id=\"D8V-un-srP\"/>\n                                    <constraint firstAttribute=\"height\" constant=\"53\" id=\"OoC-Dc-qrv\"/>\n                                    <constraint firstItem=\"XVR-0O-Ncu\" firstAttribute=\"centerY\" secondItem=\"M7n-AC-mQc\" secondAttribute=\"centerY\" id=\"pWj-h8-RUp\"/>\n                                </constraints>\n                            </view>\n                            <stepper opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" maximumValue=\"100\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Edp-UL-S3l\">\n                                <rect key=\"frame\" x=\"304\" y=\"317\" width=\"94\" height=\"29\"/>\n                                <color key=\"tintColor\" red=\"0.45882352939999999\" green=\"0.45882352939999999\" blue=\"0.45882352939999999\" alpha=\"1\" colorSpace=\"calibratedRGB\"/>\n                                <connections>\n                                    <action selector=\"onClickThreadStepper:\" destination=\"vHs-Vj-CyA\" eventType=\"valueChanged\" id=\"PfS-X8-Zxa\"/>\n                                </connections>\n                            </stepper>\n                            <collectionView clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" dataMode=\"prototypes\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"0Zt-be-xak\">\n                                <rect key=\"frame\" x=\"15\" y=\"0.0\" width=\"384\" height=\"165\"/>\n                                <color key=\"backgroundColor\" white=\"1\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"height\" constant=\"165\" id=\"tKc-Fj-WJr\"/>\n                                </constraints>\n                                <collectionViewFlowLayout key=\"collectionViewLayout\" minimumLineSpacing=\"15\" minimumInteritemSpacing=\"15\" id=\"NkG-bl-5lF\">\n                                    <size key=\"itemSize\" width=\"97\" height=\"93\"/>\n                                    <size key=\"headerReferenceSize\" width=\"0.0\" height=\"0.0\"/>\n                                    <size key=\"footerReferenceSize\" width=\"0.0\" height=\"0.0\"/>\n                                    <inset key=\"sectionInset\" minX=\"0.0\" minY=\"0.0\" maxX=\"0.0\" maxY=\"0.0\"/>\n                                </collectionViewFlowLayout>\n                                <cells>\n                                    <collectionViewCell opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" reuseIdentifier=\"GESTURE_CELL\" id=\"bbg-wO-1dN\" customClass=\"GestureCollectionViewCell\" customModule=\"GestureClassification\" customModuleProvider=\"target\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"97\" height=\"93\"/>\n                                        <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMaxY=\"YES\"/>\n                                        <view key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" insetsLayoutMarginsFromSafeArea=\"NO\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"97\" height=\"93\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <imageView userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" image=\"selection_base_default\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Cin-x8-HSe\">\n                                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"97\" height=\"93\"/>\n                                                </imageView>\n                                                <imageView userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"0Oh-Rl-b3S\">\n                                                    <rect key=\"frame\" x=\"40.666666666666664\" y=\"18\" width=\"16\" height=\"16\"/>\n                                                    <constraints>\n                                                        <constraint firstAttribute=\"height\" constant=\"16\" id=\"2Ta-Wb-QLE\"/>\n                                                        <constraint firstAttribute=\"width\" constant=\"16\" id=\"hcu-LS-yHJ\"/>\n                                                    </constraints>\n                                                </imageView>\n                                                <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Label\" textAlignment=\"center\" lineBreakMode=\"tailTruncation\" numberOfLines=\"0\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"gue-bX-W6m\">\n                                                    <rect key=\"frame\" x=\"12\" y=\"44\" width=\"73\" height=\"14.333333333333336\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"12\"/>\n                                                    <color key=\"textColor\" red=\"0.48627450979999998\" green=\"0.53333333329999999\" blue=\"0.56470588239999997\" alpha=\"1\" colorSpace=\"calibratedRGB\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </view>\n                                        <constraints>\n                                            <constraint firstItem=\"gue-bX-W6m\" firstAttribute=\"top\" secondItem=\"0Oh-Rl-b3S\" secondAttribute=\"bottom\" constant=\"10\" id=\"3Mt-MS-Ysx\"/>\n                                            <constraint firstItem=\"gue-bX-W6m\" firstAttribute=\"leading\" secondItem=\"bbg-wO-1dN\" secondAttribute=\"leading\" constant=\"12\" id=\"3fU-y8-AzA\"/>\n                                            <constraint firstAttribute=\"trailing\" secondItem=\"Cin-x8-HSe\" secondAttribute=\"trailing\" id=\"49b-1D-8c7\"/>\n                                            <constraint firstItem=\"Cin-x8-HSe\" firstAttribute=\"top\" secondItem=\"bbg-wO-1dN\" secondAttribute=\"top\" id=\"J34-TH-WiQ\"/>\n                                            <constraint firstItem=\"Cin-x8-HSe\" firstAttribute=\"leading\" secondItem=\"bbg-wO-1dN\" secondAttribute=\"leading\" id=\"Msq-sL-d80\"/>\n                                            <constraint firstItem=\"0Oh-Rl-b3S\" firstAttribute=\"top\" secondItem=\"bbg-wO-1dN\" secondAttribute=\"top\" constant=\"18\" id=\"PH8-Wz-x4C\"/>\n                                            <constraint firstItem=\"0Oh-Rl-b3S\" firstAttribute=\"centerX\" secondItem=\"bbg-wO-1dN\" secondAttribute=\"centerX\" id=\"qNb-6a-Rvm\"/>\n                                            <constraint firstAttribute=\"trailing\" secondItem=\"gue-bX-W6m\" secondAttribute=\"trailing\" constant=\"12\" id=\"suh-Z9-KKi\"/>\n                                            <constraint firstAttribute=\"bottom\" secondItem=\"Cin-x8-HSe\" secondAttribute=\"bottom\" id=\"vsc-Wd-ubH\"/>\n                                        </constraints>\n                                        <connections>\n                                            <outlet property=\"iconImageView\" destination=\"0Oh-Rl-b3S\" id=\"AhJ-O2-whb\"/>\n                                            <outlet property=\"nameLabel\" destination=\"gue-bX-W6m\" id=\"f8M-xh-ydM\"/>\n                                            <outlet property=\"selectionImageView\" destination=\"Cin-x8-HSe\" id=\"1tc-JM-1q7\"/>\n                                        </connections>\n                                    </collectionViewCell>\n                                </cells>\n                                <connections>\n                                    <outlet property=\"dataSource\" destination=\"vHs-Vj-CyA\" id=\"9sT-MH-UD3\"/>\n                                    <outlet property=\"delegate\" destination=\"vHs-Vj-CyA\" id=\"0by-eG-9Gf\"/>\n                                </connections>\n                            </collectionView>\n                        </subviews>\n                        <color key=\"backgroundColor\" white=\"1\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                        <constraints>\n                            <constraint firstItem=\"xsq-YM-pZQ\" firstAttribute=\"top\" secondItem=\"0Zt-be-xak\" secondAttribute=\"bottom\" constant=\"27\" id=\"6lZ-lz-3f4\"/>\n                            <constraint firstItem=\"Edp-UL-S3l\" firstAttribute=\"centerY\" secondItem=\"M7n-AC-mQc\" secondAttribute=\"centerY\" id=\"CBK-5L-Pwz\"/>\n                            <constraint firstItem=\"xsq-YM-pZQ\" firstAttribute=\"trailing\" secondItem=\"jRk-pV-rwc\" secondAttribute=\"trailing\" id=\"CI0-IO-ToT\"/>\n                            <constraint firstItem=\"xsq-YM-pZQ\" firstAttribute=\"leading\" secondItem=\"jRk-pV-rwc\" secondAttribute=\"leading\" id=\"FYK-hT-yg0\"/>\n                            <constraint firstItem=\"0Zt-be-xak\" firstAttribute=\"leading\" secondItem=\"jRk-pV-rwc\" secondAttribute=\"leading\" constant=\"15\" id=\"SA3-Va-WDD\"/>\n                            <constraint firstItem=\"sSo-3R-jLB\" firstAttribute=\"leading\" secondItem=\"jRk-pV-rwc\" secondAttribute=\"leading\" id=\"cvC-o7-RSM\"/>\n                            <constraint firstItem=\"sSo-3R-jLB\" firstAttribute=\"top\" secondItem=\"xsq-YM-pZQ\" secondAttribute=\"bottom\" id=\"fsm-We-JGy\"/>\n                            <constraint firstItem=\"0Zt-be-xak\" firstAttribute=\"top\" secondItem=\"jRk-pV-rwc\" secondAttribute=\"top\" id=\"jFC-aA-4hF\"/>\n                            <constraint firstItem=\"sSo-3R-jLB\" firstAttribute=\"trailing\" secondItem=\"jRk-pV-rwc\" secondAttribute=\"trailing\" id=\"nDF-0s-VZn\"/>\n                            <constraint firstAttribute=\"trailing\" secondItem=\"Edp-UL-S3l\" secondAttribute=\"trailing\" constant=\"16\" id=\"uhj-dm-4Vb\"/>\n                            <constraint firstItem=\"Edp-UL-S3l\" firstAttribute=\"leading\" secondItem=\"XVR-0O-Ncu\" secondAttribute=\"trailing\" constant=\"8\" id=\"wmj-Zl-OPk\"/>\n                            <constraint firstAttribute=\"trailing\" secondItem=\"0Zt-be-xak\" secondAttribute=\"trailing\" constant=\"15\" id=\"xsq-Jz-3Z5\"/>\n                        </constraints>\n                        <viewLayoutGuide key=\"safeArea\" id=\"e6k-BV-hPJ\"/>\n                    </view>\n                    <connections>\n                        <outlet property=\"gestureCollectionView\" destination=\"0Zt-be-xak\" id=\"ZPB-Yh-HbJ\"/>\n                        <outlet property=\"gestureCollectionViewHeight\" destination=\"tKc-Fj-WJr\" id=\"MMX-T9-H3x\"/>\n                        <outlet property=\"stepperValueLabel\" destination=\"XVR-0O-Ncu\" id=\"5z2-Z7-j05\"/>\n                        <outlet property=\"stepperView\" destination=\"sSo-3R-jLB\" id=\"XdU-Tz-jMw\"/>\n                        <outlet property=\"tableView\" destination=\"xsq-YM-pZQ\" id=\"IlV-FO-aGz\"/>\n                        <outlet property=\"tableViewHeightConstraint\" destination=\"5GW-wy-yVw\" id=\"igp-nC-oCV\"/>\n                        <outlet property=\"threadStepper\" destination=\"Edp-UL-S3l\" id=\"be0-lM-wyo\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"wO7-DD-CTz\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"910\" y=\"344\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"selection_base_default\" width=\"75\" height=\"75\"/>\n        <image name=\"tfl_logo\" width=\"160\" height=\"24\"/>\n        <image name=\"up_icon\" width=\"20\" height=\"6\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Camera Feed/CameraFeedManager.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\nimport AVFoundation\n\n// MARK: CameraFeedManagerDelegate Declaration\nprotocol CameraFeedManagerDelegate: class {\n\n  /**\n   This method delivers the pixel buffer of the current frame seen by the device's camera.\n   */\n  func didOutput(pixelBuffer: CVPixelBuffer)\n\n  /**\n   This method initimates that the camera permissions have been denied.\n   */\n  func presentCameraPermissionsDeniedAlert()\n\n  /**\n   This method initimates that there was an error in video configurtion.\n   */\n  func presentVideoConfigurationErrorAlert()\n\n  /**\n   This method initimates that a session runtime error occured.\n   */\n  func sessionRunTimeErrorOccured()\n\n  /**\n   This method initimates that the session was interrupted.\n   */\n  func sessionWasInterrupted(canResumeManually resumeManually: Bool)\n\n  /**\n   This method initimates that the session interruption has ended.\n   */\n  func sessionInterruptionEnded()\n\n}\n\n/**\n This enum holds the state of the camera initialization.\n */\nenum CameraConfiguration {\n\n  case success\n  case failed\n  case permissionDenied\n}\n\n/**\n This class manages all camera related functionality\n */\nclass CameraFeedManager: NSObject {\n\n  // MARK: Camera Related Instance Variables\n  private let session: AVCaptureSession = AVCaptureSession()\n  private let previewView: PreviewView\n  private let sessionQueue = DispatchQueue(label: \"sessionQueue\")\n  private var cameraConfiguration: CameraConfiguration = .failed\n  private lazy var videoDataOutput = AVCaptureVideoDataOutput()\n  private var isSessionRunning = false\n\n  // MARK: CameraFeedManagerDelegate\n  weak var delegate: CameraFeedManagerDelegate?\n\n  // MARK: Initializer\n  init(previewView: PreviewView) {\n    self.previewView = previewView\n    super.init()\n\n    // Initializes the session\n    session.sessionPreset = .high\n    self.previewView.session = session\n    self.previewView.previewLayer.connection?.videoOrientation = .portrait\n    self.previewView.previewLayer.videoGravity = .resizeAspectFill\n    self.attemptToConfigureSession()\n  }\n\n  // MARK: Session Start and End methods\n\n  /**\n   This method starts an AVCaptureSession based on whether the camera configuration was successful.\n   */\n  func checkCameraConfigurationAndStartSession() {\n    sessionQueue.async {\n      switch self.cameraConfiguration {\n      case .success:\n        self.addObservers()\n        self.startSession()\n      case .failed:\n        DispatchQueue.main.async {\n          self.delegate?.presentVideoConfigurationErrorAlert()\n        }\n      case .permissionDenied:\n        DispatchQueue.main.async {\n          self.delegate?.presentCameraPermissionsDeniedAlert()\n        }\n      }\n    }\n  }\n\n  /**\n   This method stops a running an AVCaptureSession.\n   */\n  func stopSession() {\n    self.removeObservers()\n    sessionQueue.async {\n      if self.session.isRunning {\n        self.session.stopRunning()\n        self.isSessionRunning = self.session.isRunning\n      }\n    }\n\n  }\n\n  /**\n   This method resumes an interrupted AVCaptureSession.\n   */\n  func resumeInterruptedSession(withCompletion completion: @escaping (Bool) -> ()) {\n\n    sessionQueue.async {\n      self.startSession()\n\n      DispatchQueue.main.async {\n        completion(self.isSessionRunning)\n      }\n    }\n  }\n\n  /**\n   This method starts the AVCaptureSession\n   **/\n  private func startSession() {\n    self.session.startRunning()\n    self.isSessionRunning = self.session.isRunning\n  }\n\n  // MARK: Session Configuration Methods.\n  /**\n   This method requests for camera permissions and handles the configuration of the session and stores the result of configuration.\n   */\n  private func attemptToConfigureSession() {\n    switch AVCaptureDevice.authorizationStatus(for: .video) {\n    case .authorized:\n      self.cameraConfiguration = .success\n    case .notDetermined:\n      self.sessionQueue.suspend()\n      self.requestCameraAccess(completion: { (granted) in\n        self.sessionQueue.resume()\n      })\n    case .denied:\n      self.cameraConfiguration = .permissionDenied\n    default:\n      break\n    }\n\n    self.sessionQueue.async {\n      self.configureSession()\n    }\n  }\n\n  /**\n   This method requests for camera permissions.\n   */\n  private func requestCameraAccess(completion: @escaping (Bool) -> ()) {\n    AVCaptureDevice.requestAccess(for: .video) { (granted) in\n      if !granted {\n        self.cameraConfiguration = .permissionDenied\n      }\n      else {\n        self.cameraConfiguration = .success\n      }\n      completion(granted)\n    }\n  }\n\n\n  /**\n   This method handles all the steps to configure an AVCaptureSession.\n   */\n  private func configureSession() {\n\n    guard cameraConfiguration == .success else {\n      return\n    }\n    session.beginConfiguration()\n\n    // Tries to add an AVCaptureDeviceInput.\n    guard addVideoDeviceInput() == true else {\n      self.session.commitConfiguration()\n      self.cameraConfiguration = .failed\n      return\n    }\n\n    // Tries to add an AVCaptureVideoDataOutput.\n    guard addVideoDataOutput() else {\n      self.session.commitConfiguration()\n      self.cameraConfiguration = .failed\n      return\n    }\n\n    session.commitConfiguration()\n    self.cameraConfiguration = .success\n  }\n\n  /**\n   This method tries to an AVCaptureDeviceInput to the current AVCaptureSession.\n   */\n  private func addVideoDeviceInput() -> Bool {\n\n    /**Tries to get the default back camera.\n     */\n    guard let camera  = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front) else {\n      fatalError(\"Cannot find camera\")\n    }\n\n    do {\n      let videoDeviceInput = try AVCaptureDeviceInput(device: camera)\n      if session.canAddInput(videoDeviceInput) {\n        session.addInput(videoDeviceInput)\n        return true\n      }\n      else {\n        return false\n      }\n    }\n    catch {\n      fatalError(\"Cannot create video device input\")\n    }\n  }\n\n  /**\n   This method tries to an AVCaptureVideoDataOutput to the current AVCaptureSession.\n   */\n  private func addVideoDataOutput() -> Bool {\n\n    let sampleBufferQueue = DispatchQueue(label: \"sampleBufferQueue\")\n    videoDataOutput.setSampleBufferDelegate(self, queue: sampleBufferQueue)\n    videoDataOutput.alwaysDiscardsLateVideoFrames = true\n    videoDataOutput.videoSettings = [ String(kCVPixelBufferPixelFormatTypeKey) : kCMPixelFormat_32BGRA]\n\n    if session.canAddOutput(videoDataOutput) {\n      session.addOutput(videoDataOutput)\n      self.videoDataOutput.connection(with: .video)?.videoOrientation = .portrait\n      self.videoDataOutput.connection(with: .video)?.isVideoMirrored = true\n      return true\n    }\n    return false\n\n  }\n\n  // MARK: Notification Observer Handling\n  private func addObservers() {\n    NotificationCenter.default.addObserver(self, selector: #selector(CameraFeedManager.sessionRuntimeErrorOccured(notification:)), name: NSNotification.Name.AVCaptureSessionRuntimeError, object: session)\n    NotificationCenter.default.addObserver(self, selector: #selector(CameraFeedManager.sessionWasInterrupted(notification:)), name: NSNotification.Name.AVCaptureSessionWasInterrupted, object: session)\n    NotificationCenter.default.addObserver(self, selector: #selector(CameraFeedManager.sessionInterruptionEnded), name: NSNotification.Name.AVCaptureSessionInterruptionEnded, object: session)\n  }\n\n  private func removeObservers() {\n    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVCaptureSessionRuntimeError, object: session)\n    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVCaptureSessionWasInterrupted, object: session)\n    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVCaptureSessionInterruptionEnded, object: session)\n  }\n\n  // MARK: Notification Observers\n  @objc func sessionWasInterrupted(notification: Notification) {\n\n    if let userInfoValue = notification.userInfo?[AVCaptureSessionInterruptionReasonKey] as AnyObject?,\n      let reasonIntegerValue = userInfoValue.integerValue,\n      let reason = AVCaptureSession.InterruptionReason(rawValue: reasonIntegerValue) {\n      print(\"Capture session was interrupted with reason \\(reason)\")\n\n      var canResumeManually = false\n      if reason == .videoDeviceInUseByAnotherClient {\n        canResumeManually = true\n      } else if reason == .videoDeviceNotAvailableWithMultipleForegroundApps {\n        canResumeManually = false\n      }\n\n      self.delegate?.sessionWasInterrupted(canResumeManually: canResumeManually)\n\n    }\n  }\n\n  @objc func sessionInterruptionEnded(notification: Notification) {\n\n    self.delegate?.sessionInterruptionEnded()\n  }\n\n  @objc func sessionRuntimeErrorOccured(notification: Notification) {\n    guard let error = notification.userInfo?[AVCaptureSessionErrorKey] as? AVError else {\n      return\n    }\n\n    print(\"Capture session runtime error: \\(error)\")\n\n    if error.code == .mediaServicesWereReset {\n      sessionQueue.async {\n        if self.isSessionRunning {\n          self.startSession()\n        } else {\n          DispatchQueue.main.async {\n            self.delegate?.sessionRunTimeErrorOccured()\n          }\n        }\n      }\n    } else {\n      self.delegate?.sessionRunTimeErrorOccured()\n\n    }\n  }\n}\n\n\n/**\n AVCaptureVideoDataOutputSampleBufferDelegate\n */\nextension CameraFeedManager: AVCaptureVideoDataOutputSampleBufferDelegate {\n\n  /** This method delegates the CVPixelBuffer of the frame seen by the camera currently.\n   */\n  func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {\n\n    // Converts the CMSampleBuffer to a CVPixelBuffer.\n    let pixelBuffer: CVPixelBuffer? = CMSampleBufferGetImageBuffer(sampleBuffer)\n\n    guard let imagePixelBuffer = pixelBuffer else {\n      return\n    }\n\n    // Delegates the pixel buffer to the ViewController.\n    delegate?.didOutput(pixelBuffer: imagePixelBuffer)\n  }\n\n}\n"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Camera Feed/PreviewView.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\nimport AVFoundation\n\nclass PreviewView: UIView {\n\n  var previewLayer: AVCaptureVideoPreviewLayer {\n    guard let layer = layer as? AVCaptureVideoPreviewLayer else {\n      fatalError(\"Layer expected is of type VideoPreviewLayer\")\n    }\n    return layer\n  }\n\n  var session: AVCaptureSession? {\n    get {\n      return previewLayer.session\n    }\n    set {\n      previewLayer.session = newValue\n    }\n  }\n\n  override class var layerClass: AnyClass {\n    return AVCaptureVideoPreviewLayer.self\n  }\n}\n"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Cells/GestureCollectionViewCell.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\nclass GestureCollectionViewCell: UICollectionViewCell {\n  @IBOutlet weak var iconImageView: UIImageView!\n  @IBOutlet weak var nameLabel: UILabel!\n  @IBOutlet weak var selectionImageView: UIImageView!\n}\n"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Cells/InfoCell.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\nclass InfoCell: UITableViewCell {\n  @IBOutlet weak var fieldNameLabel: UILabel!\n  @IBOutlet weak var infoLabel: UILabel!\n}\n"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Extension/StringExtension.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Foundation\n\nextension String {\n\n  func stringByTrimmingWhiteSpace() -> String {\n\n    return self.replacingOccurrences(of: \" \", with: \"\")\n  }\n}\n"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>TFL Gesture</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>NSCameraUsageDescription</key>\n\t<string>This app uses the camera to continuously classify the gestures the user shows.</string>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UIRequiresFullScreen</key>\n\t<true/>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Model/display_labels.txt",
    "content": "up\ndown\nleft\nright\nleft click\nright click\nscroll up\nscroll down\n"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/ModelDataHandler/ModelDataHandler.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Accelerate\nimport CoreImage\nimport TensorFlowLite\nimport UIKit\n\n// MARK: Structures That hold results\n/**\n Stores inference results for a particular frame that was successfully run through the\n TensorFlow Lite Interpreter.\n */\nstruct Result{\n  let inferenceTime: Double\n  let inferences: [Inference]\n}\n\n/**\n Stores one formatted inference.\n */\nstruct Inference {\n  let confidence: Float\n  let label: String\n}\n\n/// Information about a model file or labels file.\ntypealias FileInfo = (name: String, extension: String)\n\n// Information about the model to be loaded.\nenum Model {\n  static let modelInfo: FileInfo = (name: \"model\", extension: \"tflite\")\n  static let labelsInfo: FileInfo = (name: \"labels\", extension: \"txt\")\n}\n\n/**\n This class handles all data preprocessing and makes calls to run inference on\n a given frame through the TensorFlow Lite Interpreter. It then formats the\n inferences obtained and returns the top N results for a successful inference.\n */\nclass ModelDataHandler {\n\n  // MARK: Paremeters on which model was trained\n  let batchSize = 1\n  let wantedInputChannels = 3\n  let wantedInputWidth = 224\n  let wantedInputHeight = 224\n  let stdDeviation: Float = 127.0\n  let mean: Float = 1.0\n\n  // MARK: Constants\n  let threadCountLimit: Int32 = 10\n\n  // MARK: Instance Variables\n  /// The current thread count used by the TensorFlow Lite Interpreter.\n  let threadCount: Int\n\n  var labels: [String] = []\n  private let resultCount = 1\n  private let threshold = 0.5\n\n  /// TensorFlow Lite `Interpreter` object for performing inference on a given model.\n  private var interpreter: Interpreter\n\n  private let bgraPixel = (channels: 4, alphaComponent: 3, lastBgrComponent: 2)\n  private let rgbPixelChannels = 3\n  private let colorStrideValue = 10\n\n  /// Information about the alpha component in RGBA data.\n  private let alphaComponent = (baseOffset: 4, moduloRemainder: 3)\n\n  // MARK: Initializer\n  /**\n   This is a failable initializer for ModelDataHandler. It successfully initializes an object of the class if the model file and labels file is found, labels can be loaded and the interpreter of TensorflowLite can be initialized successfully.\n   */\n  init?(modelFileInfo: FileInfo, labelsFileInfo: FileInfo, threadCount: Int = 1) {\n    // Construct the path to the model file.\n    guard let modelPath = Bundle.main.path(\n      forResource: modelFileInfo.name,\n      ofType: modelFileInfo.extension\n    ) else {\n      print(\"Failed to load the model file with name: \\(modelFileInfo.name).\")\n      return nil\n    }\n\n    // Specify the options for the `Interpreter`.\n    self.threadCount = threadCount\n    var options = InterpreterOptions()\n    options.threadCount = threadCount\n    do {\n      // Create the `Interpreter`.\n      interpreter = try Interpreter(modelPath: modelPath, options: options)\n      // Allocate memory for the model's input `Tensor`s.\n      try interpreter.allocateTensors()\n    } catch let error {\n      print(\"Failed to create the interpreter with error: \\(error.localizedDescription)\")\n      return nil\n    }\n\n    // Opens and loads the classes listed in labels file\n    loadLabels(fromFileName: Model.labelsInfo.name, fileExtension: Model.labelsInfo.extension)\n  }\n\n  // MARK: Methods for data preprocessing and post processing.\n  /**\n   Performs image preprocessing, calls the TensorFlow Lite Interpreter methods\n   to feed the pixel buffer into the input tensor and  run inference\n   on the pixel buffer.\n   */\n  func runModel(onFrame pixelBuffer: CVPixelBuffer) -> Result? {\n\n    let sourcePixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer)\n    assert(sourcePixelFormat == kCVPixelFormatType_32ARGB ||\n      sourcePixelFormat == kCVPixelFormatType_32BGRA || sourcePixelFormat == kCVPixelFormatType_32RGBA)\n\n\n    let imageChannels = 4\n    assert(imageChannels >= wantedInputChannels)\n\n    // Crops the image to the biggest square in the center and scales it down to model dimensions.\n    guard let thumbnailPixelBuffer = centerThumbnail(from: pixelBuffer, size: CGSize(width: wantedInputWidth, height: wantedInputHeight)) else {\n      return nil\n    }\n\n    CVPixelBufferLockBaseAddress(thumbnailPixelBuffer, CVPixelBufferLockFlags(rawValue: 0))\n\n    let interval: TimeInterval\n    let outputTensor: Tensor\n    do {\n      let inputTensor = try interpreter.input(at: 0)\n\n      // Remove the alpha component from the image buffer to get the RGB data.\n      guard let rgbData = rgbDataFromBuffer(\n        thumbnailPixelBuffer,\n        byteCount: batchSize * wantedInputWidth * wantedInputHeight * wantedInputChannels,\n        isModelQuantized: inputTensor.dataType == .uInt8\n        ) else {\n          print(\"Failed to convert the image buffer to RGB data.\")\n          return nil\n      }\n\n      // Copy the RGB data to the input `Tensor`.\n      try interpreter.copy(rgbData, toInputAt: 0)\n\n      // Run inference by invoking the `Interpreter`.\n      let startDate = Date()\n      try interpreter.invoke()\n      interval = Date().timeIntervalSince(startDate) * 1000\n\n      // Get the output `Tensor` to process the inference results.\n      outputTensor = try interpreter.output(at: 0)\n    } catch let error {\n      print(\"Failed to invoke the interpreter with error: \\(error.localizedDescription)\")\n      return nil\n    }\n\n    let results: [Float]\n    switch outputTensor.dataType {\n    case .uInt8:\n      guard let quantization = outputTensor.quantizationParameters else {\n        print(\"No results returned because the quantization values for the output tensor are nil.\")\n        return nil\n      }\n      let quantizedResults = [UInt8](outputTensor.data)\n      results = quantizedResults.map {\n        quantization.scale * Float(Int($0) - quantization.zeroPoint)\n      }\n    case .float32:\n      results = [Float32](unsafeData: outputTensor.data) ?? []\n    default:\n      print(\"Output tensor data type \\(outputTensor.dataType) is unsupported for this example app.\")\n      return nil\n    }\n\n    // Process the results.\n    let topNInferences = getTopN(results: results)\n\n    // Return the inference time and inference results.\n    return Result(inferenceTime: interval, inferences: topNInferences)\n  }\n\n  /// Returns the RGB data representation of the given image buffer with the specified `byteCount`.\n  ///\n  /// - Parameters\n  ///   - buffer: The BGRA pixel buffer to convert to RGB data.\n  ///   - byteCount: The expected byte count for the RGB data calculated using the values that the\n  ///       model was trained on: `batchSize * imageWidth * imageHeight * componentsCount`.\n  ///   - isModelQuantized: Whether the model is quantized (i.e. fixed point values rather than\n  ///       floating point values).\n  /// - Returns: The RGB data representation of the image buffer or `nil` if the buffer could not be\n  ///     converted.\n  private func rgbDataFromBuffer(\n    _ buffer: CVPixelBuffer,\n    byteCount: Int,\n    isModelQuantized: Bool\n  ) -> Data? {\n    CVPixelBufferLockBaseAddress(buffer, .readOnly)\n    defer { CVPixelBufferUnlockBaseAddress(buffer, .readOnly) }\n\n    let pixelBufferFormat = CVPixelBufferGetPixelFormatType(buffer)\n    assert(pixelBufferFormat == kCVPixelFormatType_32BGRA)\n\n    guard let sourceData = CVPixelBufferGetBaseAddress(buffer) else {\n        return nil\n    }\n\n    let width = CVPixelBufferGetWidth(buffer)\n    let height = CVPixelBufferGetHeight(buffer)\n    let sourceBytesPerRow = CVPixelBufferGetBytesPerRow(buffer)\n    let destinationChannelCount = 3\n    let destinationBytesPerRow = destinationChannelCount * width\n\n    var sourceBuffer = vImage_Buffer(data: sourceData,\n                                     height: vImagePixelCount(height),\n                                     width: vImagePixelCount(width),\n                                     rowBytes: sourceBytesPerRow)\n\n    guard let destinationData = malloc(height * destinationBytesPerRow) else {\n        print(\"Error: out of memory\")\n        return nil\n    }\n\n    defer {\n        free(destinationData)\n    }\n\n    var destinationBuffer = vImage_Buffer(data: destinationData,\n                                          height: vImagePixelCount(height),\n                                          width: vImagePixelCount(width),\n                                          rowBytes: destinationBytesPerRow)\n\n    vImageConvert_BGRA8888toRGB888(&sourceBuffer, &destinationBuffer, UInt32(kvImageNoFlags))\n\n    let byteData = Data(bytes: destinationBuffer.data, count: destinationBuffer.rowBytes * height)\n    if isModelQuantized {\n        return byteData\n    }\n\n    // Not quantized, convert to floats\n    let bytes = Array<UInt8>(unsafeData: byteData)!\n    var floats = [Float]()\n    for i in 0..<bytes.count {\n        floats.append(Float(bytes[i]) / 255.0)\n    }\n    return Data(copyingBufferOf: floats)\n  }\n\n  /// Returns the top N inference results sorted in descending order.\n  private func getTopN(results: [Float]) -> [Inference] {\n    // Create a zipped array of tuples [(labelIndex: Int, confidence: Float)].\n    let zippedResults = zip(labels.indices, results)\n\n    // Sort the zipped results by confidence value in descending order.\n    let sortedResults = zippedResults.sorted { $0.1 > $1.1 }.prefix(resultCount)\n\n    // Return the `Inference` results.\n    return sortedResults.map { result in Inference(confidence: result.1, label: labels[result.0]) }\n  }\n\n  /**\n   Returns thumbnail by cropping pixel buffer to biggest square and scaling the cropped image to model dimensions.\n   */\n  private func centerThumbnail(from pixelBuffer: CVPixelBuffer, size: CGSize ) -> CVPixelBuffer? {\n\n    let imageWidth = CVPixelBufferGetWidth(pixelBuffer)\n    let imageHeight = CVPixelBufferGetHeight(pixelBuffer)\n    let pixelBufferType = CVPixelBufferGetPixelFormatType(pixelBuffer)\n\n    assert(pixelBufferType == kCVPixelFormatType_32BGRA)\n\n    let inputImageRowBytes = CVPixelBufferGetBytesPerRow(pixelBuffer)\n    let imageChannels = 4\n\n    let thumbnailSize = min(imageWidth, imageHeight)\n    CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))\n\n    var originX = 0\n    var originY = 0\n\n    if imageWidth > imageHeight {\n      originX = (imageWidth - imageHeight) / 2\n    }\n    else {\n      originY = (imageHeight - imageWidth) / 2\n    }\n\n    // Finds the biggest square in the pixel buffer and advances rows based on it.\n    guard let inputBaseAddress = CVPixelBufferGetBaseAddress(pixelBuffer)?.advanced(by: originY * inputImageRowBytes + originX * imageChannels) else {\n      return nil\n    }\n\n    // Gets vImage Buffer from input image\n    var inputVImageBuffer = vImage_Buffer(data: inputBaseAddress, height: UInt(thumbnailSize), width: UInt(thumbnailSize), rowBytes: inputImageRowBytes)\n\n    let thumbnailRowBytes = Int(size.width) * imageChannels\n    guard  let thumbnailBytes = malloc(Int(size.height) * thumbnailRowBytes) else {\n      return nil\n    }\n\n    // Allocates a vImage buffer for thumbnail image.\n    var thumbnailVImageBuffer = vImage_Buffer(data: thumbnailBytes, height: UInt(size.height), width: UInt(size.width), rowBytes: thumbnailRowBytes)\n\n    // Performs the scale operation on input image buffer and stores it in thumbnail image buffer.\n    let scaleError = vImageScale_ARGB8888(&inputVImageBuffer, &thumbnailVImageBuffer, nil, vImage_Flags(0))\n\n    CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))\n\n    guard scaleError == kvImageNoError else {\n      return nil\n    }\n\n    let releaseCallBack: CVPixelBufferReleaseBytesCallback = {mutablePointer, pointer in\n\n      if let pointer = pointer {\n        free(UnsafeMutableRawPointer(mutating: pointer))\n      }\n    }\n\n    var thumbnailPixelBuffer: CVPixelBuffer?\n\n    // Converts the thumbnail vImage buffer to CVPixelBuffer\n    let conversionStatus = CVPixelBufferCreateWithBytes(nil, Int(size.width), Int(size.height), pixelBufferType, thumbnailBytes, thumbnailRowBytes, releaseCallBack, nil, nil, &thumbnailPixelBuffer)\n\n    guard conversionStatus == kCVReturnSuccess else {\n\n      free(thumbnailBytes)\n      return nil\n    }\n\n    return thumbnailPixelBuffer\n  }\n\n  /**\n   Loads the labels from the labels file and stores it in an instance variable\n   */\n  func loadLabels(fromFileName fileName: String, fileExtension: String) {\n\n    guard let fileURL = Bundle.main.url(forResource: fileName, withExtension: fileExtension) else {\n      fatalError(\"Labels file not found in bundle. Please add a labels file with name \\(fileName).\\(fileExtension) and try again\")\n    }\n    do {\n      let contents = try String(contentsOf: fileURL, encoding: .utf8)\n      self.labels = contents.components(separatedBy: \",\")\n      self.labels.removeAll { (label) -> Bool in\n        return label == \"\"\n      }\n    }\n    catch {\n      fatalError(\"Labels file named \\(fileName).\\(fileExtension) cannot be read. Please add a valid labels file and try again.\")\n    }\n\n  }\n}\n\n// MARK: - Extensions\n\nextension Data {\n  /// Creates a new buffer by copying the buffer pointer of the given array.\n  ///\n  /// - Warning: The given array's element type `T` must be trivial in that it can be copied bit\n  ///     for bit with no indirection or reference-counting operations; otherwise, reinterpreting\n  ///     data from the resulting buffer has undefined behavior.\n  /// - Parameter array: An array with elements of type `T`.\n  init<T>(copyingBufferOf array: [T]) {\n    self = array.withUnsafeBufferPointer(Data.init)\n  }\n}\n\nextension Array {\n  /// Creates a new array from the bytes of the given unsafe data.\n  ///\n  /// - Warning: The array's `Element` type must be trivial in that it can be copied bit for bit\n  ///     with no indirection or reference-counting operations; otherwise, copying the raw bytes in\n  ///     the `unsafeData`'s buffer to a new array returns an unsafe copy.\n  /// - Note: Returns `nil` if `unsafeData.count` is not a multiple of\n  ///     `MemoryLayout<Element>.stride`.\n  /// - Parameter unsafeData: The data containing the bytes to turn into an array.\n  init?(unsafeData: Data) {\n    guard unsafeData.count % MemoryLayout<Element>.stride == 0 else { return nil }\n    #if swift(>=5.0)\n    self = unsafeData.withUnsafeBytes { .init($0.bindMemory(to: Element.self)) }\n    #else\n    self = unsafeData.withUnsafeBytes {\n      .init(UnsafeBufferPointer<Element>(\n        start: $0,\n        count: unsafeData.count / MemoryLayout<Element>.stride\n      ))\n    }\n    #endif  // swift(>=5.0)\n  }\n}\n"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/ViewControllers/InferenceViewController.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n// MARK: InferenceViewControllerDelegate Method Declarations\nprotocol InferenceViewControllerDelegate {\n\n  /**\n   This method is called when the user changes the stepper value to update number of threads used for inference.\n   */\n  func didChangeThreadCount(to count: Int)\n}\n\n/**This is used to represent the display name of a gesture and whether or not it is part of the classes on which the model is trained.\n */\nstruct GestureDisplay {\n  let name: String\n  let isEnabled: Bool\n}\n\nclass InferenceViewController: UIViewController {\n\n  // MARK: Information to display\n\n  private enum InferenceInfo: Int, CaseIterable {\n    case Resolution\n    case Crop\n    case InferenceTime\n\n    func displayString() -> String {\n\n      var toReturn = \"\"\n\n      switch self {\n      case .Resolution:\n        toReturn = \"Resolution\"\n      case .Crop:\n        toReturn = \"Crop\"\n      case .InferenceTime:\n        toReturn = \"Inference Time\"\n\n      }\n      return toReturn\n    }\n  }\n\n  // MARK: Storyboard Outlets\n  @IBOutlet weak var gestureCollectionViewHeight: NSLayoutConstraint!\n  @IBOutlet weak var stepperView: UIView!\n  @IBOutlet weak var tableView: UITableView!\n  @IBOutlet weak var threadStepper: UIStepper!\n  @IBOutlet weak var stepperValueLabel: UILabel!\n  @IBOutlet weak var gestureCollectionView: UICollectionView!\n  @IBOutlet weak var tableViewHeightConstraint: NSLayoutConstraint!\n\n  // MARK: Constants\n  private var normalCellHeight: CGFloat {\n   return lableHeight + labeltopSpacing * 2\n  }\n\n  private let lableHeight: CGFloat = 17.0\n  private let labeltopSpacing: CGFloat = 5.0\n  private let tableViewCollectionViewVerticalSpacing: CGFloat = 27.0\n  private let separatorCellHeight: CGFloat = 42.0\n  private let bottomSpacing: CGFloat = 21.0\n  private let minThreadCount = 1\n  private let bottomSheetButtonDisplayHeight: CGFloat = 44.0\n  private let infoTextColor = UIColor.black\n  private let lightTextInfoColor = UIColor(displayP3Red: 117.0/255.0, green: 117.0/255.0, blue: 117.0/255.0, alpha: 1.0)\n  private let infoFont = UIFont.systemFont(ofSize: 14.0, weight: .regular)\n  private let highlightedFont = UIFont.systemFont(ofSize: 14.0, weight: .medium)\n  private let collectionViewPadding: CGFloat = 15.0\n  private let heightPadding: CGFloat = 1.0\n  private let itemsInARow: CGFloat = 4.0\n  private let imageInset: CGFloat = 8.0\n  private let stepperSuperViewHeight: CGFloat = 53.0\n\n  var gestures: [GestureDisplay] = []\n  var result: Result?\n\n  var collectionViewHeight: CGFloat {\n    get {\n      let collectionViewWidth = self.view.bounds.size.width - (2 * collectionViewPadding)\n      let itemWidth = (collectionViewWidth - ((itemsInARow - 1) * collectionViewPadding)) / itemsInARow\n\n      return (itemWidth * 2.0) + collectionViewPadding + heightPadding\n    }\n  }\n\n  // MARK: Instance Variables\n  var inferenceResult: Result? = nil\n  var wantedInputWidth: Int = 0\n  var wantedInputHeight: Int = 0\n  var resolution: CGSize = CGSize.zero\n  var maxResults: Int = 0\n  var threadCountLimit: Int = 0\n  var currentThreadCount: Int = 0\n  private var highlightedRow = -1\n\n  // MARK: Delegate\n  var delegate: InferenceViewControllerDelegate?\n\n  // MARK: Computed properties\n  var collapsedHeight: CGFloat {\n    return collectionViewHeight + bottomSheetButtonDisplayHeight + tableViewCollectionViewVerticalSpacing\n\n  }\n\n  private var tableViewHeight: CGFloat {\n\n    return CGFloat(InferenceInfo.allCases.count - 1) * normalCellHeight + lableHeight + separatorCellHeight + labeltopSpacing\n  }\n\n  override func viewDidLoad() {\n    super.viewDidLoad()\n\n    // Set up stepper\n    threadStepper.isUserInteractionEnabled = true\n    threadStepper.maximumValue = Double(threadCountLimit)\n    threadStepper.minimumValue = Double(minThreadCount)\n    threadStepper.value = Double(currentThreadCount)\n    gestureCollectionView.reloadData()\n\n  }\n\n  override func viewWillLayoutSubviews() {\n    super.viewWillLayoutSubviews()\n\n    gestureCollectionViewHeight.constant = collectionViewHeight\n    tableViewHeightConstraint.constant = tableViewHeight\n  }\n\n  func expandedHeight() -> CGFloat {\n\n    return collapsedHeight + tableViewHeight + stepperSuperViewHeight\n  }\n\n  // MARK: Gesture Highlight Methods\n  /**\n   Displays the gestures specified in the parameter.\n   */\n  func displayGestures(gestures: [GestureDisplay]) {\n    self.gestures = gestures\n    highlightGesture(with: -1)\n  }\n\n  /**\n   Refreshes the results in collectionview by highlighting currently identified gesture.\n   */\n  func refreshResults() {\n\n    highlightIdentifiedGesture()\n    self.tableView.reloadData()\n\n  }\n\n  /**Highlights the currently identified gesture by mapping the name of top inference with the gestures that are displayed.\n   */\n  private func highlightIdentifiedGesture() {\n\n    guard let inferences = result?.inferences, inferences.count > 0 else {\n      return\n    }\n\n    guard  let index = self.gestures.index(where: {$0.name.stringByTrimmingWhiteSpace() == inferences[0].label}) else {\n      return\n    }\n\n    highlightGesture(with: index)\n\n  }\n\n  /**Highlights the currently identified gesture.\n   */\n  func highlightGesture(with index: Int) {\n\n    self.highlightedRow = index\n    self.gestureCollectionView.reloadData()\n  }\n\n  // MARK: Buttion Actions\n  /**\n   Delegate the change of number of threads to View Controller and change the stepper display.\n   */\n  @IBAction func onClickThreadStepper(_ sender: Any) {\n\n    delegate?.didChangeThreadCount(to: Int(threadStepper.value))\n    currentThreadCount = Int(threadStepper.value)\n    stepperValueLabel.text = \"\\(currentThreadCount)\"\n  }\n}\n\n// MARK: UITableView Data Source\nextension InferenceViewController: UITableViewDelegate, UITableViewDataSource {\n\n  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {\n\n    return InferenceInfo.allCases.count\n  }\n\n  func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {\n\n    let height = indexPath.row == InferenceInfo.allCases.count - 1 ? (separatorCellHeight + labeltopSpacing + lableHeight) : normalCellHeight\n\n    return height\n  }\n\n  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {\n\n    let cell = tableView.dequeueReusableCell(withIdentifier: \"INFO_CELL\") as! InfoCell\n\n    var fieldName = \"\"\n    var info = \"\"\n\n    let tuple = displayStringsForInferenceInfo(atRow: indexPath.row)\n    fieldName = tuple.0\n    info = tuple.1\n\n    cell.fieldNameLabel.font = infoFont\n    cell.fieldNameLabel.textColor = infoTextColor\n    cell.fieldNameLabel.text = fieldName\n    cell.infoLabel.text = info\n    return cell\n  }\n\n  /**\n   This method formats the display of additional information relating to the inferences.\n   */\n  func displayStringsForInferenceInfo(atRow row: Int) -> (String, String) {\n\n    var fieldName: String = \"\"\n    var info: String = \"\"\n\n    guard let inferenceInfo = InferenceInfo(rawValue: row) else {\n      return (fieldName, info)\n    }\n\n    fieldName = inferenceInfo.displayString()\n\n    switch inferenceInfo {\n    case .Resolution:\n      info = \"\\(Int(resolution.width))x\\(Int(resolution.height))\"\n    case .Crop:\n      info = \"\\(wantedInputWidth)x\\(wantedInputHeight)\"\n    case .InferenceTime:\n      guard let finalResults = result else {\n        info = \"0ms\"\n        break\n      }\n      info = String(format: \"%.2fms\", finalResults.inferenceTime)\n    }\n\n    return(fieldName, info)\n  }\n}\n\n// MARK: UICollectionView Data Source and Delegate\nextension InferenceViewController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {\n\n  func numberOfSections(in collectionView: UICollectionView) -> Int {\n    return 1\n  }\n\n  func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {\n\n    return gestures.count\n  }\n\n  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {\n\n    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: \"GESTURE_CELL\", for: indexPath) as! GestureCollectionViewCell\n\n    let gesture = gestures[indexPath.item]\n    var selectionImage: UIImage?\n\n    let imageName = gesture.name.lowercased().replacingOccurrences(of: \" \", with: \"_\")\n    cell.iconImageView.image = UIImage(named: imageName)\n\n    cell.nameLabel.text = gesture.name.uppercased().replacingOccurrences(of: \" \", with: \"\\n\")\n\n    // Shows gestures which are not part of the trained set as disabled.\n    cell.alpha = gesture.isEnabled ? 1.0 : 0.3\n\n    // Highlights gesture currently identified.\n    if indexPath.item == highlightedRow {\n      selectionImage = UIImage(named: \"selection_base\")?.resizableImage(withCapInsets: UIEdgeInsets(top: imageInset, left: imageInset, bottom: imageInset, right: imageInset), resizingMode: .stretch)\n    }\n    else {\n      selectionImage = UIImage(named: \"selection_base_default\")?.resizableImage(withCapInsets: UIEdgeInsets(top: imageInset, left: imageInset, bottom: imageInset, right: imageInset), resizingMode: .stretch)\n    }\n    cell.selectionImageView.image = selectionImage\n\n    return  cell\n  }\n\n  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {\n\n    let collectionViewWidth = self.view.bounds.size.width - 2 * collectionViewPadding\n    let itemWidth = (collectionViewWidth - ((itemsInARow - 1) * collectionViewPadding)) / itemsInARow\n    let size = CGSize(width: itemWidth, height: itemWidth)\n\n    return size\n  }\n}\n\n\n"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/ViewControllers/ViewController.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport AVFoundation\nimport UIKit\n\nclass ViewController: UIViewController {\n\n  // MARK: Storyboards Connections\n  @IBOutlet weak var bottomSheetView: CurvedView!\n  @IBOutlet weak var previewView: PreviewView!\n  @IBOutlet weak var containerViewBottomSpace: NSLayoutConstraint!\n  @IBOutlet weak var containerViewHeight: NSLayoutConstraint!\n  @IBOutlet weak var cameraUnavailableLabel: UILabel!\n  @IBOutlet weak var resumeButton: UIButton!\n  @IBOutlet weak var upImageView: UIImageView!\n\n  // MARK: Constants\n  private let animationDuration = 0.5\n  private let bottomSheetMinTransitionThreshold: CGFloat = 40.0\n\n  // MARK: Instance Variables\n  var initialBottomSpace: CGFloat = 0.0\n\n  // MARK: Controllers that manage functionality\n  // Handles all the camera related functionality\n  private lazy var cameraCapture = CameraFeedManager(previewView: previewView)\n\n  // Handles all data preprocessing and makes calls to run inference\n  private var modelDataHandler =\n    ModelDataHandler(modelFileInfo: Model.modelInfo, labelsFileInfo: Model.labelsInfo)\n\n  // Handles the presenting of results on the screen\n  private var inferenceViewController: InferenceViewController?\n\n  // MARK: View Handling Methods\n  override func viewDidLoad() {\n    super.viewDidLoad()\n\n    // Checks if ModelDataHandler initialization was successful to determine if execution should be continued or not.\n    guard modelDataHandler != nil else {\n      fatalError(\"Model set up failed\")\n    }\n\n    cameraCapture.delegate = self\n\n    // Displays the gestures\n    displayGestures()\n\n    addPanGesture()\n\n  }\n\n  override func viewWillLayoutSubviews() {\n    super.viewWillLayoutSubviews()\n    guard let expandedHeight = inferenceViewController?.expandedHeight() else {\n      return\n    }\n    containerViewHeight.constant = expandedHeight\n  }\n\n  override func viewWillAppear(_ animated: Bool) {\n    super.viewWillAppear(animated)\n    cameraCapture.checkCameraConfigurationAndStartSession()\n    changeViewState()\n  }\n\n  override func viewWillDisappear(_ animated: Bool) {\n    cameraCapture.stopSession()\n  }\n\n  override var preferredStatusBarStyle: UIStatusBarStyle {\n    return .lightContent\n  }\n\n  // MARK: Storyboard Segue Handlers\n  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {\n    super.prepare(for: segue, sender: sender)\n\n    if segue.identifier == \"EMBED\" {\n\n      guard let tempModelDataHandler = modelDataHandler else {\n        return\n      }\n      inferenceViewController = segue.destination as? InferenceViewController\n      inferenceViewController?.wantedInputHeight = tempModelDataHandler.wantedInputHeight\n      inferenceViewController?.wantedInputWidth = tempModelDataHandler.wantedInputWidth\n      inferenceViewController?.threadCountLimit = Int(tempModelDataHandler.threadCountLimit)\n      inferenceViewController?.currentThreadCount = Int(tempModelDataHandler.threadCount)\n      inferenceViewController?.delegate = self\n\n    }\n  }\n\n  // MARK: Button Actions\n  @IBAction func onClickResumeButton(_ sender: Any) {\n\n    // Let's user resume session manually by button click\n    cameraCapture.resumeInterruptedSession { (complete) in\n\n      if complete {\n        self.resumeButton.isHidden = true\n        self.cameraUnavailableLabel.isHidden = true\n      }\n      else {\n        self.presentUnableToResumeSessionAlert()\n      }\n    }\n  }\n\n  private func presentUnableToResumeSessionAlert() {\n    let alert = UIAlertController(title: \"Unable to Resume Session\", message: \"There was an error while attempting to resume session.\", preferredStyle: .alert)\n    alert.addAction(UIAlertAction(title: \"OK\", style: .default, handler: nil))\n\n    present(alert, animated: true)\n  }\n\n  // MARK: Gesture Display Handling Methods\n\n  /** This method displays the gestures on which the model could be trained. It enables only the gestures on which the model is trained. All the other gestures are shown disabled in the UI\n   */\n  private func displayGestures() {\n\n    guard let tempModelDataHandler = modelDataHandler else {\n      return\n    }\n\n    let labels = loadLabels(fromFileName: \"display_labels\", fileExtension: \"txt\")\n    let gestures:[GestureDisplay]  = labels.map { ( label ) -> GestureDisplay in\n\n      var isEnabled = true\n\n      if !tempModelDataHandler.labels.contains(label.stringByTrimmingWhiteSpace()) {\n        isEnabled = false\n      }\n\n      return GestureDisplay(name: label, isEnabled: isEnabled)\n    }\n    inferenceViewController?.displayGestures(gestures: gestures)\n  }\n\n  /**\n   Loads the labels from the labels file and stores it in an instance variable\n   */\n  private func loadLabels(fromFileName fileName: String, fileExtension: String) -> [String] {\n\n    var labels: [String] = []\n    guard let fileURL = Bundle.main.url(forResource: fileName, withExtension: fileExtension) else {\n      fatalError(\"Labels file not found in bundle. Please add a labels file with name \\(fileName).\\(fileExtension) and try again\")\n    }\n    do {\n      let contents = try String(contentsOf: fileURL, encoding: .utf8)\n      labels = contents.components(separatedBy: .newlines)\n      labels.removeAll { (label) -> Bool in\n        return label == \"\"\n      }\n    }\n    catch {\n      fatalError(\"Labels file named \\(fileName).\\(fileExtension) cannot be read. Please add a valid labels file and try again.\")\n    }\n\n    return labels\n  }\n\n}\n\n// MARK: InferenceViewControllerDelegate Methods\nextension ViewController: InferenceViewControllerDelegate {\n\n  func didChangeThreadCount(to count: Int) {\n    if modelDataHandler?.threadCount == count { return }\n    modelDataHandler =\n      ModelDataHandler(modelFileInfo: Model.modelInfo, labelsFileInfo: Model.labelsInfo)\n  }\n}\n\n// MARK: CameraFeedManagerDelegate Methods\nextension ViewController: CameraFeedManagerDelegate {\n\n  func didOutput(pixelBuffer: CVPixelBuffer) {\n\n    let height = CVPixelBufferGetHeight(pixelBuffer)\n    let width = CVPixelBufferGetWidth(pixelBuffer)\n\n    // Runs the pixel buffer through the model.\n    let result = modelDataHandler?.runModel(onFrame: pixelBuffer)\n\n    DispatchQueue.main.async {\n      // Hands off to the InferenceViewController to format and display results.\n      self.inferenceViewController?.result = result\n      self.inferenceViewController?.resolution = CGSize(width: width, height: height)\n      self.inferenceViewController?.refreshResults()\n\n    }\n  }\n\n  // MARK: Session Handling Alerts\n  func sessionWasInterrupted(canResumeManually resumeManually: Bool) {\n\n    // Updates the UI when session is interupted.\n    if resumeManually == true {\n      resumeButton.isHidden = false\n    }\n    else {\n      cameraUnavailableLabel.isHidden = false\n    }\n  }\n\n  func sessionInterruptionEnded() {\n\n    // Updates UI once session interruption has ended.\n    if !cameraUnavailableLabel.isHidden {\n      cameraUnavailableLabel.isHidden = true\n    }\n\n    if !resumeButton.isHidden {\n      resumeButton.isHidden = true\n    }\n  }\n\n  func sessionRunTimeErrorOccured() {\n    // Handles session run time error by updating the UI and providing a button if session can be manually resumed.\n    resumeButton.isHidden = false\n  }\n\n  func presentCameraPermissionsDeniedAlert() {\n    let alertController = UIAlertController(title: \"Camera Permissions Denied\", message: \"Camera permissions have been denied for this app. You can change this by going to Settings\", preferredStyle: .alert)\n\n    let cancelAction = UIAlertAction(title: \"Cancel\", style: .cancel, handler: nil)\n    let settingsAction = UIAlertAction(title: \"Settings\", style: .default) { (action) in\n      UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)\n    }\n\n    alertController.addAction(cancelAction)\n    alertController.addAction(settingsAction)\n\n    present(alertController, animated: true, completion: nil)\n  }\n\n  func presentVideoConfigurationErrorAlert() {\n    let alert = UIAlertController(title: \"Camera Configuration Failed\", message: \"There was an error while configuring camera.\", preferredStyle: .alert)\n    alert.addAction(UIAlertAction(title: \"OK\", style: .default, handler: nil))\n\n    present(alert, animated: true)\n  }\n}\n\n// MARK: Bottom Sheet Interaction Methods\nextension ViewController {\n\n  // MARK: Bottom Sheet Interaction Methods\n  /**\n   This method adds a pan gesture to make the bottom sheet interactive.\n   */\n  private func addPanGesture() {\n    let panGesture = UIPanGestureRecognizer(target: self, action: #selector(ViewController.didPan(panGesture:)))\n    bottomSheetView.addGestureRecognizer(panGesture)\n  }\n\n\n  /** Change whether bottom sheet should be in expanded or collapsed state.\n   */\n  private func changeViewState() {\n\n    guard let inferenceVC = inferenceViewController else {\n      return\n    }\n\n    if containerViewBottomSpace.constant == inferenceVC.collapsedHeight - inferenceVC.expandedHeight() {\n\n      containerViewBottomSpace.constant = 0.0\n    }\n    else {\n      containerViewBottomSpace.constant =  inferenceVC.collapsedHeight - inferenceVC.expandedHeight()\n    }\n    setImageBasedOnBottomViewState()\n  }\n\n  /**\n   Set image of the bottom sheet icon based on whether it is expanded or collapsed\n   */\n  private func setImageBasedOnBottomViewState() {\n\n    if containerViewBottomSpace.constant == 0.0 {\n      upImageView.image = UIImage(named: \"down_icon\")\n    }\n    else {\n      upImageView.image = UIImage(named: \"up_icon\")\n    }\n  }\n\n  /**\n   This method responds to the user panning on the bottom sheet.\n   */\n  @objc func didPan(panGesture: UIPanGestureRecognizer) {\n\n    // Opens or closes the bottom sheet based on the user's interaction with the bottom sheet.\n    let translation = panGesture.translation(in: view)\n\n    switch panGesture.state {\n    case .began:\n      initialBottomSpace = containerViewBottomSpace.constant\n      translateBottomSheet(withVerticalTranslation: translation.y)\n    case .changed:\n      translateBottomSheet(withVerticalTranslation: translation.y)\n    case .cancelled:\n      setBottomSheetLayout(withBottomSpace: initialBottomSpace)\n    case .ended:\n      translateBottomSheetAtEndOfPan(withVerticalTranslation: translation.y)\n      setImageBasedOnBottomViewState()\n      initialBottomSpace = 0.0\n    default:\n      break\n    }\n  }\n\n  /**\n   This method sets bottom sheet translation while pan gesture state is continuously changing.\n   */\n  private func translateBottomSheet(withVerticalTranslation verticalTranslation: CGFloat) {\n\n    let bottomSpace = initialBottomSpace - verticalTranslation\n    guard bottomSpace <= 0.0 && bottomSpace >= inferenceViewController!.collapsedHeight - bottomSheetView.bounds.size.height else {\n      return\n    }\n    setBottomSheetLayout(withBottomSpace: bottomSpace)\n  }\n\n  /**\n   This method changes bottom sheet state to either fully expanded or closed at the end of pan.\n   */\n  private func translateBottomSheetAtEndOfPan(withVerticalTranslation verticalTranslation: CGFloat) {\n\n    // Changes bottom sheet state to either fully open or closed at the end of pan.\n    let bottomSpace = bottomSpaceAtEndOfPan(withVerticalTranslation: verticalTranslation)\n    setBottomSheetLayout(withBottomSpace: bottomSpace)\n  }\n\n  /**\n   Return the final state of the bottom sheet view (whether fully collapsed or expanded) that is to be retained.\n   */\n  private func bottomSpaceAtEndOfPan(withVerticalTranslation verticalTranslation: CGFloat) -> CGFloat {\n\n    // Calculates whether to fully expand or collapse bottom sheet when pan gesture ends.\n    var bottomSpace = initialBottomSpace - verticalTranslation\n\n    var height: CGFloat = 0.0\n    if initialBottomSpace == 0.0 {\n      height = bottomSheetView.bounds.size.height\n    }\n    else {\n      height = inferenceViewController!.collapsedHeight\n    }\n\n    let currentHeight = containerViewHeight.constant + bottomSpace\n\n    if currentHeight - height <= -bottomSheetMinTransitionThreshold {\n      bottomSpace = inferenceViewController!.collapsedHeight - containerViewHeight.constant\n    }\n    else if currentHeight - height >= bottomSheetMinTransitionThreshold {\n      bottomSpace = 0.0\n    }\n    else {\n      bottomSpace = initialBottomSpace\n    }\n\n    return bottomSpace\n  }\n\n  /**\n   This method layouts the change of the bottom space of bottom sheet with respect to the view managed by this controller.\n   */\n  func setBottomSheetLayout(withBottomSpace bottomSpace: CGFloat) {\n\n    view.layoutIfNeeded()\n    containerViewBottomSpace.constant = bottomSpace\n    view.layoutIfNeeded()\n  }\n\n}\n\n"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification/Views/CurvedView.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\nclass CurvedView: UIView {\n\n  let cornerRadius: CGFloat = 24.0\n\n  override func layoutSubviews() {\n    super.layoutSubviews()\n    setCornerMask()\n  }\n\n  func setCornerMask() {\n    let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))\n\n    let shapeLayer = CAShapeLayer()\n    shapeLayer.path = path.cgPath\n    self.layer.mask = shapeLayer\n  }\n\n}\n"
  },
  {
    "path": "lite/examples/gesture_classification/ios/GestureClassification.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tAA0F2AF0219408430077CDCB /* model.tflite in Resources */ = {isa = PBXBuildFile; fileRef = AA0F2AEE219408430077CDCB /* model.tflite */; };\n\t\tAA84742A2183A31D0097B995 /* InfoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8474292183A31D0097B995 /* InfoCell.swift */; };\n\t\tAAA8CE4C21839F720049BD23 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA8CE4B21839F720049BD23 /* AppDelegate.swift */; };\n\t\tAAA8CE5121839F720049BD23 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AAA8CE4F21839F720049BD23 /* Main.storyboard */; };\n\t\tAAA8CE5321839F740049BD23 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AAA8CE5221839F740049BD23 /* Assets.xcassets */; };\n\t\tAAA8CE5621839F740049BD23 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AAA8CE5421839F740049BD23 /* LaunchScreen.storyboard */; };\n\t\tAAA8CE602183A0B20049BD23 /* PreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA8CE5E2183A0B20049BD23 /* PreviewView.swift */; };\n\t\tAAA8CE612183A0B20049BD23 /* CameraFeedManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA8CE5F2183A0B20049BD23 /* CameraFeedManager.swift */; };\n\t\tAAA8CE682183A0EB0049BD23 /* GestureCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA8CE672183A0EB0049BD23 /* GestureCollectionViewCell.swift */; };\n\t\tAAA8CE6B2183A0F40049BD23 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA8CE6A2183A0F40049BD23 /* StringExtension.swift */; };\n\t\tAAA8CE752183A1030049BD23 /* ModelDataHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA8CE742183A1030049BD23 /* ModelDataHandler.swift */; };\n\t\tAAA8CE792183A10A0049BD23 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA8CE772183A10A0049BD23 /* ViewController.swift */; };\n\t\tAAA8CE7A2183A10A0049BD23 /* InferenceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA8CE782183A10A0049BD23 /* InferenceViewController.swift */; };\n\t\tAAA8CE7D2183A1120049BD23 /* CurvedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA8CE7C2183A1120049BD23 /* CurvedView.swift */; };\n\t\tAAEA2D2F218746C800930A1D /* display_labels.txt in Resources */ = {isa = PBXBuildFile; fileRef = AAEA2D2E218746C800930A1D /* display_labels.txt */; };\n\t\tAC92C1DF2907968200593767 /* labels.txt in Resources */ = {isa = PBXBuildFile; fileRef = AC92C1DE2907968200593767 /* labels.txt */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\tAA0F2AEE219408430077CDCB /* model.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = model.tflite; sourceTree = \"<group>\"; };\n\t\tAA8474292183A31D0097B995 /* InfoCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InfoCell.swift; sourceTree = \"<group>\"; };\n\t\tAAA8CE4821839F720049BD23 /* GestureClassification.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GestureClassification.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tAAA8CE4B21839F720049BD23 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\tAAA8CE5021839F720049BD23 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\tAAA8CE5221839F740049BD23 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tAAA8CE5521839F740049BD23 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\tAAA8CE5721839F740049BD23 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tAAA8CE5E2183A0B20049BD23 /* PreviewView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewView.swift; sourceTree = \"<group>\"; };\n\t\tAAA8CE5F2183A0B20049BD23 /* CameraFeedManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraFeedManager.swift; sourceTree = \"<group>\"; };\n\t\tAAA8CE672183A0EB0049BD23 /* GestureCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GestureCollectionViewCell.swift; sourceTree = \"<group>\"; };\n\t\tAAA8CE6A2183A0F40049BD23 /* StringExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = \"<group>\"; };\n\t\tAAA8CE742183A1030049BD23 /* ModelDataHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModelDataHandler.swift; sourceTree = \"<group>\"; };\n\t\tAAA8CE772183A10A0049BD23 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = \"<group>\"; };\n\t\tAAA8CE782183A10A0049BD23 /* InferenceViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InferenceViewController.swift; sourceTree = \"<group>\"; };\n\t\tAAA8CE7C2183A1120049BD23 /* CurvedView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurvedView.swift; sourceTree = \"<group>\"; };\n\t\tAAEA2D2E218746C800930A1D /* display_labels.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = display_labels.txt; sourceTree = \"<group>\"; };\n\t\tAC92C1D32907918E00593767 /* download_model.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; name = download_model.sh; path = RunScripts/download_model.sh; sourceTree = SOURCE_ROOT; };\n\t\tAC92C1DE2907968200593767 /* labels.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = labels.txt; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tAAA8CE4521839F720049BD23 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tAAA8CE3F21839F720049BD23 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAAA8CE4A21839F720049BD23 /* GestureClassification */,\n\t\t\t\tAAA8CE4921839F720049BD23 /* Products */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAAA8CE4921839F720049BD23 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAAA8CE4821839F720049BD23 /* GestureClassification.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAAA8CE4A21839F720049BD23 /* GestureClassification */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAAA8CE4B21839F720049BD23 /* AppDelegate.swift */,\n\t\t\t\tAAA8CE7B2183A1120049BD23 /* Views */,\n\t\t\t\tAAA8CE762183A10A0049BD23 /* ViewControllers */,\n\t\t\t\tAAA8CE732183A1030049BD23 /* ModelDataHandler */,\n\t\t\t\tAAA8CE6C2183A0FB0049BD23 /* Model */,\n\t\t\t\tAAA8CE692183A0F40049BD23 /* Extension */,\n\t\t\t\tAAA8CE662183A0EB0049BD23 /* Cells */,\n\t\t\t\tAAA8CE5D2183A0B20049BD23 /* Camera Feed */,\n\t\t\t\tAAA8CE4F21839F720049BD23 /* Main.storyboard */,\n\t\t\t\tAAA8CE5221839F740049BD23 /* Assets.xcassets */,\n\t\t\t\tAAA8CE5421839F740049BD23 /* LaunchScreen.storyboard */,\n\t\t\t\tAAA8CE5721839F740049BD23 /* Info.plist */,\n\t\t\t\tAC92C1D32907918E00593767 /* download_model.sh */,\n\t\t\t);\n\t\t\tpath = GestureClassification;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAAA8CE5D2183A0B20049BD23 /* Camera Feed */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAAA8CE5E2183A0B20049BD23 /* PreviewView.swift */,\n\t\t\t\tAAA8CE5F2183A0B20049BD23 /* CameraFeedManager.swift */,\n\t\t\t);\n\t\t\tpath = \"Camera Feed\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAAA8CE662183A0EB0049BD23 /* Cells */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAA8474292183A31D0097B995 /* InfoCell.swift */,\n\t\t\t\tAAA8CE672183A0EB0049BD23 /* GestureCollectionViewCell.swift */,\n\t\t\t);\n\t\t\tpath = Cells;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAAA8CE692183A0F40049BD23 /* Extension */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAAA8CE6A2183A0F40049BD23 /* StringExtension.swift */,\n\t\t\t);\n\t\t\tpath = Extension;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAAA8CE6C2183A0FB0049BD23 /* Model */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAC92C1DE2907968200593767 /* labels.txt */,\n\t\t\t\tAA0F2AEE219408430077CDCB /* model.tflite */,\n\t\t\t\tAAEA2D2E218746C800930A1D /* display_labels.txt */,\n\t\t\t);\n\t\t\tpath = Model;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAAA8CE732183A1030049BD23 /* ModelDataHandler */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAAA8CE742183A1030049BD23 /* ModelDataHandler.swift */,\n\t\t\t);\n\t\t\tpath = ModelDataHandler;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAAA8CE762183A10A0049BD23 /* ViewControllers */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAAA8CE772183A10A0049BD23 /* ViewController.swift */,\n\t\t\t\tAAA8CE782183A10A0049BD23 /* InferenceViewController.swift */,\n\t\t\t);\n\t\t\tpath = ViewControllers;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAAA8CE7B2183A1120049BD23 /* Views */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAAA8CE7C2183A1120049BD23 /* CurvedView.swift */,\n\t\t\t);\n\t\t\tpath = Views;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\tAAA8CE4721839F720049BD23 /* GestureClassification */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = AAA8CE5A21839F740049BD23 /* Build configuration list for PBXNativeTarget \"GestureClassification\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tAC92C1D4290791C800593767 /* ShellScript */,\n\t\t\t\tAAA8CE4421839F720049BD23 /* Sources */,\n\t\t\t\tAAA8CE4521839F720049BD23 /* Frameworks */,\n\t\t\t\tAAA8CE4621839F720049BD23 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = GestureClassification;\n\t\t\tproductName = GestureClassification;\n\t\t\tproductReference = AAA8CE4821839F720049BD23 /* GestureClassification.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tAAA8CE4021839F720049BD23 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 1000;\n\t\t\t\tLastUpgradeCheck = 1000;\n\t\t\t\tORGANIZATIONNAME = \"Y Media Labs\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tAAA8CE4721839F720049BD23 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 10.0;\n\t\t\t\t\t\tLastSwiftMigration = 1000;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = AAA8CE4321839F720049BD23 /* Build configuration list for PBXProject \"GestureClassification\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = AAA8CE3F21839F720049BD23;\n\t\t\tproductRefGroup = AAA8CE4921839F720049BD23 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tAAA8CE4721839F720049BD23 /* GestureClassification */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\tAAA8CE4621839F720049BD23 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tAA0F2AF0219408430077CDCB /* model.tflite in Resources */,\n\t\t\t\tAC92C1DF2907968200593767 /* labels.txt in Resources */,\n\t\t\t\tAAA8CE5621839F740049BD23 /* LaunchScreen.storyboard in Resources */,\n\t\t\t\tAAA8CE5321839F740049BD23 /* Assets.xcassets in Resources */,\n\t\t\t\tAAA8CE5121839F720049BD23 /* Main.storyboard in Resources */,\n\t\t\t\tAAEA2D2F218746C800930A1D /* display_labels.txt in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\tAC92C1D4290791C800593767 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"${SRCROOT}/RunScripts/download_model.sh\\n\";\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tAAA8CE4421839F720049BD23 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tAAA8CE682183A0EB0049BD23 /* GestureCollectionViewCell.swift in Sources */,\n\t\t\t\tAAA8CE7D2183A1120049BD23 /* CurvedView.swift in Sources */,\n\t\t\t\tAAA8CE4C21839F720049BD23 /* AppDelegate.swift in Sources */,\n\t\t\t\tAAA8CE752183A1030049BD23 /* ModelDataHandler.swift in Sources */,\n\t\t\t\tAAA8CE6B2183A0F40049BD23 /* StringExtension.swift in Sources */,\n\t\t\t\tAAA8CE792183A10A0049BD23 /* ViewController.swift in Sources */,\n\t\t\t\tAAA8CE612183A0B20049BD23 /* CameraFeedManager.swift in Sources */,\n\t\t\t\tAAA8CE7A2183A10A0049BD23 /* InferenceViewController.swift in Sources */,\n\t\t\t\tAAA8CE602183A0B20049BD23 /* PreviewView.swift in Sources */,\n\t\t\t\tAA84742A2183A31D0097B995 /* InfoCell.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\tAAA8CE4F21839F720049BD23 /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tAAA8CE5021839F720049BD23 /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAAA8CE5421839F740049BD23 /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tAAA8CE5521839F740049BD23 /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\tAAA8CE5821839F740049BD23 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tAAA8CE5921839F740049BD23 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tAAA8CE5B21839F740049BD23 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tHEADER_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"'${SRCROOT}/Pods/TensorFlowLite/Frameworks/tensorflow_lite.framework/Headers'\",\n\t\t\t\t\t\"\\\"${PODS_ROOT}/Headers/Public\\\"\",\n\t\t\t\t\t\"\\\"${PODS_ROOT}/Headers/Public/TensorFlowLite\\\"\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = GestureClassification/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.tensorflow.GestureClassification;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 4.2;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tAAA8CE5C21839F740049BD23 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tHEADER_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"'${SRCROOT}/Pods/TensorFlowLite/Frameworks/tensorflow_lite.framework/Headers'\",\n\t\t\t\t\t\"\\\"${PODS_ROOT}/Headers/Public\\\"\",\n\t\t\t\t\t\"\\\"${PODS_ROOT}/Headers/Public/TensorFlowLite\\\"\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = GestureClassification/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.tensorflow.GestureClassification;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"\";\n\t\t\t\tSWIFT_VERSION = 4.2;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tAAA8CE4321839F720049BD23 /* Build configuration list for PBXProject \"GestureClassification\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tAAA8CE5821839F740049BD23 /* Debug */,\n\t\t\t\tAAA8CE5921839F740049BD23 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tAAA8CE5A21839F740049BD23 /* Build configuration list for PBXNativeTarget \"GestureClassification\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tAAA8CE5B21839F740049BD23 /* Debug */,\n\t\t\t\tAAA8CE5C21839F740049BD23 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = AAA8CE4021839F720049BD23 /* Project object */;\n}\n"
  },
  {
    "path": "lite/examples/gesture_classification/ios/Podfile",
    "content": "# Uncomment the next line to define a global platform for your project\nplatform :ios, '12.0'\n\ntarget 'GestureClassification' do\n  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks\n  use_frameworks!\n  # Pods for GestureClassification\n  pod 'TensorFlowLiteSwift'\nend\n"
  },
  {
    "path": "lite/examples/gesture_classification/ios/README.md",
    "content": "# TensorFlow Lite Gesture Classification iOS Example\n\n**iOS Versions Supported:** iOS 12.0 and above.\n**Xcode Version Required:** 10.0 and above\n\n## Overview\nThis is a camera app that continuously classifies the gestures that the user shows, through the front camera. The model used in this app can be trained using a webcam. The instructions to be followed to train the model and convert it to a TFLite model can be found [here](../web/README.md). These instructions walk you through building and running the demo on an iOS device.\n\n<!-- TODO(b/124116863): Add app screenshot. -->\n\n## Prerequisites\n\n* You must have Xcode installed\n\n* You must have a valid Apple Developer ID\n\n* The demo app requires a camera and must be executed on a real iOS device. You can build it and run with the iPhone Simulator but the app raises a camera not found exception.\n\n* You don't need to build the entire TensorFlow library to run the demo, it uses CocoaPods to download the TensorFlow Lite library\n\n* You'll also need the Xcode command-line tools:\n```xcode-select --install```\nIf this is a new install, you will need to run the Xcode application once to agree to the license before continuing.\n\n## Building the iOS Demo App\n\n1. Install CocoaPods if you don't have it.\n```sudo gem install cocoapods```\n\n2. Install the pod to generate the workspace file:\n```cd lite/examples/gesture_classification/ios```\n```pod install```\nIf you have installed this pod before and that command doesn't work, try\n```pod update```\nAt the end of this step you should have a file called ```GestureClassification.xcworkspace```\n\n3. Open **GestureClassification.xcworkspace** in Xcode.\n\n4. This app uses a model that can be trained using images it collects from webcam. The instructions to train and convert the model can be found [here](../web/README.md).\nYou should add the downloaded TFLite model (filename **\"model.tflite\"**) and labels file (filename **\"labels.txt\"**) to the iOS app bundle. You can do this by dragging and dropping these files to the **'Model'** folder in **'Project Navigator**'.\nWhen you drag and drop the files, you will get the following dialogue. Please click on **Finish** to add the files.\n\n5. Please change the bundle identifier to a unique identifier and select your development team in **'General->Signing'** before building the application if you are using an iOS device.\n\n6. Build and run the app in Xcode.\nYou'll have to grant permissions for the app to use the device's camera. You can show the gestures you trained in step 4 and the app identifies them in realtime!\n\n## Model Used\nThis app uses [MobileNet](https://ai.googleblog.com/2017/06/mobilenets-open-source-models-for.html) model that is trained on 0.25 alpha and at an image size of 224 X 244 X 3.\n\n## iOS App Overview\nThis app is written in both Swift and Objective C. All application functionality, image processing and results formatting is developed in Swift.\nObjective C is used via bridging to make the TensorFlow Lite C++ framework calls.\n\n\n## See Also\n\n* [Apple Developer Guide on Importing Objective C into Swift](https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_objective-c_into_swift)\nThis documentation provides information on how to use code written in Objective C for an application written in Swift.\n\n## See Also\n\n[Gesture Web Application](../web/README.md)\n[Gesture ML Script](../ml/README.md)\n[Gesture Android Example](../android/README.md)\n\n"
  },
  {
    "path": "lite/examples/gesture_classification/ios/RunScripts/download_model.sh",
    "content": "#!/bin/bash\n# Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\n# Download TF Lite model from the internet if it does not exist.\n\nMODEL_FOLDER=\"GestureClassification/Model\"\nTFLITE_MODEL=\"${MODEL_FOLDER}/model.tflite\"\nTFLITE_LABELS=\"${MODEL_FOLDER}/labels.txt\"\nTFLITE_MODEL_REMOTE_PATH=\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/gesture_classification/ios/model_metadata.tflite\"\nTFLITE_LABELS_REMOTE_PATH=\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/gesture_classification/android/labels.txt\"\n\nif [ ! -f \"${TFLITE_MODEL}\" ]; then\n    # Download model\n    curl --create-dirs -o \"${TFLITE_MODEL}\" \"${TFLITE_MODEL_REMOTE_PATH}\"\n    echo \"INFO: Downloaded TensorFlow Lite model to $RESOURCES_DIR .\"\nfi\n\nif [ ! -f \"${TFLITE_LABELS}\" ]; then\n    # Download labels\n    curl --create-dirs -o \"${TFLITE_LABELS}\" \"${TFLITE_LABELS_REMOTE_PATH}\"\n    echo \"INFO: Download TensorFlow Lite labels to $RESOURCES_DIR .\"\nfi\n"
  },
  {
    "path": "lite/examples/gesture_classification/ml/.gitignore",
    "content": "/.idea/\n/__pycache__/"
  },
  {
    "path": "lite/examples/gesture_classification/ml/README.md",
    "content": "# TensorFlow.js to TensorFlow Lite Model Conversion\n\nThis guide shows how you can go about converting the model trained with\nTensorFlowJS to TensorFlow Lite FlatBuffers.\n\n## Generate the TensorFlow.js model\n\nTo know more about generating the gesture classification tfjs model, go\n[here](../web/README.md).\n\nAfter following the steps below you will have `model.tflite` file downloaded,\nafter which you can proceed with running [Android Example](../android/README.md)\nor [iOS Example](../ios/README.md).\n\n## Colab notebook to convert the TensorFlow.js model to TensorFlow Lite\n\nYou can convert the model using this\n[Colab notebook](https://colab.research.google.com/github/tensorflow/examples/blob/master/lite/examples/gesture_classification/ml/tensorflowjs_to_tflite_colab_notebook.ipynb)\nand don't need to install any dependencies in your system.\n\nFollow the steps in the notebook and you'll be asked to upload the `model.json`\nand `model.weights.bin` files to Colab. They are the TensorFlow.js model that\nyou have generated with the [web app](../web/README.md).\n\nRun all steps in-order. At the end, `model.tflite` file will be downloaded.\n\n## Requirements\n\n* TensorFlow.js 3.8.0 or above\n* TensorFlow 2.5.0 or above\n\n## Conversion Advanced Information\n\n### Deserializing the Keras model from TensorFlow.js\n\nTo begin with, we have to convert the sequential block trained with\nTensorFlow.js to Keras H5. This is done by passing the JSON model configuration\nand the binary weights files downloaded from TensorFlow.js to the following\nconverter which generates a tf.Keras equivalent H5 model. This is achieved by\nemploying tfjs-converter's load_keras_model method which is going to deserialize\nthe Keras objects.\n\n### Merging the base model and the classification block\n\nThis step involves loading the aforementioned custom classification block that\nwas just generated and then passing the output of the base model's intermediate\nDepthwise Convolutional Layer's activation as input to the top classification\nblock.\n\n```python\n\n...\nlayer = self.base_model.get_layer('<insert the intermediate layer name here>') # e.g., conv_pw_13_relu\nmodel = Model(inputs=self.base_model.input, outputs=self.top_model(layer.output))\n...\n\n```\n\n### Generating the TensorFlow Lite model\n\nAfter obtaining the Keras model, the TensorFlow Lite Converter is used to\nconvert the model from Keras to TFLite FlatBuffers.\n\n```python\nconverter = tf.lite.TFLiteConverter.from_keras_model(model)\ntflite_model = converter.convert()\n```\n\nAll of the above actions can be performed by running `convert.py`\n\n## References\n\nTo know more about converting TensorFlow models from a SavedModel, Keras H5,\nSession Bundle, Frozen Model or TensorFlow Hub module to a web-friendly format\nand exporting a tfjs model to SavedModel, visit\n[here](https://github.com/tensorflow/tfjs-converter).\n\n## See Also\n\n[Gesture Web Application](../web/README.md)\n[Gesture Android Example](../android/README.md)\n[Gesture iOS Example](../ios/README.md)\n"
  },
  {
    "path": "lite/examples/gesture_classification/ml/tensorflowjs_to_tflite_colab_notebook.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Tce3stUlHN0L\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"tuOe1ymfHZPu\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"D6zc4Q6bxmHM\"\n      },\n      \"source\": [\n        \"# Tensorflow Lite Gesture Classification Example Conversion Script\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"This guide shows how you can go about converting the model trained with TensorFlowJS to TensorFlow Lite FlatBuffers.\\n\",\n        \"\\n\",\n        \"Run all steps in-order. At the end, `model.tflite` file will be downloaded.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"MfBg1C5NB3X0\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/mobile/examples/gesture_classification/ml/tensorflowjs_to_tflite_colab_notebook.ipynb\\\">\\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />\\n\",\n        \"    Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/mobile/examples/gesture_classification/ml/tensorflowjs_to_tflite_colab_notebook.ipynb\\\">\\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />\\n\",\n        \"    View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"yVMF3Q_HnJ09\"\n      },\n      \"source\": [\n        \"**Install Dependencies**\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"FbMsNJ4PAq2j\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!pip install -q tensorflowjs\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"WZGFStffPj_Z\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import traceback\\n\",\n        \"import logging\\n\",\n        \"import tensorflow as tf \\n\",\n        \"import os\\n\",\n        \"\\n\",\n        \"from google.colab import files\\n\",\n        \"from tensorflow import keras\\n\",\n        \"from tensorflowjs.converters import load_keras_model\\n\",\n        \"\\n\",\n        \"logging.basicConfig(level=logging.INFO)\\n\",\n        \"logger = logging.getLogger(__name__)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Lh7zgNXVx8ML\"\n      },\n      \"source\": [\n        \"***Cleanup any existing models if necessary***\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"JrMA8frMx7aa\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!rm -rf *.h5 *.tflite *.json *.bin\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ct36DONNnNZJ\"\n      },\n      \"source\": [\n        \"**Upload your Tensorflow.js Artifacts Here**\\n\",\n        \"\\n\",\n        \"i.e., The weights manifest **model.json** and the binary weights file **model-weights.bin**\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"s-_80hGtMTFb\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"files.upload()\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"_ctAZ--FnStM\"\n      },\n      \"source\": [\n        \"**Export Configuration**\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"hrzzoZP5oK7S\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Export Configuration\\n\",\n        \"\\n\",\n        \"# TensorFlow.js arguments\\n\",\n        \"\\n\",\n        \"config_json = \\\"model.json\\\" #@param {type:\\\"string\\\"}\\n\",\n        \"weights_path_prefix = None #@param {type:\\\"raw\\\"}\\n\",\n        \"model_tflite = \\\"model.tflite\\\" #@param {type:\\\"string\\\"}\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"RA0iINpNiK_p\"\n      },\n      \"source\": [\n        \"**Model Converter**\\n\",\n        \"\\n\",\n        \"The following class converts a TensorFlow.js model to a TFLite FlatBuffer\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"8QMjVgxVggQJ\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"class ModelConverter:\\n\",\n        \"    \\\"\\\"\\\"\\n\",\n        \"    Creates a ModelConverter class from a TensorFlow.js model file.\\n\",\n        \"\\n\",\n        \"    Args:\\n\",\n        \"      :param config_json_path: Full filepath of weights manifest file containing the model architecture.\\n\",\n        \"      :param weights_path_prefix: Full filepath to the directory in which the weights binaries exist.\\n\",\n        \"      :param tflite_model_file: Name of the TFLite FlatBuffer file to be exported.\\n\",\n        \"\\n\",\n        \"    :return:\\n\",\n        \"      ModelConverter class.\\n\",\n        \"    \\\"\\\"\\\"\\n\",\n        \"\\n\",\n        \"    def __init__(self,\\n\",\n        \"                 config_json_path,\\n\",\n        \"                 weights_path_prefix,\\n\",\n        \"                 tflite_model_file\\n\",\n        \"                 ):\\n\",\n        \"        self.config_json_path = config_json_path\\n\",\n        \"        self.weights_path_prefix = weights_path_prefix\\n\",\n        \"        self.tflite_model_file = tflite_model_file\\n\",\n        \"        self.keras_model_file = 'merged.h5'\\n\",\n        \"\\n\",\n        \"        # MobileNet Options\\n\",\n        \"        self.input_node_name = 'the_input'\\n\",\n        \"        self.image_size = 224\\n\",\n        \"        self.alpha = 0.25\\n\",\n        \"        self.depth_multiplier = 1\\n\",\n        \"        self._input_shape = (1, self.image_size, self.image_size, 3)\\n\",\n        \"        self.depthwise_conv_layer = 'conv_pw_13_relu'\\n\",\n        \"\\n\",\n        \"    def convert(self):\\n\",\n        \"        self.save_keras_model()\\n\",\n        \"        self._deserialize_tflite_from_keras()\\n\",\n        \"        logger.info('The TFLite model has been generated')\\n\",\n        \"\\n\",\n        \"    def save_keras_model(self):\\n\",\n        \"        top_model = load_keras_model(self.config_json_path, self.weights_path_prefix,\\n\",\n        \"                                     weights_data_buffers=None,\\n\",\n        \"                                     load_weights=True,\\n\",\n        \"                                     )\\n\",\n        \"        base_model = self.get_base_model()\\n\",\n        \"        self._merged_model = self.merge(base_model, top_model)\\n\",\n        \"\\n\",\n        \"        logger.info(\\\"The merged Keras has been generated.\\\")\\n\",\n        \"\\n\",\n        \"    def merge(self, base_model, top_model):\\n\",\n        \"        \\\"\\\"\\\"\\n\",\n        \"        Merges base model with the classification block\\n\",\n        \"        :return:  Returns the merged Keras model\\n\",\n        \"        \\\"\\\"\\\"\\n\",\n        \"        logger.info(\\\"Initializing model...\\\")\\n\",\n        \"\\n\",\n        \"        layer = base_model.get_layer(self.depthwise_conv_layer)\\n\",\n        \"        model = keras.Model(inputs=base_model.input, outputs=top_model(layer.output))\\n\",\n        \"        logger.info(\\\"Model created.\\\")\\n\",\n        \"\\n\",\n        \"        return model\\n\",\n        \"\\n\",\n        \"    def get_base_model(self):\\n\",\n        \"        \\\"\\\"\\\"\\n\",\n        \"        Builds MobileNet with the default parameters\\n\",\n        \"        :return:  Returns the base MobileNet model\\n\",\n        \"        \\\"\\\"\\\"\\n\",\n        \"        input_tensor = keras.Input(shape=self._input_shape[1:], name=self.input_node_name)\\n\",\n        \"        base_model = keras.applications.MobileNet(input_shape=self._input_shape[1:],\\n\",\n        \"                               alpha=self.alpha,\\n\",\n        \"                               depth_multiplier=self.depth_multiplier,\\n\",\n        \"                               input_tensor=input_tensor,\\n\",\n        \"                               include_top=False)\\n\",\n        \"        return base_model\\n\",\n        \"\\n\",\n        \"    def _deserialize_tflite_from_keras(self):\\n\",\n        \"        converter = tf.lite.TFLiteConverter.from_keras_model(self._merged_model)\\n\",\n        \"        tflite_model = converter.convert()\\n\",\n        \"\\n\",\n        \"        with open(self.tflite_model_file, \\\"wb\\\") as file:\\n\",\n        \"            file.write(tflite_model)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"qUeoHM-Jg7uv\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"try:\\n\",\n        \"    converter = ModelConverter(config_json,\\n\",\n        \"                               weights_path_prefix,\\n\",\n        \"                               model_tflite)\\n\",\n        \"\\n\",\n        \"    converter.convert()\\n\",\n        \"\\n\",\n        \"except ValueError as e:\\n\",\n        \"    print(traceback.format_exc())\\n\",\n        \"    print(\\\"Error occurred while converting.\\\")\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"G7noTBgTg8Fz\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"files.download(model_tflite)\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"tensorflowjs_to_tflite_colab_notebook.ipynb\",\n      \"provenance\": []\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "lite/examples/gesture_classification/web/.gitignore",
    "content": "/.cache/\n/node_modules/\n/dist/\n"
  },
  {
    "path": "lite/examples/gesture_classification/web/README.md",
    "content": "# Gesture Classification Web App\n**Browsers Supported:** Chrome, Safari.\n\nNote : Model download does not work in Firefox.\n\n## Overview\nThis is a web app used to predict gesture from a webcam using transfer learning. We will use a pretrained truncated MobileNet model and train another model using the internal MobileNet activation to predict upto 8 different classes from the webcam defined by the user.\n\n`index.html` can be opened directly inside a browser. No need to have a web server to run the web app.\n\n## About the Model\nTo learn to classify different classes from the webcam in a reasonable amount of time, we will retrain, or fine-tune, a pretrained MobileNet model, using the internal activation (the output from an internal layer of MobileNet) as input to our new model.\n\nTo do this, we'll be having two models on the page.\n\nOne model will be the pretrained MobileNet model that is truncated to output the internal activations. We'll call this the \"truncated MobileNet model\". This model does not get trained after being loaded into the browser.\n\nThe second model will take as input the output of the internal activation of the truncated MobileNet model and will predict probabilities for each of the selected output classes which can be up, down, left, right, left click, right click, scroll up and scroll down. This is the model we'll actually train in the browser.\n\nBy using the internal activation of MobileNet, we can reuse the features that MobileNet has already learned to predict the 1000 classes of ImageNet with a relatively small amount of retraining.\n\n## Model Specifications\n\nThe base model being used here is MobileNet with a width of .25 and input image size of 224 X 224. The width and the input size can be varied.\n* Width: 0.25, 0.50, 0.75 or 1.0 (default: 0.25).\n* Image size: 128, 160, 192 or 224 (default: 224).\n\nYou can choose to pick an intermediate depth wise convolutional layer such as `conv_pw_13_relu` by calling `getLayer('conv_pw_13_relu')`. Try choosing another layer and see how it affects model quality! You can use model.layers to print the layers of the model.\n\n## Prerequisites\n\n* This app requires webcam.\n\n## Importing TensorFlow.js Library\n```\n<script src=\"https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@VERSION\"> </script>\n```\nYou can change the version by replacing `VERSION` with appropriate version. eg: 0.13.0.\n\n## How to use the App\n\n1. Open index.html in your browser.\n\n2. Collect adequate samples for each of the required gestures by clicking on their icons.\n\n3. Set the parameters for training the model such as Learning rate, Batch size, Number of Epochs and Number of Hidden Units in the top sequential model.\n\n4. Click on Train button to train the model. Wait for sometime until the model is trained.\n\n5. Once the model is trained you can either choose to test or download the model. Upon downloading the model, totally 3 files will be generated which are weights manifest file (model.json), the binary weights file(model-weights.bin) and labels.txt for the chosen gestures.\n\n## Conversion to TensorFlow Lite\n  To carry on with the conversion process to TFLite model, visit [here](../ml/README.md).\n\n## Additional instruction for Safari\nEnable Develop Menu > WebRTC > Allow Media Capture on Insecure Sites\n\n# See Also\n\n\n[Gesture ML Script](../ml/README.md)\n[Gesture Android Example](../android/README.md)\n[Gesture iOS Application](../ios/README.md)\n\n"
  },
  {
    "path": "lite/examples/gesture_classification/web/controller_dataset.js",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n\n/**\n * A dataset for webcam controls which allows the user to add example Tensors\n * for particular labels. This object will concat them into two large xs and ys.\n */\n\nclass ControllerDataset {\n  constructor() {\n    this.labels = []\n  }\n\n  /**\n   * Adds an example to the controller dataset.\n   * @param {Tensor} example A tensor representing the example. It can be an\n   *     image, an activation, or any other type of Tensor.\n   * @param {number} label The label of the example. Should be a number.\n   */\n  addExample(example, label) {\n    // One-hot encode the label.\n    const y = label;\n\n    if (this.xs == null) {\n      // For the first example that gets added, keep example and y so that the\n      // ControllerDataset owns the memory of the inputs. This makes sure that\n      // if addExample() is called in a tf.tidy(), these Tensors will not get\n      // disposed.\n      this.xs = tf.keep(example);\n      this.labels.push(labelsCaptured.indexOf(y));\n    } else {\n      const oldX = this.xs;\n      this.xs = tf.keep(oldX.concat(example, 0));\n      this.labels.push(labelsCaptured.indexOf(y));\n\n      oldX.dispose();\n    }\n  }\n  addLabels(numClasses) {\n    for (var i = 0; i < this.labels.length; i++) {\n      if (this.ys == null) {\n        this.ys = tf.keep(tf.tidy(\n            () => {return tf.oneHot(\n                tf.tensor1d([this.labels[i]]).toInt(), numClasses)}));\n      } else {\n        const y = tf.tidy(\n            () => {return tf.oneHot(\n                tf.tensor1d([this.labels[i]]).toInt(), numClasses)});\n        const oldY = this.ys;\n        this.ys = tf.keep(oldY.concat(y, 0));\n        oldY.dispose();\n        y.dispose();\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/gesture_classification/web/index.html",
    "content": "<!--\nCopyright 2019 Google LLC. All Rights Reserved.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\nhttp://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n-->\n\n<!DOCTYPE html>\n<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n<meta charset=\"UTF-8\">\n<title>Tensorflow Web</title>\n<link href=\"https://fonts.googleapis.com/css?family=Roboto\" rel=\"stylesheet\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">\n</head>\n\n<body>\n  <h1>Model Training</h1>\n  <section class=\"top-section\">\n\n<!-- Hyper params. -->\n    <div class=\"left-dropdown-container\">\n\n      <!-- Learning rate -->\n      <div class=\"dropdown-wrapper\">\n        <label>LEARNING RATE</label>\n        <div class=\"dropdown\">\n\n          <!-- <select id=\"learningRate\">\n            <option value=\"0.00001\">0.00001</option>\n            <option selected value=\"0.0001\">0.0001</option>\n            <option value=\"0.01\">0.001</option>\n            <option value=\"0.03\">0.003</option>\n          </select>\n          <img class=\"vertically-center\" src=\"assets/rectangle-5.svg\" /> -->\n\n\n          <div class=\"custom-dropdown\" id=\"learningRate-dropdown\">\n            <div id=\"learningRate\" class=\"custom-select\"></div>\n            <img class=\"vertically-center\" src=\"assets/rectangle-5.svg\" />\n          </div>\n          <div id=\"learningRate-dropdown-list\" class=\"custom-option-list hide\" dropdownID = \"learningRate\">\n            <div class=\"custom-option\"><span class=\"icon\"><img src=\"assets/rectangle-6.png\"/></span>0.00001</div>\n            <div class=\"custom-option selected\"><span class=\"icon\"><img src=\"assets/rectangle-6.png\"/></span>0.0001</div>\n            <div class=\"custom-option\"><span class=\"icon\"><img src=\"assets/rectangle-6.png\"/></span>0.001</div>\n            <div class=\"custom-option\"><span class=\"icon\"><img src=\"assets/rectangle-6.png\"/></span>0.003</div>\n          </div>\n        </div>\n      </div>\n\n      <!-- Batch size -->\n      <div class=\"dropdown-wrapper\">\n        <label>BATCH SIZE</label>\n        <div class=\"dropdown\">\n            <!-- <select id=\"batchSizeFraction\">\n              <option value=\"0.05\">0.05</option>\n              <option value=\"0.1\">0.1</option>\n              <option selected value=\"0.4\">0.4</option>\n              <option value=\"1\">1</option>\n          </select>\n          <img class=\"vertically-center\" src=\"assets/rectangle-5.svg\" /> -->\n\n\n          <div class=\"custom-dropdown\" id=\"batchSizeFraction-dropdown\">\n            <div id=\"batchSizeFraction\" class=\"custom-select\"></div>\n            <img class=\"vertically-center\" src=\"assets/rectangle-5.svg\" />\n          </div>\n          <div id=\"batchSizeFraction-dropdown-list\" class=\"custom-option-list hide\" dropdownID = \"batchSizeFraction\">\n            <div class=\"custom-option\"><span class=\"icon\"><img src=\"assets/rectangle-6.png\"/></span>0.05</div>\n            <div class=\"custom-option\"><span class=\"icon\"><img src=\"assets/rectangle-6.png\"/></span>0.1</div>\n            <div class=\"custom-option selected\"><span class=\"icon\"><img src=\"assets/rectangle-6.png\"/></span>0.4</div>\n            <div class=\"custom-option\"><span class=\"icon\"><img src=\"assets/rectangle-6.png\"/></span>1</div>\n          </div>\n\n        </div>\n      </div>\n\n      <!-- Epochs -->\n      <div class=\"dropdown-wrapper\">\n        <label>EPOCHS</label>\n        <div class=\"dropdown\">\n          <!-- <select id=\"epochs\">\n            <option value=\"10\">10</option>\n            <option selected value=\"20\">20</option>\n            <option value=\"40\">40</option>\n          </select>\n          <img class=\"vertically-center\" src=\"assets/rectangle-5.svg\" /> -->\n\n          <div class=\"custom-dropdown\" id=\"epochs-dropdown\">\n            <div id=\"epochs\" class=\"custom-select\"></div>\n            <img class=\"vertically-center\" src=\"assets/rectangle-5.svg\" />\n          </div>\n          <div id=\"epochs-dropdown-list\" class=\"custom-option-list hide\" dropdownID = \"epochs\">\n            <div class=\"custom-option\"><span class=\"icon\"><img src=\"assets/rectangle-6.png\"/></span>10</div>\n            <div class=\"custom-option selected\"><span class=\"icon\"><img src=\"assets/rectangle-6.png\"/></span>20</div>\n            <div class=\"custom-option\"><span class=\"icon\"><img src=\"assets/rectangle-6.png\"/></span>40</div>\n          </div>\n\n\n        </div>\n      </div>\n\n      <!-- Hidden units -->\n      <div class=\"dropdown-wrapper\">\n        <label>HIDDEN UNITS</label>\n        <div class=\"dropdown\">\n          <!-- <select id=\"dense-units\">\n            <option value=\"10\">10</option>\n            <option selected value=\"100\">100</option>\n            <option value=\"200\">200</option>\n          </select>\n          <img class=\"vertically-center\" src=\"assets/rectangle-5.svg\" /> -->\n\n\n          <div class=\"custom-dropdown\" id=\"dense-units-dropdown\">\n            <div id=\"dense-units\" class=\"custom-select\"></div>\n            <img class=\"vertically-center\" src=\"assets/rectangle-5.svg\" />\n          </div>\n          <div id=\"dense-units-dropdown-list\" class=\"custom-option-list hide\" dropdownID = \"dense-units\">\n            <div class=\"custom-option\"><span class=\"icon\"><img src=\"assets/rectangle-6.png\"/></span>10</div>\n            <div class=\"custom-option selected\"><span class=\"icon\"><img src=\"assets/rectangle-6.png\"/></span>100</div>\n            <div class=\"custom-option\"><span class=\"icon\"><img src=\"assets/rectangle-6.png\"/></span>200</div>\n          </div>\n\n        </div>\n      </div>\n    </div>\n\n    <div class=\"webcam-container\">\n        <div class=\"webcam-outer-wrapper\" id=\"webcam-outer-wrapper\">\n          <div id=\"webcam-inner-wrapper\" class=\"webcam-inner-wrapper center\">\n            <div id=\"no-webcam\" class=\"hide\">\n              <img class=\"emoji\" src=\"assets/icn_emoji.png\"/>\n              <p>No webcam found.</p>\n              <p>You will need a webcam device to use this demo.</p>\n            </div>\n            <video autoplay playsinline muted width=\"224px\" height=\"224px\" id=\"webcam\" ></video>\n          </div>\n      </div>\n    </div>\n\n    <div class=\"right-button-container\">\n\n      <div>\n        <!-- Train Model -->\n        <button id=\"train\" class=\"train-button disabled\"><span id=\"train-status\">TRAIN MODEL</span></button>\n      </div>\n\n      <div>\n        <!-- Test Model -->\n        <button class=\"test-button disabled\" id=\"predict\">TEST</button>\n        <button class=\"stop-button hide\" id=\"stop-predict\">STOP</button>\n      </div>\n\n      <div>\n        <!-- Download Model -->\n        <button id=\"download-model\" class=\"download-button disabled\" download >DOWNLOAD MODEL</button>\n      </div>\n    </div>\n\n  </section>\n\n  <!-- User Help  -->\n  <div id=\"user-help\" style=\"visibility:hidden\">\n    <img src=\"assets/shape.svg\" /><span id=\"user-help-text\"></span>\n  </div>\n\n  <section id=\"bottom-section\" class=\"bottom-section\">\n    <div class=\"control-button-wrapper\">\n      <div class=\"control-inner-wrapper\" id=\"up-button\">\n      <div class=\"control-button\"  id=\"up\">\n        <div class=\"control-icon center\" id=\"up-icon\"><div><img src=\"assets/up.svg\"/></div>UP</div>\n        <div class=\"add-icon invisible\" id=\"up-add-icon\">\n          <img class=\"center\" src=\"assets/icon.svg\"/>\n        </div>\n        <canvas width=\"224\" height=\"224\" class=\"thumb hide\" id=\"up-thumb\"></canvas>\n      </div>\n      </div>\n      <div class=\"sample-count\" id=\"up-total\"><img src=\"assets/invalid-name.svg\"/></div>\n\n    </div>\n    <div class=\"control-button-wrapper\">\n      <div class=\"control-inner-wrapper\" id=\"down-button\">\n      <div class=\"control-button\" id=\"down\">\n        <div class=\"control-icon center\" id=\"down-icon\"><div><img src=\"assets/down.svg\"/></div>DOWN</div>\n        <div class=\"add-icon invisible\" id=\"down-add-icon\">\n          <img class=\"center\" src=\"assets/icon.svg\"/></div>\n        <canvas width=\"224\" height=\"224\" class=\"thumb hide\" id=\"down-thumb\"></canvas>\n      </div>\n      </div>\n      <div class=\"sample-count\" id=\"down-total\"><img src=\"assets/invalid-name.svg\"/></div>\n\n    </div>\n    <div class=\"control-button-wrapper\">\n      <div class=\"control-inner-wrapper\" id=\"left-button\">\n      <div class=\"control-button\" id=\"left\">\n        <div class=\"control-icon center\" id=\"left-icon\"><div><img src=\"assets/left.svg\"/></div>LEFT</div>\n        <div class=\"add-icon invisible\" id=\"left-add-icon\">\n          <img class=\"center\" src=\"assets/icon.svg\"/>\n        </div>\n        <canvas width=\"224\" height=\"224\" class=\"thumb hide\" id=\"left-thumb\"></canvas>\n      </div>\n    </div>\n      <div class=\"sample-count\" id=\"left-total\"><img src=\"assets/invalid-name.svg\"/></div>\n\n    </div>\n    <div class=\"control-button-wrapper\">\n      <div class=\"control-inner-wrapper\" id=\"right-button\">\n      <div class=\"control-button\" id=\"right\">\n        <div class=\"control-icon center\" id=\"right-icon\"><div><img src=\"assets/right.svg\"/></div>RIGHT</div>\n        <div class=\"add-icon invisible\" id=\"right-add-icon\" >\n          <img class=\"center\" src=\"assets/icon.svg\"/>\n        </div>\n        <canvas width=\"224\" height=\"224\" class=\"thumb hide\" id=\"right-thumb\"></canvas>\n      </div>\n      </div>\n      <div class=\"sample-count\" id=\"right-total\"><img src=\"assets/invalid-name.svg\"/></div>\n\n    </div>\n    <div class=\"control-button-wrapper\">\n      <div class=\"control-inner-wrapper\" id=\"leftclick-button\">\n      <div class=\"control-button\" id=\"leftclick\">\n        <div class=\"control-icon center\" id=\"leftclick-icon\"><div><img src=\"assets/leftclick.svg\"/></div>LEFT CLICK</div>\n        <div class=\"add-icon invisible\" id=\"leftclick-add-icon\">\n          <img class=\"center\" src=\"assets/icon.svg\"/>\n        </div>\n          <canvas width=\"224\" height=\"224\" class=\"thumb hide\" id=\"leftclick-thumb\"></canvas>\n      </div>\n      </div>\n      <div class=\"sample-count\" id=\"leftclick-total\"><img src=\"assets/invalid-name.svg\"/></div>\n\n    </div>\n    <div class=\"control-button-wrapper\">\n      <div class=\"control-inner-wrapper\" id=\"rightclick-button\">\n      <div class=\"control-button\" id=\"rightclick\">\n        <div class=\"control-icon center\" id=\"rightclick-icon\"><div><img src=\"assets/rightclick.svg\"/></div>RIGHT CLICK</div>\n        <div class=\"add-icon invisible\" id=\"rightclick-add-icon\">\n          <img class=\"center\" src=\"assets/icon.svg\"/>\n        </div>\n        <canvas width=\"224\" height=\"224\" class=\"thumb hide\" id=\"rightclick-thumb\"></canvas>\n      </div>\n      </div>\n      <div class=\"sample-count\" id=\"rightclick-total\"><img src=\"assets/invalid-name.svg\"/></div>\n\n    </div>\n    <div class=\"control-button-wrapper\">\n      <div class=\"control-inner-wrapper\" id=\"scrollup-button\">\n      <div class=\"control-button\" id=\"scrollup\">\n        <div class=\"control-icon center\" id=\"scrollup-icon\"><div><img src=\"assets/scrollup.svg\"/></div>SCROLL UP</div>\n        <div class=\"add-icon invisible\" id=\"scrollup-add-icon\" >\n          <img class=\"center\" src=\"assets/icon.svg\"/>\n        </div>\n        <canvas width=\"224\" height=\"224\" class=\"thumb hide\" id=\"scrollup-thumb\"></canvas>\n      </div>\n      </div>\n      <div class=\"sample-count\" id=\"scrollup-total\"><img src=\"assets/invalid-name.svg\"/></div>\n\n    </div>\n    <div class=\"control-button-wrapper\">\n      <div class=\"control-inner-wrapper\" id=\"scrolldown-button\">\n      <div class=\"control-button\" id=\"scrolldown\">\n        <div class=\"control-icon center\" id=\"scrolldown-icon\"><div><img src=\"assets/scrolldown.svg\"/></div>SCROLL DOWN</div>\n        <div class=\"add-icon invisible\" id=\"scrolldown-add-icon\">\n          <img class=\"center\" src=\"assets/icon.svg\"/>\n        </div>\n        <canvas width=\"224\" height=\"224\" class=\"thumb hide\" id=\"scrolldown-thumb\"></canvas>\n      </div>\n      </div>\n      <div class=\"sample-count\" id=\"scrolldown-total\"><img src=\"assets/invalid-name.svg\"/></div>\n\n    </div>\n  </section>\n</body>\n\n<!-- Load TensorFlow.js -->\n<script src=\"https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest\"> </script>\n\n\n<script src=\"ui.js\"></script>\n<script src=\"webcam.js\"></script>\n<script src=\"controller_dataset.js\"></script>\n<script src=\"index.js\"></script>\n</html>\n"
  },
  {
    "path": "lite/examples/gesture_classification/web/index.js",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n\n// The number of classes we want to predict.\n\nvar NUM_CLASSES = 0;\n\n// A webcam class that generates Tensors from the images from the webcam.\nconst webcam = new Webcam(document.getElementById('webcam'));\n\n// The dataset object where we will store activations.\nconst controllerDataset = new ControllerDataset(NUM_CLASSES);\n\nlet mobilenet;\nlet model;\n\n// Loads mobilenet and returns a model that returns the internal activation\n// we'll use as input to our classifier model.\nasync function loadMobilenet() {\n  const mobilenet = await tf.loadLayersModel(\n      'https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_0.25_224/model.json');\n\n  // Return a model that outputs an internal activation.\n  const layer = mobilenet.getLayer('conv_pw_13_relu');\n  return tf.model({inputs: mobilenet.inputs, outputs: layer.output});\n}\n\n// When the UI buttons are pressed, read a frame from the webcam and associate\n// it with the class label given by the button. up, down, left, right are\n// labels 0, 1, 2, 3 respectively.\nui.setExampleHandler(label => {\n  tf.tidy(() => {\n    const img = webcam.capture();\n    controllerDataset.addExample(mobilenet.predict(img), label);\n\n    NUM_CLASSES = totals.filter(total => total > 0).length;\n\n    // Draw the preview thumbnail.\n    ui.drawThumb(img, label);\n  });\n  ui.trainStatus('TRAIN');\n});\n\n/**\n * Sets up and trains the classifier.\n */\nasync function train() {\n  if (controllerDataset.xs == null) {\n    throw new Error('Add some examples before training!');\n  }\n\n  // Creates a 2-layer fully connected model. By creating a separate model,\n  // rather than adding layers to the mobilenet model, we \"freeze\" the weights\n  // of the mobilenet model, and only train weights from the new model.\n  model = tf.sequential({\n    layers: [\n      // Flattens the input to a vector so we can use it in a dense layer. While\n      // technically a layer, this only performs a reshape (and has no training\n      // parameters).\n      tf.layers.flatten({inputShape: [7, 7, 256]}),\n      // Layer 1\n      tf.layers.dense({\n        units: ui.getDenseUnits(),\n        activation: 'relu',\n        kernelInitializer: tf.initializers.varianceScaling(\n            {scale: 1.0, mode: 'fanIn', distribution: 'normal'}),\n        useBias: true\n      }),\n      // Layer 2. The number of units of the last layer should correspond\n      // to the number of classes we want to predict.\n      tf.layers.dense({\n        units: NUM_CLASSES,\n        kernelInitializer: tf.initializers.varianceScaling(\n            {scale: 1.0, mode: 'fanIn', distribution: 'normal'}),\n        useBias: false,\n        activation: 'softmax'\n      })\n    ]\n  });\n\n  // Creates the optimizers which drives training of the model.\n  const optimizer = tf.train.adam(ui.getLearningRate());\n  // We use categoricalCrossentropy which is the loss function we use for\n  // categorical classification which measures the error between our predicted\n  // probability distribution over classes (probability that an input is of each\n  // class), versus the label (100% probability in the true class)>\n  model.compile({optimizer: optimizer, loss: 'categoricalCrossentropy'});\n\n  // We parameterize batch size as a fraction of the entire dataset because the\n  // number of examples that are collected depends on how many examples the user\n  // collects. This allows us to have a flexible batch size.\n  const batchSize =\n      Math.floor(controllerDataset.xs.shape[0] * ui.getBatchSizeFraction());\n  if (!(batchSize > 0)) {\n    throw new Error(\n        `Batch size is 0 or NaN. Please choose a non-zero fraction.`);\n  }\n  let loss = 0;\n  // Train the model! Model.fit() will shuffle xs & ys so we don't have to.\n  model.fit(controllerDataset.xs, controllerDataset.ys, {\n    batchSize,\n    epochs: ui.getEpochs(),\n    callbacks: {\n      onBatchEnd: async (batch, logs) => {\n        document.getElementById('train').className =\n            'train-model-button train-status';\n        loss = logs.loss.toFixed(5);\n        ui.trainStatus('LOSS: ' + loss);\n      },\n      onTrainEnd: () => {\n        if (loss > 1) {\n          document.getElementById('user-help-text').innerText =\n              'Model is not trained well. Add more samples and train again';\n        } else {\n          document.getElementById('user-help-text').innerText =\n              'Test or download the model. You can even add images for other classes and train again.';\n          ui.enableModelTest();\n          ui.enableModelDownload();\n        }\n      }\n    }\n  });\n}\n\nlet isPredicting = false;\n\nasync function predict() {\n  ui.isPredicting();\n  while (isPredicting) {\n    const predictedClass = tf.tidy(() => {\n      // Capture the frame from the webcam.\n      const img = webcam.capture();\n\n      // Make a prediction through mobilenet, getting the internal activation of\n      // the mobilenet model.\n      const activation = mobilenet.predict(img);\n\n      // Make a prediction through our newly-trained model using the activation\n      // from mobilenet as input.\n      const predictions = model.predict(activation);\n\n      // Returns the index with the maximum probability. This number corresponds\n      // to the class the model thinks is the most probable given the input.\n      return predictions.as1D().argMax();\n    });\n\n    const classId = (await predictedClass.data())[0];\n    predictedClass.dispose();\n\n    ui.predictClass(classId);\n    await tf.nextFrame();\n  }\n  ui.donePredicting();\n}\n\ndocument.getElementById('train').addEventListener('click', async () => {\n  ui.trainStatus('ENCODING...');\n  controllerDataset.ys = null;\n  controllerDataset.addLabels(NUM_CLASSES);\n  ui.trainStatus('TRAINING...');\n  await tf.nextFrame();\n  await tf.nextFrame();\n  isPredicting = false;\n  train();\n});\ndocument.getElementById('predict').addEventListener('click', () => {\n  isPredicting = true;\n  predict();\n});\n\ndocument.getElementById('stop-predict').addEventListener('click', () => {\n  isPredicting = false;\n  predict();\n});\n\nasync function init() {\n  try {\n    await webcam.setup();\n    console.log('Webcam is on');\n  } catch (e) {\n    console.log(e);\n    document.getElementById('no-webcam').style.display = 'block';\n    document.getElementById('webcam-inner-wrapper').className =\n        'webcam-inner-wrapper center grey-bg';\n    document.getElementById('bottom-section').style.pointerEvents = 'none';\n  }\n\n  mobilenet = await loadMobilenet();\n\n  // Warm up the model. This uploads weights to the GPU and compiles the WebGL\n  // programs so the first time we collect data from the webcam it will be\n  // quick.\n  tf.tidy(() => mobilenet.predict(webcam.capture()));\n\n  ui.init();\n}\n\nvar a = document.createElement('a');\ndocument.body.appendChild(a);\na.style = 'display: none';\n\ndocument.getElementById('download-model').onclick =\n    async () => {\n  await model.save('downloads://model');\n\n  var text = controlsCaptured.join(',');\n  var blob = new Blob([text], {type: 'text/csv;charset=utf-8'});\n  var url = window.URL.createObjectURL(blob);\n  a.href = url;\n  a.download = 'labels.txt';\n  a.click();\n  window.URL.revokeObjectURL(url);\n}\n\n// Initialize the application.\ninit();\n"
  },
  {
    "path": "lite/examples/gesture_classification/web/style.css",
    "content": "*{\nbox-sizing: border-box;\nfont-family: 'Roboto', sans-serif;\nletter-spacing: 1.5px;\n}\nbody{\n  width: 1024px;\n  margin: 0 auto;\n}\nh1{\n  color: #ef6c00;\n  font-size: 24px;\n  margin-top: 40px;\n}\n.center{\n   position: absolute;\n   top: 50%;\n   left: 50%;\n   transform: translate(-50%,-50%);\n}\n.vertically-center{\n   position: absolute;\n   top: 50%;\n   transform: translateY(-50%);\n}\n\n.disabled{\n  opacity: 0.48;\n  pointer-events: none;\n}\n.move-up{\n  top: -50%;\n  transition: top 0.1s;\n}\n.hide{\n  display: none;\n}\n.top-section{\n  padding: 60px 0 30px 0;\n  justify-content: space-between;\n  font-size: 14px;\n}\n.left-dropdown-container,.right-button-container{\n  width: 25%;\n  display: inline-block;\n  vertical-align: middle;\n}\n.right-button-container{\n  text-align: right;\n}\n.grey-bg{\n  background: #c8d0d8;\n}\n.webcam-container{\n  width: 49%;\n  display: inline-block;\n  vertical-align: middle;\n}\n\n.webcam-outer-wrapper{\n  width: 268px;\n  height: 268px;\n  margin: 0 auto;\n  border: 2px solid #c8d0d8;\n  border-radius: 50%;\n  position: relative;\n}\n\n.webcam-inner-wrapper{\n  height: 224px;\n  width: 224px;\n  border: 2px solid #c8d0d8;\n  border-radius: 50%;\n  overflow: hidden;\n\ttext-align: center;\n\tcolor: #ffffff;\n}\n\n#no-webcam{\n\tpadding: 40px 10px;\n\tfont-size: 16px;\n}\n\n#no-webcam p{\n\tmargin: 10px 0;\n}\n\n#no-webcam p:last-child{\n\tfont-weight: normal;\n}\n\nvideo{\n  transform: rotateY(180deg);\n    -webkit-transform:rotateY(180deg); /* Safari and Chrome */\n    -moz-transform:rotateY(180deg);\n}\n\nlabel{\n  color: #ababab;\n  margin-bottom: 5px;\n  display: block;\n  font-weight: 700;\n  font-size: 16px;\n}\n\n.dropdown-wrapper{\n  margin-bottom: 48px;\n}\n.dropdown-wrapper:last-child{\n  margin-bottom: 0;\n}\n.dropdown{\n  position: relative;\n  display: inline-block;\n}\n\n.dropdown img{\n  right: 10px;\n}\n.custom-dropdown{\n  padding: 10px 30px 10px 0px;\n  -webkit-appearance: none;\n  border: none;\n  outline:none;\n  color: #202830;\n  font-weight: bolder;\n}\nselect::-ms-expand {\n    display: none;\n}\n\n.custom-dropdown div, .custom-dropdown img{\n  pointer-events: none;\n}\n\n.custom-option-list{\n  position: absolute;\n  top: 10px;\n  left: 0;\n  border-radius: 4px;\n  background-color: #ffffff;\n  box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.24);\n  padding: 10px;\n  z-index: 1;\n  min-width: 144px;\n}\n\n.custom-option{\nmargin-bottom: 5px;\ncursor: pointer;\nfont-weight: 400;\ndisplay: flex;\n}\n\n.custom-option:last-child{\nmargin-bottom: 0px;\n}\n\n.custom-option:hover, .custom-option.selected{\n  font-weight: bold;\n}\n\n.custom-option span.icon{\n  visibility: hidden;\n  margin-right: 10px;\n}\n\n.custom-option.selected span.icon{\n  visibility: visible;\n}\n\n.right-button-container button{\n  min-width: 206px;\n  padding: 16px;\n  border-radius: 24px;\n  background-color: #00db8b;\n  color: #ffffff;\n  margin-bottom: 16px;\n  border: none;color: #ffffff;\n  background-color: #00db8b;\n  cursor: pointer;\n  font-size: 14px;\n  letter-spacing: 1.8px;\n  font-weight: bold;\n}\n.right-button-container button.disabled{\n  border: 2px solid #c8d0d8;\n  color: #c8d0d8;\n  background-color: transparent;\n}\n.right-button-container button.train-status{\n  background-color: transparent;\n  color: #00db8b;\n  border: 2px solid #00db8b;\n  pointer-events: none;\n}\n.right-button-container button:focus{\n  outline: none;\n}\n.right-button-container button:hover{\n  box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.24);\n}\n\n.right-button-container button.stop-button{\n  background-color: #ff5471;\n}\n\n.bottom-section{\n  display: flex;\n  justify-content: space-between;\n}\n.control-button-wrapper{\n  text-align: center;\n}\n.control-inner-wrapper{\n  border: 3px solid #ffffff;\n  padding: 10px;\n  border-radius: 5px;\n}\n.control-inner-wrapper.active{\n  border: 3px solid #00db8b;\n}\n.control-button{\n    border: 1px solid #c8d0d8;\n    width: 88px;\n    height: 88px;\n    border-radius: 5px;\n    color:  #7c8890;\n    padding: 0px 10px;\n    position: relative;\n    cursor: pointer;\n    font-size: 12px !important;\n\t\t-webkit-user-select: none;  /* Chrome all / Safari all */\n\t\t-moz-user-select: none;     /* Firefox all */\n\t\t-ms-user-select: none;      /* IE 10+ */\n\t\t-o-user-select: none;\n\t\tuser-select: none;\n}\n.control-button:hover{\n    box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.24);\n}\n.control-icon, .add-icon{\n  pointer-events: none;\n}\n\n.control-icon img{\n  margin-bottom: 10px;\n}\n\ncanvas.thumb{\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  top: 0;\n  left: 0;\n}\n.add-icon{\n  width: 100%;\n  height: 100%;\n}\n.invisible{\n  opacity: 0;\n}\n\n.sample-count{\n  margin-top: 10px;\n}\n#user-help{\n  text-align: center;\n  margin-bottom: 130px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n#user-help img{\n  height: 20px;\n  width: 20px;\n  margin-right: 10px;\n\n}\n#user-help-text{\n  color: #202830;\n  font-size: 16px;\n}\n"
  },
  {
    "path": "lite/examples/gesture_classification/web/ui.js",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n\nvar ui = ui || {}\n\n\nconst CONTROLS = [\n  'up', 'down', 'left', 'right', 'leftclick', 'rightclick', 'scrollup',\n  'scrolldown'\n];\nvar controlsCaptured = [], labelsCaptured = [];\n\nui.init =\n    function() {\n  document.getElementById('user-help').style.visibility = 'visible';\n  document.getElementById('user-help-text').innerText =\n      'Add images to the classes below by clicking or holding';\n  var controlButtons = document.getElementsByClassName('control-button');\n  for (var i = 0; i < controlButtons.length; i++) {\n    controlButtons[i].addEventListener('mouseover', function(event) {\n      if (event.target.classList.contains('control-button')) {\n        document.getElementById(event.target.id + '-icon').className =\n            'control-icon center move-up';\n        document.getElementById(event.target.id + '-add-icon').className =\n            'add-icon';\n      }\n    });\n    controlButtons[i].addEventListener('mouseout', function(event) {\n      if (event.target.classList.contains('control-button')) {\n        document.getElementById(event.target.id + '-icon').className =\n            'control-icon center';\n        document.getElementById(event.target.id + '-add-icon').className =\n            'add-icon invisible';\n      }\n    });\n  }\n}\n\n\n\nfunction\nhideAllDropdowns() {\n  let dropdownLists = document.getElementsByClassName('custom-option-list');\n  for (var j = 0; j < dropdownLists.length; j++) {\n    dropdownLists[j].className = 'custom-option-list hide';\n  }\n}\n\nvar customDropdowns = document.getElementsByClassName('custom-dropdown');\nfor (var i = 0; i < customDropdowns.length; i++) {\n  customDropdowns[i].addEventListener('click', (event) => {\n    hideAllDropdowns();\n    const id = event.target.id + '-list';\n    document.getElementById(id).className = 'custom-option-list';\n  });\n}\n\nvar customDropdownOptions = document.getElementsByClassName('custom-option');\nfor (var i = 0; i < customDropdownOptions.length; i++) {\n  customDropdownOptions[i].addEventListener('click', (event) => {\n    let dropdownID = event.target.parentNode.getAttribute('dropdownID');\n    let dropdownList = document.getElementById(dropdownID + '-dropdown-list');\n    dropdownList.getElementsByClassName('selected')[0].className =\n        'custom-option';\n    event.target.className = 'custom-option selected';\n    event.target.parentNode.className = 'custom-option-list hide';\n    document.getElementById(dropdownID).innerText = event.target.innerText;\n  });\n}\n\ndocument.body.addEventListener('click', (event) => {\n  if (event.target.className !== 'custom-dropdown') {\n    hideAllDropdowns();\n  }\n})\n\nconst trainStatusElement = document.getElementById('train-status');\nconst downloadModel = document.getElementById('download-model');\n\n// Set hyper params from UI values.\nconst learningRateElement = document.getElementById('learningRate');\nvar selectedLearningRateValue =\n    document.getElementById('learningRate-dropdown-list')\n        .getElementsByClassName('selected')[0]\n        .innerText;\nlearningRateElement.innerText = selectedLearningRateValue;\n\nui.getLearningRate = () => +learningRateElement.innerText;\n\nconst batchSizeFractionElement = document.getElementById('batchSizeFraction');\nvar batchSizeFractionValue =\n    document.getElementById('batchSizeFraction-dropdown-list')\n        .getElementsByClassName('selected')[0]\n        .innerText;\nbatchSizeFractionElement.innerText = batchSizeFractionValue;\n\nui.getBatchSizeFraction = () => +batchSizeFractionElement.innerText;\n\nconst epochsElement = document.getElementById('epochs');\nvar epochsValue = document.getElementById('epochs-dropdown-list')\n                      .getElementsByClassName('selected')[0]\n                      .innerText;\nepochsElement.innerText = epochsValue;\n\nui.getEpochs = () => +epochsElement.innerText;\n\nconst denseUnitsElement = document.getElementById('dense-units');\nvar denseUnitsValue = document.getElementById('dense-units-dropdown-list')\n                          .getElementsByClassName('selected')[0]\n                          .innerText;\ndenseUnitsElement.innerText = denseUnitsValue;\n\nui.getDenseUnits = () => +denseUnitsElement.innerText;\n\nfunction removeActiveClass() {\n  let activeElement = document.getElementsByClassName('active');\n  while (activeElement.length > 0) {\n    activeElement[0].className = 'control-inner-wrapper';\n  }\n}\n\nui.predictClass =\n    function(classId) {\n  removeActiveClass();\n  classId = Math.floor(classId);\n  document.getElementById(controlsCaptured[classId] + '-button').className =\n      'control-inner-wrapper active';\n  document.body.setAttribute('data-active', controlsCaptured[classId]);\n}\n\n    ui.isPredicting =\n        function() {\n  document.getElementById('predict').className = 'test-button hide';\n  document.getElementById('webcam-outer-wrapper').style.border =\n      '4px solid #00db8b';\n  document.getElementById('stop-predict').className = 'stop-button';\n  document.getElementById('bottom-section').style.pointerEvents = 'none';\n  downloadModel.className = 'disabled';\n};\n\nui.donePredicting =\n            function() {\n  document.getElementById('predict').className = 'test-button';\n  document.getElementById('webcam-outer-wrapper').style.border =\n      '2px solid #c8d0d8';\n  document.getElementById('stop-predict').className = 'stop-button hide';\n  document.getElementById('bottom-section').style.pointerEvents = 'all';\n  downloadModel.className = '';\n  removeActiveClass();\n}\n\n            ui.trainStatus =\n                function(status) {\n  trainStatusElement.innerText = status;\n}\n\n                ui.enableModelDownload =\n                    function() {\n  downloadModel.className = '';\n}\n\n                    ui.enableModelTest =\n                        function() {\n  document.getElementById('predict').className = 'test-button';\n}\n\nvar addExampleHandler;\n\nui.setExampleHandler = function(handler) {\n  addExampleHandler = handler;\n};\nlet mouseDown = false;\nconst totals = [0, 0, 0, 0, 0, 0, 0, 0];\n\nconst upButton = document.getElementById('up');\nconst downButton = document.getElementById('down');\nconst leftButton = document.getElementById('left');\nconst rightButton = document.getElementById('right');\nconst leftClickButton = document.getElementById('leftclick');\nconst rightClickButton = document.getElementById('rightclick');\nconst scrollUpButton = document.getElementById('scrollup');\nconst scrollDownButton = document.getElementById('scrolldown');\n\nconst thumbDisplayed = {};\nfunction timeout(ms) {\n  return new Promise(resolve => setTimeout(resolve, ms));\n}\nui.handler =\n    async function(label) {\n  mouseDown = true;\n  const id = CONTROLS[label];\n  const button = document.getElementById(id);\n  const total = document.getElementById(id + '-total');\n\n  document.body.removeAttribute('data-active');\n\n\n  while (mouseDown) {\n    totals[label] = totals[label] + 1;\n    total.innerText = totals[label];\n    for (var i = 0; i < totals.length; i++) {\n      if (totals[i] > 0) {\n        var isPresent = false;\n        for (var j = 0; j < controlsCaptured.length; j++) {\n          if (CONTROLS[i] === controlsCaptured[j]) {\n            isPresent = true;\n            break;\n          }\n        }\n        if (!isPresent) {\n          controlsCaptured.push(CONTROLS[i]);\n          labelsCaptured.push(i);\n          break;\n        }\n      }\n    }\n    addExampleHandler(label);\n    await Promise.all([tf.nextFrame(), timeout(300)]);\n  }\n  document.body.setAttribute('data-active', CONTROLS[label]);\n  if (controlsCaptured.length >= 2) {\n    document.getElementById('train').className = 'train-button';\n    ui.trainStatus('TRAIN');\n    document.getElementById('predict').className = 'test-button disabled';\n    downloadModel.className = 'disabled';\n    document.getElementById('user-help-text').innerText =\n        'Add more images or train the model';\n  } else {\n    document.getElementById('user-help-text').innerText =\n        'Minimum of 2 classes required to train the model';\n  }\n}\n\n    upButton.addEventListener('mousedown', () => ui.handler(0));\nupButton.addEventListener('mouseup', () => {\n  mouseDown = false;\n});\n\ndownButton.addEventListener('mousedown', () => ui.handler(1));\ndownButton.addEventListener('mouseup', () => mouseDown = false);\n\nleftButton.addEventListener('mousedown', () => ui.handler(2));\nleftButton.addEventListener('mouseup', () => mouseDown = false);\n\nrightButton.addEventListener('mousedown', () => ui.handler(3));\nrightButton.addEventListener('mouseup', () => mouseDown = false);\n\nleftClickButton.addEventListener('mousedown', () => ui.handler(4));\nleftClickButton.addEventListener('mouseup', () => mouseDown = false);\n\nrightClickButton.addEventListener('mousedown', () => ui.handler(5));\nrightClickButton.addEventListener('mouseup', () => mouseDown = false);\n\nscrollUpButton.addEventListener('mousedown', () => ui.handler(6));\nscrollUpButton.addEventListener('mouseup', () => mouseDown = false);\n\nscrollDownButton.addEventListener('mousedown', () => ui.handler(7));\nscrollDownButton.addEventListener('mouseup', () => mouseDown = false);\n\nui.drawThumb =\n    function(img, label) {\n  if (thumbDisplayed[label] == null) {\n    const thumbCanvas = document.getElementById(CONTROLS[label] + '-thumb');\n    thumbCanvas.style.display = 'block';\n    document.getElementById(CONTROLS[label] + '-icon').style.top = '-50%';\n    ui.draw(img, thumbCanvas);\n  }\n}\n\n    ui.draw = function(image, canvas) {\n  const [width, height] = [224, 224];\n  const ctx = canvas.getContext('2d');\n  const imageData = new ImageData(width, height);\n  const data = image.dataSync();\n  for (let i = 0; i < height * width; ++i) {\n    const j = i * 4;\n    imageData.data[j + 0] = (data[i * 3 + 0] + 1) * 127;\n    imageData.data[j + 1] = (data[i * 3 + 1] + 1) * 127;\n    imageData.data[j + 2] = (data[i * 3 + 2] + 1) * 127;\n    imageData.data[j + 3] = 255;\n  }\n  ctx.putImageData(imageData, 0, 0);\n}\n"
  },
  {
    "path": "lite/examples/gesture_classification/web/webcam.js",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n\n/**\n * A class that wraps webcam video elements to capture Tensor4Ds.\n */\n\n\nclass Webcam {\n  /**\n   * @param {HTMLVideoElement} webcamElement A HTMLVideoElement representing the\n   *     webcam feed.\n   */\n  constructor(webcamElement) {\n    this.webcamElement = webcamElement;\n  }\n\n  /**\n   * Captures a frame from the webcam and normalizes it between -1 and 1.\n   * Returns a batched image (1-element batch) of shape [1, w, h, c].\n   */\n\n\n  capture() {\n    return tf.tidy(() => {\n      // Reads the image as a Tensor from the webcam <video> element.\n      const webcamImage = tf.browser.fromPixels(this.webcamElement);\n\n      const reversedImage = webcamImage.reverse(1);\n\n      // Crop the image so we're using the center square of the rectangular\n      // webcam.\n      const croppedImage = this.cropImage(reversedImage);\n\n      // Expand the outer most dimension so we have a batch size of 1.\n      const batchedImage = croppedImage.expandDims(0);\n\n      // Normalize the image between -1 and 1. The image comes in between 0-255,\n      // so we divide by 127 and subtract 1.\n      return batchedImage.toFloat().div(tf.scalar(127)).sub(tf.scalar(1));\n    });\n  }\n\n  /**\n   * Crops an image tensor so we get a square image with no white space.\n   * @param {Tensor4D} img An input image Tensor to crop.\n   */\n\n\n  cropImage(img) {\n    const size = Math.min(img.shape[0], img.shape[1]);\n    const centerHeight = img.shape[0] / 2;\n    const beginHeight = centerHeight - (size / 2);\n    const centerWidth = img.shape[1] / 2;\n    const beginWidth = centerWidth - (size / 2);\n    return img.slice([beginHeight, beginWidth, 0], [size, size, 3]);\n  }\n\n  /**\n   * Adjusts the video size so we can make a centered square crop without\n   * including whitespace.\n   * @param {number} width The real width of the video element.\n   * @param {number} height The real height of the video element.\n   */\n\n\n  adjustVideoSize(width, height) {\n    const aspectRatio = width / height;\n    if (width >= height) {\n      this.webcamElement.width = aspectRatio * this.webcamElement.height;\n    } else if (width < height) {\n      this.webcamElement.height = this.webcamElement.width / aspectRatio;\n    }\n  }\n\n  async setup() {\n    return new Promise((resolve, reject) => {\n      navigator.getUserMedia = navigator.getUserMedia ||\n          navigator.webkitGetUserMedia || navigator.mozGetUserMedia ||\n          navigator.msGetUserMedia;\n      if (navigator.getUserMedia) {\n        navigator.getUserMedia(\n            {video: {width: 224, height: 224}},\n            stream => {\n              this.webcamElement.srcObject = stream;\n              this.webcamElement.addEventListener('loadeddata', async () => {\n                this.adjustVideoSize(\n                    this.webcamElement.videoWidth,\n                    this.webcamElement.videoHeight);\n                resolve();\n              }, false);\n            },\n            error => {\n              reject(error);\n            });\n      } else {\n        reject();\n      }\n    });\n  }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android/README.md",
    "content": "# TensorFlow Lite Image Classification Demo\n\n### Overview\n\nThis is a camera app that continuously classifies the objects in the frames\nseen by your device's back camera, with the option to use a quantized\n[MobileNet V1](https://tfhub.dev/tensorflow/lite-model/mobilenet_v1_1.0_224_quantized/1/metadata/1),\n[EfficientNet Lite0](https://tfhub.dev/tensorflow/lite-model/efficientnet/lite0/int8/2),\n[EfficientNet Lite1](https://tfhub.dev/tensorflow/lite-model/efficientnet/lite1/int8/2),\nor\n[EfficientNet Lite2](https://tfhub.dev/tensorflow/lite-model/efficientnet/lite2/int8/2)\nmodel trained on Imagenet (ILSVRC-2012-CLS). These instructions\nwalk you through building and running the demo on an Android device.\n\nThe model files are downloaded via Gradle scripts when you build and run the\napp. You don't need to do any steps to download TFLite models into the project\nexplicitly.\n\nThis application should be run on a physical Android device.\n\n![App example showing UI controls. Result is espresso.](screenshot1.jpg?raw=true \"Screenshot with controls\")\n\n![App example without UI controls. Result is espresso.](screenshot2.jpg?raw=true \"Screenshot without controls\")\n\nThis sample demonstrates how to use TensorFlow Lite with Kotlin. If\nyou would like to see an example using Java, please go to the\n[android_java sample directory](../android_java).\n\n\n## Build the demo using Android Studio\n\n### Prerequisites\n\n* The **[Android Studio](https://developer.android.com/studio/index.html)**\n    IDE (Android Studio 2021.2.1 or newer). This sample has been tested on\n    Android Studio Chipmunk\n\n* A physical Android device with a minimum OS version of SDK 23 (Android 6.0 -\n    Marshmallow) with developer mode enabled. The process of enabling developer\n    mode may vary by device.\n\n### Building\n\n* Open Android Studio. From the Welcome screen, select Open an existing\n    Android Studio project.\n\n* From the Open File or Project window that appears, navigate to and select\n    the tensorflow-lite/examples/image_classification/android directory.\n    Click OK.\n\n* If it asks you to do a Gradle Sync, click OK.\n\n* With your Android device connected to your computer and developer mode\n    enabled, click on the green Run arrow in Android Studio.\n\n### Models used\n\nDownloading, extraction, and placing the models into the assets folder is\nmanaged automatically by the download.gradle file.\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/build.gradle",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nplugins {\n    id 'com.android.application'\n    id 'org.jetbrains.kotlin.android'\n    id 'kotlin-kapt'\n    id 'androidx.navigation.safeargs'\n    id 'de.undercouch.download'\n}\n\nandroid {\n    compileSdk 32\n\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.imageclassification\"\n        minSdk 23\n        targetSdk 32\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n\n    kotlinOptions {\n        jvmTarget = '1.8'\n    }\n\n    buildFeatures {\n        viewBinding true\n    }\n\n    androidResources {\n        noCompress 'tflite'\n    }\n}\n\n// import DownloadModels task\nproject.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'\nproject.ext.TEST_ASSETS_DIR = projectDir.toString() + '/src/androidTest/assets'\n\n// Download default models; if you wish to use your own models then\n// place them in the \"assets\" directory and comment out this line.\napply from: 'download_models.gradle'\n\ndependencies {\n\n    // Kotlin lang\n    implementation 'androidx.core:core-ktx:1.15.0'\n\n    // App compat and UI things\n    implementation 'androidx.appcompat:appcompat:1.4.2'\n    implementation 'com.google.android.material:material:1.6.1'\n    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'\n\n    // Navigation library\n    def nav_version = \"2.4.2\"\n    implementation \"androidx.navigation:navigation-fragment-ktx:$nav_version\"\n    implementation \"androidx.navigation:navigation-ui-ktx:$nav_version\"\n\n    // CameraX core library\n    def camerax_version = '1.2.0-alpha02'\n    implementation \"androidx.camera:camera-core:$camerax_version\"\n\n    // CameraX Camera2 extensions\n    implementation \"androidx.camera:camera-camera2:$camerax_version\"\n\n    // CameraX Lifecycle library\n    implementation \"androidx.camera:camera-lifecycle:$camerax_version\"\n\n    // CameraX View class\n    implementation \"androidx.camera:camera-view:$camerax_version\"\n\n    //WindowManager\n    implementation 'androidx.window:window:1.1.0-alpha02'\n\n    // Unit testing\n    testImplementation 'junit:junit:4.13.2'\n\n    // Instrumented testing\n    androidTestImplementation 'androidx.test.ext:junit:1.1.3'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n\n    implementation 'org.tensorflow:tensorflow-lite-task-vision:0.4.0'\n    // Import the GPU delegate plugin Library for GPU inference\n    implementation 'org.tensorflow:tensorflow-lite-gpu-delegate-plugin:0.4.0'\n    implementation 'org.tensorflow:tensorflow-lite-gpu:2.9.0'\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/download_models.gradle",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ntask downloadModelFile(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_classification/android/mobilenet_v1_1.0_224_quantized_1_metadata_1.tflite'\n    dest project.ext.ASSET_DIR + '/mobilenetv1.tflite'\n    overwrite false\n}\n\ntask downloadModelFile0(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_classification/android/efficientnet_lite0_int8_2.tflite'\n    dest project.ext.ASSET_DIR + '/efficientnet-lite0.tflite'\n    overwrite false\n}\n\ntask downloadModelFile1(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_classification/android/efficientnet_lite1_int8_2.tflite'\n    dest project.ext.ASSET_DIR + '/efficientnet-lite1.tflite'\n    overwrite false\n}\n\ntask downloadModelFile2(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_classification/android/efficientnet_lite2_int8_2.tflite'\n    dest project.ext.ASSET_DIR + '/efficientnet-lite2.tflite'\n    overwrite false\n}\n\ntask copyTestModel(type: Copy, dependsOn: downloadModelFile) {\n    from project.ext.ASSET_DIR + '/mobilenetv1.tflite'\n    into project.ext.TEST_ASSETS_DIR\n}\n\npreBuild.dependsOn downloadModelFile, downloadModelFile0, downloadModelFile1, downloadModelFile2,\n        copyTestModel\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/androidTest/java/org/tensorflow/lite/examples/imageclassification/ImageClassificationTest.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.imageclassification\n\nimport android.content.res.AssetManager\nimport android.graphics.Bitmap\nimport android.graphics.BitmapFactory\nimport android.view.Surface\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.ext.junit.runners.AndroidJUnit4\n\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\nimport org.junit.Assert.*\nimport org.tensorflow.lite.support.label.Category\nimport org.tensorflow.lite.task.vision.classifier.Classifications\nimport java.io.InputStream\nimport java.lang.Exception\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass ImageClassificationTest {\n\n    val controlCategories = listOf<Category>(\n        Category.create(\"cup\", \"cup\", 0.76953125f),\n        Category.create(\"coffee mug\", \"coffee mug\", 0.125f),\n        Category.create(\"espresso\", \"espresso\", 0.0546875f)\n    )\n\n    @Test\n    @Throws(Exception::class)\n    fun classificationResultsShouldNotChange() {\n        val imageClassifierHelper = ImageClassifierHelper(\n            context = InstrumentationRegistry.getInstrumentation().context,\n            imageClassifierListener = object : ImageClassifierHelper.ClassifierListener {\n                override fun onError(error: String) {\n                    // no op\n                }\n\n                override fun onResults(\n                    results: List<Classifications>?,\n                    inferenceTime: Long\n                ) {\n                    assertNotNull(results)\n\n                    // Verify that the classified data and control\n                    // data have the same number of categories\n                    assertEquals(controlCategories.size, results!![0].categories.size)\n\n                    // Loop through the categories\n                    for (i in controlCategories.indices) {\n                        // Verify that the labels are consistent\n                        assertEquals(controlCategories[i].label, results[0].categories[i].label)\n                    }\n                }\n            }, threshold = 0.0f\n        )\n\n        // Create Bitmap and convert to TensorImage\n        val bitmap = loadImage(\"coffee.jpg\")\n        // Run the image classifier on the sample image. Assume device portrait orientation.\n        imageClassifierHelper.classify(bitmap!!, Surface.ROTATION_90)\n    }\n\n    @Throws(Exception::class)\n    private fun loadImage(fileName: String): Bitmap? {\n        val assetManager: AssetManager =\n            InstrumentationRegistry.getInstrumentation().context.assets\n        val inputStream: InputStream = assetManager.open(fileName)\n        return BitmapFactory.decodeStream(inputStream)\n    }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:dist=\"http://schemas.android.com/apk/distribution\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"org.tensorflow.lite.examples.imageclassification\">\n\n    <!-- Enable instant app support -->\n    <dist:module dist:instant=\"true\" />\n\n    <!-- Declare features -->\n    <uses-feature android:name=\"android.hardware.camera\" />\n\n    <!-- Declare permissions -->\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:taskAffinity=\"\"\n        tools:ignore=\"AllowBackup\">\n\n        <activity\n            android:name=\".MainActivity\"\n            android:clearTaskOnLaunch=\"true\"\n            android:configChanges=\"orientation|screenLayout|screenSize|smallestScreenSize\"\n            android:exported=\"true\"\n            android:resizeableActivity=\"true\"\n            android:rotationAnimation=\"seamless\"\n            android:theme=\"@style/AppTheme\"\n            tools:targetApi=\"O\">\n\n            <!-- Main app intent filter -->\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n\n            <!-- Declare notch support -->\n            <meta-data\n                android:name=\"android.notch_support\"\n                android:value=\"true\" />\n        </activity>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/java/org/tensorflow/lite/examples/imageclassification/ImageClassifierHelper.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.imageclassification\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.os.SystemClock\nimport android.util.Log\nimport android.view.Surface\nimport org.tensorflow.lite.gpu.CompatibilityList\nimport org.tensorflow.lite.support.image.ImageProcessor\nimport org.tensorflow.lite.support.image.TensorImage\nimport org.tensorflow.lite.support.image.ops.Rot90Op\nimport org.tensorflow.lite.task.core.BaseOptions\nimport org.tensorflow.lite.task.core.vision.ImageProcessingOptions\nimport org.tensorflow.lite.task.vision.classifier.Classifications\nimport org.tensorflow.lite.task.vision.classifier.ImageClassifier\n\nclass ImageClassifierHelper(\n    var threshold: Float = 0.5f,\n    var numThreads: Int = 2,\n    var maxResults: Int = 3,\n    var currentDelegate: Int = 0,\n    var currentModel: Int = 0,\n    val context: Context,\n    val imageClassifierListener: ClassifierListener?\n) {\n    private var imageClassifier: ImageClassifier? = null\n\n    init {\n        setupImageClassifier()\n    }\n\n    fun clearImageClassifier() {\n        imageClassifier = null\n    }\n\n    private fun setupImageClassifier() {\n        val optionsBuilder = ImageClassifier.ImageClassifierOptions.builder()\n            .setScoreThreshold(threshold)\n            .setMaxResults(maxResults)\n\n        val baseOptionsBuilder = BaseOptions.builder().setNumThreads(numThreads)\n\n        when (currentDelegate) {\n            DELEGATE_CPU -> {\n                // Default\n            }\n            DELEGATE_GPU -> {\n                if (CompatibilityList().isDelegateSupportedOnThisDevice) {\n                    baseOptionsBuilder.useGpu()\n                } else {\n                    imageClassifierListener?.onError(\"GPU is not supported on this device\")\n                }\n            }\n            DELEGATE_NNAPI -> {\n                baseOptionsBuilder.useNnapi()\n            }\n        }\n\n        optionsBuilder.setBaseOptions(baseOptionsBuilder.build())\n\n        val modelName =\n            when (currentModel) {\n                MODEL_MOBILENETV1 -> \"mobilenetv1.tflite\"\n                MODEL_EFFICIENTNETV0 -> \"efficientnet-lite0.tflite\"\n                MODEL_EFFICIENTNETV1 -> \"efficientnet-lite1.tflite\"\n                MODEL_EFFICIENTNETV2 -> \"efficientnet-lite2.tflite\"\n                else -> \"mobilenetv1.tflite\"\n            }\n\n        try {\n            imageClassifier =\n                ImageClassifier.createFromFileAndOptions(context, modelName, optionsBuilder.build())\n        } catch (e: IllegalStateException) {\n            imageClassifierListener?.onError(\n                \"Image classifier failed to initialize. See error logs for details\"\n            )\n            Log.e(TAG, \"TFLite failed to load model with error: \" + e.message)\n        }\n    }\n\n    fun classify(image: Bitmap, rotation: Int) {\n        if (imageClassifier == null) {\n            setupImageClassifier()\n        }\n\n        // Inference time is the difference between the system time at the start and finish of the\n        // process\n        var inferenceTime = SystemClock.uptimeMillis()\n\n        // Create preprocessor for the image.\n        // See https://www.tensorflow.org/lite/inference_with_metadata/\n        //            lite_support#imageprocessor_architecture\n        val imageProcessor =\n            ImageProcessor.Builder()\n                .build()\n\n        // Preprocess the image and convert it into a TensorImage for classification.\n        val tensorImage = imageProcessor.process(TensorImage.fromBitmap(image))\n\n        val imageProcessingOptions = ImageProcessingOptions.builder()\n            .setOrientation(getOrientationFromRotation(rotation))\n            .build()\n\n        val results = imageClassifier?.classify(tensorImage, imageProcessingOptions)\n        inferenceTime = SystemClock.uptimeMillis() - inferenceTime\n        imageClassifierListener?.onResults(\n            results,\n            inferenceTime\n        )\n    }\n\n    // Receive the device rotation (Surface.x values range from 0->3) and return EXIF orientation\n    // http://jpegclub.org/exif_orientation.html\n    private fun getOrientationFromRotation(rotation: Int) : ImageProcessingOptions.Orientation {\n        when (rotation) {\n            Surface.ROTATION_270 ->\n                return ImageProcessingOptions.Orientation.BOTTOM_RIGHT\n            Surface.ROTATION_180 ->\n                return ImageProcessingOptions.Orientation.RIGHT_BOTTOM\n            Surface.ROTATION_90 ->\n                return ImageProcessingOptions.Orientation.TOP_LEFT\n            else ->\n                return ImageProcessingOptions.Orientation.RIGHT_TOP\n        }\n    }\n\n    interface ClassifierListener {\n        fun onError(error: String)\n        fun onResults(\n            results: List<Classifications>?,\n            inferenceTime: Long\n        )\n    }\n\n    companion object {\n        const val DELEGATE_CPU = 0\n        const val DELEGATE_GPU = 1\n        const val DELEGATE_NNAPI = 2\n        const val MODEL_MOBILENETV1 = 0\n        const val MODEL_EFFICIENTNETV0 = 1\n        const val MODEL_EFFICIENTNETV1 = 2\n        const val MODEL_EFFICIENTNETV2 = 3\n\n        private const val TAG = \"ImageClassifierHelper\"\n    }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/java/org/tensorflow/lite/examples/imageclassification/MainActivity.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.imageclassification\n\nimport android.os.Build\nimport androidx.appcompat.app.AppCompatActivity\nimport android.os.Bundle\nimport org.tensorflow.lite.examples.imageclassification.databinding.ActivityMainBinding\n\nclass MainActivity : AppCompatActivity() {\n    private lateinit var activityMainBinding: ActivityMainBinding\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)\n        setContentView(activityMainBinding.root)\n    }\n\n    override fun onBackPressed() {\n        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {\n            // Workaround for Android Q memory leak issue in IRequestFinishCallback$Stub.\n            // (https://issuetracker.google.com/issues/139738913)\n            finishAfterTransition()\n        } else {\n            super.onBackPressed()\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/java/org/tensorflow/lite/examples/imageclassification/fragments/CameraFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.imageclassification.fragments\n\nimport android.annotation.SuppressLint\nimport android.content.res.Configuration\nimport android.graphics.Bitmap\nimport android.os.Bundle\nimport android.util.DisplayMetrics\nimport android.util.Log\nimport android.view.*\nimport android.widget.AdapterView\nimport android.widget.Toast\nimport androidx.camera.core.AspectRatio\nimport androidx.camera.core.ImageProxy\nimport androidx.camera.core.Camera\nimport androidx.camera.core.CameraSelector\nimport androidx.camera.core.ImageAnalysis\nimport androidx.camera.core.Preview\nimport androidx.camera.lifecycle.ProcessCameraProvider\nimport androidx.core.content.ContextCompat\nimport androidx.fragment.app.Fragment\nimport androidx.navigation.Navigation\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport org.tensorflow.lite.examples.imageclassification.ImageClassifierHelper\nimport org.tensorflow.lite.examples.imageclassification.R\nimport org.tensorflow.lite.examples.imageclassification.databinding.FragmentCameraBinding\nimport org.tensorflow.lite.task.vision.classifier.Classifications\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Executors\n\nclass CameraFragment : Fragment(), ImageClassifierHelper.ClassifierListener {\n\n    companion object {\n        private const val TAG = \"Image Classifier\"\n    }\n\n    private var _fragmentCameraBinding: FragmentCameraBinding? = null\n    private val fragmentCameraBinding\n        get() = _fragmentCameraBinding!!\n\n    private lateinit var imageClassifierHelper: ImageClassifierHelper\n    private lateinit var bitmapBuffer: Bitmap\n    private val classificationResultsAdapter by lazy {\n        ClassificationResultsAdapter().apply {\n            updateAdapterSize(imageClassifierHelper.maxResults)\n        }\n    }\n    private var preview: Preview? = null\n    private var imageAnalyzer: ImageAnalysis? = null\n    private var camera: Camera? = null\n    private var cameraProvider: ProcessCameraProvider? = null\n\n    /** Blocking camera operations are performed using this executor */\n    private lateinit var cameraExecutor: ExecutorService\n\n    override fun onResume() {\n        super.onResume()\n\n        if (!PermissionsFragment.hasPermissions(requireContext())) {\n            Navigation.findNavController(requireActivity(), R.id.fragment_container)\n                .navigate(CameraFragmentDirections.actionCameraToPermissions())\n        }\n    }\n\n    override fun onDestroyView() {\n        _fragmentCameraBinding = null\n        super.onDestroyView()\n\n        // Shut down our background executor\n        cameraExecutor.shutdown()\n    }\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View {\n        _fragmentCameraBinding = FragmentCameraBinding.inflate(inflater, container, false)\n\n        return fragmentCameraBinding.root\n    }\n\n    @SuppressLint(\"MissingPermission\")\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n\n        imageClassifierHelper =\n            ImageClassifierHelper(context = requireContext(), imageClassifierListener = this)\n\n        with(fragmentCameraBinding.recyclerviewResults) {\n            layoutManager = LinearLayoutManager(requireContext())\n            adapter = classificationResultsAdapter\n        }\n\n        cameraExecutor = Executors.newSingleThreadExecutor()\n\n        fragmentCameraBinding.viewFinder.post {\n            // Set up the camera and its use cases\n            setUpCamera()\n        }\n\n        // Attach listeners to UI control widgets\n        initBottomSheetControls()\n    }\n\n    // Initialize CameraX, and prepare to bind the camera use cases\n    private fun setUpCamera() {\n        val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())\n        cameraProviderFuture.addListener(\n            {\n                // CameraProvider\n                cameraProvider = cameraProviderFuture.get()\n\n                // Build and bind the camera use cases\n                bindCameraUseCases()\n            },\n            ContextCompat.getMainExecutor(requireContext())\n        )\n    }\n\n    private fun initBottomSheetControls() {\n        // When clicked, lower classification score threshold floor\n        fragmentCameraBinding.bottomSheetLayout.thresholdMinus.setOnClickListener {\n            if (imageClassifierHelper.threshold >= 0.1) {\n                imageClassifierHelper.threshold -= 0.1f\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, raise classification score threshold floor\n        fragmentCameraBinding.bottomSheetLayout.thresholdPlus.setOnClickListener {\n            if (imageClassifierHelper.threshold < 0.9) {\n                imageClassifierHelper.threshold += 0.1f\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, reduce the number of objects that can be classified at a time\n        fragmentCameraBinding.bottomSheetLayout.maxResultsMinus.setOnClickListener {\n            if (imageClassifierHelper.maxResults > 1) {\n                imageClassifierHelper.maxResults--\n                updateControlsUi()\n                classificationResultsAdapter.updateAdapterSize(size = imageClassifierHelper.maxResults)\n            }\n        }\n\n        // When clicked, increase the number of objects that can be classified at a time\n        fragmentCameraBinding.bottomSheetLayout.maxResultsPlus.setOnClickListener {\n            if (imageClassifierHelper.maxResults < 3) {\n                imageClassifierHelper.maxResults++\n                updateControlsUi()\n                classificationResultsAdapter.updateAdapterSize(size = imageClassifierHelper.maxResults)\n            }\n        }\n\n        // When clicked, decrease the number of threads used for classification\n        fragmentCameraBinding.bottomSheetLayout.threadsMinus.setOnClickListener {\n            if (imageClassifierHelper.numThreads > 1) {\n                imageClassifierHelper.numThreads--\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, increase the number of threads used for classification\n        fragmentCameraBinding.bottomSheetLayout.threadsPlus.setOnClickListener {\n            if (imageClassifierHelper.numThreads < 4) {\n                imageClassifierHelper.numThreads++\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, change the underlying hardware used for inference. Current options are CPU\n        // GPU, and NNAPI\n        fragmentCameraBinding.bottomSheetLayout.spinnerDelegate.setSelection(0, false)\n        fragmentCameraBinding.bottomSheetLayout.spinnerDelegate.onItemSelectedListener =\n            object : AdapterView.OnItemSelectedListener {\n                override fun onItemSelected(\n                    parent: AdapterView<*>?,\n                    view: View?,\n                    position: Int,\n                    id: Long\n                ) {\n                    imageClassifierHelper.currentDelegate = position\n                    updateControlsUi()\n                }\n\n                override fun onNothingSelected(parent: AdapterView<*>?) {\n                    /* no op */\n                }\n            }\n\n        // When clicked, change the underlying model used for object classification\n        fragmentCameraBinding.bottomSheetLayout.spinnerModel.setSelection(0, false)\n        fragmentCameraBinding.bottomSheetLayout.spinnerModel.onItemSelectedListener =\n            object : AdapterView.OnItemSelectedListener {\n                override fun onItemSelected(\n                    parent: AdapterView<*>?,\n                    view: View?,\n                    position: Int,\n                    id: Long\n                ) {\n                    imageClassifierHelper.currentModel = position\n                    updateControlsUi()\n                }\n\n                override fun onNothingSelected(parent: AdapterView<*>?) {\n                    /* no op */\n                }\n            }\n    }\n\n    // Update the values displayed in the bottom sheet. Reset classifier.\n    private fun updateControlsUi() {\n        fragmentCameraBinding.bottomSheetLayout.maxResultsValue.text =\n            imageClassifierHelper.maxResults.toString()\n\n        fragmentCameraBinding.bottomSheetLayout.thresholdValue.text =\n            String.format(\"%.2f\", imageClassifierHelper.threshold)\n        fragmentCameraBinding.bottomSheetLayout.threadsValue.text =\n            imageClassifierHelper.numThreads.toString()\n        // Needs to be cleared instead of reinitialized because the GPU\n        // delegate needs to be initialized on the thread using it when applicable\n        imageClassifierHelper.clearImageClassifier()\n    }\n\n    override fun onConfigurationChanged(newConfig: Configuration) {\n        super.onConfigurationChanged(newConfig)\n        imageAnalyzer?.targetRotation = fragmentCameraBinding.viewFinder.display.rotation\n    }\n\n    // Declare and bind preview, capture and analysis use cases\n    @SuppressLint(\"UnsafeOptInUsageError\")\n    private fun bindCameraUseCases() {\n\n        // CameraProvider\n        val cameraProvider =\n            cameraProvider ?: throw IllegalStateException(\"Camera initialization failed.\")\n\n        // CameraSelector - makes assumption that we're only using the back camera\n        val cameraSelector =\n            CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()\n\n        // Preview. Only using the 4:3 ratio because this is the closest to our models\n        preview =\n            Preview.Builder()\n                .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n                .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)\n                .build()\n\n        // ImageAnalysis. Using RGBA 8888 to match how our models work\n        imageAnalyzer =\n            ImageAnalysis.Builder()\n                .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n                .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)\n                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)\n                .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)\n                .build()\n                // The analyzer can then be assigned to the instance\n                .also {\n                    it.setAnalyzer(cameraExecutor) { image ->\n                        if (!::bitmapBuffer.isInitialized) {\n                            // The image rotation and RGB image buffer are initialized only once\n                            // the analyzer has started running\n                            bitmapBuffer = Bitmap.createBitmap(\n                                image.width,\n                                image.height,\n                                Bitmap.Config.ARGB_8888\n                            )\n                        }\n\n                        classifyImage(image)\n                    }\n                }\n\n        // Must unbind the use-cases before rebinding them\n        cameraProvider.unbindAll()\n\n        try {\n            // A variable number of use-cases can be passed here -\n            // camera provides access to CameraControl & CameraInfo\n            camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalyzer)\n\n            // Attach the viewfinder's surface provider to preview use case\n            preview?.setSurfaceProvider(fragmentCameraBinding.viewFinder.surfaceProvider)\n        } catch (exc: Exception) {\n            Log.e(TAG, \"Use case binding failed\", exc)\n        }\n    }\n\n    private fun getScreenOrientation() : Int {\n        val outMetrics = DisplayMetrics()\n\n        val display: Display?\n        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {\n            display = requireActivity().display\n            display?.getRealMetrics(outMetrics)\n        } else {\n            @Suppress(\"DEPRECATION\")\n            display = requireActivity().windowManager.defaultDisplay\n            @Suppress(\"DEPRECATION\")\n            display.getMetrics(outMetrics)\n        }\n\n        return display?.rotation ?: 0\n    }\n\n    private fun classifyImage(image: ImageProxy) {\n        // Copy out RGB bits to the shared bitmap buffer\n        image.use { bitmapBuffer.copyPixelsFromBuffer(image.planes[0].buffer) }\n\n        // Pass Bitmap and rotation to the image classifier helper for processing and classification\n        imageClassifierHelper.classify(bitmapBuffer, getScreenOrientation())\n    }\n\n    @SuppressLint(\"NotifyDataSetChanged\")\n    override fun onError(error: String) {\n        activity?.runOnUiThread {\n            Toast.makeText(requireContext(), error, Toast.LENGTH_SHORT).show()\n            classificationResultsAdapter.updateResults(null)\n            classificationResultsAdapter.notifyDataSetChanged()\n        }\n    }\n\n    @SuppressLint(\"NotifyDataSetChanged\")\n    override fun onResults(\n        results: List<Classifications>?,\n        inferenceTime: Long\n    ) {\n        activity?.runOnUiThread {\n            // Show result on bottom sheet\n            classificationResultsAdapter.updateResults(results)\n            classificationResultsAdapter.notifyDataSetChanged()\n            fragmentCameraBinding.bottomSheetLayout.inferenceTimeVal.text =\n                String.format(\"%d ms\", inferenceTime)\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/java/org/tensorflow/lite/examples/imageclassification/fragments/ClassificationResultsAdapter.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.imageclassification.fragments\n\nimport android.view.LayoutInflater\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport org.tensorflow.lite.examples.imageclassification.databinding.ItemClassificationResultBinding\nimport org.tensorflow.lite.support.label.Category\nimport org.tensorflow.lite.task.vision.classifier.Classifications\nimport kotlin.math.min\n\nclass ClassificationResultsAdapter :\n    RecyclerView.Adapter<ClassificationResultsAdapter.ViewHolder>() {\n    companion object {\n        private const val NO_VALUE = \"--\"\n    }\n\n    private var categories: MutableList<Category?> = mutableListOf()\n    private var adapterSize: Int = 0\n\n    fun updateResults(listClassifications: List<Classifications>?) {\n        categories = MutableList(adapterSize) { null }\n        listClassifications?.let { it ->\n            if (it.isNotEmpty()) {\n                val sortedCategories = it[0].categories.sortedBy { it?.index }\n                val min = min(sortedCategories.size, categories.size)\n                for (i in 0 until min) {\n                    categories[i] = sortedCategories[i]\n                }\n            }\n        }\n    }\n\n    fun updateAdapterSize(size: Int) {\n        adapterSize = size\n    }\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {\n        val binding = ItemClassificationResultBinding.inflate(\n            LayoutInflater.from(parent.context),\n            parent,\n            false\n        )\n        return ViewHolder(binding)\n    }\n\n    override fun onBindViewHolder(holder: ViewHolder, position: Int) {\n        categories[position].let { category ->\n            holder.bind(category?.label, category?.score)\n        }\n    }\n\n    override fun getItemCount(): Int = categories.size\n\n    inner class ViewHolder(private val binding: ItemClassificationResultBinding) :\n        RecyclerView.ViewHolder(binding.root) {\n\n        fun bind(label: String?, score: Float?) {\n            with(binding) {\n                tvLabel.text = label ?: NO_VALUE\n                tvScore.text = if (score != null) String.format(\"%.2f\", score) else NO_VALUE\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/java/org/tensorflow/lite/examples/imageclassification/fragments/PermissionsFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.imageclassification.fragments\n\nimport android.Manifest\nimport android.content.Context\nimport android.content.pm.PackageManager\nimport android.os.Bundle\nimport android.widget.Toast\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.core.content.ContextCompat\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.lifecycleScope\nimport androidx.navigation.Navigation\nimport org.tensorflow.lite.examples.imageclassification.R\n\nprivate val PERMISSIONS_REQUIRED = arrayOf(Manifest.permission.CAMERA)\n\nclass PermissionsFragment : Fragment() {\n\n    private val requestPermissionLauncher =\n        registerForActivityResult(\n            ActivityResultContracts.RequestPermission()\n        ) { isGranted: Boolean ->\n            if (isGranted) {\n                Toast.makeText(context, \"Permission request granted\", Toast.LENGTH_LONG).show()\n                navigateToCamera()\n            } else {\n                Toast.makeText(context, \"Permission request denied\", Toast.LENGTH_LONG).show()\n            }\n        }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        when {\n            ContextCompat.checkSelfPermission(\n                requireContext(),\n                Manifest.permission.CAMERA\n            ) == PackageManager.PERMISSION_GRANTED -> {\n                navigateToCamera()\n            }\n            else -> {\n                requestPermissionLauncher.launch(\n                    Manifest.permission.CAMERA\n                )\n            }\n        }\n    }\n\n    private fun navigateToCamera() {\n        lifecycleScope.launchWhenStarted {\n            Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(\n                PermissionsFragmentDirections.actionPermissionsToCamera()\n            )\n        }\n    }\n\n    companion object {\n\n        /** Convenience method used to check if all permissions required by this app are granted */\n        fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all {\n            ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/res/color/selector_ic.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"true\" android:color=\"@color/icPressed\" />\n    <item android:state_focused=\"true\" android:color=\"@color/icFocused\" />\n    <item android:color=\"@color/icActive\" />\n</selector>"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/res/drawable/ic_minus.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13H5v-2h14v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/res/drawable/ic_plus.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path android:pathData=\"M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"85.84757\"\n                android:endY=\"92.4963\"\n                android:startX=\"42.9492\"\n                android:startY=\"49.59793\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:background=\"@android:color/transparent\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <androidx.fragment.app.FragmentContainerView\n            android:id=\"@+id/fragment_container\"\n            android:name=\"androidx.navigation.fragment.NavHostFragment\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:background=\"@android:color/transparent\"\n            android:keepScreenOn=\"true\"\n            app:defaultNavHost=\"true\"\n            app:navGraph=\"@navigation/nav_graph\"\n            android:layout_marginTop=\"?android:attr/actionBarSize\"\n            tools:context=\".MainActivity\"/>\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:layout_alignParentTop=\"true\"\n            android:background=\"@color/toolbar_background\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/tfl_logo\" />\n        </androidx.appcompat.widget.Toolbar>\n\n    </RelativeLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/res/layout/fragment_camera.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/camera_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.camera.view.PreviewView\n        android:id=\"@+id/view_finder\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:scaleType=\"fillStart\" />\n\n    <include\n        android:id=\"@+id/bottom_sheet_layout\"\n        layout=\"@layout/info_bottom_sheet\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/recyclerview_results\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"top\"\n        android:layout_marginBottom=\"@dimen/bottom_sheet_peek_height\"\n        android:background=\"@color/bottom_sheet_background\"\n        android:clipToPadding=\"true\"\n        android:padding=\"@dimen/bottom_sheet_padding\"\n        app:layout_anchor=\"@id/bottom_sheet_layout\"\n        app:layout_anchorGravity=\"top\"\n        app:layout_behavior=\"com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior\" />\n\n    <View\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"1dp\"\n        android:background=\"@color/bounding_box_color\"\n        app:layout_anchor=\"@id/recyclerview_results\"\n        app:layout_anchorGravity=\"bottom\" />\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/res/layout/info_bottom_sheet.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.core.widget.NestedScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/bottom_sheet_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_gravity=\"center_horizontal\"\n    android:background=\"@color/bottom_sheet_background\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:padding=\"@dimen/bottom_sheet_padding\"\n    app:behavior_hideable=\"false\"\n    app:behavior_peekHeight=\"@dimen/bottom_sheet_peek_height\"\n    app:layout_behavior=\"com.google.android.material.bottomsheet.BottomSheetBehavior\">\n\n    <androidx.appcompat.widget.LinearLayoutCompat\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <!-- Chevron indicating that the bottom sheet is expandable -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerHorizontal=\"true\"\n                android:layout_centerVertical=\"true\"\n                android:contentDescription=\"@string/alt_bottom_sheet_chevron\"\n                android:src=\"@drawable/icn_chevron_up\" />\n        </RelativeLayout>\n\n        <!-- Inference time row -->\n        <androidx.appcompat.widget.LinearLayoutCompat\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/inference_time_label\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:text=\"@string/label_interence_time\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <TextView\n                android:id=\"@+id/inference_time_val\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:gravity=\"end\"\n                android:text=\"0ms\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n        </androidx.appcompat.widget.LinearLayoutCompat>\n\n        <!-- ML confidence threshold adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:text=\"@string/label_confidence_threshold\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\">\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threshold_minus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_threshold_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/threshold_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:gravity=\"center\"\n                    android:minEms=\"@integer/bottom_sheet_control_text_min_ems\"\n                    android:text=\"0.50\"\n                    android:textColor=\"@color/bottom_sheet_text_color\"\n                    android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threshold_plus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_threshold_button_plus\"\n                    android:src=\"@drawable/ic_plus\" />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- ML max results adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:text=\"@string/label_max_results\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center\"\n                android:orientation=\"horizontal\">\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/max_results_minus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_max_results_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/max_results_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:gravity=\"center\"\n                    android:minEms=\"@integer/bottom_sheet_control_text_min_ems\"\n                    android:text=\"3\"\n                    android:textColor=\"@color/bottom_sheet_text_color\"\n                    android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/max_results_plus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_max_results_button_plus\"\n                    android:src=\"@drawable/ic_plus\" />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- Number of threads adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:text=\"@string/label_threads\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\">\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threads_minus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_thread_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/threads_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:gravity=\"center\"\n                    android:minEms=\"@integer/bottom_sheet_control_text_min_ems\"\n                    android:text=\"2\"\n                    android:textColor=\"@color/bottom_sheet_text_color\"\n                    android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threads_plus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_thread_button_plus\"\n                    android:src=\"@drawable/ic_plus\" />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- Delegate selection row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/label_delegate\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <androidx.appcompat.widget.AppCompatSpinner\n                android:id=\"@+id/spinner_delegate\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:entries=\"@array/delegate_spinner_titles\"\n                android:minWidth=\"@dimen/bottom_sheet_spinner_delegate_min_width\"\n                android:spinnerMode=\"dropdown\"\n                android:theme=\"@style/BottomSheetSpinnerItemStyle\" />\n\n        </RelativeLayout>\n\n        <!-- Model selection row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/label_models\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <androidx.appcompat.widget.AppCompatSpinner\n                android:id=\"@+id/spinner_model\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:entries=\"@array/models_spinner_titles\"\n                android:minWidth=\"@dimen/bottom_sheet_spinner_model_min_width\"\n                android:spinnerMode=\"dropdown\"\n                android:theme=\"@style/BottomSheetSpinnerItemStyle\" />\n\n        </RelativeLayout>\n    </androidx.appcompat.widget.LinearLayoutCompat>\n</androidx.core.widget.NestedScrollView>\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/res/layout/item_classification_result.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <TextView\n        android:id=\"@+id/tvLabel\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:textColor=\"@color/bottom_sheet_text_color\"\n        android:textSize=\"@dimen/bottom_sheet_text_size\"\n        app:layout_constraintEnd_toStartOf=\"@+id/tvScore\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <TextView\n        android:id=\"@+id/tvScore\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:textSize=\"@dimen/bottom_sheet_text_size\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/res/navigation/nav_graph.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/nav_graph\"\n    app:startDestination=\"@id/permissions_fragment\">\n\n    <fragment\n        android:id=\"@+id/permissions_fragment\"\n        android:name=\"org.tensorflow.lite.examples.imageclassification.fragments.PermissionsFragment\"\n        android:label=\"PermissionsFragment\">\n\n        <action\n            android:id=\"@+id/action_permissions_to_camera\"\n            app:destination=\"@id/camera_fragment\"\n            app:popUpTo=\"@id/permissions_fragment\"\n            app:popUpToInclusive=\"true\" />\n\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/camera_fragment\"\n        android:name=\"org.tensorflow.lite.examples.imageclassification.fragments.CameraFragment\"\n        android:label=\"CameraFragment\">\n\n        <action\n            android:id=\"@+id/action_camera_to_permissions\"\n            app:destination=\"@id/permissions_fragment\"\n            app:popUpTo=\"@id/camera_fragment\"\n            app:popUpToInclusive=\"true\" />\n    </fragment>\n</navigation>\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n\n<resources>\n    <!-- TF Branding Orange -->\n    <color name=\"bounding_box_color\">#FF6F00</color>\n    <color name=\"toolbar_background\">#EEEEEE</color>\n    <color name=\"ic_launcher_background\">#FFFFFF</color>\n    <color name=\"bottom_sheet_background\">#EEEEEE</color>\n    <color name=\"bottom_sheet_text_color\">@android:color/black</color>\n    <color name=\"icActive\">#FFFFFFFF</color>\n    <color name=\"icFocused\">#DDFFFFFF</color>\n    <color name=\"icPressed\">#AAFFFFFF</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n\n    <dimen name=\"stroke_small\">4dp</dimen>\n    <dimen name=\"round_button_medium\">64dp</dimen>\n\n    <!-- Bottom Sheet -->\n    <dimen name=\"bottom_sheet_text_size\">20sp</dimen>\n    <dimen name=\"bottom_sheet_padding\">16dp</dimen>\n    <dimen name=\"bottom_sheet_peek_height\">50dp</dimen>\n    <dimen name=\"bottom_sheet_default_row_margin\">16dp</dimen>\n    <dimen name=\"bottom_sheet_control_btn_size\">48dp</dimen>\n    <dimen name=\"bottom_sheet_control_text_side_margin\">10dp</dimen>\n    <dimen name=\"bottom_sheet_spinner_delegate_min_width\">160dp</dimen>\n    <dimen name=\"bottom_sheet_spinner_model_min_width\">240dp</dimen>\n\n    <integer name=\"bottom_sheet_control_text_min_ems\">3</integer>\n</resources>\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n        ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n        ~\n        ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n        ~ you may not use this file except in compliance with the License.\n        ~ You may obtain a copy of the License at\n        ~\n        ~       http://www.apache.org/licenses/LICENSE-2.0\n        ~\n        ~ Unless required by applicable law or agreed to in writing, software\n        ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n        ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n        ~ See the License for the specific language governing permissions and\n        ~ limitations under the License.\n    -->\n<resources>\n    <string name=\"app_name\">TFLite Image Classification</string>\n\n    <string name=\"alt_bottom_sheet_chevron\">Bottom sheet expandable indicator</string>\n\n    <string name=\"alt_bottom_sheet_max_results_button_minus\">Decreasing maximum classified results\n        button</string>\n    <string name=\"alt_bottom_sheet_max_results_button_plus\">Increasing maximum classified results\n        button</string>\n    <string name=\"alt_bottom_sheet_threshold_button_minus\">Decreasing threshold of classified image\n        results button</string>\n    <string name=\"alt_bottom_sheet_threshold_button_plus\">Increasing threshold of classified image\n        results button</string>\n    <string name=\"alt_bottom_sheet_thread_button_minus\">Decrease the number of threads used</string>\n    <string name=\"alt_bottom_sheet_thread_button_plus\">Increase the number of threads used</string>\n\n    <string name=\"label_interence_time\">Inference Time</string>\n    <string name=\"label_fps\">Frames per Second</string>\n    <string name=\"label_confidence_threshold\">Threshold</string>\n    <string name=\"label_max_results\">Max Results</string>\n    <string name=\"label_threads\">Number of Threads</string>\n    <string name=\"label_delegate\">Delegate</string>\n    <string name=\"label_models\">ML Model</string>\n\n    <string-array name=\"delegate_spinner_titles\">\n        <item>CPU</item>\n        <item>GPU</item>\n        <item>NNAPI</item>\n    </string-array>\n\n    <string-array name=\"models_spinner_titles\">\n        <item>MobileNet V1</item>\n        <item>EfficientNet Lite0</item>\n        <item>EfficientNet Lite1</item>\n        <item>EfficientNet Lite2</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n    </style>\n\n    <style name=\"BottomSheetSpinnerItemStyle\" parent=\"Widget.AppCompat.Light.DropDownItem.Spinner\">\n        <item name=\"android:textSize\">@dimen/bottom_sheet_text_size</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "lite/examples/image_classification/android/app/src/test/java/org/tensorflow/lite/examples/imageclassification/ExampleUnitTest.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.imageclassification\n\nimport org.junit.Test\n\nimport org.junit.Assert.*\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\nclass ExampleUnitTest {\n    @Test\n    fun addition_isCorrect() {\n        assertEquals(4, 2 + 2)\n    }\n}"
  },
  {
    "path": "lite/examples/image_classification/android/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n    dependencies {\n        classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.4.2'\n        classpath 'de.undercouch:gradle-download-task:4.1.2'\n    }\n}\nplugins {\n    id 'com.android.application' version '7.2.1' apply false\n    id 'com.android.library' version '7.2.1' apply false\n    id 'org.jetbrains.kotlin.android' version '1.6.21' apply false\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}"
  },
  {
    "path": "lite/examples/image_classification/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/image_classification/android/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app\"s APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official\n# Enables namespacing of each library's R class so that its R class includes only the\n# resources declared in the library itself and none from the library's dependencies,\n# thereby reducing the size of the R class for that library\nandroid.nonTransitiveRClass=true"
  },
  {
    "path": "lite/examples/image_classification/android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/image_classification/android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/image_classification/android/settings.gradle",
    "content": "pluginManagement {\n    repositories {\n        gradlePluginPortal()\n        google()\n        mavenCentral()\n    }\n}\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\nrootProject.name = \"TFLite Image Classification\"\ninclude ':app'\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/README.md",
    "content": "# TensorFlow Lite Image Classification Demo\n\n### Overview\n\nThis is a camera app that continuously classifies the objects in the frames\nseen by your device's back camera, with the option to use a quantized\n[MobileNet V1](https://tfhub.dev/tensorflow/lite-model/mobilenet_v1_1.0_224_quantized/1/metadata/1),\n[EfficientNet Lite0](https://tfhub.dev/tensorflow/lite-model/efficientnet/lite0/int8/2),\n[EfficientNet Lite1](https://tfhub.dev/tensorflow/lite-model/efficientnet/lite1/int8/2),\nor\n[EfficientNet Lite2](https://tfhub.dev/tensorflow/lite-model/efficientnet/lite2/int8/2)\nmodel trained on Imagenet (ILSVRC-2012-CLS). These instructions\nwalk you through building and running the demo on an Android device.\n\nThe model files are downloaded via Gradle scripts when you build and run the\napp. You don't need to do any steps to download TFLite models into the project\nexplicitly.\n\nThis application should be run on a physical Android device.\n\n![App example showing UI controls. Result is espresso.](screenshot1.jpg?raw=true \"Screenshot with controls\")\n\n![App example without UI controls. Result is espresso.](screenshot2.jpg?raw=true \"Screenshot without controls\")\n\nThis sample demonstrates how to use TensorFlow Lite with Java. If\nyou would like to see an example using Kotlin, please go to the\n[android sample directory](../android).\n\n\n## Build the demo using Android Studio\n\n### Prerequisites\n\n* The **[Android Studio](https://developer.android.com/studio/index.html)**\n    IDE (Android Studio 2021.2.1 or newer). This sample has been tested on\n    Android Studio Chipmunk\n\n* A physical Android device with a minimum OS version of SDK 23 (Android 6.0 -\n    Marshmallow) with developer mode enabled. The process of enabling developer\n    mode may vary by device.\n\n### Building\n\n* Open Android Studio. From the Welcome screen, select Open an existing\n    Android Studio project.\n\n* From the Open File or Project window that appears, navigate to and select\n    the tensorflow-lite/examples/image_classification/android directory.\n    Click OK.\n\n* If it asks you to do a Gradle Sync, click OK.\n\n* With your Android device connected to your computer and developer mode\n    enabled, click on the green Run arrow in Android Studio.\n\n### Models used\n\nDownloading, extraction, and placing the models into the assets folder is\nmanaged automatically by the download.gradle file.\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/build.gradle",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nplugins {\n    id 'com.android.application'\n    id 'org.jetbrains.kotlin.android'\n    id 'kotlin-kapt'\n    id 'androidx.navigation.safeargs'\n    id 'de.undercouch.download'\n}\n\nandroid {\n    compileSdk 32\n\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.imageclassification\"\n        minSdk 23\n        targetSdk 32\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n\n    buildFeatures {\n        viewBinding true\n    }\n\n    androidResources {\n        noCompress 'tflite'\n    }\n}\n\n// import DownloadModels task\nproject.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'\nproject.ext.TEST_ASSETS_DIR = projectDir.toString() + '/src/androidTest/assets'\n\n// Download default models; if you wish to use your own models then\n// place them in the \"assets\" directory and comment out this line.\napply from: 'download_models.gradle'\n\ndependencies {\n\n    // App compat and UI things\n    implementation 'androidx.appcompat:appcompat:1.4.2'\n    implementation 'com.google.android.material:material:1.6.1'\n    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'\n\n    // Navigation library\n    implementation 'androidx.navigation:navigation-fragment-ktx:2.5.1'\n    implementation 'androidx.navigation:navigation-ui-ktx:2.5.1'\n\n    // CameraX core library\n    def camerax_version = '1.2.0-alpha04'\n    implementation \"androidx.camera:camera-core:$camerax_version\"\n\n    // CameraX Camera2 extensions\n    implementation \"androidx.camera:camera-camera2:$camerax_version\"\n\n    // CameraX Lifecycle library\n    implementation \"androidx.camera:camera-lifecycle:$camerax_version\"\n\n    // CameraX View class\n    implementation \"androidx.camera:camera-view:$camerax_version\"\n\n    // Unit testing\n    testImplementation 'junit:junit:4.13.2'\n\n    // Instrumented testing\n    androidTestImplementation 'androidx.test.ext:junit:1.1.3'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n\n    // Tensorflow lite dependencies\n    implementation 'org.tensorflow:tensorflow-lite-task-vision:0.4.0'\n    // Import the GPU delegate plugin Library for GPU inference\n    implementation 'org.tensorflow:tensorflow-lite-gpu:2.9.0'\n    implementation 'org.tensorflow:tensorflow-lite-gpu-delegate-plugin:0.4.0'\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/download_models.gradle",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ntask downloadModelFile(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_classification/android_java/mobilenet_v1_1.0_224_quantized_1_metadata_1.tflite'\n    dest project.ext.ASSET_DIR + '/mobilenetv1.tflite'\n    overwrite false\n}\n\ntask downloadModelFile0(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_classification/android_java/efficientnet_lite0_int8_2.tflite'\n    dest project.ext.ASSET_DIR + '/efficientnet-lite0.tflite'\n    overwrite false\n}\n\ntask downloadModelFile1(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_classification/android_java/efficientnet_lite1_int8_2.tflite'\n    dest project.ext.ASSET_DIR + '/efficientnet-lite1.tflite'\n    overwrite false\n}\n\ntask downloadModelFile2(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_classification/android_java/efficientnet_lite2_int8_2.tflite'\n    dest project.ext.ASSET_DIR + '/efficientnet-lite2.tflite'\n    overwrite false\n}\n\ntask copyTestModel(type: Copy, dependsOn: downloadModelFile) {\n    from project.ext.ASSET_DIR + '/mobilenetv1.tflite'\n    into project.ext.TEST_ASSETS_DIR\n}\n\npreBuild.dependsOn downloadModelFile, downloadModelFile0, downloadModelFile1, downloadModelFile2,\n        copyTestModel\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/androidTest/java/org/tensorflow/lite/examples/imageclassification/ImageClassificationTest.java",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.imageclassification;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\n\nimport android.content.res.AssetManager;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport androidx.test.ext.junit.runners.AndroidJUnit4;\nimport androidx.test.platform.app.InstrumentationRegistry;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.tensorflow.lite.support.label.Category;\nimport org.tensorflow.lite.task.vision.classifier.Classifications;\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ImageClassificationTest {\n    List<Category> controlCategories = new ArrayList<>(Arrays.asList(\n            new Category(\"cup\", 0.7578125f))\n    );\n\n    @Test\n    public void classificationResultsShouldNotChange() {\n        ImageClassifierHelper helper = ImageClassifierHelper.create(\n                InstrumentationRegistry.getInstrumentation().getContext(),\n                new ImageClassifierHelper.ClassifierListener() {\n                    @Override\n                    public void onError(String error) {\n                        // no-op\n                    }\n\n                    @Override\n                    public void onResults(\n                            List<Classifications> results,\n                            long inferenceTime\n                    ) {\n                        assertNotNull(results.get(0));\n\n                        // Verify that the classified data and control\n                        // data have the same number of categories\n                        assertEquals(controlCategories.size(),\n                                results.get(0).getCategories().size());\n\n                        // Loop through the categories\n                        for (int i = 0; i < results.size(); i++) {\n                            // Verify that the labels are consistent\n                            assertEquals(\n                                    controlCategories.get(i).getLabel(),\n                                    results.get(0).getCategories().get(i).getLabel()\n                            );\n                        }\n                    }\n                });\n        helper.setThreshold(0.0f);\n        helper.classify(loadImage(\"coffee.jpg\"), 0);\n    }\n\n    private Bitmap loadImage(String fileName) {\n        AssetManager assetManager = InstrumentationRegistry\n                .getInstrumentation()\n                .getContext()\n                .getAssets();\n        try {\n            InputStream inputStream = assetManager.open(fileName);\n            return BitmapFactory.decodeStream(inputStream);\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n}"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:dist=\"http://schemas.android.com/apk/distribution\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"org.tensorflow.lite.examples.imageclassification\">\n\n    <!-- Enable instant app support -->\n    <dist:module dist:instant=\"true\" />\n\n    <!-- Declare features -->\n    <uses-feature android:name=\"android.hardware.camera\" />\n\n    <!-- Declare permissions -->\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:taskAffinity=\"\"\n        tools:ignore=\"AllowBackup\">\n        <activity\n            android:name=\".MainActivity\"\n            android:clearTaskOnLaunch=\"true\"\n            android:configChanges=\"orientation|screenLayout|screenSize|smallestScreenSize\"\n            android:exported=\"true\"\n            android:resizeableActivity=\"true\"\n            android:rotationAnimation=\"seamless\"\n            android:theme=\"@style/AppTheme\"\n            tools:targetApi=\"O\">\n\n            <!-- Main app intent filter -->\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n\n            <!-- Declare notch support -->\n            <meta-data\n                android:name=\"android.notch_support\"\n                android:value=\"true\" />\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/main/java/org/tensorflow/lite/examples/imageclassification/ImageClassifierHelper.java",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.imageclassification;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.os.SystemClock;\nimport android.util.Log;\nimport java.io.IOException;\nimport java.util.List;\nimport org.tensorflow.lite.gpu.CompatibilityList;\nimport org.tensorflow.lite.support.image.ImageProcessor;\nimport org.tensorflow.lite.support.image.TensorImage;\nimport org.tensorflow.lite.support.image.ops.Rot90Op;\nimport org.tensorflow.lite.task.core.BaseOptions;\nimport org.tensorflow.lite.task.vision.classifier.Classifications;\nimport org.tensorflow.lite.task.vision.classifier.ImageClassifier;\n\n/** Helper class for wrapping Image Classification actions */\npublic class ImageClassifierHelper {\n    private static final String TAG = \"ImageClassifierHelper\";\n    private static final int DELEGATE_CPU = 0;\n    private static final int DELEGATE_GPU = 1;\n    private static final int DELEGATE_NNAPI = 2;\n    private static final int MODEL_MOBILENETV1 = 0;\n    private static final int MODEL_EFFICIENTNETV0 = 1;\n    private static final int MODEL_EFFICIENTNETV1 = 2;\n    private static final int MODEL_EFFICIENTNETV2 = 3;\n\n    private float threshold;\n    private int numThreads;\n    private int maxResults;\n    private int currentDelegate;\n    private int currentModel;\n    private final Context context;\n    private final ClassifierListener imageClassifierListener;\n    private ImageClassifier imageClassifier;\n\n    /** Helper class for wrapping Image Classification actions */\n    public ImageClassifierHelper(Float threshold,\n                                 int numThreads,\n                                 int maxResults,\n                                 int currentDelegate,\n                                 int currentModel,\n                                 Context context,\n                                 ClassifierListener imageClassifierListener) {\n        this.threshold = threshold;\n        this.numThreads = numThreads;\n        this.maxResults = maxResults;\n        this.currentDelegate = currentDelegate;\n        this.currentModel = currentModel;\n        this.context = context;\n        this.imageClassifierListener = imageClassifierListener;\n        setupImageClassifier();\n    }\n\n    public static ImageClassifierHelper create(\n            Context context,\n            ClassifierListener listener\n    ) {\n        return new ImageClassifierHelper(\n                0.5f,\n                2,\n                3,\n                0,\n                0,\n                context,\n                listener\n        );\n    }\n\n    public float getThreshold() {\n        return threshold;\n    }\n\n    public void setThreshold(float threshold) {\n        this.threshold = threshold;\n    }\n\n    public int getNumThreads() {\n        return numThreads;\n    }\n\n    public void setNumThreads(int numThreads) {\n        this.numThreads = numThreads;\n    }\n\n    public int getMaxResults() {\n        return maxResults;\n    }\n\n    public void setMaxResults(int maxResults) {\n        this.maxResults = maxResults;\n    }\n\n    public void setCurrentDelegate(int currentDelegate) {\n        this.currentDelegate = currentDelegate;\n    }\n\n    public void setCurrentModel(int currentModel) {\n        this.currentModel = currentModel;\n    }\n\n    private void setupImageClassifier() {\n        ImageClassifier.ImageClassifierOptions.Builder optionsBuilder =\n                ImageClassifier.ImageClassifierOptions.builder()\n                        .setScoreThreshold(threshold)\n                        .setMaxResults(maxResults);\n\n        BaseOptions.Builder baseOptionsBuilder =\n                BaseOptions.builder().setNumThreads(numThreads);\n\n        switch (currentDelegate) {\n            case DELEGATE_CPU:\n                // Default\n                break;\n            case DELEGATE_GPU:\n                if (new CompatibilityList().isDelegateSupportedOnThisDevice()) {\n                    baseOptionsBuilder.useGpu();\n                } else {\n                    imageClassifierListener.onError(\"GPU is not supported on \"\n                            + \"this device\");\n                }\n                break;\n            case DELEGATE_NNAPI:\n                baseOptionsBuilder.useNnapi();\n        }\n\n        String modelName;\n        switch (currentModel) {\n            case MODEL_MOBILENETV1:\n                modelName = \"mobilenetv1.tflite\";\n                break;\n            case MODEL_EFFICIENTNETV0:\n                modelName = \"efficientnet-lite0.tflite\";\n                break;\n            case MODEL_EFFICIENTNETV1:\n                modelName = \"efficientnet-lite1.tflite\";\n                break;\n            case MODEL_EFFICIENTNETV2:\n                modelName = \"efficientnet-lite2.tflite\";\n                break;\n            default:\n                modelName = \"mobilenetv1.tflite\";\n        }\n        try {\n            imageClassifier =\n                    ImageClassifier.createFromFileAndOptions(\n                            context,\n                            modelName,\n                            optionsBuilder.build());\n        } catch (IOException e) {\n            imageClassifierListener.onError(\"Image classifier failed to \"\n                    + \"initialize. See error logs for details\");\n            Log.e(TAG, \"TFLite failed to load model with error: \"\n                    + e.getMessage());\n        }\n    }\n\n    public void classify(Bitmap image, int imageRotation) {\n        if (imageClassifier == null) {\n            setupImageClassifier();\n        }\n\n        // Inference time is the difference between the system time at the start\n        // and finish of the process\n        long inferenceTime = SystemClock.uptimeMillis();\n\n        // Create preprocessor for the image.\n        // See https://www.tensorflow.org/lite/inference_with_metadata/\n        //            lite_support#imageprocessor_architecture\n        ImageProcessor imageProcessor =\n                new ImageProcessor.Builder().add(new Rot90Op(-imageRotation / 90)).build();\n\n        // Preprocess the image and convert it into a TensorImage for classification.\n        TensorImage tensorImage =\n                imageProcessor.process(TensorImage.fromBitmap(image));\n\n        // Classify the input image\n        imageClassifier.classify(tensorImage);\n\n        List<Classifications> result = imageClassifier.classify(tensorImage);\n\n        inferenceTime = SystemClock.uptimeMillis() - inferenceTime;\n        imageClassifierListener.onResults(result, inferenceTime);\n    }\n\n    public void clearImageClassifier() {\n        imageClassifier = null;\n    }\n\n    /** Listener for passing results back to calling class */\n    public interface ClassifierListener {\n        void onError(String error);\n\n        void onResults(List<Classifications> results, long inferenceTime);\n    }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/main/java/org/tensorflow/lite/examples/imageclassification/MainActivity.java",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.imageclassification;\n\nimport android.os.Build;\nimport android.os.Bundle;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AppCompatActivity;\nimport org.tensorflow.lite.examples.imageclassification.databinding.ActivityMainBinding;\n\n/** Entrypoint for app */\npublic class MainActivity extends AppCompatActivity {\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        ActivityMainBinding activityMainBinding = ActivityMainBinding.inflate(getLayoutInflater());\n        setContentView(activityMainBinding.getRoot());\n    }\n\n    @Override\n    public void onBackPressed() {\n        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {\n            // Workaround for Android Q memory leak issue in IRequestFinishCallback$Stub.\n            // (https://issuetracker.google.com/issues/139738913)\n            finishAfterTransition();\n        } else {\n            super.onBackPressed();\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/main/java/org/tensorflow/lite/examples/imageclassification/fragments/CameraFragment.java",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.imageclassification.fragments;\n\nimport android.content.res.Configuration;\nimport android.graphics.Bitmap;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AdapterView;\nimport android.widget.Toast;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.camera.core.AspectRatio;\nimport androidx.camera.core.CameraSelector;\nimport androidx.camera.core.ImageAnalysis;\nimport androidx.camera.core.ImageProxy;\nimport androidx.camera.core.Preview;\nimport androidx.camera.lifecycle.ProcessCameraProvider;\nimport androidx.core.content.ContextCompat;\nimport androidx.fragment.app.Fragment;\nimport androidx.navigation.Navigation;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport com.google.common.util.concurrent.ListenableFuture;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport org.tensorflow.lite.examples.imageclassification.ImageClassifierHelper;\nimport org.tensorflow.lite.examples.imageclassification.R;\nimport org.tensorflow.lite.examples.imageclassification.databinding.FragmentCameraBinding;\nimport org.tensorflow.lite.task.vision.classifier.Classifications;\n\n/** Fragment for displaying and controlling the device camera and other UI */\npublic class CameraFragment extends Fragment\n        implements ImageClassifierHelper.ClassifierListener {\n    private static final String TAG = \"Image Classifier\";\n\n    private FragmentCameraBinding fragmentCameraBinding;\n    private ImageClassifierHelper imageClassifierHelper;\n    private Bitmap bitmapBuffer;\n    private ClassificationResultAdapter classificationResultsAdapter;\n    private ImageAnalysis imageAnalyzer;\n    private ProcessCameraProvider cameraProvider;\n    private final Object task = new Object();\n\n    /**\n     * Blocking camera operations are performed using this executor\n     */\n    private ExecutorService cameraExecutor;\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull LayoutInflater inflater,\n                             @Nullable ViewGroup container,\n                             @Nullable Bundle savedInstanceState) {\n        fragmentCameraBinding = FragmentCameraBinding\n                .inflate(inflater, container, false);\n        return fragmentCameraBinding.getRoot();\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n\n        if (!PermissionsFragment.hasPermission(requireContext())) {\n            Navigation.findNavController(requireActivity(), R.id.fragment_container)\n                    .navigate(\n                            CameraFragmentDirections.actionCameraToPermissions()\n                    );\n        }\n    }\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n\n        // Shut down our background executor\n        cameraExecutor.shutdown();\n        synchronized (task) {\n            imageClassifierHelper.clearImageClassifier();\n        }\n    }\n\n    @Override\n    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n        super.onViewCreated(view, savedInstanceState);\n        cameraExecutor = Executors.newSingleThreadExecutor();\n        imageClassifierHelper = ImageClassifierHelper.create(requireContext()\n                , this);\n\n        // setup result adapter\n        classificationResultsAdapter = new ClassificationResultAdapter();\n        classificationResultsAdapter\n                .updateAdapterSize(imageClassifierHelper.getMaxResults());\n        fragmentCameraBinding.recyclerviewResults\n                .setAdapter(classificationResultsAdapter);\n        fragmentCameraBinding.recyclerviewResults\n                .setLayoutManager(new LinearLayoutManager(requireContext()));\n\n        // Set up the camera and its use cases\n        fragmentCameraBinding.viewFinder.post(this::setUpCamera);\n\n        // Attach listeners to UI control widgets\n        initBottomSheetControls();\n    }\n\n    @Override\n    public void onConfigurationChanged(@NonNull Configuration newConfig) {\n        super.onConfigurationChanged(newConfig);\n        imageAnalyzer.setTargetRotation(\n                fragmentCameraBinding.viewFinder.getDisplay().getRotation()\n        );\n    }\n\n    private void initBottomSheetControls() {\n        // When clicked, lower classification score threshold floor\n        fragmentCameraBinding.bottomSheetLayout.thresholdMinus\n                .setOnClickListener(view -> {\n                    float threshold = imageClassifierHelper.getThreshold();\n                    if (threshold >= 0.1) {\n                        imageClassifierHelper.setThreshold(threshold - 0.1f);\n                        updateControlsUi();\n                    }\n                });\n\n        // When clicked, raise classification score threshold floor\n        fragmentCameraBinding.bottomSheetLayout.thresholdPlus\n                .setOnClickListener(view -> {\n                    float threshold = imageClassifierHelper.getThreshold();\n                    if (threshold < 0.9) {\n                        imageClassifierHelper.setThreshold(threshold + 0.1f);\n                        updateControlsUi();\n                    }\n                });\n\n        // When clicked, reduce the number of objects that can be classified\n        // at a time\n        fragmentCameraBinding.bottomSheetLayout.maxResultsMinus\n                .setOnClickListener(view -> {\n                    int maxResults = imageClassifierHelper.getMaxResults();\n                    if (maxResults > 1) {\n                        imageClassifierHelper.setMaxResults(maxResults - 1);\n                        updateControlsUi();\n                        classificationResultsAdapter.updateAdapterSize(\n                                imageClassifierHelper.getMaxResults()\n                        );\n                    }\n                });\n\n        // When clicked, increase the number of objects that can be\n        // classified at a time\n        fragmentCameraBinding.bottomSheetLayout.maxResultsPlus\n                .setOnClickListener(view -> {\n                    int maxResults = imageClassifierHelper.getMaxResults();\n                    if (maxResults < 3) {\n                        imageClassifierHelper.setMaxResults(maxResults + 1);\n                        updateControlsUi();\n                        classificationResultsAdapter.updateAdapterSize(\n                                imageClassifierHelper.getMaxResults()\n                        );\n                    }\n                });\n\n        // When clicked, decrease the number of threads used for classification\n        fragmentCameraBinding.bottomSheetLayout.threadsMinus\n                .setOnClickListener(view -> {\n                    int numThreads = imageClassifierHelper.getNumThreads();\n                    if (numThreads > 1) {\n                        imageClassifierHelper.setNumThreads(numThreads - 1);\n                        updateControlsUi();\n                    }\n                });\n\n        // When clicked, increase the number of threads used for classification\n        fragmentCameraBinding.bottomSheetLayout.threadsPlus\n                .setOnClickListener(view -> {\n                    int numThreads = imageClassifierHelper.getNumThreads();\n                    if (numThreads < 4) {\n                        imageClassifierHelper.setNumThreads(numThreads + 1);\n                        updateControlsUi();\n                    }\n                });\n\n        // When clicked, change the underlying hardware used for inference.\n        // Current options are CPU,GPU, and NNAPI\n        fragmentCameraBinding.bottomSheetLayout.spinnerDelegate\n                .setSelection(0, false);\n        fragmentCameraBinding.bottomSheetLayout.spinnerDelegate\n                .setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {\n                    @Override\n                    public void onItemSelected(AdapterView<?> adapterView,\n                                               View view,\n                                               int position,\n                                               long id) {\n                        imageClassifierHelper.setCurrentDelegate(position);\n                        updateControlsUi();\n                    }\n\n                    @Override\n                    public void onNothingSelected(AdapterView<?> adapterView) {\n                        // no-op\n                    }\n                });\n\n        // When clicked, change the underlying model used for object\n        // classification\n        fragmentCameraBinding.bottomSheetLayout.spinnerModel\n                .setSelection(0, false);\n        fragmentCameraBinding.bottomSheetLayout.spinnerModel\n                .setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {\n                    @Override\n                    public void onItemSelected(AdapterView<?> adapterView,\n                                               View view,\n                                               int position,\n                                               long id) {\n                        imageClassifierHelper.setCurrentModel(position);\n                        updateControlsUi();\n                    }\n\n                    @Override\n                    public void onNothingSelected(AdapterView<?> adapterView) {\n                        // no-op\n                    }\n                });\n    }\n\n    // Update the values displayed in the bottom sheet. Reset classifier.\n    private void updateControlsUi() {\n        fragmentCameraBinding.bottomSheetLayout.maxResultsValue\n                .setText(String.valueOf(imageClassifierHelper.getMaxResults()));\n        fragmentCameraBinding.bottomSheetLayout.thresholdValue\n                .setText(String.format(Locale.US, \"%.2f\",\n                        imageClassifierHelper.getThreshold()));\n        fragmentCameraBinding.bottomSheetLayout.threadsValue\n                .setText(String.valueOf(imageClassifierHelper.getNumThreads()));\n        // Needs to be cleared instead of reinitialized because the GPU\n        // delegate needs to be initialized on the thread using it when\n        // applicable\n        synchronized (task) {\n            imageClassifierHelper.clearImageClassifier();\n        }\n    }\n\n    // Initialize CameraX, and prepare to bind the camera use cases\n    private void setUpCamera() {\n        ListenableFuture<ProcessCameraProvider> cameraProviderFuture =\n                ProcessCameraProvider.getInstance(requireContext());\n        cameraProviderFuture.addListener(() -> {\n            try {\n                cameraProvider = cameraProviderFuture.get();\n\n                // Build and bind the camera use cases\n                bindCameraUseCases();\n            } catch (ExecutionException | InterruptedException e) {\n                e.printStackTrace();\n            }\n        }, ContextCompat.getMainExecutor(requireContext()));\n    }\n\n    // Declare and bind preview, capture and analysis use cases\n    private void bindCameraUseCases() {\n        // CameraSelector - makes assumption that we're only using the back\n        // camera\n        CameraSelector.Builder cameraSelectorBuilder = new CameraSelector.Builder();\n        CameraSelector cameraSelector = cameraSelectorBuilder\n                .requireLensFacing(CameraSelector.LENS_FACING_BACK).build();\n\n        // Preview. Only using the 4:3 ratio because this is the closest to\n        // our model\n        Preview preview = new Preview.Builder()\n                .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n                .setTargetRotation(\n                        fragmentCameraBinding.viewFinder\n                                .getDisplay().getRotation()\n                )\n                .build();\n\n        // ImageAnalysis. Using RGBA 8888 to match how our models work\n        imageAnalyzer = new ImageAnalysis.Builder()\n                .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n                .setTargetRotation(fragmentCameraBinding.viewFinder.getDisplay().getRotation())\n                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)\n                .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)\n                .build();\n\n        // The analyzer can then be assigned to the instance\n        imageAnalyzer.setAnalyzer(cameraExecutor, image -> {\n            if (bitmapBuffer == null) {\n                bitmapBuffer = Bitmap.createBitmap(\n                        image.getWidth(),\n                        image.getHeight(),\n                        Bitmap.Config.ARGB_8888);\n            }\n            classifyImage(image);\n        });\n\n        // Must unbind the use-cases before rebinding them\n        cameraProvider.unbindAll();\n\n        try {\n            // A variable number of use-cases can be passed here -\n            // camera provides access to CameraControl & CameraInfo\n            cameraProvider.bindToLifecycle(\n                    this,\n                    cameraSelector,\n                    preview,\n                    imageAnalyzer\n            );\n\n            // Attach the viewfinder's surface provider to preview use case\n            preview.setSurfaceProvider(\n                    fragmentCameraBinding.viewFinder.getSurfaceProvider()\n            );\n        } catch (Exception exc) {\n            Log.e(TAG, \"Use case binding failed\", exc);\n        }\n    }\n\n    private void classifyImage(@NonNull ImageProxy image) {\n        // Copy out RGB bits to the shared bitmap buffer\n        bitmapBuffer.copyPixelsFromBuffer(image.getPlanes()[0].getBuffer());\n\n        int imageRotation = image.getImageInfo().getRotationDegrees();\n        image.close();\n        synchronized (task) {\n            // Pass Bitmap and rotation to the image classifier helper for\n            // processing and classification\n            imageClassifierHelper.classify(bitmapBuffer, imageRotation);\n        }\n    }\n\n    @Override\n    public void onError(String error) {\n        requireActivity().runOnUiThread(() -> {\n            Toast.makeText(requireContext(), error, Toast.LENGTH_SHORT).show();\n            classificationResultsAdapter.updateResults(new ArrayList<>());\n        });\n    }\n\n    @Override\n    public void onResults(List<Classifications> results, long inferenceTime) {\n        requireActivity().runOnUiThread(() -> {\n            classificationResultsAdapter.updateResults(results.get(0).getCategories());\n            fragmentCameraBinding.bottomSheetLayout.inferenceTimeVal\n                    .setText(String.format(Locale.US, \"%d ms\", inferenceTime));\n        });\n    }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/main/java/org/tensorflow/lite/examples/imageclassification/fragments/ClassificationResultAdapter.java",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.imageclassification.fragments;\n\nimport android.annotation.SuppressLint;\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Locale;\nimport org.tensorflow.lite.examples.imageclassification.databinding.ItemClassificationResultBinding;\nimport org.tensorflow.lite.support.label.Category;\n\n/** Adapter for displaying the list of classifications for the image */\npublic class ClassificationResultAdapter\n        extends RecyclerView.Adapter<ClassificationResultAdapter.ViewHolder> {\n    private static final String NO_VALUE = \"--\";\n    private List<Category> categories = new ArrayList<>();\n    private int adapterSize = 0;\n\n    @SuppressLint(\"NotifyDataSetChanged\")\n    public void updateResults(List<Category> categories) {\n        List<Category> sortedCategories = new ArrayList<>(categories);\n        Collections.sort(sortedCategories, new Comparator<Category>() {\n            @Override\n            public int compare(Category category1, Category category2) {\n                return category1.getIndex() - category2.getIndex();\n            }\n        });\n        this.categories = new ArrayList<>(Collections.nCopies(adapterSize, null));\n        int min = Math.min(sortedCategories.size(), adapterSize);\n        for (int i = 0; i < min; i++) {\n            this.categories.set(i, sortedCategories.get(i));\n        }\n        notifyDataSetChanged();\n    }\n\n    public void updateAdapterSize(int size) {\n        adapterSize = size;\n    }\n\n    @NonNull\n    @Override\n    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n        ItemClassificationResultBinding binding = ItemClassificationResultBinding\n                .inflate(LayoutInflater.from(parent.getContext()), parent, false);\n        return new ViewHolder(binding);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {\n        holder.bind(categories.get(position));\n    }\n\n    @Override\n    public int getItemCount() {\n        return categories.size();\n    }\n\n    /** Data structure for items in list */\n    public static class ViewHolder extends RecyclerView.ViewHolder {\n        private final TextView tvLabel;\n        private final TextView tvScore;\n\n        public ViewHolder(@NonNull ItemClassificationResultBinding binding) {\n            super(binding.getRoot());\n            tvLabel = binding.tvLabel;\n            tvScore = binding.tvScore;\n        }\n\n        public void bind(Category category) {\n            if (category != null) {\n                tvLabel.setText(category.getLabel());\n                tvScore.setText(String.format(Locale.US, \"%.2f\", category.getScore()));\n            } else {\n                tvLabel.setText(NO_VALUE);\n                tvScore.setText(NO_VALUE);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/main/java/org/tensorflow/lite/examples/imageclassification/fragments/PermissionsFragment.java",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.imageclassification.fragments;\n\nimport android.Manifest;\nimport android.content.Context;\nimport android.content.pm.PackageManager;\nimport android.widget.Toast;\nimport androidx.activity.result.ActivityResultLauncher;\nimport androidx.activity.result.contract.ActivityResultContracts;\nimport androidx.core.content.ContextCompat;\nimport androidx.fragment.app.Fragment;\nimport androidx.navigation.Navigation;\nimport org.tensorflow.lite.examples.imageclassification.R;\n\n/** Handles requesting permissions for the device before using the camera */\npublic class PermissionsFragment extends Fragment {\n\n    /**\n     * Convenience method used to check if all permissions required by this\n     * app are granted\n     */\n    public static boolean hasPermission(Context context) {\n        return ContextCompat.checkSelfPermission(context,\n                Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;\n    }\n\n    private final ActivityResultLauncher<String> requestPermissionLauncher\n            = registerForActivityResult(\n            new ActivityResultContracts.RequestPermission(), isGranted -> {\n                if (isGranted) {\n                    Toast.makeText(requireContext(),\n                                    \"Permission request granted\",\n                                    Toast.LENGTH_LONG)\n                            .show();\n                    navigateToCamera();\n                } else {\n                    Toast.makeText(requireContext(),\n                                    \"Permission request denied\",\n                                    Toast.LENGTH_LONG)\n                            .show();\n                }\n            });\n\n    @Override\n    public void onStart() {\n        super.onStart();\n        if (ContextCompat.checkSelfPermission(requireContext(),\n                Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {\n            navigateToCamera();\n        } else {\n            requestPermissionLauncher.launch(Manifest.permission.CAMERA);\n        }\n    }\n\n    private void navigateToCamera() {\n        Navigation.findNavController(requireActivity(), R.id.fragment_container)\n                .navigate(PermissionsFragmentDirections.actionPermissionsToCamera());\n    }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/main/res/drawable/ic_minus.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13H5v-2h14v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/main/res/drawable/ic_plus.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path android:pathData=\"M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"85.84757\"\n                android:endY=\"92.4963\"\n                android:startX=\"42.9492\"\n                android:startY=\"49.59793\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:background=\"@android:color/transparent\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <androidx.fragment.app.FragmentContainerView\n            android:id=\"@+id/fragment_container\"\n            android:name=\"androidx.navigation.fragment.NavHostFragment\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:background=\"@android:color/transparent\"\n            android:keepScreenOn=\"true\"\n            app:defaultNavHost=\"true\"\n            app:navGraph=\"@navigation/nav_graph\"\n            android:layout_marginTop=\"?android:attr/actionBarSize\"\n            tools:context=\".MainActivity\"/>\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:layout_alignParentTop=\"true\"\n            android:background=\"@color/toolbar_background\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/tfl_logo\" />\n        </androidx.appcompat.widget.Toolbar>\n\n    </RelativeLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/main/res/layout/fragment_camera.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/camera_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.camera.view.PreviewView\n        android:id=\"@+id/view_finder\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:scaleType=\"fillStart\" />\n\n    <include\n        android:id=\"@+id/bottom_sheet_layout\"\n        layout=\"@layout/info_bottom_sheet\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/recyclerview_results\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"top\"\n        android:layout_marginBottom=\"@dimen/bottom_sheet_peek_height\"\n        android:background=\"@color/bottom_sheet_background\"\n        android:clipToPadding=\"true\"\n        android:padding=\"@dimen/bottom_sheet_padding\"\n        app:layout_anchor=\"@id/bottom_sheet_layout\"\n        app:layout_anchorGravity=\"top\"\n        app:layout_behavior=\"com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior\" />\n\n    <View\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"@dimen/bottom_sheet_seperate_results_line\"\n        android:background=\"@color/bounding_box_color\"\n        app:layout_anchor=\"@id/recyclerview_results\"\n        app:layout_anchorGravity=\"bottom\" />\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/main/res/layout/info_bottom_sheet.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.core.widget.NestedScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/bottom_sheet_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_gravity=\"center_horizontal\"\n    android:background=\"@color/bottom_sheet_background\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:padding=\"@dimen/bottom_sheet_padding\"\n    app:behavior_hideable=\"false\"\n    app:behavior_peekHeight=\"@dimen/bottom_sheet_peek_height\"\n    app:layout_behavior=\"com.google.android.material.bottomsheet.BottomSheetBehavior\">\n\n    <androidx.appcompat.widget.LinearLayoutCompat\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <!-- Chevron indicating that the bottom sheet is expandable -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerHorizontal=\"true\"\n                android:layout_centerVertical=\"true\"\n                android:contentDescription=\"@string/alt_bottom_sheet_chevron\"\n                android:src=\"@drawable/icn_chevron_up\" />\n        </RelativeLayout>\n\n        <!-- Inference time row -->\n        <androidx.appcompat.widget.LinearLayoutCompat\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/inference_time_label\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:text=\"@string/label_interence_time\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <TextView\n                android:id=\"@+id/inference_time_val\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:gravity=\"end\"\n                android:text=\"@string/default_inference_time\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n        </androidx.appcompat.widget.LinearLayoutCompat>\n\n        <!-- ML confidence threshold adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:text=\"@string/label_confidence_threshold\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\">\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threshold_minus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_threshold_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/threshold_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:gravity=\"center\"\n                    android:minEms=\"@integer/bottom_sheet_control_text_min_ems\"\n                    android:text=\"@string/default_threshold\"\n                    android:textColor=\"@color/bottom_sheet_text_color\"\n                    android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threshold_plus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_threshold_button_plus\"\n                    android:src=\"@drawable/ic_plus\" />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- ML max results adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:text=\"@string/label_max_results\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center\"\n                android:orientation=\"horizontal\">\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/max_results_minus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_max_results_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/max_results_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:gravity=\"center\"\n                    android:minEms=\"@integer/bottom_sheet_control_text_min_ems\"\n                    android:text=\"@string/default_max_results\"\n                    android:textColor=\"@color/bottom_sheet_text_color\"\n                    android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/max_results_plus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_max_results_button_plus\"\n                    android:src=\"@drawable/ic_plus\" />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- Number of threads adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:text=\"@string/label_threads\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\">\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threads_minus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_thread_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/threads_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:gravity=\"center\"\n                    android:minEms=\"@integer/bottom_sheet_control_text_min_ems\"\n                    android:text=\"2\"\n                    android:textColor=\"@color/bottom_sheet_text_color\"\n                    android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threads_plus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_thread_button_plus\"\n                    android:src=\"@drawable/ic_plus\" />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- Delegate selection row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/label_delegate\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <androidx.appcompat.widget.AppCompatSpinner\n                android:id=\"@+id/spinner_delegate\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:entries=\"@array/delegate_spinner_titles\"\n                android:minWidth=\"@dimen/bottom_sheet_spinner_delegate_min_width\"\n                android:spinnerMode=\"dropdown\"\n                android:theme=\"@style/BottomSheetSpinnerItemStyle\" />\n\n        </RelativeLayout>\n\n        <!-- Model selection row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/label_models\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <androidx.appcompat.widget.AppCompatSpinner\n                android:id=\"@+id/spinner_model\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:entries=\"@array/models_spinner_titles\"\n                android:minWidth=\"@dimen/bottom_sheet_spinner_model_min_width\"\n                android:spinnerMode=\"dropdown\"\n                android:theme=\"@style/BottomSheetSpinnerItemStyle\" />\n\n        </RelativeLayout>\n    </androidx.appcompat.widget.LinearLayoutCompat>\n</androidx.core.widget.NestedScrollView>\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/main/res/layout/item_classification_result.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <TextView\n        android:id=\"@+id/tvLabel\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:textColor=\"@color/bottom_sheet_text_color\"\n        android:textSize=\"@dimen/bottom_sheet_text_size\"\n        app:layout_constraintEnd_toStartOf=\"@+id/tvScore\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <TextView\n        android:id=\"@+id/tvScore\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:textSize=\"@dimen/bottom_sheet_text_size\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/main/res/navigation/nav_graph.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/nav_graph\"\n    app:startDestination=\"@id/permissions_fragment\">\n\n    <fragment\n        android:id=\"@+id/permissions_fragment\"\n        android:name=\"org.tensorflow.lite.examples.imageclassification.fragments.PermissionsFragment\"\n        android:label=\"PermissionsFragment\">\n\n        <action\n            android:id=\"@+id/action_permissions_to_camera\"\n            app:destination=\"@id/camera_fragment\"\n            app:popUpTo=\"@id/permissions_fragment\"\n            app:popUpToInclusive=\"true\" />\n\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/camera_fragment\"\n        android:name=\"org.tensorflow.lite.examples.imageclassification.fragments.CameraFragment\"\n        android:label=\"CameraFragment\">\n\n        <action\n            android:id=\"@+id/action_camera_to_permissions\"\n            app:destination=\"@id/permissions_fragment\"\n            app:popUpTo=\"@id/camera_fragment\"\n            app:popUpToInclusive=\"true\" />\n    </fragment>\n</navigation>\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n\n<resources>\n    <!-- TF Branding Orange -->\n    <color name=\"bounding_box_color\">#FF6F00</color>\n    <color name=\"toolbar_background\">#EEEEEE</color>\n    <color name=\"ic_launcher_background\">#FFFFFF</color>\n    <color name=\"bottom_sheet_background\">#EEEEEE</color>\n    <color name=\"bottom_sheet_text_color\">@android:color/black</color>\n    <color name=\"icActive\">#FFFFFFFF</color>\n    <color name=\"icFocused\">#DDFFFFFF</color>\n    <color name=\"icPressed\">#AAFFFFFF</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n\n    <dimen name=\"stroke_small\">4dp</dimen>\n    <dimen name=\"round_button_medium\">64dp</dimen>\n\n    <!-- Bottom Sheet -->\n    <dimen name=\"bottom_sheet_text_size\">20sp</dimen>\n    <dimen name=\"bottom_sheet_padding\">16dp</dimen>\n    <dimen name=\"bottom_sheet_peek_height\">50dp</dimen>\n    <dimen name=\"bottom_sheet_default_row_margin\">16dp</dimen>\n    <dimen name=\"bottom_sheet_control_btn_size\">48dp</dimen>\n    <dimen name=\"bottom_sheet_control_text_side_margin\">10dp</dimen>\n    <dimen name=\"bottom_sheet_spinner_delegate_min_width\">160dp</dimen>\n    <dimen name=\"bottom_sheet_spinner_model_min_width\">240dp</dimen>\n    <dimen name=\"bottom_sheet_seperate_results_line\">1dp</dimen>\n\n    <integer name=\"bottom_sheet_control_text_min_ems\">3</integer>\n</resources>\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n    <string name=\"app_name\">TFLite Image Classification</string>\n\n    <string name=\"alt_bottom_sheet_chevron\">Bottom sheet expandable indicator</string>\n\n    <string name=\"alt_bottom_sheet_max_results_button_minus\">Decreasing maximum classified results\n        button</string>\n    <string name=\"alt_bottom_sheet_max_results_button_plus\">Increasing maximum classified results\n        button</string>\n    <string name=\"alt_bottom_sheet_threshold_button_minus\">Decreasing threshold of classified image\n        results button</string>\n    <string name=\"alt_bottom_sheet_threshold_button_plus\">Increasing threshold of classified image\n        results button</string>\n    <string name=\"alt_bottom_sheet_thread_button_minus\">Decrease the number of threads used</string>\n    <string name=\"alt_bottom_sheet_thread_button_plus\">Increase the number of threads used</string>\n\n    <string name=\"label_interence_time\">Inference Time</string>\n    <string name=\"label_fps\">Frames per Second</string>\n    <string name=\"label_confidence_threshold\">Threshold</string>\n    <string name=\"label_max_results\">Max Results</string>\n    <string name=\"label_threads\">Number of Threads</string>\n    <string name=\"label_delegate\">Delegate</string>\n    <string name=\"label_models\">ML Model</string>\n    <string name=\"default_threshold\">0.50</string>\n    <string name=\"default_inference_time\">0ms</string>\n    <string name=\"default_max_results\">3</string>\n\n    <string-array name=\"delegate_spinner_titles\">\n        <item>CPU</item>\n        <item>GPU</item>\n        <item>NNAPI</item>\n    </string-array>\n\n    <string-array name=\"models_spinner_titles\">\n        <item>MobileNet V1</item>\n        <item>EfficientNet Lite0</item>\n        <item>EfficientNet Lite1</item>\n        <item>EfficientNet Lite2</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n    </style>\n\n    <style name=\"BottomSheetSpinnerItemStyle\" parent=\"Widget.AppCompat.Light.DropDownItem.Spinner\">\n        <item name=\"android:textSize\">@dimen/bottom_sheet_text_size</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n    dependencies {\n        classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.5.1'\n        classpath 'de.undercouch:gradle-download-task:4.1.2'\n    }\n}\nplugins {\n    id 'com.android.application' version '7.2.0' apply false\n    id 'com.android.library' version '7.2.0' apply false\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app\"s APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Enables namespacing of each library's R class so that its R class includes only the\n# resources declared in the library itself and none from the library's dependencies,\n# thereby reducing the size of the R class for that library\nandroid.nonTransitiveRClass=true"
  },
  {
    "path": "lite/examples/image_classification/android_java/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/image_classification/android_java/settings.gradle",
    "content": "pluginManagement {\n    repositories {\n        gradlePluginPortal()\n        google()\n        mavenCentral()\n    }\n}\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\nrootProject.name = \"TFLite Image Classification\"\ninclude ':app'\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/.google/packaging.yaml",
    "content": "# GOOGLE SAMPLE PACKAGING DATA\n#\n# This file is used by Google as part of our samples packaging process.\n# End users may safely ignore this file. It has no relevance to other systems.\n---\nstatus:       PUBLISHED\ntechnologies: [Android, TensorFlow, Google Play Services]\n# Note that categories are run through .toLowerCase() and then .capitalize(), so \"AI\" becomes \"Ai\"\ncategories:   [Artificial Intelligence, Machine Learning]\nlanguages:    [Java, Kotlin]\nsolutions:    [Mobile]\ngithub:       tensorflow-examples\nlevel:        INTERMEDIATE\nicon:         screenshots/icon-web.png\napiRefs:\n- android:com.google.android.gms.tflite.java.TfLite\nlicense: apache2\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/README.md",
    "content": "# Tensor​Flow Lite in Play Services image classification Android example application\n\nThis example performs real-time image classification on live camera frames. It\ndemonstrates loading a model using Tensorflow Lite in Google Play Services and\nusing it to classify the camera feed.\n\nIt performs the following operations:\n\n1.  Initializes camera preview and image analysis frame streams using CameraX\n2.  Loads a EfficientNet-Lite model using Tensorflow Lite in Play Services\n3.  Performs inference on the transformed frames\n4.  Reports the image recognized on the screen\n\n**Terms:**\nBy accessing or using TensorFlow Lite in Google Play services, you agree\nto the\n[Terms of Service](https://www.tensorflow.org/lite/android/play_services#tos).\nPlease read and understand all applicable terms and policies before accessing\nthe APIs.\n\n### Model\n\nThis app uses EfficientNet-Lite model for demonstration. For details of the\nmodel used, visit\n[Image classification](https://www.tensorflow.org/lite/examples/image_classification/overview).\n\nDownloading, extracting, and placing the model in the assets folder is managed\nautomatically by download.gradle.\n\n## Requirements\n\n*   Android Studio (installed on a Linux, Mac or Windows machine)\n*   Android device in\n    [developer mode](https://developer.android.com/studio/debug/dev-options)\n    with USB debugging enabled\n*   USB cable (to connect Android device to your computer)\n\n## Build and run\n\n### Step 1. Clone the TensorFlow examples source code\n\nClone the TensorFlow examples GitHub repository to your computer to get the demo\napplication.\n\n```\ngit clone https://github.com/tensorflow/examples\n```\n\nOpen the example application's project directory in Android Studio. To do this,\nopen Android Studio and select `Open an existing project`, setting the folder to\n`examples/lite/examples/image_classification/android_play_services`\n\n### Step 2. Build the Android Studio project\n\nSelect `Build -> Make Project` and check that the project builds successfully.\nYou will need Android SDK configured in the settings. You'll need at least SDK\nversion 23. The `build.gradle` file will prompt you to download any missing\nlibraries. The `download.gradle` file directs gradle to download the model used\nin the example, placing it into `assets`.\n\n#### Optional: Switch between source code language (Kotlin and Java)\n\nThis sample app demonstrates two implementation options:\n\n*   [Default] Kotlin: Under `app/src/kotlin`\n*   Java: Under `app/src/java`\n\nThe [`build.gradle`](app/build.gradle) inside the `app` folder shows how to\nchange `flavorDimensions \"language\"` to switch between the two options.\n\nInside **Android Studio**, you can change the build variant to whichever one you\nwant to build and run — just go to `Build > Select Build Variant` and select one\nfrom the drop-down menu. See\n[configure product flavors in Android Studio](https://developer.android.com/studio/build/build-variants#product-flavors)\nfor more details.\n\n### Step 3. Install and run the app\n\nConnect the Android device to the computer and be sure to approve any ADB\npermission prompts that appear on your phone. Select `Run -> Run app.` Select\nthe deployment target in the connected devices to the device on which the app\nwill be installed. This will install the app on the device.\n\nTo test the app, open the app called `Image Classification` on your device. When\nyou run the app the first time, the app will request permission to access the\ncamera. Re-installing the app may require you to uninstall the previous\ninstallations.\n\n## Screenshots\n\n![demo](https://storage.googleapis.com/download.tensorflow.org/tflite/examples/android_play_services_demo.gif \"demo animation\")\n![screenshot 1](screenshots/screenshot-1.jpg \"screenshot 1\")\n\n## Source Code Structure\n\n*   `app/src/kotlin`: Sample app implemented in Kotlin language.\n*   `app/src/java`: Sample app implemented in Java language.\n*   `app/src/main`: Resources and Assets.\n*   `app/src/androidTest`: Instrumentation test code that are shared between\n    build variants.\n*   `app/src/androidTestKotlin`: Instrumentation test code that only applies to\n    Kotlin implementation.\n*   `app/src/androidTestJava`: Instrumentation test code that only applies to\n    Java implementation.\n\n> Note: Gradle expects the directory structure to match the following:\n> `$module`/src/`$build_variant`/(`$language`|res|assets|AndroidManifest.xml|...)\n> For this app:\n>\n> *   `$module` == app\n> *   `$build_variant` == kotlin\n> *   `$language` == kotlin\n\n### Assets folder\n\n*Do not delete the assets folder content*. If you explicitly deleted the files,\nchoose `Build -> Rebuild` to re-download the deleted model files into the assets\nfolder.\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/build.gradle",
    "content": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\napply plugin: \"com.android.application\"\napply plugin: \"kotlin-android\"\napply plugin: \"de.undercouch.download\"\n\n\nandroid {\n    namespace 'org.tensorflow.lite.examples.classification.playservices'\n    compileSdkVersion 31\n\n    defaultConfig {\n        applicationId 'org.tensorflow.lite.examples.classification.playservices'\n        // ImageClassificationHelper class uses android.util.Size(), which is\n        // available from SDK 21.\n        minSdkVersion 23\n        targetSdkVersion 36\n        versionCode 1\n        versionName \"0.0.1\"\n\n        testApplicationId 'org.tensorflow.lite.examples.classification.playservices.test'\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        all {\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n    compileOptions {\n        sourceCompatibility rootProject.ext.java_version\n        targetCompatibility rootProject.ext.java_version\n        coreLibraryDesugaringEnabled true\n    }\n\n    kotlinOptions {\n        jvmTarget = rootProject.ext.java_version\n    }\n\n    buildFeatures {\n        viewBinding true\n    }\n\n    flavorDimensions 'language'\n    productFlavors {\n        kotlin {\n            getIsDefault().set(true)\n            dimension 'language'\n            applicationIdSuffix \".kotlin\"\n            versionNameSuffix \"-kotlin\"\n        }\n        java {\n            dimension 'language'\n            applicationIdSuffix \".java\"\n            versionNameSuffix \"-java\"\n        }\n    }\n    sourceSets {\n        main {\n            assets {\n                srcDirs 'src/main/assets'\n            }\n        }\n        androidTest {\n            assets {\n                srcDirs 'src/androidTest/assets'\n            }\n        }\n    }\n}\n\ndependencies {\n    implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'\n\n    // App compat and UI things\n    implementation 'androidx.appcompat:appcompat:1.4.1'\n    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'\n    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'\n    implementation 'androidx.activity:activity-ktx:1.4.0'\n\n    // CameraX\n    def camerax_version = \"1.1.0-alpha12\"\n    implementation \"androidx.camera:camera-core:${camerax_version}\"\n    implementation \"androidx.camera:camera-camera2:${camerax_version}\"\n    implementation \"androidx.camera:camera-lifecycle:${camerax_version}\"\n\n    // CameraX View class\n    implementation \"androidx.camera:camera-view:1.0.0-alpha32\"\n\n    // Tensorflow Lite in Play Services dependencies\n    implementation \"com.google.android.gms:play-services-tflite-gpu:16.4.0\"\n    implementation \"com.google.android.gms:play-services-tflite-java:16.4.0\"\n    implementation \"com.google.android.gms:play-services-tflite-support:16.4.0\"\n\n    // Testing-only dependencies\n    testImplementation 'org.robolectric:robolectric:4.4'\n    testImplementation 'junit:junit:4.13.2'\n    androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.3'\n    androidTestImplementation 'androidx.test:core-ktx:1.4.0'\n    def androidx_version = \"1.4.0\"\n    androidTestImplementation \"androidx.test:runner:${androidx_version}\"\n    androidTestImplementation \"androidx.test:rules:${androidx_version}\"\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n    androidTestImplementation 'com.google.truth:truth:1.1.3'\n    androidTestImplementation 'com.google.android.gms:play-services-tasks:18.0.1'\n\n    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.3'\n\n}\n\n// Download default models; if you wish to use your own models then\n// place them in the \"assets\" directory and comment out this line.\nproject.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'\napply from:'download.gradle'\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/download.gradle",
    "content": "def modelEfficientNetFloatDownloadUrl = \"https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_classification/android_play_services/efficientnet_lite0_fp32_2.tflite\"\ndef modelEfficientNetFloatFile = \"efficientnet-lite0-fp32.tflite\"\n\ntask downloadEfficientNetFloat(type: Download) {\n    src \"${modelEfficientNetFloatDownloadUrl}\"\n    dest project.ext.ASSET_DIR + \"/${modelEfficientNetFloatFile}\"\n    overwrite false\n}\n\npreBuild.dependsOn downloadEfficientNetFloat"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/proguard-rules.pro",
    "content": "# The instrumentation tests use Tasks.await, but the instrumented code doesn't.\n# This proguard config makes sure the method is not pruned at runtime.\n-keepclasseswithmembers class com.google.android.gms.tasks.** { *; }"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/androidTest/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n</manifest>\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/androidTest/kotlin/org/tensorflow/lite/examples/classification/playservices/InstrumentationTest.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.classification.playservices\n\nimport android.content.Context\nimport android.graphics.BitmapFactory\nimport androidx.test.core.app.ApplicationProvider\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.platform.app.InstrumentationRegistry\nimport com.google.android.gms.tasks.Tasks\nimport com.google.android.gms.tflite.java.TfLite\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n@RunWith(AndroidJUnit4::class)\nclass InstrumentationTest {\n\n  private val maxResult = 3\n\n  @Test\n  fun classify_returnKResult() {\n    val targetContext = ApplicationProvider.getApplicationContext() as Context\n    Tasks.await(TfLite.initialize(targetContext))\n    val classifier = createClassifier(targetContext, maxResult)\n\n    val context = InstrumentationRegistry.getInstrumentation().context\n    val inputImage = BitmapFactory.decodeStream(context.assets.open(\"testInput.jpg\"))\n    val result = classifier.classify(inputImage, 0)\n    assertThat(result).hasSize(maxResult)\n  }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/androidTestJava/kotlin/org/tensorflow/lite/examples/classification/playservices/TestUtil.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.classification.playservices\n\nimport android.content.Context\n\n// Create the classifier with Java implementation.\ninternal fun createClassifier(context: Context, maxResult: Int): ImageClassificationHelper {\n  return ImageClassificationHelper.create(context, maxResult, /*useGpu=*/ false)\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/androidTestKotlin/kotlin/org/tensorflow/lite/examples/classification/playservices/TestUtil.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.classification.playservices\n\nimport android.content.Context\n\n// Create the classifier with Kotlin implementation.\ninternal fun createClassifier(context: Context, maxResult: Int): ImageClassificationHelper {\n  return ImageClassificationHelper(context, maxResult, /*useGpu=*/ false)\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/java/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2020 Google LLC\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.tensorflow.lite.examples.classification.playservices\">\n    <!-- ImageClassificationHelper class uses android.util.Size(), which is available after SDK 21.\n    -->\n    <uses-sdk\n        android:minSdkVersion=\"23\"\n        android:targetSdkVersion=\"36\" />\n</manifest>\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/java/java/org/tensorflow/lite/examples/classification/playservices/CameraActivity.java",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.classification.playservices;\n\nimport android.Manifest;\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.pm.PackageManager;\nimport android.graphics.Bitmap;\nimport android.graphics.Matrix;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.View;\nimport androidx.activity.result.ActivityResultLauncher;\nimport androidx.activity.result.contract.ActivityResultContracts.RequestPermission;\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.camera.core.AspectRatio;\nimport androidx.camera.core.CameraSelector;\nimport androidx.camera.core.ImageAnalysis;\nimport androidx.camera.core.ImageAnalysis.Analyzer;\nimport androidx.camera.core.ImageProxy;\nimport androidx.camera.core.Preview;\nimport androidx.camera.lifecycle.ProcessCameraProvider;\nimport androidx.core.content.ContextCompat;\nimport com.google.android.gms.tasks.Task;\nimport com.google.android.gms.tasks.Tasks;\nimport com.google.android.gms.tflite.client.TfLiteInitializationOptions;\nimport com.google.android.gms.tflite.java.TfLite;\nimport com.google.common.util.concurrent.ListenableFuture;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport org.tensorflow.lite.examples.classification.playservices.ImageClassificationHelper\n        .Recognition;\nimport org.tensorflow.lite.examples.classification.playservices.databinding.ActivityCameraBinding;\n\n/** Activity that displays the camera and performs object detection on the incoming frames. */\npublic final class CameraActivity extends AppCompatActivity {\n\n  private static final String TAG = \"CameraActivity\";\n  // Number of recognition results to show in the UI\n  private static final int MAX_REPORT = 3;\n  private static final String PERMISSION = Manifest.permission.CAMERA;\n\n  private final ExecutorService executor = Executors.newSingleThreadExecutor();\n\n  private ActivityResultLauncher<String> requestPermissionLauncher;\n  private ActivityCameraBinding activityCameraBinding;\n  private Bitmap bitmapBuffer;\n  private boolean pauseAnalysis = false;\n  private int imageRotationDegrees = 0;\n  // Initialize TFLite once\n  private Task<Void> initializeTask;\n  // The classifier is create after initialization succeeded\n  private ImageClassificationHelper classifier;\n\n  @Override\n  protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    activityCameraBinding = ActivityCameraBinding.inflate(this.getLayoutInflater());\n    setContentView(activityCameraBinding.getRoot());\n\n    AtomicBoolean isGpuInitialized = new AtomicBoolean(false);\n\n    if (initializeTask == null) {\n          // Initialize TFLite asynchronously\n          initializeTask = TfLite.initialize(\n                          this,\n                          TfLiteInitializationOptions\n                                  .builder()\n                                  .setEnableGpuDelegateSupport(true)\n                                  .build())\n                  .continueWithTask(task -> {\n                      if (task.isSuccessful()) {\n                          isGpuInitialized.set(true);\n                          return Tasks.forResult(null);\n                      } else {\n                          // Fallback to initialize interpreter without GPU\n                          isGpuInitialized.set(false);\n                          return TfLite.initialize(CameraActivity.this);\n                      }\n                  })\n                  .addOnSuccessListener(unused -> {\n                      startInitialization(isGpuInitialized.get());\n                  })\n                  .addOnFailureListener(err -> {\n                      Log.e(TAG, \"Failed to initialize the classifier.\", err);\n                  });\n      }\n\n    // Request for permission\n    requestPermissionLauncher = requestPermission();\n\n    // Set up camera\n    activityCameraBinding.cameraCaptureButton.setOnClickListener(setUpCameraCaptureButton());\n\n  }\n\n  private void startInitialization(boolean isGpuInitialized) {\n    Log.d(TAG, \"TFLite in Play Services initialized successfully.\");\n    // Create ImageClassificationHelper AFTER TfLite.initialize() succeeded. This\n    // guarantees that all the interactions with TFLite happens after initialization.\n    try {\n      classifier = ImageClassificationHelper.create(\n              CameraActivity.this,\n              MAX_REPORT,\n              isGpuInitialized\n      );\n    } catch (Exception e) {\n      Log.d(TAG, \"ImageClassificationHelper initialization error\");\n    }\n  }\n\n\n  @Override\n  protected void onDestroy() {\n    // Terminate all outstanding analyzing jobs (if there is any)\n    executor.shutdown();\n    try {\n      if (!executor.awaitTermination(1000, TimeUnit.MILLISECONDS)) {\n        Log.w(TAG, \"Failed to terminate.\");\n      }\n    } catch (InterruptedException e) {\n      Log.e(TAG, \"Exit was interrupted.\", e);\n    }\n\n    // Release TFLite resources\n    if (classifier != null) {\n      classifier.close();\n    }\n    super.onDestroy();\n  }\n\n  @Override\n  protected void onResume() {\n    super.onResume();\n\n    // Request permissions each time the app resumes, since they can be revoked at any time\n    if (!hasPermission(this)) {\n      requestPermissionLauncher.launch(PERMISSION);\n    } else {\n      bindCameraUseCases();\n    }\n  }\n\n  /** Declare and bind preview and analysis use cases */\n  @SuppressLint(\"UnsafeExperimentalUsageError\")\n  private void bindCameraUseCases() {\n    activityCameraBinding.viewFinder.post(\n            () -> {\n              ListenableFuture<ProcessCameraProvider> cameraProviderFuture =\n                      ProcessCameraProvider.getInstance(this);\n              cameraProviderFuture.addListener(\n                      () -> {\n                        // Camera provider is now guaranteed to be available\n                        ProcessCameraProvider cameraProvider;\n                        try {\n                          cameraProvider = cameraProviderFuture.get();\n                        } catch (ExecutionException | InterruptedException e) {\n                          Log.e(TAG, \"Failed to get Camera.\", e);\n                          return;\n                        }\n\n                        // Set up the view finder use case to display camera preview\n                        Preview preview =\n                                new Preview.Builder()\n                                        .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n                                        .setTargetRotation(\n                                                activityCameraBinding\n                                                        .viewFinder\n                                                        .getDisplay()\n                                                        .getRotation())\n                                        .build();\n\n                        // Set up the image analysis use case which will process frames in real time\n                        ImageAnalysis imageAnalysis =\n                                new ImageAnalysis.Builder()\n                                        .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n                                        .setTargetRotation(\n                                                activityCameraBinding.viewFinder\n                                                        .getDisplay()\n                                                        .getRotation()\n                                        )\n                                        .setBackpressureStrategy(\n                                                ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST\n                                        )\n                                        .setOutputImageFormat(\n                                                ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888\n                                        )\n                                        .build();\n\n                        imageAnalysis.setAnalyzer(executor, new ClassificationAnalyzer());\n\n                        // Apply declared configs to CameraX using the same lifecycle owner\n                        cameraProvider.unbindAll();\n                        cameraProvider.bindToLifecycle(\n                                this,\n                                CameraSelector.DEFAULT_BACK_CAMERA,\n                                preview,\n                                imageAnalysis\n                        );\n\n                        // Use the camera object to link our preview use case with the view\n                        preview.setSurfaceProvider(\n                                activityCameraBinding\n                                        .viewFinder\n                                        .getSurfaceProvider()\n                        );\n                      },\n                      ContextCompat.getMainExecutor(this));\n            });\n  }\n\n  /** Image Analyzer used for classifying image. */\n  private class ClassificationAnalyzer implements Analyzer {\n    private int frameCounter = 0;\n    private long lastFpsTimeMillis = System.currentTimeMillis();\n\n    @Override\n    public void analyze(@NonNull ImageProxy image) {\n      if (bitmapBuffer == null) {\n        // The image rotation and RGB image buffer are initialized only after the analyzer has\n        // started running\n        imageRotationDegrees = image.getImageInfo().getRotationDegrees();\n        bitmapBuffer =\n                Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);\n      }\n\n      // Early exit: image analysis is in paused state, or TFLite initialization has not finished\n      if (pauseAnalysis || classifier == null) {\n        image.close();\n        return;\n      }\n\n      // Copy out RGB bits to our shared buffer\n      bitmapBuffer.copyPixelsFromBuffer(image.getPlanes()[0].getBuffer());\n\n      // Perform the image classification for the current frame\n      List<Recognition> recognitions = classifier.classify(bitmapBuffer, imageRotationDegrees);\n\n      reportRecognition(recognitions);\n\n      // Compute the FPS of the entire pipeline\n      int frameCount = 10;\n      if (++frameCounter % frameCount == 0) {\n        frameCounter = 0;\n        long currentTimeMillis = System.currentTimeMillis();\n        long deltaTimeMillis = currentTimeMillis - lastFpsTimeMillis;\n        float fps = 1000 * (float) frameCount / deltaTimeMillis;\n        Log.d(TAG, \"FPS: \" + fps);\n        lastFpsTimeMillis = currentTimeMillis;\n      }\n      image.close();\n    }\n  }\n\n  /** Displays recognition results on screen. */\n  private void reportRecognition(List<Recognition> recognitions) {\n    activityCameraBinding.viewFinder.post(\n            () -> {\n              // Early exit: if recognition is empty\n              if (recognitions.isEmpty()) {\n                activityCameraBinding.textPrediction.setVisibility(View.GONE);\n                return;\n              }\n\n              // Update the text and UI\n              StringBuilder text = new StringBuilder();\n              for (Recognition recognition : recognitions) {\n                text.append(\n                        String.format(\n                                Locale.getDefault(),\n                                \"%.2f %s\\n\",\n                                recognition.getConfidence(),\n                                recognition.getTitle()));\n              }\n              activityCameraBinding.textPrediction.setText(text);\n\n              // Make sure all UI elements are visible\n              activityCameraBinding.textPrediction.setVisibility(View.VISIBLE);\n            });\n  }\n\n  /** Returns the callback used when clicking the camera capture button. */\n  private View.OnClickListener setUpCameraCaptureButton() {\n    return listener -> {\n      // Disable all camera controls\n      listener.setEnabled(false);\n      if (pauseAnalysis) {\n        // If image analysis is in paused state, resume it\n        pauseAnalysis = false;\n        activityCameraBinding.imagePredicted.setVisibility(View.GONE);\n      } else {\n        // Otherwise, pause image analysis and freeze image\n        pauseAnalysis = true;\n        Matrix matrix = new Matrix();\n        matrix.postRotate(imageRotationDegrees);\n        Bitmap uprightImage =\n                Bitmap.createBitmap(\n                        bitmapBuffer,\n                        0,\n                        0,\n                        bitmapBuffer.getWidth(),\n                        bitmapBuffer.getHeight(),\n                        matrix,\n                        true);\n        activityCameraBinding.imagePredicted.setImageBitmap(uprightImage);\n        activityCameraBinding.imagePredicted.setVisibility(View.VISIBLE);\n      }\n\n      // Re-enable camera controls\n      listener.setEnabled(true);\n    };\n  }\n\n  /** Registers request permission callback. */\n  private ActivityResultLauncher<String> requestPermission() {\n    return registerForActivityResult(\n            new RequestPermission(),\n            isGranted -> {\n              if (isGranted) {\n                bindCameraUseCases();\n              } else {\n                finish(); // If we don't have the required permissions, we can't run\n              }\n            });\n  }\n\n  /** Convenience method used to check if all permissions required by this app are granted. */\n  private boolean hasPermission(Context context) {\n    return ContextCompat.checkSelfPermission(context, PERMISSION)\n            == PackageManager.PERMISSION_GRANTED;\n  }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/java/java/org/tensorflow/lite/examples/classification/playservices/ImageClassificationHelper.java",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.classification.playservices;\n\nimport static java.lang.Math.min;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.util.Log;\nimport android.util.Size;\nimport java.io.Closeable;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.PriorityQueue;\nimport org.tensorflow.lite.DataType;\nimport org.tensorflow.lite.InterpreterApi;\nimport org.tensorflow.lite.InterpreterApi.Options.TfLiteRuntime;\nimport org.tensorflow.lite.Tensor;\nimport org.tensorflow.lite.gpu.GpuDelegateFactory;\nimport org.tensorflow.lite.support.common.FileUtil;\nimport org.tensorflow.lite.support.common.TensorOperator;\nimport org.tensorflow.lite.support.common.TensorProcessor;\nimport org.tensorflow.lite.support.common.ops.NormalizeOp;\nimport org.tensorflow.lite.support.image.ImageProcessor;\nimport org.tensorflow.lite.support.image.TensorImage;\nimport org.tensorflow.lite.support.image.ops.ResizeOp;\nimport org.tensorflow.lite.support.image.ops.ResizeWithCropOrPadOp;\nimport org.tensorflow.lite.support.image.ops.Rot90Op;\nimport org.tensorflow.lite.support.label.TensorLabel;\nimport org.tensorflow.lite.support.tensorbuffer.TensorBuffer;\n\n/** Helper class used to communicate between our app and the TF image classification model. */\nclass ImageClassificationHelper implements Closeable {\n\n  private static final String TAG = \"ImageClassification\";\n  // ClassifierFloatEfficientNet model\n  private static final String MODEL_PATH = \"efficientnet-lite0-fp32.tflite\";\n  private static final String LABELS_PATH = \"labels_without_background.txt\";\n  // Float model does not need dequantization in the post-processing. Setting mean and std as 0.0f\n  // and 1.0f, respectively, to bypass the normalization\n  private static final float PROBABILITY_MEAN = 0.0f;\n  private static final float PROBABILITY_STD = 1.0f;\n  private static final float IMAGE_MEAN = 127.0f;\n  private static final float IMAGE_STD = 128.0f;\n  private static final TensorOperator PREPROCESS_NORMALIZE_OP =\n          new NormalizeOp(IMAGE_MEAN, IMAGE_STD);\n  private static final TensorOperator POSTPROCESS_NORMALIZE_OP =\n          new NormalizeOp(PROBABILITY_MEAN, PROBABILITY_STD);\n  /** Processor to apply post processing of the output probability. */\n  private static final TensorProcessor PROBABILITY_PROCESSOR =\n          new TensorProcessor.Builder().add(POSTPROCESS_NORMALIZE_OP).build();\n\n  /** Abstraction object that wraps a classification output in an easy to parse way. */\n  public static class Recognition {\n    private final String title;\n    private final Float confidence;\n\n    public Recognition(String title, Float confidence) {\n      this.title = title;\n      this.confidence = confidence;\n    }\n\n    public String getTitle() {\n      return title;\n    }\n\n    public Float getConfidence() {\n      return confidence;\n    }\n  }\n\n  /**\n   * Factory method to create an instance of {@code ImageClassificationHelper}.\n   *\n   * @param maxResults the number of {@link Recognition} that will be returned when classifying\n   */\n  public static ImageClassificationHelper create(\n          Context context,\n          int maxResults,\n          boolean isGpuInitialized\n  )\n          throws IOException {\n    // Use TFLite in Play Services runtime by setting the option to FROM_SYSTEM_ONLY\n    InterpreterApi.Options options = new InterpreterApi\n            .Options()\n            .setRuntime(TfLiteRuntime.FROM_SYSTEM_ONLY);\n\n    if (isGpuInitialized) {\n      options.addDelegateFactory(new GpuDelegateFactory());\n    }\n\n    InterpreterApi interpreter =\n            InterpreterApi.create(FileUtil.loadMappedFile(context, MODEL_PATH), options);\n\n    int[] inputShape = interpreter.getInputTensor(/* inputIndex */ 0).shape();\n    Size tfInputSize =\n            new Size(inputShape[2], inputShape[1]); // Order of axis is: {1, height, width, 3}\n\n    Tensor outputTensor = interpreter.getOutputTensor(/* probabilityTensorIndex */ 0);\n    TensorBuffer outputProbabilityBuffer =\n            TensorBuffer.createFixedSize(outputTensor.shape(), outputTensor.dataType());\n\n    // Read labels from file\n    List<String> labels = FileUtil.loadLabels(context, LABELS_PATH);\n\n    return new ImageClassificationHelper(\n            maxResults, labels, interpreter, tfInputSize, outputProbabilityBuffer);\n  }\n\n  // Return the top maxResults classification result\n  private final int maxResults;\n  private final List<String> labels;\n  // Only use interpreter after initialization finished in CameraActivity\n  private final InterpreterApi interpreter;\n  private final Size tfInputSize;\n  private final TensorBuffer outputProbabilityBuffer;\n  private ImageProcessor tfImageProcessor;\n  private TensorImage tfInputBuffer = new TensorImage(DataType.UINT8);\n\n  private ImageClassificationHelper(\n          int maxResults,\n          List<String> labels,\n          InterpreterApi interpreter,\n          Size tfInputSize,\n          TensorBuffer outputProbabilityBuffer) {\n    this.maxResults = maxResults;\n    this.labels = labels;\n    this.interpreter = interpreter;\n    this.tfInputSize = tfInputSize;\n    this.outputProbabilityBuffer = outputProbabilityBuffer;\n  }\n\n  /** Returns the top {@link #maxResults} recognition result in the input {@code bitmapBuffer}. */\n  public List<Recognition> classify(final Bitmap bitmapBuffer, int imageRotationDegrees) {\n    // Loads the input bitmapBuffer\n    tfInputBuffer = loadImage(bitmapBuffer, imageRotationDegrees);\n    Log.d(TAG, \"tensorSize: \" + tfInputBuffer.getWidth() + \" x \" + tfInputBuffer.getHeight());\n\n    // Runs the inference call\n    interpreter.run(tfInputBuffer.getBuffer(), outputProbabilityBuffer.getBuffer().rewind());\n\n    // Gets the map of label and probability\n    Map<String, Float> labeledProbability =\n            new TensorLabel(labels, PROBABILITY_PROCESSOR.process(outputProbabilityBuffer))\n                    .getMapWithFloatValue();\n\n    return getTopKProbability(labeledProbability, maxResults);\n  }\n\n  /** Releases TFLite resources. */\n  @Override\n  public void close() {\n    interpreter.close();\n  }\n\n  /** Loads input image, and applies preprocessing. */\n  private TensorImage loadImage(final Bitmap bitmapBuffer, int imageRotationDegrees) {\n    // Initializes preprocessor if null\n    if (tfImageProcessor == null) {\n      int cropSize = min(bitmapBuffer.getWidth(), bitmapBuffer.getHeight());\n      tfImageProcessor =\n              new ImageProcessor.Builder()\n                      .add(new ResizeWithCropOrPadOp(cropSize, cropSize))\n                      .add(\n                              new ResizeOp(\n                                      tfInputSize.getHeight(),\n                                      tfInputSize.getWidth(),\n                                      ResizeOp.ResizeMethod.NEAREST_NEIGHBOR))\n                      .add(new Rot90Op(-imageRotationDegrees / 90))\n                      .add(PREPROCESS_NORMALIZE_OP)\n                      .build();\n      Log.d(TAG, \"tfImageProcessor initialized successfully. imageSize: \" + cropSize);\n    }\n    tfInputBuffer.load(bitmapBuffer);\n    return tfImageProcessor.process(tfInputBuffer);\n  }\n\n  /** Gets the top {@code maxResults} results. */\n  private static List<Recognition> getTopKProbability(\n          Map<String, Float> labelProb, int maxResults) {\n    // Sorts the recognition by confidence from HIGH to LOW\n    PriorityQueue<Recognition> priorityQueue =\n            new PriorityQueue<>(\n                    maxResults,\n                    (Recognition a, Recognition b) -> Float\n                            .compare(b.getConfidence(), a.getConfidence())\n            );\n\n    for (Entry<String, Float> entry : labelProb.entrySet()) {\n      priorityQueue.add(new Recognition(entry.getKey(), entry.getValue()));\n    }\n    List<Recognition> recognitions = new ArrayList<>();\n    int recognitionsSize = min(priorityQueue.size(), maxResults);\n    for (int i = 0; i < recognitionsSize; ++i) {\n      recognitions.add(priorityQueue.poll());\n    }\n    return recognitions;\n  }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/kotlin/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2020 Google LLC\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.tensorflow.lite.examples.classification.playservices\">\n    <!-- ImageClassificationHelper class uses android.util.Size(), which is available after SDK 21.\n    -->\n    <uses-sdk\n        android:minSdkVersion=\"23\"\n        android:targetSdkVersion=\"36\" />\n</manifest>\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/kotlin/kotlin/org/tensorflow/lite/examples/classification/playservices/CameraActivity.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.classification.playservices\n\nimport android.Manifest\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.content.pm.PackageManager\nimport android.graphics.Bitmap\nimport android.graphics.Matrix\nimport android.os.Bundle\nimport android.util.Log\nimport android.view.View\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.camera.core.AspectRatio\nimport androidx.camera.core.CameraSelector\nimport androidx.camera.core.ImageAnalysis\nimport androidx.camera.core.Preview\nimport androidx.camera.lifecycle.ProcessCameraProvider\nimport androidx.core.app.ActivityCompat\nimport androidx.core.content.ContextCompat\nimport androidx.lifecycle.LifecycleOwner\nimport com.google.android.gms.tasks.Task\nimport com.google.android.gms.tasks.Tasks\nimport com.google.android.gms.tflite.client.TfLiteInitializationOptions\nimport com.google.android.gms.tflite.java.TfLite\nimport java.util.concurrent.Executors\nimport java.util.concurrent.TimeUnit\nimport kotlin.random.Random\nimport org.tensorflow.lite.examples.classification.playservices.databinding.ActivityCameraBinding\n\n/** Activity that displays the camera and performs object detection on the incoming frames */\nclass CameraActivity : AppCompatActivity() {\n\n  private lateinit var activityCameraBinding: ActivityCameraBinding\n\n  private lateinit var bitmapBuffer: Bitmap\n\n  private val executor = Executors.newSingleThreadExecutor()\n  private val permissions = listOf(Manifest.permission.CAMERA)\n  private val permissionsRequestCode = Random.nextInt(0, 10000)\n\n  private var lensFacing: Int = CameraSelector.LENS_FACING_BACK\n  private val isFrontFacing\n    get() = lensFacing == CameraSelector.LENS_FACING_FRONT\n\n  private var pauseAnalysis = false\n  private var imageRotationDegrees: Int = 0\n  private var useGpu = false;\n\n  // Initialize TFLite once. Must be called before creating the classifier\n  private val initializeTask: Task<Void> by lazy {\n    TfLite.initialize(\n      this,\n      TfLiteInitializationOptions.builder()\n        .setEnableGpuDelegateSupport(true)\n        .build()\n    ).continueWithTask { task ->\n        if (task.isSuccessful) {\n          useGpu = true;\n          return@continueWithTask Tasks.forResult(null)\n        } else {\n          // Fallback to initialize interpreter without GPU\n          return@continueWithTask TfLite.initialize(this)\n        }\n      }\n      .addOnFailureListener {\n        Log.e(TAG, \"TFLite in Play Services failed to initialize.\", it)\n      }\n  }\n  private var classifier: ImageClassificationHelper? = null\n\n  override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n    activityCameraBinding = ActivityCameraBinding.inflate(layoutInflater)\n    setContentView(activityCameraBinding.root)\n\n    // Initialize TFLite asynchronously\n    initializeTask\n      .addOnSuccessListener {\n        Log.d(TAG, \"TFLite in Play Services initialized successfully.\")\n        classifier = ImageClassificationHelper(this, MAX_REPORT, useGpu)\n      }\n\n    activityCameraBinding.cameraCaptureButton.setOnClickListener {\n      // Disable all camera controls\n      it.isEnabled = false\n      if (pauseAnalysis) {\n        // If image analysis is in paused state, resume it\n        pauseAnalysis = false\n        activityCameraBinding.imagePredicted.visibility = View.GONE\n      } else {\n        // Otherwise, pause image analysis and freeze image\n        pauseAnalysis = true\n        val matrix =\n          Matrix().apply {\n            postRotate(imageRotationDegrees.toFloat())\n            if (isFrontFacing) postScale(-1f, 1f)\n          }\n        val uprightImage =\n          Bitmap.createBitmap(\n            bitmapBuffer,\n            0,\n            0,\n            bitmapBuffer.width,\n            bitmapBuffer.height,\n            matrix,\n            true\n          )\n        activityCameraBinding.imagePredicted.setImageBitmap(uprightImage)\n        activityCameraBinding.imagePredicted.visibility = View.VISIBLE\n      }\n\n      // Re-enable camera controls\n      it.isEnabled = true\n    }\n  }\n\n  override fun onDestroy() {\n    // Terminate all outstanding analyzing jobs (if there is any).\n    executor.apply {\n      shutdown()\n      awaitTermination(1000, TimeUnit.MILLISECONDS)\n    }\n    // Release TFLite resources\n    classifier?.close()\n    super.onDestroy()\n  }\n\n  /** Declare and bind preview and analysis use cases */\n  @SuppressLint(\"UnsafeExperimentalUsageError\")\n  private fun bindCameraUseCases() =\n    activityCameraBinding.viewFinder.post {\n      val cameraProviderFuture = ProcessCameraProvider.getInstance(this)\n      cameraProviderFuture.addListener(\n        {\n          // Camera provider is now guaranteed to be available\n          val cameraProvider = cameraProviderFuture.get()\n\n          // Set up the view finder use case to display camera preview\n          val preview =\n            Preview.Builder()\n              .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n              .setTargetRotation(activityCameraBinding.viewFinder.display.rotation)\n              .build()\n\n          // Set up the image analysis use case which will process frames in real time\n          val imageAnalysis =\n            ImageAnalysis.Builder()\n              .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n              .setTargetRotation(activityCameraBinding.viewFinder.display.rotation)\n              .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)\n              .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)\n              .build()\n\n          var frameCounter = 0\n          var lastFpsTimestamp = System.currentTimeMillis()\n\n          imageAnalysis.setAnalyzer(\n            executor,\n            ImageAnalysis.Analyzer { image ->\n              if (!::bitmapBuffer.isInitialized) {\n                // The image rotation and RGB image buffer are initialized only once\n                // the analyzer has started running\n                imageRotationDegrees = image.imageInfo.rotationDegrees\n                bitmapBuffer =\n                  Bitmap.createBitmap(image.width, image.height, Bitmap.Config.ARGB_8888)\n              }\n\n              // Early exit: image analysis is in paused state, or TFLite is not initialized\n              if (pauseAnalysis || classifier == null) {\n                image.close()\n                return@Analyzer\n              }\n\n              // Copy out RGB bits to our shared buffer\n              image.use { bitmapBuffer.copyPixelsFromBuffer(image.planes[0].buffer) }\n\n              // Perform the image classification for the current frame\n              val recognitions = classifier?.classify(bitmapBuffer, imageRotationDegrees)\n\n              reportRecognition(recognitions)\n\n              // Compute the FPS of the entire pipeline\n              val frameCount = 10\n              if (++frameCounter % frameCount == 0) {\n                frameCounter = 0\n                val now = System.currentTimeMillis()\n                val delta = now - lastFpsTimestamp\n                val fps = 1000 * frameCount.toFloat() / delta\n                Log.d(TAG, \"FPS: ${\"%.02f\".format(fps)}\")\n                lastFpsTimestamp = now\n              }\n            }\n          )\n\n          // Create a new camera selector each time, enforcing lens facing\n          val cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()\n\n          // Apply declared configs to CameraX using the same lifecycle owner\n          cameraProvider.unbindAll()\n          cameraProvider.bindToLifecycle(\n            this as LifecycleOwner,\n            cameraSelector,\n            preview,\n            imageAnalysis\n          )\n\n          // Use the camera object to link our preview use case with the view\n          preview.setSurfaceProvider(activityCameraBinding.viewFinder.surfaceProvider)\n        },\n        ContextCompat.getMainExecutor(this)\n      )\n    }\n\n  /** Displays recognition results on screen. */\n  private fun reportRecognition(\n    recognitions: List<ImageClassificationHelper.Recognition>?,\n  ) =\n    activityCameraBinding.viewFinder.post {\n\n      // Early exit: if recognition is null, or there are not enough recognition results.\n      if (recognitions == null || recognitions.size < MAX_REPORT) {\n        activityCameraBinding.textPrediction.visibility = View.GONE\n        return@post\n      }\n\n      // Update the text and UI\n      activityCameraBinding.textPrediction.text =\n        recognitions.subList(0, MAX_REPORT).joinToString(separator = \"\\n\") {\n          \"${\"%.2f\".format(it.confidence)} ${it.title}\"\n        }\n\n      // Make sure all UI elements are visible\n      activityCameraBinding.textPrediction.visibility = View.VISIBLE\n    }\n\n  override fun onResume() {\n    super.onResume()\n\n    // Request permissions each time the app resumes, since they can be revoked at any time\n    if (!hasPermissions(this)) {\n      ActivityCompat.requestPermissions(this, permissions.toTypedArray(), permissionsRequestCode)\n    } else {\n      bindCameraUseCases()\n    }\n  }\n\n  override fun onRequestPermissionsResult(\n    requestCode: Int,\n    permissions: Array<out String>,\n    grantResults: IntArray,\n  ) {\n    super.onRequestPermissionsResult(requestCode, permissions, grantResults)\n    if (requestCode == permissionsRequestCode && hasPermissions(this)) {\n      bindCameraUseCases()\n    } else {\n      finish() // If we don't have the required permissions, we can't run\n    }\n  }\n\n  /** Convenience method used to check if all permissions required by this app are granted */\n  private fun hasPermissions(context: Context) =\n    permissions.all {\n      ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED\n    }\n\n  companion object {\n    private val TAG = CameraActivity::class.java.simpleName\n    private const val MAX_REPORT = 3\n  }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/kotlin/kotlin/org/tensorflow/lite/examples/classification/playservices/ImageClassificationHelper.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.classification.playservices\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.util.Log\nimport android.util.Size\nimport java.io.Closeable\nimport java.util.PriorityQueue\nimport kotlin.math.min\nimport org.tensorflow.lite.DataType\nimport org.tensorflow.lite.InterpreterApi\nimport org.tensorflow.lite.InterpreterApi.Options.TfLiteRuntime\nimport org.tensorflow.lite.gpu.GpuDelegateFactory\nimport org.tensorflow.lite.support.common.FileUtil\nimport org.tensorflow.lite.support.common.TensorProcessor\nimport org.tensorflow.lite.support.common.ops.NormalizeOp\nimport org.tensorflow.lite.support.image.ImageProcessor\nimport org.tensorflow.lite.support.image.TensorImage\nimport org.tensorflow.lite.support.image.ops.ResizeOp\nimport org.tensorflow.lite.support.image.ops.ResizeWithCropOrPadOp\nimport org.tensorflow.lite.support.image.ops.Rot90Op\nimport org.tensorflow.lite.support.label.TensorLabel\nimport org.tensorflow.lite.support.tensorbuffer.TensorBuffer\n\n/** Helper class used to communicate between our app and the TF image classification model */\nclass ImageClassificationHelper(context: Context, private val maxResult: Int, private val useGpu: Boolean) : Closeable {\n\n  /** Abstraction object that wraps a classification output in an easy to parse way */\n  data class Recognition(val id: String, val title: String, val confidence: Float)\n\n  private val preprocessNormalizeOp = NormalizeOp(IMAGE_MEAN, IMAGE_STD)\n  private val postprocessNormalizeOp = NormalizeOp(PROBABILITY_MEAN, PROBABILITY_STD)\n  private val labels by lazy { FileUtil.loadLabels(context, LABELS_PATH) }\n  private var tfInputBuffer = TensorImage(DataType.UINT8)\n  private var tfImageProcessor: ImageProcessor? = null\n\n  // Processor to apply post processing of the output probability\n  private val probabilityProcessor = TensorProcessor.Builder().add(postprocessNormalizeOp).build()\n\n  // Use TFLite in Play Services runtime by setting the option to FROM_SYSTEM_ONLY\n  private val interpreterInitializer = lazy {\n    val interpreterOption = InterpreterApi.Options()\n      .setRuntime(TfLiteRuntime.FROM_SYSTEM_ONLY)\n\n    if (useGpu) {\n      interpreterOption.addDelegateFactory(GpuDelegateFactory())\n    }\n\n    InterpreterApi.create(FileUtil.loadMappedFile(context, MODEL_PATH), interpreterOption)\n\n  }\n  // Only use interpreter after initialization finished in CameraActivity\n  private val interpreter: InterpreterApi by interpreterInitializer\n  private val tfInputSize by lazy {\n    val inputIndex = 0\n    val inputShape = interpreter.getInputTensor(inputIndex).shape()\n    Size(inputShape[2], inputShape[1]) // Order of axis is: {1, height, width, 3}\n  }\n\n  // Output probability TensorBuffer\n  private val outputProbabilityBuffer: TensorBuffer by lazy {\n    val probabilityTensorIndex = 0\n    val probabilityShape =\n      interpreter.getOutputTensor(probabilityTensorIndex).shape() // {1, NUM_CLASSES}\n    val probabilityDataType = interpreter.getOutputTensor(probabilityTensorIndex).dataType()\n    TensorBuffer.createFixedSize(probabilityShape, probabilityDataType)\n  }\n\n  /** Classifies the input bitmapBuffer. */\n  fun classify(bitmapBuffer: Bitmap, imageRotationDegrees: Int): List<Recognition> {\n    // Loads the input bitmapBuffer\n    tfInputBuffer = loadImage(bitmapBuffer, imageRotationDegrees)\n    Log.d(TAG, \"tensorSize: ${tfInputBuffer.width} x ${tfInputBuffer.height}\")\n\n    // Runs the inference call\n    interpreter.run(tfInputBuffer.buffer, outputProbabilityBuffer.buffer.rewind())\n\n    // Gets the map of label and probability\n    val labeledProbability =\n      TensorLabel(labels, probabilityProcessor.process(outputProbabilityBuffer)).mapWithFloatValue\n\n    return getTopKProbability(labeledProbability)\n  }\n\n  /** Releases TFLite resources if initialized. */\n  override fun close() {\n    if (interpreterInitializer.isInitialized()) {\n      interpreter.close()\n    }\n  }\n\n  /** Loads input image, and applies preprocessing. */\n  private fun loadImage(bitmapBuffer: Bitmap, imageRotationDegrees: Int): TensorImage {\n    // Initializes preprocessor if null\n    return (tfImageProcessor\n        ?: run {\n          val cropSize = minOf(bitmapBuffer.width, bitmapBuffer.height)\n          ImageProcessor.Builder()\n            .add(ResizeWithCropOrPadOp(cropSize, cropSize))\n            .add(\n              ResizeOp(\n                tfInputSize.height,\n                tfInputSize.width,\n                ResizeOp.ResizeMethod.NEAREST_NEIGHBOR\n              )\n            )\n            .add(Rot90Op(-imageRotationDegrees / 90))\n            .add(preprocessNormalizeOp)\n            .build()\n            .also {\n              tfImageProcessor = it\n              Log.d(TAG, \"tfImageProcessor initialized successfully. imageSize: $cropSize\")\n            }\n        })\n      .process(tfInputBuffer.apply { load(bitmapBuffer) })\n  }\n\n  /** Gets the top-k results. */\n  private fun getTopKProbability(labelProb: Map<String, Float>): List<Recognition> {\n    // Sort the recognition by confidence from high to low.\n    val pq: PriorityQueue<Recognition> =\n      PriorityQueue(maxResult, compareByDescending<Recognition> { it.confidence })\n    pq += labelProb.map { (label, prob) -> Recognition(label, label, prob) }\n    return List(min(maxResult, pq.size)) { pq.poll()!! }\n  }\n\n  companion object {\n    private val TAG = ImageClassificationHelper::class.java.simpleName\n\n    // ClassifierFloatEfficientNet model\n    private const val MODEL_PATH = \"efficientnet-lite0-fp32.tflite\"\n    private const val LABELS_PATH = \"labels_without_background.txt\"\n    // Float model does not need dequantization in the post-processing. Setting mean and std as\n    // 0.0f and 1.0f, respectively, to bypass the normalization\n    private const val PROBABILITY_MEAN = 0.0f\n    private const val PROBABILITY_STD = 1.0f\n    private const val IMAGE_MEAN = 127.0f\n    private const val IMAGE_STD = 128.0f\n  }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2020 Google LLC\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n    <!-- ImageClassificationHelper class uses android.util.Size(), which is available from SDK 21.\n    -->\n    <uses-sdk\n        android:minSdkVersion=\"23\"\n        android:targetSdkVersion=\"36\" />\n    <!-- Declare features -->\n    <uses-feature android:name=\"android.hardware.camera\" />\n\n    <!-- Declare permissions -->\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:taskAffinity=\"\"\n        android:theme=\"@style/AppTheme\"\n        tools:ignore=\"AllowBackup,GoogleAppIndexingWarning\">\n\n        <activity\n            android:name=\"org.tensorflow.lite.examples.classification.playservices.CameraActivity\"\n            android:rotationAnimation=\"seamless\"\n            tools:targetApi=\"O\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/main/assets/labels_without_background.txt",
    "content": "tench\ngoldfish\ngreat white shark\ntiger shark\nhammerhead\nelectric ray\nstingray\ncock\nhen\nostrich\nbrambling\ngoldfinch\nhouse finch\njunco\nindigo bunting\nrobin\nbulbul\njay\nmagpie\nchickadee\nwater ouzel\nkite\nbald eagle\nvulture\ngreat grey owl\nEuropean fire salamander\ncommon newt\neft\nspotted salamander\naxolotl\nbullfrog\ntree frog\ntailed frog\nloggerhead\nleatherback turtle\nmud turtle\nterrapin\nbox turtle\nbanded gecko\ncommon iguana\nAmerican chameleon\nwhiptail\nagama\nfrilled lizard\nalligator lizard\nGila monster\ngreen lizard\nAfrican chameleon\nKomodo dragon\nAfrican crocodile\nAmerican alligator\ntriceratops\nthunder snake\nringneck snake\nhognose snake\ngreen snake\nking snake\ngarter snake\nwater snake\nvine snake\nnight snake\nboa constrictor\nrock python\nIndian cobra\ngreen mamba\nsea snake\nhorned viper\ndiamondback\nsidewinder\ntrilobite\nharvestman\nscorpion\nblack and gold garden spider\nbarn spider\ngarden spider\nblack widow\ntarantula\nwolf spider\ntick\ncentipede\nblack grouse\nptarmigan\nruffed grouse\nprairie chicken\npeacock\nquail\npartridge\nAfrican grey\nmacaw\nsulphur-crested cockatoo\nlorikeet\ncoucal\nbee eater\nhornbill\nhummingbird\njacamar\ntoucan\ndrake\nred-breasted merganser\ngoose\nblack swan\ntusker\nechidna\nplatypus\nwallaby\nkoala\nwombat\njellyfish\nsea anemone\nbrain coral\nflatworm\nnematode\nconch\nsnail\nslug\nsea slug\nchiton\nchambered nautilus\nDungeness crab\nrock crab\nfiddler crab\nking crab\nAmerican lobster\nspiny lobster\ncrayfish\nhermit crab\nisopod\nwhite stork\nblack stork\nspoonbill\nflamingo\nlittle blue heron\nAmerican egret\nbittern\ncrane\nlimpkin\nEuropean gallinule\nAmerican coot\nbustard\nruddy turnstone\nred-backed sandpiper\nredshank\ndowitcher\noystercatcher\npelican\nking penguin\nalbatross\ngrey whale\nkiller whale\ndugong\nsea lion\nChihuahua\nJapanese spaniel\nMaltese dog\nPekinese\nShih-Tzu\nBlenheim spaniel\npapillon\ntoy terrier\nRhodesian ridgeback\nAfghan hound\nbasset\nbeagle\nbloodhound\nbluetick\nblack-and-tan coonhound\nWalker hound\nEnglish foxhound\nredbone\nborzoi\nIrish wolfhound\nItalian greyhound\nwhippet\nIbizan hound\nNorwegian elkhound\notterhound\nSaluki\nScottish deerhound\nWeimaraner\nStaffordshire bullterrier\nAmerican Staffordshire terrier\nBedlington terrier\nBorder terrier\nKerry blue terrier\nIrish terrier\nNorfolk terrier\nNorwich terrier\nYorkshire terrier\nwire-haired fox terrier\nLakeland terrier\nSealyham terrier\nAiredale\ncairn\nAustralian terrier\nDandie Dinmont\nBoston bull\nminiature schnauzer\ngiant schnauzer\nstandard schnauzer\nScotch terrier\nTibetan terrier\nsilky terrier\nsoft-coated wheaten terrier\nWest Highland white terrier\nLhasa\nflat-coated retriever\ncurly-coated retriever\ngolden retriever\nLabrador retriever\nChesapeake Bay retriever\nGerman short-haired pointer\nvizsla\nEnglish setter\nIrish setter\nGordon setter\nBrittany spaniel\nclumber\nEnglish springer\nWelsh springer spaniel\ncocker spaniel\nSussex spaniel\nIrish water spaniel\nkuvasz\nschipperke\ngroenendael\nmalinois\nbriard\nkelpie\nkomondor\nOld English sheepdog\nShetland sheepdog\ncollie\nBorder collie\nBouvier des Flandres\nRottweiler\nGerman shepherd\nDoberman\nminiature pinscher\nGreater Swiss Mountain dog\nBernese mountain dog\nAppenzeller\nEntleBucher\nboxer\nbull mastiff\nTibetan mastiff\nFrench bulldog\nGreat Dane\nSaint Bernard\nEskimo dog\nmalamute\nSiberian husky\ndalmatian\naffenpinscher\nbasenji\npug\nLeonberg\nNewfoundland\nGreat Pyrenees\nSamoyed\nPomeranian\nchow\nkeeshond\nBrabancon griffon\nPembroke\nCardigan\ntoy poodle\nminiature poodle\nstandard poodle\nMexican hairless\ntimber wolf\nwhite wolf\nred wolf\ncoyote\ndingo\ndhole\nAfrican hunting dog\nhyena\nred fox\nkit fox\nArctic fox\ngrey fox\ntabby\ntiger cat\nPersian cat\nSiamese cat\nEgyptian cat\ncougar\nlynx\nleopard\nsnow leopard\njaguar\nlion\ntiger\ncheetah\nbrown bear\nAmerican black bear\nice bear\nsloth bear\nmongoose\nmeerkat\ntiger beetle\nladybug\nground beetle\nlong-horned beetle\nleaf beetle\ndung beetle\nrhinoceros beetle\nweevil\nfly\nbee\nant\ngrasshopper\ncricket\nwalking stick\ncockroach\nmantis\ncicada\nleafhopper\nlacewing\ndragonfly\ndamselfly\nadmiral\nringlet\nmonarch\ncabbage butterfly\nsulphur butterfly\nlycaenid\nstarfish\nsea urchin\nsea cucumber\nwood rabbit\nhare\nAngora\nhamster\nporcupine\nfox squirrel\nmarmot\nbeaver\nguinea pig\nsorrel\nzebra\nhog\nwild boar\nwarthog\nhippopotamus\nox\nwater buffalo\nbison\nram\nbighorn\nibex\nhartebeest\nimpala\ngazelle\nArabian camel\nllama\nweasel\nmink\npolecat\nblack-footed ferret\notter\nskunk\nbadger\narmadillo\nthree-toed sloth\norangutan\ngorilla\nchimpanzee\ngibbon\nsiamang\nguenon\npatas\nbaboon\nmacaque\nlangur\ncolobus\nproboscis monkey\nmarmoset\ncapuchin\nhowler monkey\ntiti\nspider monkey\nsquirrel monkey\nMadagascar cat\nindri\nIndian elephant\nAfrican elephant\nlesser panda\ngiant panda\nbarracouta\neel\ncoho\nrock beauty\nanemone fish\nsturgeon\ngar\nlionfish\npuffer\nabacus\nabaya\nacademic gown\naccordion\nacoustic guitar\naircraft carrier\nairliner\nairship\naltar\nambulance\namphibian\nanalog clock\napiary\napron\nashcan\nassault rifle\nbackpack\nbakery\nbalance beam\nballoon\nballpoint\nBand Aid\nbanjo\nbannister\nbarbell\nbarber chair\nbarbershop\nbarn\nbarometer\nbarrel\nbarrow\nbaseball\nbasketball\nbassinet\nbassoon\nbathing cap\nbath towel\nbathtub\nbeach wagon\nbeacon\nbeaker\nbearskin\nbeer bottle\nbeer glass\nbell cote\nbib\nbicycle-built-for-two\nbikini\nbinder\nbinoculars\nbirdhouse\nboathouse\nbobsled\nbolo tie\nbonnet\nbookcase\nbookshop\nbottlecap\nbow\nbow tie\nbrass\nbrassiere\nbreakwater\nbreastplate\nbroom\nbucket\nbuckle\nbulletproof vest\nbullet train\nbutcher shop\ncab\ncaldron\ncandle\ncannon\ncanoe\ncan opener\ncardigan\ncar mirror\ncarousel\ncarpenter's kit\ncarton\ncar wheel\ncash machine\ncassette\ncassette player\ncastle\ncatamaran\nCD player\ncello\ncellular telephone\nchain\nchainlink fence\nchain mail\nchain saw\nchest\nchiffonier\nchime\nchina cabinet\nChristmas stocking\nchurch\ncinema\ncleaver\ncliff dwelling\ncloak\nclog\ncocktail shaker\ncoffee mug\ncoffeepot\ncoil\ncombination lock\ncomputer keyboard\nconfectionery\ncontainer ship\nconvertible\ncorkscrew\ncornet\ncowboy boot\ncowboy hat\ncradle\ncrane\ncrash helmet\ncrate\ncrib\nCrock Pot\ncroquet ball\ncrutch\ncuirass\ndam\ndesk\ndesktop computer\ndial telephone\ndiaper\ndigital clock\ndigital watch\ndining table\ndishrag\ndishwasher\ndisk brake\ndock\ndogsled\ndome\ndoormat\ndrilling platform\ndrum\ndrumstick\ndumbbell\nDutch oven\nelectric fan\nelectric guitar\nelectric locomotive\nentertainment center\nenvelope\nespresso maker\nface powder\nfeather boa\nfile\nfireboat\nfire engine\nfire screen\nflagpole\nflute\nfolding chair\nfootball helmet\nforklift\nfountain\nfountain pen\nfour-poster\nfreight car\nFrench horn\nfrying pan\nfur coat\ngarbage truck\ngasmask\ngas pump\ngoblet\ngo-kart\ngolf ball\ngolfcart\ngondola\ngong\ngown\ngrand piano\ngreenhouse\ngrille\ngrocery store\nguillotine\nhair slide\nhair spray\nhalf track\nhammer\nhamper\nhand blower\nhand-held computer\nhandkerchief\nhard disc\nharmonica\nharp\nharvester\nhatchet\nholster\nhome theater\nhoneycomb\nhook\nhoopskirt\nhorizontal bar\nhorse cart\nhourglass\niPod\niron\njack-o'-lantern\njean\njeep\njersey\njigsaw puzzle\njinrikisha\njoystick\nkimono\nknee pad\nknot\nlab coat\nladle\nlampshade\nlaptop\nlawn mower\nlens cap\nletter opener\nlibrary\nlifeboat\nlighter\nlimousine\nliner\nlipstick\nLoafer\nlotion\nloudspeaker\nloupe\nlumbermill\nmagnetic compass\nmailbag\nmailbox\nmaillot\nmaillot\nmanhole cover\nmaraca\nmarimba\nmask\nmatchstick\nmaypole\nmaze\nmeasuring cup\nmedicine chest\nmegalith\nmicrophone\nmicrowave\nmilitary uniform\nmilk can\nminibus\nminiskirt\nminivan\nmissile\nmitten\nmixing bowl\nmobile home\nModel T\nmodem\nmonastery\nmonitor\nmoped\nmortar\nmortarboard\nmosque\nmosquito net\nmotor scooter\nmountain bike\nmountain tent\nmouse\nmousetrap\nmoving van\nmuzzle\nnail\nneck brace\nnecklace\nnipple\nnotebook\nobelisk\noboe\nocarina\nodometer\noil filter\norgan\noscilloscope\noverskirt\noxcart\noxygen mask\npacket\npaddle\npaddlewheel\npadlock\npaintbrush\npajama\npalace\npanpipe\npaper towel\nparachute\nparallel bars\npark bench\nparking meter\npassenger car\npatio\npay-phone\npedestal\npencil box\npencil sharpener\nperfume\nPetri dish\nphotocopier\npick\npickelhaube\npicket fence\npickup\npier\npiggy bank\npill bottle\npillow\nping-pong ball\npinwheel\npirate\npitcher\nplane\nplanetarium\nplastic bag\nplate rack\nplow\nplunger\nPolaroid camera\npole\npolice van\nponcho\npool table\npop bottle\npot\npotter's wheel\npower drill\nprayer rug\nprinter\nprison\nprojectile\nprojector\npuck\npunching bag\npurse\nquill\nquilt\nracer\nracket\nradiator\nradio\nradio telescope\nrain barrel\nrecreational vehicle\nreel\nreflex camera\nrefrigerator\nremote control\nrestaurant\nrevolver\nrifle\nrocking chair\nrotisserie\nrubber eraser\nrugby ball\nrule\nrunning shoe\nsafe\nsafety pin\nsaltshaker\nsandal\nsarong\nsax\nscabbard\nscale\nschool bus\nschooner\nscoreboard\nscreen\nscrew\nscrewdriver\nseat belt\nsewing machine\nshield\nshoe shop\nshoji\nshopping basket\nshopping cart\nshovel\nshower cap\nshower curtain\nski\nski mask\nsleeping bag\nslide rule\nsliding door\nslot\nsnorkel\nsnowmobile\nsnowplow\nsoap dispenser\nsoccer ball\nsock\nsolar dish\nsombrero\nsoup bowl\nspace bar\nspace heater\nspace shuttle\nspatula\nspeedboat\nspider web\nspindle\nsports car\nspotlight\nstage\nsteam locomotive\nsteel arch bridge\nsteel drum\nstethoscope\nstole\nstone wall\nstopwatch\nstove\nstrainer\nstreetcar\nstretcher\nstudio couch\nstupa\nsubmarine\nsuit\nsundial\nsunglass\nsunglasses\nsunscreen\nsuspension bridge\nswab\nsweatshirt\nswimming trunks\nswing\nswitch\nsyringe\ntable lamp\ntank\ntape player\nteapot\nteddy\ntelevision\ntennis ball\nthatch\ntheater curtain\nthimble\nthresher\nthrone\ntile roof\ntoaster\ntobacco shop\ntoilet seat\ntorch\ntotem pole\ntow truck\ntoyshop\ntractor\ntrailer truck\ntray\ntrench coat\ntricycle\ntrimaran\ntripod\ntriumphal arch\ntrolleybus\ntrombone\ntub\nturnstile\ntypewriter keyboard\numbrella\nunicycle\nupright\nvacuum\nvase\nvault\nvelvet\nvending machine\nvestment\nviaduct\nviolin\nvolleyball\nwaffle iron\nwall clock\nwallet\nwardrobe\nwarplane\nwashbasin\nwasher\nwater bottle\nwater jug\nwater tower\nwhiskey jug\nwhistle\nwig\nwindow screen\nwindow shade\nWindsor tie\nwine bottle\nwing\nwok\nwooden spoon\nwool\nworm fence\nwreck\nyawl\nyurt\nweb site\ncomic book\ncrossword puzzle\nstreet sign\ntraffic light\nbook jacket\nmenu\nplate\nguacamole\nconsomme\nhot pot\ntrifle\nice cream\nice lolly\nFrench loaf\nbagel\npretzel\ncheeseburger\nhotdog\nmashed potato\nhead cabbage\nbroccoli\ncauliflower\nzucchini\nspaghetti squash\nacorn squash\nbutternut squash\ncucumber\nartichoke\nbell pepper\ncardoon\nmushroom\nGranny Smith\nstrawberry\norange\nlemon\nfig\npineapple\nbanana\njackfruit\ncustard apple\npomegranate\nhay\ncarbonara\nchocolate sauce\ndough\nmeat loaf\npizza\npotpie\nburrito\nred wine\nespresso\ncup\neggnog\nalp\nbubble\ncliff\ncoral reef\ngeyser\nlakeside\npromontory\nsandbar\nseashore\nvalley\nvolcano\nballplayer\ngroom\nscuba diver\nrapeseed\ndaisy\nyellow lady's slipper\ncorn\nacorn\nhip\nbuckeye\ncoral fungus\nagaric\ngyromitra\nstinkhorn\nearthstar\nhen-of-the-woods\nbolete\near\ntoilet tissue\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2020 Google LLC\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<vector\n        xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:height=\"108dp\"\n        android:width=\"108dp\"\n        android:viewportHeight=\"108\"\n        android:viewportWidth=\"108\">\n    <path android:fillColor=\"#008577\"\n          android:pathData=\"M0,0h108v108h-108z\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M9,0L9,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,0L19,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M29,0L29,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M39,0L39,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M49,0L49,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M59,0L59,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M69,0L69,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M79,0L79,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M89,0L89,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M99,0L99,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,9L108,9\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,19L108,19\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,29L108,29\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,39L108,39\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,49L108,49\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,59L108,59\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,69L108,69\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,79L108,79\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,89L108,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,99L108,99\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,29L89,29\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,39L89,39\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,49L89,49\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,59L89,59\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,69L89,69\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,79L89,79\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M29,19L29,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M39,19L39,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M49,19L49,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M59,19L59,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M69,19L69,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M79,19L79,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/main/res/drawable/ic_shutter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2019 Google LLC\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/ic_shutter_pressed\" />\n    <item android:state_focused=\"true\" android:drawable=\"@drawable/ic_shutter_focused\" />\n    <item android:drawable=\"@drawable/ic_shutter_normal\" />\n</selector>"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/main/res/drawable/ic_shutter_focused.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2019 Google LLC\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"74\"\n    android:viewportHeight=\"74\">\n    <path android:fillColor=\"#FFFFFF\" android:fillType=\"evenOdd\"\n        android:pathData=\"M73.1,37C73.1,17.0637 56.9373,0.9 37,0.9C17.0627,0.9 0.9,17.0637 0.9,37C0.9,56.9373 17.0627,73.1 37,73.1C56.9373,73.1 73.1,56.9373 73.1,37\"\n        android:strokeColor=\"#00000000\" android:strokeWidth=\"1\"/>\n    <path android:fillColor=\"#58A0C4\" android:fillType=\"evenOdd\"\n        android:pathData=\"M67.4,37C67.4,53.7895 53.7895,67.4 37,67.4C20.2105,67.4 6.6,53.7895 6.6,37C6.6,20.2105 20.2105,6.6 37,6.6C53.7895,6.6 67.4,20.2105 67.4,37\"\n        android:strokeColor=\"#00000000\" android:strokeWidth=\"1\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/main/res/drawable/ic_shutter_normal.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2019 Google LLC\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"74\"\n    android:viewportHeight=\"74\">\n    <path android:fillColor=\"#FFFFFF\" android:fillType=\"evenOdd\"\n        android:pathData=\"M73.1,37C73.1,17.0637 56.9373,0.9 37,0.9C17.0627,0.9 0.9,17.0637 0.9,37C0.9,56.9373 17.0627,73.1 37,73.1C56.9373,73.1 73.1,56.9373 73.1,37\"\n        android:strokeColor=\"#00000000\" android:strokeWidth=\"1\"/>\n    <path android:fillColor=\"#CFD7DB\" android:fillType=\"evenOdd\"\n        android:pathData=\"M67.4,37C67.4,53.7895 53.7895,67.4 37,67.4C20.2105,67.4 6.6,53.7895 6.6,37C6.6,20.2105 20.2105,6.6 37,6.6C53.7895,6.6 67.4,20.2105 67.4,37\"\n        android:strokeColor=\"#00000000\" android:strokeWidth=\"1\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/main/res/drawable/ic_shutter_pressed.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2019 Google LLC\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"74\"\n    android:viewportHeight=\"74\">\n    <path android:fillColor=\"#FFFFFF\" android:fillType=\"evenOdd\"\n        android:pathData=\"M73.1,37C73.1,17.0637 56.9373,0.9 37,0.9C17.0627,0.9 0.9,17.0637 0.9,37C0.9,56.9373 17.0627,73.1 37,73.1C56.9373,73.1 73.1,56.9373 73.1,37\"\n        android:strokeColor=\"#00000000\" android:strokeWidth=\"1\"/>\n    <path android:fillColor=\"#58A0C4\" android:fillType=\"evenOdd\"\n        android:pathData=\"M67.4,37C67.4,53.7895 53.7895,67.4 37,67.4C20.2105,67.4 6.6,53.7895 6.6,37C6.6,20.2105 20.2105,6.6 37,6.6C53.7895,6.6 67.4,20.2105 67.4,37\"\n        android:strokeColor=\"#00000000\" android:strokeWidth=\"1\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/main/res/drawable/shape_rectangle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2020 Google LLC\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <corners\n        android:radius=\"4dp\"\n        android:topRightRadius=\"0dp\"\n        android:bottomRightRadius=\"0dp\"\n        android:bottomLeftRadius=\"0dp\" />\n    <stroke\n        android:width=\"4dp\"\n        android:color=\"@android:color/white\" />\n    <solid android:color=\"@android:color/transparent\"/>\n</shape>"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/main/res/layout/activity_camera.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2020 Google LLC\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<androidx.constraintlayout.widget.ConstraintLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/camera_container\"\n    android:background=\"@android:color/black\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.camera.view.PreviewView\n        android:id=\"@+id/view_finder\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"/>\n\n    <ImageView\n        android:id=\"@+id/image_predicted\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:scaleType=\"centerCrop\"\n        android:visibility=\"gone\" />\n\n    <TextView\n        android:id=\"@+id/text_prediction\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"@dimen/margin_xsmall\"\n        android:text=\"@string/unknown\"\n        android:textAllCaps=\"true\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Large\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <View\n        android:id=\"@+id/box_prediction\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:background=\"@drawable/shape_rectangle\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\" />\n\n    <!-- Camera control buttons -->\n\n    <ImageButton\n        android:id=\"@+id/camera_capture_button\"\n        android:layout_width=\"@dimen/round_button_large\"\n        android:layout_height=\"@dimen/round_button_large\"\n        android:layout_marginBottom=\"@dimen/shutter_button_margin\"\n        android:scaleType=\"fitCenter\"\n        android:background=\"@drawable/ic_shutter\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        android:contentDescription=\"@string/capture_button_alt\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/main/res/layout-land/activity_camera.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2020 Google LLC\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<androidx.constraintlayout.widget.ConstraintLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/camera_container\"\n    android:background=\"@android:color/black\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.camera.view.PreviewView\n        android:id=\"@+id/view_finder\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"/>\n\n    <ImageView\n        android:id=\"@+id/image_predicted\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:scaleType=\"centerCrop\"\n        android:visibility=\"gone\" />\n\n    <TextView\n        android:id=\"@+id/text_prediction\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"@dimen/margin_xsmall\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        android:textAllCaps=\"true\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Display1\"\n        android:text=\"@string/unknown\" />\n\n    <View\n        android:id=\"@+id/box_prediction\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:background=\"@drawable/shape_rectangle\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\" />\n\n    <!-- Camera control buttons -->\n\n    <ImageButton\n        android:id=\"@+id/camera_capture_button\"\n        android:layout_width=\"@dimen/round_button_large\"\n        android:layout_height=\"@dimen/round_button_large\"\n        android:layout_marginEnd=\"@dimen/shutter_button_margin\"\n        android:scaleType=\"fitCenter\"\n        android:background=\"@drawable/ic_shutter\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        android:contentDescription=\"@string/capture_button_alt\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2019 Google LLC\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2019 Google LLC\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2020 Google LLC\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<resources>\n    <dimen name=\"margin_xsmall\">16dp</dimen>\n    <dimen name=\"margin_small\">32dp</dimen>\n    <dimen name=\"margin_medium\">48dp</dimen>\n    <dimen name=\"margin_large\">64dp</dimen>\n    <dimen name=\"margin_xlarge\">92dp</dimen>\n\n    <dimen name=\"spacing_small\">4dp</dimen>\n    <dimen name=\"spacing_medium\">8dp</dimen>\n    <dimen name=\"spacing_large\">16dp</dimen>\n\n    <dimen name=\"round_button_small\">32dp</dimen>\n    <dimen name=\"round_button_medium\">64dp</dimen>\n    <dimen name=\"round_button_large\">92dp</dimen>\n\n    <dimen name=\"shutter_button_margin\">80dp</dimen>\n</resources>"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2020 Google LLC\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<resources>\n    <string name=\"app_name\">Image Classification</string>\n    <string name=\"capture_button_alt\">Capture</string>\n    <string name=\"unknown\">UNKNOWN</string>\n</resources>\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2020 Google LLC\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.DayNight.NoActionBar\">\n        <item name=\"android:immersive\">true</item>\n        <item name=\"android:windowFullscreen\">true</item>\n        <item name=\"android:windowTranslucentStatus\">true</item>\n        <item name=\"android:windowTranslucentNavigation\">true</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/build.gradle",
    "content": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nbuildscript {\n\n    ext {\n        // Top-level variables used for versioning\n        ext.kotlin_version = '2.1.0'\n        ext.java_version = JavaVersion.VERSION_1_8\n    }\n\n    repositories {\n        google()\n        mavenCentral()\n    }\n\n    dependencies {\n        classpath 'com.android.tools.build:gradle:8.2.0'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n        classpath 'com.diffplug.spotless:spotless-plugin-gradle:5.11.1'\n        classpath 'de.undercouch:gradle-download-task:5.5.0'\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n        maven { // repo for TFLite snapshot\n            name 'ossrh-snapshot'\n            url 'https://oss.sonatype.org/content/repositories/snapshots'\n        }\n    }\n}\n\nsubprojects {\n    apply plugin: 'com.diffplug.spotless'\n    spotless {\n        java {\n            target \"**/*.java\"\n            trimTrailingWhitespace()\n            removeUnusedImports()\n            googleJavaFormat()\n            endWithNewline()\n        }\n        kotlin {\n            target \"**/*.kt\"\n            trimTrailingWhitespace()\n            endWithNewline()\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app's APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n\n# Automatically convert third-party libraries to use AndroidX\nandroid.enableJetifier=true\n\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/image_classification/android_play_services/settings.gradle",
    "content": "include 'app'\n"
  },
  {
    "path": "lite/examples/image_classification/ios/ImageClassification/AppDelegate.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n  var window: UIWindow?\n\n  func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil\n  ) -> Bool {\n    return true\n  }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/ios/ImageClassification/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\"images\":[{\"size\":\"60x60\",\"expected-size\":\"180\",\"filename\":\"180.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"40x40\",\"expected-size\":\"80\",\"filename\":\"80.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"40x40\",\"expected-size\":\"120\",\"filename\":\"120.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"60x60\",\"expected-size\":\"120\",\"filename\":\"120.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"57x57\",\"expected-size\":\"57\",\"filename\":\"57.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"58\",\"filename\":\"58.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"29x29\",\"expected-size\":\"29\",\"filename\":\"29.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"87\",\"filename\":\"87.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"57x57\",\"expected-size\":\"114\",\"filename\":\"114.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"60\",\"filename\":\"60.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"1024x1024\",\"filename\":\"1024.png\",\"expected-size\":\"1024\",\"idiom\":\"ios-marketing\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"scale\":\"1x\"},{\"size\":\"40x40\",\"expected-size\":\"80\",\"filename\":\"80.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"72x72\",\"expected-size\":\"72\",\"filename\":\"72.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"76x76\",\"expected-size\":\"152\",\"filename\":\"152.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"50x50\",\"expected-size\":\"100\",\"filename\":\"100.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"29x29\",\"expected-size\":\"58\",\"filename\":\"58.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"76x76\",\"expected-size\":\"76\",\"filename\":\"76.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"29\",\"filename\":\"29.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"50x50\",\"expected-size\":\"50\",\"filename\":\"50.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"72x72\",\"expected-size\":\"144\",\"filename\":\"144.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"40x40\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"83.5x83.5\",\"expected-size\":\"167\",\"filename\":\"167.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"20\",\"filename\":\"20.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"20x20\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"}]}"
  },
  {
    "path": "lite/examples/image_classification/ios/ImageClassification/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/image_classification/ios/ImageClassification/Assets.xcassets/darkOrLight.colorset/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  },\n  \"colors\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"color\" : {\n        \"color-space\" : \"srgb\",\n        \"components\" : {\n          \"red\" : \"0.000\",\n          \"alpha\" : \"1.000\",\n          \"blue\" : \"0.000\",\n          \"green\" : \"0.000\"\n        }\n      }\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"appearances\" : [\n        {\n          \"appearance\" : \"luminosity\",\n          \"value\" : \"light\"\n        }\n      ],\n      \"color\" : {\n        \"color-space\" : \"srgb\",\n        \"components\" : {\n          \"red\" : \"0.000\",\n          \"alpha\" : \"1.000\",\n          \"blue\" : \"0.000\",\n          \"green\" : \"0.000\"\n        }\n      }\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"appearances\" : [\n        {\n          \"appearance\" : \"luminosity\",\n          \"value\" : \"dark\"\n        }\n      ],\n      \"color\" : {\n        \"color-space\" : \"srgb\",\n        \"components\" : {\n          \"red\" : \"1.000\",\n          \"alpha\" : \"1.000\",\n          \"blue\" : \"1.000\",\n          \"green\" : \"1.000\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "lite/examples/image_classification/ios/ImageClassification/Assets.xcassets/down_icon.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icnChevronDown.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icnChevronDown@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icnChevronDown@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/image_classification/ios/ImageClassification/Assets.xcassets/tfl_logo.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"tfl_logo.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"tfl_logo@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"tfl_logo@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/image_classification/ios/ImageClassification/Assets.xcassets/up_icon.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icnChevronUp.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icnChevronUp@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icnChevronUp@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/image_classification/ios/ImageClassification/Camera Feed/CameraFeedManager.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\nimport AVFoundation\n\n// MARK: CameraFeedManagerDelegate Declaration\nprotocol CameraFeedManagerDelegate: AnyObject {\n\n  /**\n  This method delivers the pixel buffer of the current frame seen by the device's camera.\n */\n  func didOutput(pixelBuffer: CVPixelBuffer)\n\n  /**\n   This method initimates that the camera permissions have been denied.\n   */\n  func presentCameraPermissionsDeniedAlert()\n\n  /**\n   This method initimates that there was an error in video configurtion.\n   */\n  func presentVideoConfigurationErrorAlert()\n\n  /**\n   This method initimates that a session runtime error occured.\n   */\n  func sessionRunTimeErrorOccured()\n\n  /**\n   This method initimates that the session was interrupted.\n   */\n  func sessionWasInterrupted(canResumeManually resumeManually: Bool)\n\n  /**\n   This method initimates that the session interruption has ended.\n   */\n  func sessionInterruptionEnded()\n\n}\n\n/**\n This enum holds the state of the camera initialization.\n */\nenum CameraConfiguration {\n\n  case success\n  case failed\n  case permissionDenied\n}\n\n/**\n This class manages all camera related functionality\n */\nclass CameraFeedManager: NSObject {\n\n  // MARK: Camera Related Instance Variables\n  private let session: AVCaptureSession = AVCaptureSession()\n  private let previewView: PreviewView\n  private let sessionQueue = DispatchQueue(label: \"sessionQueue\")\n  private var cameraConfiguration: CameraConfiguration = .failed\n  private lazy var videoDataOutput = AVCaptureVideoDataOutput()\n  private var isSessionRunning = false\n\n  // MARK: CameraFeedManagerDelegate\n  weak var delegate: CameraFeedManagerDelegate?\n\n  // MARK: Initializer\n  init(previewView: PreviewView) {\n    self.previewView = previewView\n    super.init()\n\n    // Initializes the session\n    session.sessionPreset = .high\n    self.previewView.session = session\n    self.previewView.previewLayer.connection?.videoOrientation = .portrait\n    self.previewView.previewLayer.videoGravity = .resizeAspectFill\n    self.attemptToConfigureSession()\n  }\n\n  // MARK: Session Start and End methods\n\n  /**\n This method starts an AVCaptureSession based on whether the camera configuration was successful.\n */\n  func checkCameraConfigurationAndStartSession() {\n    sessionQueue.async {\n      switch self.cameraConfiguration {\n      case .success:\n        self.addObservers()\n        self.startSession()\n      case .failed:\n        DispatchQueue.main.async {\n          self.delegate?.presentVideoConfigurationErrorAlert()\n        }\n      case .permissionDenied:\n        DispatchQueue.main.async {\n          self.delegate?.presentCameraPermissionsDeniedAlert()\n        }\n      }\n    }\n  }\n\n  /**\n   This method stops a running an AVCaptureSession.\n   */\n  func stopSession() {\n    self.removeObservers()\n    sessionQueue.async {\n      if self.session.isRunning {\n        self.session.stopRunning()\n        self.isSessionRunning = self.session.isRunning\n      }\n    }\n\n  }\n\n  /**\n   This method resumes an interrupted AVCaptureSession.\n   */\n  func resumeInterruptedSession(withCompletion completion: @escaping (Bool) -> ()) {\n\n    sessionQueue.async {\n      self.startSession()\n\n      DispatchQueue.main.async {\n        completion(self.isSessionRunning)\n      }\n    }\n  }\n\n  /**\n This method starts the AVCaptureSession\n **/\n  private func startSession() {\n    self.session.startRunning()\n    self.isSessionRunning = self.session.isRunning\n  }\n\n  // MARK: Session Configuration Methods.\n  /**\n This method requests for camera permissions and handles the configuration of the session and stores the result of configuration.\n */\n  private func attemptToConfigureSession() {\n    switch AVCaptureDevice.authorizationStatus(for: .video) {\n    case .authorized:\n      self.cameraConfiguration = .success\n    case .notDetermined:\n      self.sessionQueue.suspend()\n      self.requestCameraAccess(completion: { (granted) in\n        self.sessionQueue.resume()\n      })\n    case .denied:\n      self.cameraConfiguration = .permissionDenied\n    default:\n      break\n    }\n\n    self.sessionQueue.async {\n      self.configureSession()\n    }\n  }\n\n  /**\n   This method requests for camera permissions.\n   */\n  private func requestCameraAccess(completion: @escaping (Bool) -> ()) {\n    AVCaptureDevice.requestAccess(for: .video) { (granted) in\n      if !granted {\n        self.cameraConfiguration = .permissionDenied\n      }\n      else {\n        self.cameraConfiguration = .success\n      }\n      completion(granted)\n    }\n  }\n\n\n  /**\n   This method handles all the steps to configure an AVCaptureSession.\n   */\n  private func configureSession() {\n\n    guard cameraConfiguration == .success else {\n      return\n    }\n    session.beginConfiguration()\n\n    // Tries to add an AVCaptureDeviceInput.\n    guard addVideoDeviceInput() == true else {\n      self.session.commitConfiguration()\n      self.cameraConfiguration = .failed\n      return\n    }\n\n    // Tries to add an AVCaptureVideoDataOutput.\n    guard addVideoDataOutput() else {\n      self.session.commitConfiguration()\n      self.cameraConfiguration = .failed\n      return\n    }\n\n    session.commitConfiguration()\n    self.cameraConfiguration = .success\n  }\n\n  /**\n This method tries to an AVCaptureDeviceInput to the current AVCaptureSession.\n */\n  private func addVideoDeviceInput() -> Bool {\n\n    /**Tries to get the default back camera.\n     */\n    guard let camera  = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else {\n      return false\n    }\n\n    do {\n      let videoDeviceInput = try AVCaptureDeviceInput(device: camera)\n      if session.canAddInput(videoDeviceInput) {\n        session.addInput(videoDeviceInput)\n        return true\n      }\n      else {\n        return false\n      }\n    }\n    catch {\n      fatalError(\"Cannot create video device input\")\n    }\n  }\n\n  /**\n   This method tries to an AVCaptureVideoDataOutput to the current AVCaptureSession.\n   */\n  private func addVideoDataOutput() -> Bool {\n\n    let sampleBufferQueue = DispatchQueue(label: \"sampleBufferQueue\")\n    videoDataOutput.setSampleBufferDelegate(self, queue: sampleBufferQueue)\n    videoDataOutput.alwaysDiscardsLateVideoFrames = true\n    videoDataOutput.videoSettings = [ String(kCVPixelBufferPixelFormatTypeKey) : kCMPixelFormat_32BGRA]\n\n    if session.canAddOutput(videoDataOutput) {\n      session.addOutput(videoDataOutput)\n      videoDataOutput.connection(with: .video)?.videoOrientation = .portrait\n      return true\n    }\n    return false\n  }\n\n  // MARK: Notification Observer Handling\n  private func addObservers() {\n    NotificationCenter.default.addObserver(self, selector: #selector(CameraFeedManager.sessionRuntimeErrorOccured(notification:)), name: NSNotification.Name.AVCaptureSessionRuntimeError, object: session)\n    NotificationCenter.default.addObserver(self, selector: #selector(CameraFeedManager.sessionWasInterrupted(notification:)), name: NSNotification.Name.AVCaptureSessionWasInterrupted, object: session)\n    NotificationCenter.default.addObserver(self, selector: #selector(CameraFeedManager.sessionInterruptionEnded), name: NSNotification.Name.AVCaptureSessionInterruptionEnded, object: session)\n  }\n\n  private func removeObservers() {\n    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVCaptureSessionRuntimeError, object: session)\n    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVCaptureSessionWasInterrupted, object: session)\n    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVCaptureSessionInterruptionEnded, object: session)\n  }\n\n  // MARK: Notification Observers\n  @objc func sessionWasInterrupted(notification: Notification) {\n\n    if let userInfoValue = notification.userInfo?[AVCaptureSessionInterruptionReasonKey] as AnyObject?,\n      let reasonIntegerValue = userInfoValue.integerValue,\n      let reason = AVCaptureSession.InterruptionReason(rawValue: reasonIntegerValue) {\n      print(\"Capture session was interrupted with reason \\(reason)\")\n\n      var canResumeManually = false\n      if reason == .videoDeviceInUseByAnotherClient {\n        canResumeManually = true\n      } else if reason == .videoDeviceNotAvailableWithMultipleForegroundApps {\n        canResumeManually = false\n      }\n\n      self.delegate?.sessionWasInterrupted(canResumeManually: canResumeManually)\n\n    }\n  }\n\n  @objc func sessionInterruptionEnded(notification: Notification) {\n\n    self.delegate?.sessionInterruptionEnded()\n  }\n\n  @objc func sessionRuntimeErrorOccured(notification: Notification) {\n    guard let error = notification.userInfo?[AVCaptureSessionErrorKey] as? AVError else {\n      return\n    }\n\n    print(\"Capture session runtime error: \\(error)\")\n\n    if error.code == .mediaServicesWereReset {\n      sessionQueue.async {\n        if self.isSessionRunning {\n          self.startSession()\n        } else {\n          DispatchQueue.main.async {\n            self.delegate?.sessionRunTimeErrorOccured()\n          }\n        }\n      }\n    } else {\n      self.delegate?.sessionRunTimeErrorOccured()\n\n    }\n  }\n}\n\n\n/**\n AVCaptureVideoDataOutputSampleBufferDelegate\n */\nextension CameraFeedManager: AVCaptureVideoDataOutputSampleBufferDelegate {\n\n  /** This method delegates the CVPixelBuffer of the frame seen by the camera currently.\n */\n  func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {\n\n    // Converts the CMSampleBuffer to a CVPixelBuffer.\n    let pixelBuffer: CVPixelBuffer? = CMSampleBufferGetImageBuffer(sampleBuffer)\n\n    guard let imagePixelBuffer = pixelBuffer else {\n      return\n    }\n\n    // Delegates the pixel buffer to the ViewController.\n    delegate?.didOutput(pixelBuffer: imagePixelBuffer)\n  }\n\n}\n"
  },
  {
    "path": "lite/examples/image_classification/ios/ImageClassification/Camera Feed/PreviewView.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\nimport AVFoundation\n\n/**\n Displays a preview of the image being processed. By default, this uses the device's camera frame,\n but will use a still image copied from clipboard if `shouldUseClipboardImage` is set to true.\n */\nclass PreviewView: UIView {\n\n  var shouldUseClipboardImage: Bool = false {\n    didSet {\n      if shouldUseClipboardImage {\n        if imageView.superview == nil {\n          addSubview(imageView)\n          let constraints = [\n            NSLayoutConstraint(item: imageView, attribute: .top,\n                               relatedBy: .equal,\n                               toItem: self, attribute: .top,\n                               multiplier: 1, constant: 0),\n            NSLayoutConstraint(item: imageView, attribute: .leading,\n                               relatedBy: .equal,\n                               toItem: self, attribute: .leading,\n                               multiplier: 1, constant: 0),\n            NSLayoutConstraint(item: imageView, attribute: .trailing,\n                               relatedBy: .equal,\n                               toItem: self, attribute: .trailing,\n                               multiplier: 1, constant: 0),\n            NSLayoutConstraint(item: imageView, attribute: .bottom,\n                               relatedBy: .equal,\n                               toItem: self, attribute: .bottom,\n                               multiplier: 1, constant: 0),\n          ]\n          addConstraints(constraints)\n        }\n\n      } else {\n        imageView.removeFromSuperview()\n      }\n    }\n  }\n\n  lazy private var imageView: UIImageView = {\n    let imageView = UIImageView()\n    imageView.contentMode = .scaleAspectFill\n    imageView.translatesAutoresizingMaskIntoConstraints = false\n    return imageView\n  }()\n\n  var image: UIImage? {\n    get {\n      return imageView.image\n    }\n    set {\n      imageView.image = newValue\n    }\n  }\n\n  var previewLayer: AVCaptureVideoPreviewLayer {\n    guard let layer = layer as? AVCaptureVideoPreviewLayer else {\n      fatalError(\"Layer expected is of type VideoPreviewLayer\")\n    }\n    return layer\n  }\n\n  var session: AVCaptureSession? {\n    get {\n      return previewLayer.session\n    }\n    set {\n      previewLayer.session = newValue\n    }\n  }\n\n  override class var layerClass: AnyClass {\n    return AVCaptureVideoPreviewLayer.self\n  }\n\n}\n"
  },
  {
    "path": "lite/examples/image_classification/ios/ImageClassification/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>TFL Classify</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>NSCameraUsageDescription</key>\n\t<string>This app uses camera to classify the objects that appear in the camera feed.</string>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UIRequiresFullScreen</key>\n\t<true/>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UIUserInterfaceStyle</key>\n\t<string>Light</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "lite/examples/image_classification/ios/ImageClassification/Storyboards/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"13122.16\" systemVersion=\"17A277\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"13104.12\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "lite/examples/image_classification/ios/ImageClassification/Storyboards/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"20037\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"7aX-iP-Lxp\">\n    <device id=\"retina6_7\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"20020\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"yBP-eL-iHP\">\n            <objects>\n                <viewController id=\"7aX-iP-Lxp\" customClass=\"ViewController\" customModule=\"ImageClassification\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"pbw-Hb-35N\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"428\" height=\"926\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"lZs-Kh-vPx\" customClass=\"PreviewView\" customModule=\"ImageClassification\" customModuleProvider=\"target\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"24\" width=\"428\" height=\"868\"/>\n                                <color key=\"backgroundColor\" white=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                            </view>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ddd-K8-X1g\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"392\" width=\"428\" height=\"500\"/>\n                                <subviews>\n                                    <containerView opaque=\"NO\" clipsSubviews=\"YES\" contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"RaE-UX-KCt\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"44\" width=\"428\" height=\"456\"/>\n                                        <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                        <connections>\n                                            <segue destination=\"5Mj-3V-GSf\" kind=\"embed\" identifier=\"EMBED\" id=\"lEP-sh-HOn\"/>\n                                        </connections>\n                                    </containerView>\n                                    <imageView userInteractionEnabled=\"NO\" contentMode=\"center\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" image=\"down_icon\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"mga-a8-Bfe\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"428\" height=\"44\"/>\n                                        <constraints>\n                                            <constraint firstAttribute=\"height\" constant=\"44\" id=\"Xue-Nw-sWi\"/>\n                                        </constraints>\n                                    </imageView>\n                                </subviews>\n                                <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"RaE-UX-KCt\" secondAttribute=\"trailing\" id=\"6VW-wL-32Y\"/>\n                                    <constraint firstAttribute=\"height\" constant=\"500\" id=\"ADz-jv-vdU\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"mga-a8-Bfe\" secondAttribute=\"trailing\" id=\"KYy-5A-Ppg\"/>\n                                    <constraint firstItem=\"RaE-UX-KCt\" firstAttribute=\"leading\" secondItem=\"ddd-K8-X1g\" secondAttribute=\"leading\" id=\"bep-9h-hfD\"/>\n                                    <constraint firstItem=\"RaE-UX-KCt\" firstAttribute=\"top\" secondItem=\"ddd-K8-X1g\" secondAttribute=\"top\" constant=\"44\" id=\"bzl-to-SOj\"/>\n                                    <constraint firstItem=\"mga-a8-Bfe\" firstAttribute=\"leading\" secondItem=\"ddd-K8-X1g\" secondAttribute=\"leading\" id=\"dcW-2n-mPE\"/>\n                                    <constraint firstAttribute=\"bottom\" secondItem=\"RaE-UX-KCt\" secondAttribute=\"bottom\" id=\"fN4-kS-BCh\"/>\n                                    <constraint firstItem=\"mga-a8-Bfe\" firstAttribute=\"top\" secondItem=\"ddd-K8-X1g\" secondAttribute=\"top\" id=\"q1w-Ft-eGg\"/>\n                                </constraints>\n                            </view>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Duv-Jf-94E\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"892\" width=\"428\" height=\"34\"/>\n                                <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                            </view>\n                            <label hidden=\"YES\" opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Camera Unavailable\" textAlignment=\"center\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"uPJ-nc-1Tt\">\n                                <rect key=\"frame\" x=\"119\" y=\"415.66666666666669\" width=\"190\" height=\"25\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"height\" constant=\"25\" id=\"1ZG-uB-8vo\"/>\n                                </constraints>\n                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"22\"/>\n                                <nil key=\"textColor\"/>\n                                <nil key=\"highlightedColor\"/>\n                            </label>\n                            <button hidden=\"YES\" opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" buttonType=\"system\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"j6w-zW-1a4\">\n                                <rect key=\"frame\" x=\"157.66666666666666\" y=\"448.66666666666669\" width=\"112.99999999999997\" height=\"30\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"height\" constant=\"30\" id=\"nfj-XO-QE3\"/>\n                                </constraints>\n                                <state key=\"normal\" title=\"Resume Session\"/>\n                                <connections>\n                                    <action selector=\"onClickResumeButton:\" destination=\"7aX-iP-Lxp\" eventType=\"touchUpInside\" id=\"Tqc-dh-pm3\"/>\n                                </connections>\n                            </button>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"XDd-uV-ojM\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"-1\" width=\"428\" height=\"100\"/>\n                                <subviews>\n                                    <imageView userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" image=\"tfl_logo\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"G8c-DH-tst\">\n                                        <rect key=\"frame\" x=\"16\" y=\"56\" width=\"160\" height=\"24\"/>\n                                        <constraints>\n                                            <constraint firstAttribute=\"height\" constant=\"24\" id=\"5IJ-w5-3I7\"/>\n                                            <constraint firstAttribute=\"width\" constant=\"160\" id=\"PgE-Id-weI\"/>\n                                        </constraints>\n                                    </imageView>\n                                </subviews>\n                                <color key=\"backgroundColor\" red=\"0.0\" green=\"0.0\" blue=\"0.0\" alpha=\"0.50341497319999995\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                <constraints>\n                                    <constraint firstItem=\"G8c-DH-tst\" firstAttribute=\"leading\" secondItem=\"XDd-uV-ojM\" secondAttribute=\"leading\" constant=\"16\" id=\"Ytb-p6-o4U\"/>\n                                    <constraint firstAttribute=\"height\" constant=\"100\" id=\"ek6-xh-waT\"/>\n                                    <constraint firstAttribute=\"bottom\" secondItem=\"G8c-DH-tst\" secondAttribute=\"bottom\" constant=\"20\" id=\"zOk-72-jU2\"/>\n                                </constraints>\n                            </view>\n                        </subviews>\n                        <viewLayoutGuide key=\"safeArea\" id=\"Ijl-bc-mDl\"/>\n                        <color key=\"backgroundColor\" white=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                        <constraints>\n                            <constraint firstItem=\"Ijl-bc-mDl\" firstAttribute=\"bottom\" secondItem=\"ddd-K8-X1g\" secondAttribute=\"bottom\" id=\"0yl-Zb-EVZ\"/>\n                            <constraint firstItem=\"j6w-zW-1a4\" firstAttribute=\"top\" secondItem=\"uPJ-nc-1Tt\" secondAttribute=\"bottom\" constant=\"8\" id=\"25h-co-xEa\"/>\n                            <constraint firstItem=\"j6w-zW-1a4\" firstAttribute=\"centerX\" secondItem=\"uPJ-nc-1Tt\" secondAttribute=\"centerX\" id=\"2NJ-ah-RbB\"/>\n                            <constraint firstItem=\"lZs-Kh-vPx\" firstAttribute=\"top\" secondItem=\"Ijl-bc-mDl\" secondAttribute=\"top\" constant=\"-20\" id=\"AXY-MW-RIS\"/>\n                            <constraint firstItem=\"uPJ-nc-1Tt\" firstAttribute=\"centerY\" secondItem=\"Ijl-bc-mDl\" secondAttribute=\"centerY\" constant=\"-40\" id=\"EbJ-3L-1Xy\"/>\n                            <constraint firstItem=\"Ijl-bc-mDl\" firstAttribute=\"trailing\" secondItem=\"lZs-Kh-vPx\" secondAttribute=\"trailing\" id=\"GuN-L7-8gu\"/>\n                            <constraint firstItem=\"uPJ-nc-1Tt\" firstAttribute=\"centerX\" secondItem=\"Ijl-bc-mDl\" secondAttribute=\"centerX\" id=\"H9F-0R-f67\"/>\n                            <constraint firstItem=\"lZs-Kh-vPx\" firstAttribute=\"leading\" secondItem=\"Ijl-bc-mDl\" secondAttribute=\"leading\" id=\"Igg-Hr-MT8\"/>\n                            <constraint firstItem=\"XDd-uV-ojM\" firstAttribute=\"leading\" secondItem=\"Ijl-bc-mDl\" secondAttribute=\"leading\" id=\"OoA-aE-6PR\"/>\n                            <constraint firstItem=\"Duv-Jf-94E\" firstAttribute=\"leading\" secondItem=\"Ijl-bc-mDl\" secondAttribute=\"leading\" id=\"PCb-JF-Et4\"/>\n                            <constraint firstItem=\"Duv-Jf-94E\" firstAttribute=\"leading\" secondItem=\"Ijl-bc-mDl\" secondAttribute=\"leading\" id=\"WGe-KE-rAX\"/>\n                            <constraint firstItem=\"Ijl-bc-mDl\" firstAttribute=\"bottom\" secondItem=\"lZs-Kh-vPx\" secondAttribute=\"bottom\" id=\"XAo-78-Zow\"/>\n                            <constraint firstItem=\"Ijl-bc-mDl\" firstAttribute=\"trailing\" secondItem=\"Duv-Jf-94E\" secondAttribute=\"trailing\" id=\"YF7-vk-KVf\"/>\n                            <constraint firstAttribute=\"trailing\" secondItem=\"ddd-K8-X1g\" secondAttribute=\"trailing\" id=\"ax4-7h-oga\"/>\n                            <constraint firstAttribute=\"bottom\" secondItem=\"Duv-Jf-94E\" secondAttribute=\"bottom\" id=\"bG8-1y-b0U\"/>\n                            <constraint firstItem=\"Ijl-bc-mDl\" firstAttribute=\"trailing\" secondItem=\"Duv-Jf-94E\" secondAttribute=\"trailing\" id=\"eV9-TY-bHz\"/>\n                            <constraint firstItem=\"Duv-Jf-94E\" firstAttribute=\"top\" secondItem=\"Ijl-bc-mDl\" secondAttribute=\"bottom\" id=\"lh1-8o-rHB\"/>\n                            <constraint firstItem=\"XDd-uV-ojM\" firstAttribute=\"trailing\" secondItem=\"Ijl-bc-mDl\" secondAttribute=\"trailing\" id=\"mos-EZ-RzJ\"/>\n                            <constraint firstItem=\"ddd-K8-X1g\" firstAttribute=\"leading\" secondItem=\"pbw-Hb-35N\" secondAttribute=\"leading\" id=\"rmj-y1-88M\"/>\n                            <constraint firstItem=\"Ijl-bc-mDl\" firstAttribute=\"top\" secondItem=\"XDd-uV-ojM\" secondAttribute=\"top\" constant=\"45\" id=\"yX7-FR-ywb\"/>\n                        </constraints>\n                    </view>\n                    <connections>\n                        <outlet property=\"bottomSheetStateImageView\" destination=\"mga-a8-Bfe\" id=\"W8k-ir-pYB\"/>\n                        <outlet property=\"bottomSheetView\" destination=\"ddd-K8-X1g\" id=\"WY6-8z-eIc\"/>\n                        <outlet property=\"bottomSheetViewBottomSpace\" destination=\"0yl-Zb-EVZ\" id=\"BcA-AX-QG1\"/>\n                        <outlet property=\"bottomViewHeightConstraint\" destination=\"ADz-jv-vdU\" id=\"1B3-Q8-yy0\"/>\n                        <outlet property=\"cameraUnavailableLabel\" destination=\"uPJ-nc-1Tt\" id=\"xlw-29-JPB\"/>\n                        <outlet property=\"previewView\" destination=\"lZs-Kh-vPx\" id=\"4Ms-eq-S68\"/>\n                        <outlet property=\"resumeButton\" destination=\"j6w-zW-1a4\" id=\"LHb-hP-Y6k\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"JUu-fJ-Imh\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"-186.40000000000001\" y=\"623.83808095952031\"/>\n        </scene>\n        <!--Inference View Controller-->\n        <scene sceneID=\"hNi-xI-ucc\">\n            <objects>\n                <viewController id=\"5Mj-3V-GSf\" customClass=\"InferenceViewController\" customModule=\"ImageClassification\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" clipsSubviews=\"YES\" contentMode=\"scaleToFill\" id=\"zPd-NZ-qOm\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"428\" height=\"456\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <tableView clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" alwaysBounceVertical=\"YES\" scrollEnabled=\"NO\" dataMode=\"prototypes\" style=\"plain\" separatorStyle=\"none\" allowsSelection=\"NO\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"28\" sectionFooterHeight=\"28\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Xyb-Ug-x0L\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"43.999999999999986\" width=\"428\" height=\"215.66666666666663\"/>\n                                <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                <prototypes>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" reuseIdentifier=\"INFO_CELL\" rowHeight=\"161\" id=\"093-9d-oIK\" customClass=\"InfoCell\" customModule=\"ImageClassification\" customModuleProvider=\"target\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"44.666666030883789\" width=\"428\" height=\"161\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"093-9d-oIK\" id=\"5sR-dW-Xhb\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"428\" height=\"161\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Label\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" numberOfLines=\"0\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Nm8-LQ-AUy\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"5\" width=\"36\" height=\"17\"/>\n                                                    <constraints>\n                                                        <constraint firstAttribute=\"height\" constant=\"17\" id=\"UMg-1J-r50\"/>\n                                                    </constraints>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" weight=\"medium\" pointSize=\"14\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                                <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Label\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Qpb-Fh-hqp\">\n                                                    <rect key=\"frame\" x=\"376\" y=\"5\" width=\"36\" height=\"17\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" weight=\"medium\" pointSize=\"14\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                                <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"wnY-Vf-09k\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"42\" width=\"396\" height=\"1\"/>\n                                                    <color key=\"backgroundColor\" red=\"0.45882352939999999\" green=\"0.45882352939999999\" blue=\"0.45882352939999999\" alpha=\"0.19554016487730061\" colorSpace=\"custom\" customColorSpace=\"calibratedRGB\"/>\n                                                    <constraints>\n                                                        <constraint firstAttribute=\"height\" constant=\"1\" id=\"qD3-RZ-TCh\"/>\n                                                    </constraints>\n                                                </view>\n                                            </subviews>\n                                            <constraints>\n                                                <constraint firstItem=\"Nm8-LQ-AUy\" firstAttribute=\"top\" secondItem=\"5sR-dW-Xhb\" secondAttribute=\"top\" constant=\"5\" id=\"3xa-IG-B6M\"/>\n                                                <constraint firstAttribute=\"trailing\" secondItem=\"wnY-Vf-09k\" secondAttribute=\"trailing\" constant=\"16\" id=\"CCe-V3-ymS\"/>\n                                                <constraint firstItem=\"wnY-Vf-09k\" firstAttribute=\"leading\" secondItem=\"5sR-dW-Xhb\" secondAttribute=\"leading\" constant=\"16\" id=\"IWZ-fh-Z8E\"/>\n                                                <constraint firstItem=\"wnY-Vf-09k\" firstAttribute=\"top\" secondItem=\"Nm8-LQ-AUy\" secondAttribute=\"bottom\" constant=\"20\" id=\"MC1-8H-M27\"/>\n                                                <constraint firstAttribute=\"trailing\" secondItem=\"Qpb-Fh-hqp\" secondAttribute=\"trailing\" constant=\"16\" id=\"NIC-Yf-8J1\"/>\n                                                <constraint firstItem=\"Nm8-LQ-AUy\" firstAttribute=\"leading\" secondItem=\"5sR-dW-Xhb\" secondAttribute=\"leading\" constant=\"16\" id=\"U1V-ma-Zrd\"/>\n                                                <constraint firstItem=\"Qpb-Fh-hqp\" firstAttribute=\"centerY\" secondItem=\"Nm8-LQ-AUy\" secondAttribute=\"centerY\" id=\"nlg-0n-OvQ\"/>\n                                                <constraint firstItem=\"Qpb-Fh-hqp\" firstAttribute=\"leading\" relation=\"greaterThanOrEqual\" secondItem=\"Nm8-LQ-AUy\" secondAttribute=\"trailing\" id=\"puB-Fa-7tb\"/>\n                                            </constraints>\n                                        </tableViewCellContentView>\n                                        <connections>\n                                            <outlet property=\"fieldNameLabel\" destination=\"Nm8-LQ-AUy\" id=\"kWh-Yg-sk4\"/>\n                                            <outlet property=\"infoLabel\" destination=\"Qpb-Fh-hqp\" id=\"IBK-qO-FoK\"/>\n                                        </connections>\n                                    </tableViewCell>\n                                </prototypes>\n                                <connections>\n                                    <outlet property=\"dataSource\" destination=\"5Mj-3V-GSf\" id=\"3JZ-Qb-ohM\"/>\n                                    <outlet property=\"delegate\" destination=\"5Mj-3V-GSf\" id=\"htz-FK-D9O\"/>\n                                </connections>\n                            </tableView>\n                            <view clipsSubviews=\"YES\" contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Z8v-g6-QwO\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"271.66666666666669\" width=\"428\" height=\"184.33333333333331\"/>\n                                <subviews>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Threads\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"cVP-pT-liH\">\n                                        <rect key=\"frame\" x=\"16\" y=\"10\" width=\"53\" height=\"17\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                        <color key=\"textColor\" systemColor=\"darkTextColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"1\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" numberOfLines=\"0\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"sya-ll-mpl\">\n                                        <rect key=\"frame\" x=\"307.33333333333331\" y=\"10\" width=\"6.6666666666666856\" height=\"17\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                        <color key=\"textColor\" systemColor=\"darkTextColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Threshold\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"4Cg-oC-zPS\">\n                                        <rect key=\"frame\" x=\"16\" y=\"54\" width=\"65\" height=\"17\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Model\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"g5R-th-PXQ\">\n                                        <rect key=\"frame\" x=\"16\" y=\"147\" width=\"40\" height=\"17\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <stepper opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" value=\"0.5\" minimumValue=\"0.10000000000000001\" maximumValue=\"0.90000000000000002\" stepValue=\"0.10000000000000001\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"mzI-dd-bw1\">\n                                        <rect key=\"frame\" x=\"318\" y=\"46.333333333333314\" width=\"94\" height=\"32\"/>\n                                        <connections>\n                                            <action selector=\"thresholdStepperValueChanged:\" destination=\"5Mj-3V-GSf\" eventType=\"valueChanged\" id=\"4yR-eq-elR\"/>\n                                        </connections>\n                                    </stepper>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"0.5\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"KYZ-iq-DM9\">\n                                        <rect key=\"frame\" x=\"293.66666666666669\" y=\"54\" width=\"20.333333333333314\" height=\"17\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <textField opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"left\" contentVerticalAlignment=\"center\" borderStyle=\"roundedRect\" textAlignment=\"natural\" minimumFontSize=\"17\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ytR-dw-IFO\">\n                                        <rect key=\"frame\" x=\"212\" y=\"138.33333333333331\" width=\"200\" height=\"34\"/>\n                                        <constraints>\n                                            <constraint firstAttribute=\"width\" constant=\"200\" id=\"iK7-lV-HLq\"/>\n                                        </constraints>\n                                        <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                        <textInputTraits key=\"textInputTraits\"/>\n                                    </textField>\n                                    <stepper opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" value=\"3\" minimumValue=\"1\" maximumValue=\"10\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"xSy-AE-zLG\">\n                                        <rect key=\"frame\" x=\"318\" y=\"2.3333333333333144\" width=\"94\" height=\"32\"/>\n                                        <color key=\"tintColor\" red=\"0.45882352939999999\" green=\"0.45882352939999999\" blue=\"0.45882352939999999\" alpha=\"1\" colorSpace=\"calibratedRGB\"/>\n                                        <connections>\n                                            <action selector=\"onClickThreadStepper:\" destination=\"5Mj-3V-GSf\" eventType=\"valueChanged\" id=\"KfM-cT-epT\"/>\n                                        </connections>\n                                    </stepper>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Max Results\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Sij-zg-Hgb\">\n                                        <rect key=\"frame\" x=\"16\" y=\"98\" width=\"78\" height=\"17\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <stepper opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" value=\"3\" minimumValue=\"1\" maximumValue=\"10\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"LkQ-2t-2Pi\">\n                                        <rect key=\"frame\" x=\"318\" y=\"90.333333333333314\" width=\"94\" height=\"32\"/>\n                                        <connections>\n                                            <action selector=\"maxResultStepperValueChanged:\" destination=\"5Mj-3V-GSf\" eventType=\"valueChanged\" id=\"7rV-YE-EA5\"/>\n                                        </connections>\n                                    </stepper>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"3\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ows-wH-7yW\">\n                                        <rect key=\"frame\" x=\"305\" y=\"98\" width=\"9\" height=\"17\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                </subviews>\n                                <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                <constraints>\n                                    <constraint firstItem=\"cVP-pT-liH\" firstAttribute=\"top\" secondItem=\"Z8v-g6-QwO\" secondAttribute=\"top\" constant=\"10\" id=\"3VX-Ou-X19\"/>\n                                    <constraint firstItem=\"mzI-dd-bw1\" firstAttribute=\"leading\" secondItem=\"KYZ-iq-DM9\" secondAttribute=\"trailing\" constant=\"4\" id=\"9U7-RH-cva\"/>\n                                    <constraint firstItem=\"4Cg-oC-zPS\" firstAttribute=\"centerY\" secondItem=\"mzI-dd-bw1\" secondAttribute=\"centerY\" id=\"9kA-0W-9dO\"/>\n                                    <constraint firstItem=\"LkQ-2t-2Pi\" firstAttribute=\"leading\" secondItem=\"ows-wH-7yW\" secondAttribute=\"trailing\" constant=\"4\" id=\"BT2-IA-cqg\"/>\n                                    <constraint firstItem=\"KYZ-iq-DM9\" firstAttribute=\"centerY\" secondItem=\"mzI-dd-bw1\" secondAttribute=\"centerY\" id=\"EUE-k3-2sC\"/>\n                                    <constraint firstItem=\"sya-ll-mpl\" firstAttribute=\"leading\" relation=\"greaterThanOrEqual\" secondItem=\"cVP-pT-liH\" secondAttribute=\"trailing\" constant=\"1\" id=\"Hbf-xy-3Lq\"/>\n                                    <constraint firstItem=\"g5R-th-PXQ\" firstAttribute=\"centerY\" secondItem=\"ytR-dw-IFO\" secondAttribute=\"centerY\" id=\"Kv9-ih-RhU\"/>\n                                    <constraint firstItem=\"4Cg-oC-zPS\" firstAttribute=\"leading\" secondItem=\"cVP-pT-liH\" secondAttribute=\"leading\" id=\"Mpg-2C-Cph\"/>\n                                    <constraint firstItem=\"ows-wH-7yW\" firstAttribute=\"centerY\" secondItem=\"LkQ-2t-2Pi\" secondAttribute=\"centerY\" id=\"QaP-qZ-Dc9\"/>\n                                    <constraint firstItem=\"xSy-AE-zLG\" firstAttribute=\"trailing\" secondItem=\"mzI-dd-bw1\" secondAttribute=\"trailing\" id=\"TJ5-Hr-oTG\"/>\n                                    <constraint firstItem=\"mzI-dd-bw1\" firstAttribute=\"top\" secondItem=\"xSy-AE-zLG\" secondAttribute=\"bottom\" constant=\"12\" id=\"TiZ-vM-9tZ\"/>\n                                    <constraint firstItem=\"xSy-AE-zLG\" firstAttribute=\"leading\" secondItem=\"sya-ll-mpl\" secondAttribute=\"trailing\" constant=\"4\" id=\"YNc-Fz-qdX\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"xSy-AE-zLG\" secondAttribute=\"trailing\" constant=\"16\" id=\"YU3-Va-ffa\"/>\n                                    <constraint firstItem=\"LkQ-2t-2Pi\" firstAttribute=\"top\" secondItem=\"mzI-dd-bw1\" secondAttribute=\"bottom\" constant=\"12\" id=\"cHK-yc-wuH\"/>\n                                    <constraint firstItem=\"ytR-dw-IFO\" firstAttribute=\"top\" secondItem=\"mzI-dd-bw1\" secondAttribute=\"bottom\" constant=\"60\" id=\"cec-dk-hLW\"/>\n                                    <constraint firstItem=\"LkQ-2t-2Pi\" firstAttribute=\"centerY\" secondItem=\"Sij-zg-Hgb\" secondAttribute=\"centerY\" id=\"e0W-na-i7Y\"/>\n                                    <constraint firstItem=\"ytR-dw-IFO\" firstAttribute=\"trailing\" secondItem=\"mzI-dd-bw1\" secondAttribute=\"trailing\" id=\"fxj-1Y-MZ8\"/>\n                                    <constraint firstItem=\"cVP-pT-liH\" firstAttribute=\"leading\" secondItem=\"Z8v-g6-QwO\" secondAttribute=\"leading\" constant=\"16\" id=\"jSU-9y-8sx\"/>\n                                    <constraint firstAttribute=\"bottom\" secondItem=\"ytR-dw-IFO\" secondAttribute=\"bottom\" constant=\"12\" id=\"nY7-0M-Q51\"/>\n                                    <constraint firstItem=\"mzI-dd-bw1\" firstAttribute=\"trailing\" secondItem=\"LkQ-2t-2Pi\" secondAttribute=\"trailing\" id=\"oNt-So-n3H\"/>\n                                    <constraint firstItem=\"sya-ll-mpl\" firstAttribute=\"centerY\" secondItem=\"cVP-pT-liH\" secondAttribute=\"centerY\" id=\"oZw-VF-iW6\"/>\n                                    <constraint firstItem=\"xSy-AE-zLG\" firstAttribute=\"centerY\" secondItem=\"cVP-pT-liH\" secondAttribute=\"centerY\" id=\"sNp-Zo-pSD\"/>\n                                    <constraint firstItem=\"g5R-th-PXQ\" firstAttribute=\"leading\" secondItem=\"cVP-pT-liH\" secondAttribute=\"leading\" id=\"tI0-qd-fzR\"/>\n                                    <constraint firstItem=\"Sij-zg-Hgb\" firstAttribute=\"leading\" secondItem=\"4Cg-oC-zPS\" secondAttribute=\"leading\" id=\"upp-4I-RzW\"/>\n                                </constraints>\n                            </view>\n                        </subviews>\n                        <viewLayoutGuide key=\"safeArea\" id=\"4cu-EG-0vu\"/>\n                        <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                        <constraints>\n                            <constraint firstItem=\"Z8v-g6-QwO\" firstAttribute=\"leading\" secondItem=\"4cu-EG-0vu\" secondAttribute=\"leading\" id=\"F6g-74-dPv\"/>\n                            <constraint firstItem=\"Xyb-Ug-x0L\" firstAttribute=\"trailing\" secondItem=\"4cu-EG-0vu\" secondAttribute=\"trailing\" id=\"HPH-AI-7jk\"/>\n                            <constraint firstItem=\"Xyb-Ug-x0L\" firstAttribute=\"leading\" secondItem=\"4cu-EG-0vu\" secondAttribute=\"leading\" id=\"Puh-OI-w4Q\"/>\n                            <constraint firstItem=\"Z8v-g6-QwO\" firstAttribute=\"bottom\" secondItem=\"zPd-NZ-qOm\" secondAttribute=\"bottom\" id=\"bwd-2Q-ih8\"/>\n                            <constraint firstItem=\"Xyb-Ug-x0L\" firstAttribute=\"top\" secondItem=\"4cu-EG-0vu\" secondAttribute=\"top\" id=\"jpa-Sv-Shd\"/>\n                            <constraint firstItem=\"Z8v-g6-QwO\" firstAttribute=\"trailing\" secondItem=\"4cu-EG-0vu\" secondAttribute=\"trailing\" id=\"o4S-dT-Mfa\"/>\n                            <constraint firstItem=\"Z8v-g6-QwO\" firstAttribute=\"top\" secondItem=\"Xyb-Ug-x0L\" secondAttribute=\"bottom\" constant=\"12\" id=\"qnf-Q0-tkr\"/>\n                        </constraints>\n                    </view>\n                    <connections>\n                        <outlet property=\"maxResultLabel\" destination=\"ows-wH-7yW\" id=\"t7W-xb-myA\"/>\n                        <outlet property=\"maxResultStepper\" destination=\"LkQ-2t-2Pi\" id=\"9tj-uJ-uDi\"/>\n                        <outlet property=\"modelTextField\" destination=\"ytR-dw-IFO\" id=\"FWW-57-Wvb\"/>\n                        <outlet property=\"tableView\" destination=\"Xyb-Ug-x0L\" id=\"RyP-IW-3np\"/>\n                        <outlet property=\"threadStepper\" destination=\"xSy-AE-zLG\" id=\"qxy-j8-1J8\"/>\n                        <outlet property=\"threadValueLabel\" destination=\"sya-ll-mpl\" id=\"lRW-Jo-rMQ\"/>\n                        <outlet property=\"thresholdStepper\" destination=\"mzI-dd-bw1\" id=\"oZU-it-EdL\"/>\n                        <outlet property=\"thresholdValueLabel\" destination=\"KYZ-iq-DM9\" id=\"jOf-MD-uRD\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"9Ld-UH-DeT\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"565.60000000000002\" y=\"696.25187406296857\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"down_icon\" width=\"20\" height=\"6\"/>\n        <image name=\"tfl_logo\" width=\"266.66665649414062\" height=\"42.666667938232422\"/>\n        <systemColor name=\"darkTextColor\">\n            <color white=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n        </systemColor>\n    </resources>\n</document>\n"
  },
  {
    "path": "lite/examples/image_classification/ios/ImageClassification/TFLite/ImageClassificationHelper.swift",
    "content": "// Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport TensorFlowLiteTaskVision\nimport UIKit\n\n/// A result from the `Classifications`.\nstruct ImageClassificationResult {\n  let inferenceTime: Double\n  let classifications: Classifications\n}\n\n/// Information about a model file or labels file.\ntypealias FileInfo = (name: String, extension: String)\n\n/// This class handles all data preprocessing and makes calls to run inference on a given frame\n/// by invoking the TFLite `ImageClassifier`. It then returns the top N results for a successful \n/// inference.\nclass ImageClassificationHelper {\n\n  // MARK: - Model Parameters\n\n  /// TensorFlow Lite `Interpreter` object for performing inference on a given model.\n  private var classifier: ImageClassifier\n\n  /// Information about the alpha component in RGBA data.\n  private let alphaComponent = (baseOffset: 4, moduloRemainder: 3)\n\n  // MARK: - Initialization\n\n  /// A failable initializer for `ClassificationHelper`. A new instance is created if the model and\n  /// labels files are successfully loaded from the app's main bundle. Default `threadCount` is 1.\n  init?(modelFileInfo: FileInfo, threadCount: Int, resultCount: Int, scoreThreshold: Float) {\n    let modelFilename = modelFileInfo.name\n    // Construct the path to the model file.\n    guard\n      let modelPath = Bundle.main.path(\n        forResource: modelFilename,\n        ofType: modelFileInfo.extension\n      )\n    else {\n      print(\"Failed to load the model file with name: \\(modelFilename).\")\n      return nil\n    }\n\n    // Configures the initialization options.\n    let options = ImageClassifierOptions(modelPath: modelPath)\n    options.baseOptions.computeSettings.cpuSettings.numThreads = Int32(threadCount)\n    options.classificationOptions.maxResults = resultCount\n    options.classificationOptions.scoreThreshold = scoreThreshold\n\n    do {\n      classifier = try ImageClassifier.classifier(options: options)\n    } catch let error {\n      print(\"Failed to create the interpreter with error: \\(error.localizedDescription)\")\n      return nil\n    }\n  }\n\n  // MARK: - Internal Methods\n\n  /// Performs image preprocessing, invokes the `ImageClassifier`, and processes the inference\n  /// results.\n  func classify(frame pixelBuffer: CVPixelBuffer) -> ImageClassificationResult? {\n    // Convert the `CVPixelBuffer` object to an `MLImage` object.\n    guard let mlImage = MLImage(pixelBuffer: pixelBuffer) else { return nil }\n\n    // Run inference using the `ImageClassifier{ object.\n    do {\n      let startDate = Date()\n      let classificationResults = try classifier.classify(mlImage: mlImage)\n      let inferenceTime = Date().timeIntervalSince(startDate) * 1000\n\n      // As all models used in this sample app are single-head models, gets the classification\n      // result from the first (and only) classification head and return to the view controller to\n      // display.\n      guard let classifications = classificationResults.classifications.first else { return nil }\n      return ImageClassificationResult(\n        inferenceTime: inferenceTime, classifications: classifications)\n    } catch let error {\n      print(\"Failed to invoke the interpreter with error: \\(error.localizedDescription)\")\n      return nil\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/ios/ImageClassification/ViewControllers/InferenceViewController.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n// MARK: InferenceViewControllerDelegate Method Declarations\nprotocol InferenceViewControllerDelegate {\n\n  /**\n   This method is called when the user changes the value to update model used for inference.\n   **/\n  func viewController(\n    _ viewController: InferenceViewController,\n    needPerformActions action: InferenceViewController.Action)\n}\n\nclass InferenceViewController: UIViewController {\n\n  enum Action {\n    case changeThreadCount(Int)\n    case changeScoreThreshold(Float)\n    case changeMaxResults(Int)\n    case changeModel(ModelType)\n  }\n\n  // MARK: Sections and Information to display\n  private enum InferenceSections: Int, CaseIterable {\n    case Results\n    case InferenceInfo\n  }\n\n  private enum InferenceInfo: Int, CaseIterable {\n    case Resolution\n    case InferenceTime\n\n    func displayString() -> String {\n\n      var toReturn = \"\"\n\n      switch self {\n      case .Resolution:\n        toReturn = \"Resolution\"\n      case .InferenceTime:\n        toReturn = \"Inference Time\"\n\n      }\n      return toReturn\n    }\n  }\n\n  // MARK: Storyboard Outlets\n  @IBOutlet weak var tableView: UITableView!\n  @IBOutlet weak var threadStepper: UIStepper!\n  @IBOutlet weak var threadValueLabel: UILabel!\n\n  @IBOutlet weak var thresholdStepper: UIStepper!\n  @IBOutlet weak var thresholdValueLabel: UILabel!\n\n  @IBOutlet weak var maxResultStepper: UIStepper!\n  @IBOutlet weak var maxResultLabel: UILabel!\n\n  @IBOutlet weak var modelTextField: UITextField!\n\n  // MARK: Constants\n  private let normalCellHeight: CGFloat = 27.0\n  private let separatorCellHeight: CGFloat = 42.0\n  private let bottomSheetButtonDisplayHeight: CGFloat = 44.0\n  private let minThreadCount = 1\n  private let lightTextInfoColor = UIColor(\n    displayP3Red: 117.0 / 255.0, green: 117.0 / 255.0, blue: 117.0 / 255.0, alpha: 1.0)\n  private let infoFont = UIFont.systemFont(ofSize: 14.0, weight: .regular)\n  private let highlightedFont = UIFont.systemFont(ofSize: 14.0, weight: .medium)\n\n  // MARK: Instance Variables\n  var inferenceResult: ImageClassificationResult? = nil\n  var wantedInputWidth = 0\n  var wantedInputHeight = 0\n  var resolution = CGSize.zero\n  var maxResults = DefaultConstants.maxResults\n  var currentThreadCount = DefaultConstants.threadCount\n  var scoreThreshold = DefaultConstants.scoreThreshold\n  var modelSelectIndex = 0\n  private var infoTextColor = UIColor.black\n\n  private var modelSelect: ModelType {\n    if modelSelectIndex < ModelType.allCases.count {\n      return ModelType.allCases[modelSelectIndex]\n    } else {\n      return .efficientnetLite0\n    }\n  }\n\n  // MARK: Delegate\n  var delegate: InferenceViewControllerDelegate?\n\n  // MARK: Computed properties\n  var collapsedHeight: CGFloat {\n    return normalCellHeight * CGFloat(maxResults - 1) + bottomSheetButtonDisplayHeight\n  }\n\n  override func viewDidLoad() {\n    super.viewDidLoad()\n\n    // Set up stepper\n    threadStepper.isUserInteractionEnabled = true\n    threadStepper.value = Double(currentThreadCount)\n\n    // Set the info text color on iOS 11 and higher.\n    if #available(iOS 11, *) {\n      infoTextColor = UIColor(named: \"darkOrLight\")!\n    }\n    setupUI()\n  }\n\n  // MARK: private func\n  private func setupUI() {\n    threadStepper.value = Double(currentThreadCount)\n    threadValueLabel.text = \"\\(currentThreadCount)\"\n\n    maxResultStepper.value = Double(maxResults)\n    maxResultLabel.text = \"\\(maxResults)\"\n\n    thresholdStepper.value = Double(scoreThreshold)\n    thresholdValueLabel.text = \"\\(scoreThreshold)\"\n\n    modelTextField.text = modelSelect.title\n\n    let picker = UIPickerView()\n    picker.delegate = self\n    picker.dataSource = self\n    modelTextField.inputView = picker\n\n    let doneButton = UIButton(frame: CGRect(x: 0, y: 0, width: 60, height: 44))\n    doneButton.setTitle(\"Done\", for: .normal)\n    doneButton.setTitleColor(.blue, for: .normal)\n    doneButton.addTarget(\n      self, action: #selector(choseModelDoneButtonTouchUpInside(_:)), for: .touchUpInside)\n    let inputAccessoryView = UIView(\n      frame: CGRect(\n        x: 0,\n        y: 0,\n        width: UIScreen.main.bounds.size.width,\n        height: 44))\n    inputAccessoryView.backgroundColor = .gray\n    inputAccessoryView.addSubview(doneButton)\n    modelTextField.inputAccessoryView = inputAccessoryView\n  }\n\n  // MARK: Buttion Actions\n  /**\n   Delegate the change of number of threads to View Controller and change the stepper display.\n   */\n  @IBAction func onClickThreadStepper(_ sender: Any) {\n    currentThreadCount = Int(threadStepper.value)\n    threadValueLabel.text = \"\\(currentThreadCount)\"\n    delegate?.viewController(self, needPerformActions: .changeThreadCount(currentThreadCount))\n  }\n\n  @IBAction func thresholdStepperValueChanged(_ sender: UIStepper) {\n    scoreThreshold = Float(sender.value)\n    delegate?.viewController(self, needPerformActions: .changeScoreThreshold(scoreThreshold))\n    thresholdValueLabel.text = \"\\(scoreThreshold)\"\n  }\n\n  @IBAction func maxResultStepperValueChanged(_ sender: UIStepper) {\n    maxResults = Int(sender.value)\n    delegate?.viewController(self, needPerformActions: .changeMaxResults(maxResults))\n    maxResultLabel.text = \"\\(maxResults)\"\n  }\n\n  @objc\n  func choseModelDoneButtonTouchUpInside(_ sender: UIButton) {\n    delegate?.viewController(self, needPerformActions: .changeModel(modelSelect))\n    modelTextField.text = modelSelect.title\n    modelTextField.resignFirstResponder()\n  }\n}\n\n// MARK: UITableView Data Source\nextension InferenceViewController: UITableViewDelegate, UITableViewDataSource {\n\n  func numberOfSections(in tableView: UITableView) -> Int {\n    return InferenceSections.allCases.count\n  }\n\n  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {\n    guard let inferenceSection = InferenceSections(rawValue: section) else {\n      return 0\n    }\n\n    var rowCount = 0\n    switch inferenceSection {\n    case .Results:\n      rowCount = maxResults\n    case .InferenceInfo:\n      rowCount = InferenceInfo.allCases.count\n    }\n    return rowCount\n  }\n\n  func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {\n    return normalCellHeight\n  }\n\n  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {\n    let cell = tableView.dequeueReusableCell(withIdentifier: \"INFO_CELL\") as! InfoCell\n\n    guard let inferenceSection = InferenceSections(rawValue: indexPath.section) else {\n      return cell\n    }\n\n    var fieldName = \"\"\n    var info = \"\"\n    var font = infoFont\n    var color = infoTextColor\n\n    switch inferenceSection {\n    case .Results:\n\n      let tuple = displayStringsForResults(atRow: indexPath.row)\n      fieldName = tuple.0\n      info = tuple.1\n\n      if indexPath.row == 0 {\n        font = highlightedFont\n        color = infoTextColor\n      } else {\n        font = infoFont\n        color = lightTextInfoColor\n      }\n\n    case .InferenceInfo:\n      let tuple = displayStringsForInferenceInfo(atRow: indexPath.row)\n      fieldName = tuple.0\n      info = tuple.1\n\n    }\n    cell.fieldNameLabel.font = font\n    cell.fieldNameLabel.textColor = color\n    cell.fieldNameLabel.text = fieldName\n    cell.infoLabel.text = info\n    return cell\n  }\n\n  // MARK: Format Display of information in the bottom sheet\n  /**\n   This method formats the display of the inferences for the current frame.\n   */\n  func displayStringsForResults(atRow row: Int) -> (String, String) {\n    var fieldName: String = \"\"\n    var info: String = \"\"\n\n    guard let tempResult = inferenceResult, tempResult.classifications.categories.count > 0 else {\n\n      if row == 1 {\n        fieldName = \"No Results\"\n        info = \"\"\n      } else {\n        fieldName = \"\"\n        info = \"\"\n      }\n      return (fieldName, info)\n    }\n\n    if row < tempResult.classifications.categories.count {\n      let category = tempResult.classifications.categories[row]\n      fieldName = category.label ?? \"\"\n      info = String(format: \"%.2f\", category.score * 100.0) + \"%\"\n    } else {\n      fieldName = \"\"\n      info = \"\"\n    }\n\n    return (fieldName, info)\n  }\n\n  /**\n   This method formats the display of additional information relating to the inferences.\n   */\n  func displayStringsForInferenceInfo(atRow row: Int) -> (String, String) {\n\n    var fieldName: String = \"\"\n    var info: String = \"\"\n\n    guard let inferenceInfo = InferenceInfo(rawValue: row) else {\n      return (fieldName, info)\n    }\n\n    fieldName = inferenceInfo.displayString()\n\n    switch inferenceInfo {\n    case .Resolution:\n      info = \"\\(Int(resolution.width))x\\(Int(resolution.height))\"\n    case .InferenceTime:\n      guard let finalResults = inferenceResult else {\n        info = \"0ms\"\n        break\n      }\n      info = String(format: \"%.2fms\", finalResults.inferenceTime)\n    }\n\n    return (fieldName, info)\n  }\n}\n\nextension InferenceViewController: UIPickerViewDelegate, UIPickerViewDataSource {\n  func numberOfComponents(in pickerView: UIPickerView) -> Int {\n    return 1\n  }\n\n  func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {\n    return ModelType.allCases.count\n  }\n\n  func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int)\n    -> String?\n  {\n    if row < ModelType.allCases.count {\n      return ModelType.allCases[row].title\n    } else {\n      return nil\n    }\n  }\n\n  func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {\n    modelSelectIndex = row\n  }\n}\n\nclass InfoCell: UITableViewCell {\n  @IBOutlet weak var fieldNameLabel: UILabel!\n  @IBOutlet weak var infoLabel: UILabel!\n}\n"
  },
  {
    "path": "lite/examples/image_classification/ios/ImageClassification/ViewControllers/ViewController.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport AVFoundation\nimport UIKit\n\nclass ViewController: UIViewController {\n\n  // MARK: Storyboards Connections\n  @IBOutlet weak var previewView: PreviewView!\n  @IBOutlet weak var cameraUnavailableLabel: UILabel!\n  @IBOutlet weak var resumeButton: UIButton!\n  @IBOutlet weak var bottomSheetView: UIView!\n\n  @IBOutlet weak var bottomSheetViewBottomSpace: NSLayoutConstraint!\n  @IBOutlet weak var bottomSheetStateImageView: UIImageView!\n  @IBOutlet weak var bottomViewHeightConstraint: NSLayoutConstraint!\n\n  // MARK: Constants\n  private let animationDuration = 0.5\n  private let collapseTransitionThreshold: CGFloat = -40.0\n  private let expandTransitionThreshold: CGFloat = 40.0\n  private let delayBetweenInferencesMs = 1000.0\n\n  // MARK: Instance Variables\n  private let inferenceQueue = DispatchQueue(label: \"org.tensorflow.lite.inferencequeue\")\n  private var previousInferenceTimeMs = Date.distantPast.timeIntervalSince1970 * 1000\n  private var isInferenceQueueBusy = false\n  private var initialBottomSpace: CGFloat = 0.0\n  private var threadCount = DefaultConstants.threadCount\n  private var maxResults = DefaultConstants.maxResults {\n    didSet {\n      guard let inferenceVC = inferenceViewController else { return }\n      bottomViewHeightConstraint.constant = inferenceVC.collapsedHeight + 290\n      view.layoutSubviews()\n    }\n  }\n  private var scoreThreshold = DefaultConstants.scoreThreshold\n  private var model: ModelType = .efficientnetLite0\n\n  // MARK: Controllers that manage functionality\n  // Handles all the camera related functionality\n  private lazy var cameraCapture = CameraFeedManager(previewView: previewView)\n\n  // Handles all data preprocessing and makes calls to run inference through the\n  // `ImageClassificationHelper`.\n  private var imageClassificationHelper: ImageClassificationHelper? =\n    ImageClassificationHelper(\n      modelFileInfo: DefaultConstants.model.modelFileInfo,\n      threadCount: DefaultConstants.threadCount,\n      resultCount: DefaultConstants.maxResults,\n      scoreThreshold: DefaultConstants.scoreThreshold)\n\n  // Handles the presenting of results on the screen\n  private var inferenceViewController: InferenceViewController?\n\n  // MARK: View Handling Methods\n  override func viewDidLoad() {\n    super.viewDidLoad()\n\n    guard imageClassificationHelper != nil else {\n      fatalError(\"Model initialization failed.\")\n    }\n\n    cameraCapture.delegate = self\n    addPanGesture()\n\n    guard let inferenceVC = inferenceViewController else { return }\n    bottomViewHeightConstraint.constant = inferenceVC.collapsedHeight + 290\n    view.layoutSubviews()\n  }\n\n  override func viewWillAppear(_ animated: Bool) {\n    super.viewWillAppear(animated)\n    DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {\n      self.changeBottomViewState()\n    }\n    #if !targetEnvironment(simulator)\n      cameraCapture.checkCameraConfigurationAndStartSession()\n    #endif\n  }\n\n  #if !targetEnvironment(simulator)\n    override func viewWillDisappear(_ animated: Bool) {\n      super.viewWillDisappear(animated)\n      cameraCapture.stopSession()\n    }\n  #endif\n\n  override var preferredStatusBarStyle: UIStatusBarStyle {\n    return .lightContent\n  }\n\n  func presentUnableToResumeSessionAlert() {\n    let alert = UIAlertController(\n      title: \"Unable to Resume Session\",\n      message: \"There was an error while attempting to resume session.\",\n      preferredStyle: .alert\n    )\n    alert.addAction(UIAlertAction(title: \"OK\", style: .default, handler: nil))\n\n    self.present(alert, animated: true)\n  }\n\n  // MARK: Storyboard Segue Handlers\n  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {\n    super.prepare(for: segue, sender: sender)\n\n    if segue.identifier == \"EMBED\" {\n      inferenceViewController = segue.destination as? InferenceViewController\n      inferenceViewController?.maxResults = maxResults\n      inferenceViewController?.currentThreadCount = threadCount\n      inferenceViewController?.delegate = self\n    }\n  }\n}\n\n// MARK: InferenceViewControllerDelegate Methods\nextension ViewController: InferenceViewControllerDelegate {\n  func viewController(\n    _ viewController: InferenceViewController,\n    needPerformActions action: InferenceViewController.Action\n  ) {\n    var isModelNeedsRefresh = false\n    switch action {\n    case .changeThreadCount(let threadCount):\n      if self.threadCount != threadCount {\n        isModelNeedsRefresh = true\n      }\n      self.threadCount = threadCount\n    case .changeScoreThreshold(let scoreThreshold):\n      if self.scoreThreshold != scoreThreshold {\n        isModelNeedsRefresh = true\n      }\n      self.scoreThreshold = scoreThreshold\n    case .changeMaxResults(let maxResults):\n      if self.maxResults != maxResults {\n        isModelNeedsRefresh = true\n      }\n      self.maxResults = maxResults\n    case .changeModel(let model):\n      if self.model != model {\n        isModelNeedsRefresh = true\n      }\n      self.model = model\n    }\n    if isModelNeedsRefresh {\n      imageClassificationHelper = ImageClassificationHelper(\n        modelFileInfo: model.modelFileInfo,\n        threadCount: threadCount,\n        resultCount: maxResults,\n        scoreThreshold: scoreThreshold\n      )\n    }\n  }\n}\n\n// MARK: CameraFeedManagerDelegate Methods\nextension ViewController: CameraFeedManagerDelegate {\n\n  func didOutput(pixelBuffer: CVPixelBuffer) {\n    // Make sure the model will not run too often, making the results changing quickly and hard to\n    // read.\n    let currentTimeMs = Date().timeIntervalSince1970 * 1000\n    guard (currentTimeMs - previousInferenceTimeMs) >= delayBetweenInferencesMs else { return }\n    previousInferenceTimeMs = currentTimeMs\n\n    // Drop this frame if the model is still busy classifying a previous frame.\n    guard !isInferenceQueueBusy else { return }\n\n    inferenceQueue.async { [weak self] in\n      guard let self = self else { return }\n\n      self.isInferenceQueueBusy = true\n\n      // Pass the pixel buffer to TensorFlow Lite to perform inference.\n      let result = self.imageClassificationHelper?.classify(frame: pixelBuffer)\n\n      self.isInferenceQueueBusy = false\n\n      // Display results by handing off to the InferenceViewController.\n      DispatchQueue.main.async {\n        let resolution = CGSize(\n          width: CVPixelBufferGetWidth(pixelBuffer), height: CVPixelBufferGetHeight(pixelBuffer))\n        self.inferenceViewController?.inferenceResult = result\n        self.inferenceViewController?.resolution = resolution\n        self.inferenceViewController?.tableView.reloadData()\n      }\n    }\n  }\n\n  // MARK: Session Handling Alerts\n  func sessionWasInterrupted(canResumeManually resumeManually: Bool) {\n\n    // Updates the UI when session is interupted.\n    if resumeManually {\n      self.resumeButton.isHidden = false\n    } else {\n      self.cameraUnavailableLabel.isHidden = false\n    }\n  }\n\n  func sessionInterruptionEnded() {\n    // Updates UI once session interruption has ended.\n    if !self.cameraUnavailableLabel.isHidden {\n      self.cameraUnavailableLabel.isHidden = true\n    }\n\n    if !self.resumeButton.isHidden {\n      self.resumeButton.isHidden = true\n    }\n  }\n\n  func sessionRunTimeErrorOccured() {\n    // Handles session run time error by updating the UI and providing a button if session can be\n    // manually resumed.\n    self.resumeButton.isHidden = false\n    previewView.shouldUseClipboardImage = true\n  }\n\n  func presentCameraPermissionsDeniedAlert() {\n    let alertController = UIAlertController(\n      title: \"Camera Permissions Denied\",\n      message:\n        \"Camera permissions have been denied for this app. You can change this by going to Settings\",\n      preferredStyle: .alert)\n\n    let cancelAction = UIAlertAction(title: \"Cancel\", style: .cancel, handler: nil)\n    let settingsAction = UIAlertAction(title: \"Settings\", style: .default) { (action) in\n      UIApplication.shared.open(\n        URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)\n    }\n    alertController.addAction(cancelAction)\n    alertController.addAction(settingsAction)\n\n    present(alertController, animated: true, completion: nil)\n\n    previewView.shouldUseClipboardImage = true\n  }\n\n  func presentVideoConfigurationErrorAlert() {\n    let alert = UIAlertController(\n      title: \"Camera Configuration Failed\", message: \"There was an error while configuring camera.\",\n      preferredStyle: .alert)\n    alert.addAction(UIAlertAction(title: \"OK\", style: .default, handler: nil))\n\n    self.present(alert, animated: true)\n    previewView.shouldUseClipboardImage = true\n  }\n}\n\n// MARK: Bottom Sheet Interaction Methods\nextension ViewController {\n\n  // MARK: Bottom Sheet Interaction Methods\n  /**\n   This method adds a pan gesture to make the bottom sheet interactive.\n   */\n  private func addPanGesture() {\n    let panGesture = UIPanGestureRecognizer(\n      target: self, action: #selector(ViewController.didPan(panGesture:)))\n    bottomSheetView.addGestureRecognizer(panGesture)\n  }\n\n  /** Change whether bottom sheet should be in expanded or collapsed state.\n   */\n  private func changeBottomViewState() {\n    guard let inferenceVC = inferenceViewController else {\n      return\n    }\n\n    if bottomSheetViewBottomSpace.constant == inferenceVC.collapsedHeight\n      - bottomSheetView.bounds.size.height\n    {\n      bottomSheetViewBottomSpace.constant = 0.0\n    } else {\n      bottomSheetViewBottomSpace.constant =\n        inferenceVC.collapsedHeight - bottomSheetView.bounds.size.height\n    }\n    setImageBasedOnBottomViewState()\n  }\n\n  /**\n   Set image of the bottom sheet icon based on whether it is expanded or collapsed\n   */\n  private func setImageBasedOnBottomViewState() {\n    if bottomSheetViewBottomSpace.constant == 0.0 {\n      bottomSheetStateImageView.image = UIImage(named: \"down_icon\")\n    } else {\n      bottomSheetStateImageView.image = UIImage(named: \"up_icon\")\n    }\n  }\n\n  /**\n   This method responds to the user panning on the bottom sheet.\n   */\n  @objc func didPan(panGesture: UIPanGestureRecognizer) {\n    // Opens or closes the bottom sheet based on the user's interaction with the bottom sheet.\n    let translation = panGesture.translation(in: view)\n\n    switch panGesture.state {\n    case .began:\n      initialBottomSpace = bottomSheetViewBottomSpace.constant\n      translateBottomSheet(withVerticalTranslation: translation.y)\n    case .changed:\n      translateBottomSheet(withVerticalTranslation: translation.y)\n    case .cancelled:\n      setBottomSheetLayout(withBottomSpace: initialBottomSpace)\n    case .ended:\n      translateBottomSheetAtEndOfPan(withVerticalTranslation: translation.y)\n      setImageBasedOnBottomViewState()\n      initialBottomSpace = 0.0\n    default:\n      break\n    }\n  }\n\n  /**\n   This method sets bottom sheet translation while pan gesture state is continuously changing.\n   */\n  private func translateBottomSheet(withVerticalTranslation verticalTranslation: CGFloat) {\n    let bottomSpace = initialBottomSpace - verticalTranslation\n    guard\n      bottomSpace <= 0.0\n        && bottomSpace >= inferenceViewController!.collapsedHeight\n          - bottomSheetView.bounds.size.height\n    else {\n      return\n    }\n    setBottomSheetLayout(withBottomSpace: bottomSpace)\n  }\n\n  /**\n   This method changes bottom sheet state to either fully expanded or closed at the end of pan.\n   */\n  private func translateBottomSheetAtEndOfPan(withVerticalTranslation verticalTranslation: CGFloat)\n  {\n    // Changes bottom sheet state to either fully open or closed at the end of pan.\n    let bottomSpace = bottomSpaceAtEndOfPan(withVerticalTranslation: verticalTranslation)\n    setBottomSheetLayout(withBottomSpace: bottomSpace)\n  }\n\n  /**\n   Return the final state of the bottom sheet view (whether fully collapsed or expanded) that is to\n   be retained.\n   */\n  private func bottomSpaceAtEndOfPan(withVerticalTranslation verticalTranslation: CGFloat)\n    -> CGFloat\n  {\n    // Calculates whether to fully expand or collapse bottom sheet when pan gesture ends.\n    var bottomSpace = initialBottomSpace - verticalTranslation\n\n    var height: CGFloat = 0.0\n    if initialBottomSpace == 0.0 {\n      height = bottomSheetView.bounds.size.height\n    } else {\n      height = inferenceViewController!.collapsedHeight\n    }\n\n    let currentHeight = bottomSheetView.bounds.size.height + bottomSpace\n\n    if currentHeight - height <= collapseTransitionThreshold {\n      bottomSpace = inferenceViewController!.collapsedHeight - bottomSheetView.bounds.size.height\n    } else if currentHeight - height >= expandTransitionThreshold {\n      bottomSpace = 0.0\n    } else {\n      bottomSpace = initialBottomSpace\n    }\n\n    return bottomSpace\n  }\n\n  /**\n   This method layouts the change of the bottom space of bottom sheet with respect to the view\n   managed by this controller.\n   */\n  func setBottomSheetLayout(withBottomSpace bottomSpace: CGFloat) {\n    view.setNeedsLayout()\n    bottomSheetViewBottomSpace.constant = bottomSpace\n    view.setNeedsLayout()\n  }\n\n}\n\n// Define default constants\nenum DefaultConstants {\n  static let threadCount = 4\n  static let maxResults = 3\n  static let scoreThreshold: Float = 0.2\n  static let model: ModelType = .efficientnetLite0\n}\n\n/// TFLite model types\nenum ModelType: CaseIterable {\n  case efficientnetLite0\n  case efficientnetLite1\n  case efficientnetLite2\n  case efficientnetLite3\n  case efficientnetLite4\n\n  var modelFileInfo: FileInfo {\n    switch self {\n    case .efficientnetLite0:\n      return FileInfo(\"efficientnet_lite0\", \"tflite\")\n    case .efficientnetLite1:\n      return FileInfo(\"efficientnet_lite1\", \"tflite\")\n    case .efficientnetLite2:\n      return FileInfo(\"efficientnet_lite2\", \"tflite\")\n    case .efficientnetLite3:\n      return FileInfo(\"efficientnet_lite3\", \"tflite\")\n    case .efficientnetLite4:\n      return FileInfo(\"efficientnet_lite4\", \"tflite\")\n    }\n  }\n\n  var title: String {\n    switch self {\n    case .efficientnetLite0:\n      return \"EfficientNet-Lite0\"\n    case .efficientnetLite1:\n      return \"EfficientNet-Lite1\"\n    case .efficientnetLite2:\n      return \"EfficientNet-Lite2\"\n    case .efficientnetLite3:\n      return \"EfficientNet-Lite3\"\n    case .efficientnetLite4:\n      return \"EfficientNet-Lite4\"\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/image_classification/ios/ImageClassification.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tAA5B0EEA217E5E300026FF23 /* InferenceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA5B0EE9217E5E300026FF23 /* InferenceViewController.swift */; };\n\t\tAAE79E5A213521DA00E7AC1F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE79E59213521DA00E7AC1F /* AppDelegate.swift */; };\n\t\tAAE79E5F213521DA00E7AC1F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AAE79E5D213521DA00E7AC1F /* Main.storyboard */; };\n\t\tAAE79E61213521DB00E7AC1F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AAE79E60213521DB00E7AC1F /* Assets.xcassets */; };\n\t\tAAE79E64213521DB00E7AC1F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AAE79E62213521DB00E7AC1F /* LaunchScreen.storyboard */; };\n\t\tAAE79E952135237A00E7AC1F /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE79E942135237A00E7AC1F /* ViewController.swift */; };\n\t\tAAE79E9A2135268800E7AC1F /* PreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE79E982135268800E7AC1F /* PreviewView.swift */; };\n\t\tAAE79E9B2135268800E7AC1F /* CameraFeedManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE79E992135268800E7AC1F /* CameraFeedManager.swift */; };\n\t\tBF43FFC12846FEC000D8FB74 /* ImageClassificationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF43FFC02846FEC000D8FB74 /* ImageClassificationHelper.swift */; };\n\t\tBF863DEB284F01A00061B710 /* efficientnet_lite0.tflite in Resources */ = {isa = PBXBuildFile; fileRef = BF863DE6284F01A00061B710 /* efficientnet_lite0.tflite */; };\n\t\tBF863DEC284F01A00061B710 /* efficientnet_lite2.tflite in Resources */ = {isa = PBXBuildFile; fileRef = BF863DE7284F01A00061B710 /* efficientnet_lite2.tflite */; };\n\t\tBF863DED284F01A00061B710 /* efficientnet_lite3.tflite in Resources */ = {isa = PBXBuildFile; fileRef = BF863DE8284F01A00061B710 /* efficientnet_lite3.tflite */; };\n\t\tBF863DEE284F01A00061B710 /* efficientnet_lite4.tflite in Resources */ = {isa = PBXBuildFile; fileRef = BF863DE9284F01A00061B710 /* efficientnet_lite4.tflite */; };\n\t\tBF863DEF284F01A00061B710 /* efficientnet_lite1.tflite in Resources */ = {isa = PBXBuildFile; fileRef = BF863DEA284F01A00061B710 /* efficientnet_lite1.tflite */; };\n\t\tF0A0B641A9D84A33D025544E /* Pods_ImageClassification.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E76B784C111DBC26EEEC02A /* Pods_ImageClassification.framework */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\t14CAE291077E814D03E79CB3 /* Pods-ImageClassification.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-ImageClassification.release.xcconfig\"; path = \"Target Support Files/Pods-ImageClassification/Pods-ImageClassification.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t4E76B784C111DBC26EEEC02A /* Pods_ImageClassification.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ImageClassification.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tAA5B0EE9217E5E300026FF23 /* InferenceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InferenceViewController.swift; sourceTree = \"<group>\"; };\n\t\tAAE79E56213521DA00E7AC1F /* ImageClassification.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ImageClassification.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tAAE79E59213521DA00E7AC1F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\tAAE79E5E213521DA00E7AC1F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\tAAE79E60213521DB00E7AC1F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tAAE79E63213521DB00E7AC1F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\tAAE79E65213521DB00E7AC1F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tAAE79E942135237A00E7AC1F /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = \"<group>\"; };\n\t\tAAE79E982135268800E7AC1F /* PreviewView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewView.swift; sourceTree = \"<group>\"; };\n\t\tAAE79E992135268800E7AC1F /* CameraFeedManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraFeedManager.swift; sourceTree = \"<group>\"; };\n\t\tBF43FFC02846FEC000D8FB74 /* ImageClassificationHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageClassificationHelper.swift; sourceTree = \"<group>\"; };\n\t\tBF863DE6284F01A00061B710 /* efficientnet_lite0.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = efficientnet_lite0.tflite; sourceTree = \"<group>\"; };\n\t\tBF863DE7284F01A00061B710 /* efficientnet_lite2.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = efficientnet_lite2.tflite; sourceTree = \"<group>\"; };\n\t\tBF863DE8284F01A00061B710 /* efficientnet_lite3.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = efficientnet_lite3.tflite; sourceTree = \"<group>\"; };\n\t\tBF863DE9284F01A00061B710 /* efficientnet_lite4.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = efficientnet_lite4.tflite; sourceTree = \"<group>\"; };\n\t\tBF863DEA284F01A00061B710 /* efficientnet_lite1.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = efficientnet_lite1.tflite; sourceTree = \"<group>\"; };\n\t\tE7F867EEDB032059F98AA497 /* Pods-ImageClassification.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-ImageClassification.debug.xcconfig\"; path = \"Target Support Files/Pods-ImageClassification/Pods-ImageClassification.debug.xcconfig\"; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tAAE79E53213521DA00E7AC1F /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF0A0B641A9D84A33D025544E /* Pods_ImageClassification.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t44E4033AB526374BD9AFCCE0 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t4E76B784C111DBC26EEEC02A /* Pods_ImageClassification.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t8A2442349A519525D29B3656 /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tE7F867EEDB032059F98AA497 /* Pods-ImageClassification.debug.xcconfig */,\n\t\t\t\t14CAE291077E814D03E79CB3 /* Pods-ImageClassification.release.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAA5FEB7A21355CCC00F9BD4B /* TFLite */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBF43FFC02846FEC000D8FB74 /* ImageClassificationHelper.swift */,\n\t\t\t\tBF863DE6284F01A00061B710 /* efficientnet_lite0.tflite */,\n\t\t\t\tBF863DEA284F01A00061B710 /* efficientnet_lite1.tflite */,\n\t\t\t\tBF863DE7284F01A00061B710 /* efficientnet_lite2.tflite */,\n\t\t\t\tBF863DE8284F01A00061B710 /* efficientnet_lite3.tflite */,\n\t\t\t\tBF863DE9284F01A00061B710 /* efficientnet_lite4.tflite */,\n\t\t\t);\n\t\t\tpath = TFLite;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAA5FEB7F21355D0300F9BD4B /* Storyboards */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAAE79E62213521DB00E7AC1F /* LaunchScreen.storyboard */,\n\t\t\t\tAAE79E5D213521DA00E7AC1F /* Main.storyboard */,\n\t\t\t);\n\t\t\tpath = Storyboards;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAAE79E4D213521DA00E7AC1F = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAAE79E58213521DA00E7AC1F /* ImageClassification */,\n\t\t\t\tAAE79E57213521DA00E7AC1F /* Products */,\n\t\t\t\t8A2442349A519525D29B3656 /* Pods */,\n\t\t\t\t44E4033AB526374BD9AFCCE0 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAAE79E57213521DA00E7AC1F /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAAE79E56213521DA00E7AC1F /* ImageClassification.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAAE79E58213521DA00E7AC1F /* ImageClassification */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAAE79E972135268800E7AC1F /* Camera Feed */,\n\t\t\t\tAAE79E932135236700E7AC1F /* ViewControllers */,\n\t\t\t\tAA5FEB7A21355CCC00F9BD4B /* TFLite */,\n\t\t\t\tAA5FEB7F21355D0300F9BD4B /* Storyboards */,\n\t\t\t\tAAE79E59213521DA00E7AC1F /* AppDelegate.swift */,\n\t\t\t\tAAE79E60213521DB00E7AC1F /* Assets.xcassets */,\n\t\t\t\tAAE79E65213521DB00E7AC1F /* Info.plist */,\n\t\t\t);\n\t\t\tpath = ImageClassification;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAAE79E932135236700E7AC1F /* ViewControllers */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAAE79E942135237A00E7AC1F /* ViewController.swift */,\n\t\t\t\tAA5B0EE9217E5E300026FF23 /* InferenceViewController.swift */,\n\t\t\t);\n\t\t\tpath = ViewControllers;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAAE79E972135268800E7AC1F /* Camera Feed */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAAE79E982135268800E7AC1F /* PreviewView.swift */,\n\t\t\t\tAAE79E992135268800E7AC1F /* CameraFeedManager.swift */,\n\t\t\t);\n\t\t\tpath = \"Camera Feed\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\tAAE79E55213521DA00E7AC1F /* ImageClassification */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = AAE79E7E213521DB00E7AC1F /* Build configuration list for PBXNativeTarget \"ImageClassification\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t0139AEC41FA0921D37A71365 /* [CP] Check Pods Manifest.lock */,\n\t\t\t\tAA055D8B2162320A00B25948 /* ShellScript */,\n\t\t\t\tAAE79E52213521DA00E7AC1F /* Sources */,\n\t\t\t\tAAE79E53213521DA00E7AC1F /* Frameworks */,\n\t\t\t\tAAE79E54213521DA00E7AC1F /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = ImageClassification;\n\t\t\tproductName = ImageClassification;\n\t\t\tproductReference = AAE79E56213521DA00E7AC1F /* ImageClassification.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tAAE79E4E213521DA00E7AC1F /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 0940;\n\t\t\t\tLastUpgradeCheck = 1330;\n\t\t\t\tORGANIZATIONNAME = \"Y Media Labs\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tAAE79E55213521DA00E7AC1F = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.4.1;\n\t\t\t\t\t\tLastSwiftMigration = 1110;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = AAE79E51213521DA00E7AC1F /* Build configuration list for PBXProject \"ImageClassification\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = AAE79E4D213521DA00E7AC1F;\n\t\t\tproductRefGroup = AAE79E57213521DA00E7AC1F /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tAAE79E55213521DA00E7AC1F /* ImageClassification */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\tAAE79E54213521DA00E7AC1F /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tAAE79E64213521DB00E7AC1F /* LaunchScreen.storyboard in Resources */,\n\t\t\t\tBF863DEE284F01A00061B710 /* efficientnet_lite4.tflite in Resources */,\n\t\t\t\tAAE79E61213521DB00E7AC1F /* Assets.xcassets in Resources */,\n\t\t\t\tBF863DEC284F01A00061B710 /* efficientnet_lite2.tflite in Resources */,\n\t\t\t\tBF863DED284F01A00061B710 /* efficientnet_lite3.tflite in Resources */,\n\t\t\t\tBF863DEF284F01A00061B710 /* efficientnet_lite1.tflite in Resources */,\n\t\t\t\tAAE79E5F213521DA00E7AC1F /* Main.storyboard in Resources */,\n\t\t\t\tBF863DEB284F01A00061B710 /* efficientnet_lite0.tflite in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t0139AEC41FA0921D37A71365 /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-ImageClassification-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\tAA055D8B2162320A00B25948 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"# Type a script or drag a script file from your workspace to insert its path.\\n\\\"$SRCROOT/RunScripts/download_models.sh\\\"\\n\";\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tAAE79E52213521DA00E7AC1F /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tAAE79E952135237A00E7AC1F /* ViewController.swift in Sources */,\n\t\t\t\tBF43FFC12846FEC000D8FB74 /* ImageClassificationHelper.swift in Sources */,\n\t\t\t\tAAE79E9A2135268800E7AC1F /* PreviewView.swift in Sources */,\n\t\t\t\tAAE79E9B2135268800E7AC1F /* CameraFeedManager.swift in Sources */,\n\t\t\t\tAA5B0EEA217E5E300026FF23 /* InferenceViewController.swift in Sources */,\n\t\t\t\tAAE79E5A213521DA00E7AC1F /* AppDelegate.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\tAAE79E5D213521DA00E7AC1F /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tAAE79E5E213521DA00E7AC1F /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAAE79E62213521DB00E7AC1F /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tAAE79E63213521DB00E7AC1F /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\tAAE79E7C213521DB00E7AC1F /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 4.2;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tAAE79E7D213521DB00E7AC1F /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tSWIFT_VERSION = 4.2;\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tAAE79E7F213521DB00E7AC1F /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = E7F867EEDB032059F98AA497 /* Pods-ImageClassification.debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"Apple Development\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = ImageClassification/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.tensorflow.ImageClassification;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tAAE79E80213521DB00E7AC1F /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 14CAE291077E814D03E79CB3 /* Pods-ImageClassification.release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"Apple Development\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = ImageClassification/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.tensorflow.ImageClassification;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tAAE79E51213521DA00E7AC1F /* Build configuration list for PBXProject \"ImageClassification\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tAAE79E7C213521DB00E7AC1F /* Debug */,\n\t\t\t\tAAE79E7D213521DB00E7AC1F /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tAAE79E7E213521DB00E7AC1F /* Build configuration list for PBXNativeTarget \"ImageClassification\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tAAE79E7F213521DB00E7AC1F /* Debug */,\n\t\t\t\tAAE79E80213521DB00E7AC1F /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = AAE79E4E213521DA00E7AC1F /* Project object */;\n}\n"
  },
  {
    "path": "lite/examples/image_classification/ios/Podfile",
    "content": "# Uncomment the next line to define a global platform for your project\n platform :ios, '12.0'\n\ntarget 'ImageClassification' do\n  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks\n  use_frameworks!\n   pod 'TensorFlowLiteTaskVision'\nend\n"
  },
  {
    "path": "lite/examples/image_classification/ios/README.md",
    "content": "# TensorFlow Lite image classification iOS example application\n\n## Overview\n\nThis is an example application for [TensorFlow Lite](https://tensorflow.org/lite)\non iOS. It uses [Image classification](https://www.tensorflow.org/lite/examples/image_classification/overview)\nto continuously classify whatever it sees from the device's back camera, using\na quantized MobileNet model. The application must be run on device.\n\nThese instructions walk you through building and\nrunning the demo on an iOS device.\n\n<!-- TODO(b/124116863): Add app screenshot. -->\n\n### Model\n\nFor details of the model used, visit [Image classification](https://www.tensorflow.org/lite/examples/image_classification/overview).\n\nThe model will be downloaded as part of the build process.\n\n### iOS app details\n\nThe app is written entirely in Swift and uses the TensorFlow Lite Task Library's\nImageClassifier(https://www.tensorflow.org/lite/inference_with_metadata/task_library/image_classifier#run_inference_in_ios)\nfor performing image classification.\n\nNote: Objective-C developers should use the TensorFlow Lite Task Library's\n[Objective-C API](https://www.tensorflow.org/lite/inference_with_metadata/task_library/image_classifier#objective_c).\n\n## Requirements\n\n*   Device with iOS 12.0 or above\n\n*   Xcode 13.0 or above\n\n*   Valid Apple Developer ID\n\n*   Xcode command-line tools (run `xcode-select --install`)\n\n*   [CocoaPods](https://cocoapods.org/) (run `sudo gem install cocoapods`)\n\nIf this is a new install, you will need to run the Xcode application once to\nagree to the license before continuing.\n\nNote: The demo app requires a camera and must be executed on a real iOS device.\nYou can build it and run with the iPhone Simulator, but the app will raise a\n`Camera not found` exception.\n\n## Build and run\n\n1.  Clone this GitHub repository to your workstation. `git clone\n    https://github.com/tensorflow/examples.git`\n\n2.  Install the pod to generate the workspace file: `cd\n    examples/lite/examples/image_classification/ios && pod install`\n\nNote: If you have installed this pod before and that command doesn't work, try\n`pod update`.\n\nAt the end of this step you should have a directory called\n`ImageClassification.xcworkspace`.\n\n1.  Open the project in Xcode with the following command: `open\n    ImageClassification.xcworkspace`\n\nThis launches Xcode and opens the `ImageClassification` project.\n\n1.  Select the `ImageClassification` project in the left hand navigation to open\n    the project configuration. In the **Signing** section of the **General**\n    tab, select your development team from the dropdown.\n\n2.  In order to build the project, you must modify the **Bundle Identifier** in\n    the **Identity** section so that it is unique across all Xcode projects. To\n    create a unique identifier, try adding your initials and a number to the end\n    of the string.\n\n3.  With an iOS device connected, build and run the app in Xcode.\n\nYou'll have to grant permissions for the app to use the device's camera. Point\nthe camera at various objects and enjoy seeing how the model classifies things!\n\n## Model references\n\n_Do not delete the empty references_ to the .tflite files after you\nclone the repo and open the project. These references will be fulfilled once the\nmodel files are downloaded when the application is built and run for\nthe first time. If you delete the references to them, you can still find that\nthe .tflite files are downloaded to the `TFLite` folder, the next time you\nbuild the application. You will have to add the references to these files in the\nbundle separately in that case.\n"
  },
  {
    "path": "lite/examples/image_classification/ios/RunScripts/download_models.sh",
    "content": "#!/bin/bash\n# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\nset -ex\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nEFFICIENTNET_LITE0_URL=\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_classification/ios/lite-model_efficientnet_lite0_uint8_2.tflite\"\nEFFICIENTNET_LITE1_URL=\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_classification/ios/lite-model_efficientnet_lite1_uint8_2.tflite\"\nEFFICIENTNET_LITE2_URL=\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_classification/ios/lite-model_efficientnet_lite2_uint8_2.tflite\"\nEFFICIENTNET_LITE3_URL=\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_classification/ios/lite-model_efficientnet_lite3_uint8_2.tflite\"\nEFFICIENTNET_LITE4_URL=\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_classification/ios/lite-model_efficientnet_lite4_uint8_2.tflite\"\n\nEFFICIENTNET_LITE0_NAME=\"efficientnet_lite0.tflite\"\nEFFICIENTNET_LITE1_NAME=\"efficientnet_lite1.tflite\"\nEFFICIENTNET_LITE2_NAME=\"efficientnet_lite2.tflite\"\nEFFICIENTNET_LITE3_NAME=\"efficientnet_lite3.tflite\"\nEFFICIENTNET_LITE4_NAME=\"efficientnet_lite4.tflite\"\n\nDOWNLOADS_DIR=$(mktemp -d)\n\ncd \"$SCRIPT_DIR\"\n\ndownload() {\n  local usage=\"Usage: download_and_extract URL DIR\"\n  local url=\"${1:?${usage}}\"\n  local dir=\"${2:?${usage}}\"\n  local name=\"${3:?${usage}}\"\n  echo \"downloading ${url}\" >&2\n  mkdir -p \"${dir}\"\n  tempdir=$(mktemp -d)\n\n  curl -L ${url} > ${tempdir}/${name}\n  cp -R ${tempdir}/* ${dir}/\n  rm -rf ${tempdir}\n}\n\nhas_download=false\n\nif [ -f ../ImageClassification/TFLite/${EFFICIENTNET_LITE0_NAME} ]\nthen\necho \"File ${EFFICIENTNET_LITE0_NAME} exists.\"\nelse\nhas_download=true\ndownload \"${EFFICIENTNET_LITE0_URL}\" \"${DOWNLOADS_DIR}/models\" \"${EFFICIENTNET_LITE0_NAME}\"\nfile ${DOWNLOADS_DIR}/models\nfi\n\nif [ -f ../ImageClassification/TFLite/${EFFICIENTNET_LITE1_NAME} ]\nthen\necho \"File ${EFFICIENTNET_LITE1_NAME} exists.\"\nelse\nhas_download=true\ndownload \"${EFFICIENTNET_LITE1_URL}\" \"${DOWNLOADS_DIR}/models\" \"${EFFICIENTNET_LITE1_NAME}\"\nfile ${DOWNLOADS_DIR}/models\nfi\n\nif [ -f ../ImageClassification/TFLite/${EFFICIENTNET_LITE2_NAME} ]\nthen\necho \"File ${EFFICIENTNET_LITE2_NAME} exists.\"\nelse\nhas_download=true\ndownload \"${EFFICIENTNET_LITE2_URL}\" \"${DOWNLOADS_DIR}/models\" \"${EFFICIENTNET_LITE2_NAME}\"\nfile ${DOWNLOADS_DIR}/models\nfi\n\nif [ -f ../ImageClassification/TFLite/${EFFICIENTNET_LITE3_NAME} ]\nthen\necho \"File ${EFFICIENTNET_LITE3_NAME} exists.\"\nelse\nhas_download=true\ndownload \"${EFFICIENTNET_LITE3_URL}\" \"${DOWNLOADS_DIR}/models\" \"${EFFICIENTNET_LITE3_NAME}\"\nfile ${DOWNLOADS_DIR}/models\nfi\n\nif [ -f ../ImageClassification/TFLite/${EFFICIENTNET_LITE4_NAME} ]\nthen\necho \"File ${EFFICIENTNET_LITE4_NAME} exists.\"\nelse\nhas_download=true\ndownload \"${EFFICIENTNET_LITE4_URL}\" \"${DOWNLOADS_DIR}/models\" \"${EFFICIENTNET_LITE4_NAME}\"\nfile ${DOWNLOADS_DIR}/models\nfi\n\nif ${has_download}\nthen\ncp ${DOWNLOADS_DIR}/models/* ../ImageClassification/TFLite\nrm -rf ${DOWNLOADS_DIR}\nfi\n\n"
  },
  {
    "path": "lite/examples/image_classification/metadata/README.md",
    "content": "# Example script for populating TensorFlow Lite metadata\n\nNote: TensorFlow Lite Metadata is in experimental (beta) phase.\n\nDevelopers should follow the steps outline in\n[this guide](https://www.tensorflow.org/lite/convert/metadata) prior to running\nthis script. `metadata_writer_for_image_classifier.py` is an example script on\nhow to populate the metadata for a MobileNet V1 160x160 0.75 quantized model.\n\nFor other models, developers should take care to update the lines in the script\nwith the metadata for other models prior to running it.\n"
  },
  {
    "path": "lite/examples/image_classification/metadata/metadata_writer_for_image_classifier.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Writes metadata and label file to the image classifier models.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\n\nfrom absl import app\nfrom absl import flags\nimport flatbuffers\nimport tensorflow as tf\n\n# pylint: disable=g-direct-tensorflow-import\nfrom tflite_support import metadata_schema_py_generated as _metadata_fb\nfrom tflite_support import metadata as _metadata\n# pylint: enable=g-direct-tensorflow-import\n\nFLAGS = flags.FLAGS\n\n\ndef define_flags():\n  flags.DEFINE_string(\"model_file\", None,\n                      \"Path and file name to the TFLite model file.\")\n  flags.DEFINE_string(\"label_file\", None, \"Path to the label file.\")\n  flags.DEFINE_string(\"export_directory\", None,\n                      \"Path to save the TFLite model files with metadata.\")\n  flags.mark_flag_as_required(\"model_file\")\n  flags.mark_flag_as_required(\"label_file\")\n  flags.mark_flag_as_required(\"export_directory\")\n\n\nclass ModelSpecificInfo(object):\n  \"\"\"Holds information that is specificly tied to an image classifier.\"\"\"\n\n  def __init__(self, name, version, image_width, image_height, image_min,\n               image_max, mean, std, num_classes, author):\n    self.name = name\n    self.version = version\n    self.image_width = image_width\n    self.image_height = image_height\n    self.image_min = image_min\n    self.image_max = image_max\n    self.mean = mean\n    self.std = std\n    self.num_classes = num_classes\n    self.author = author\n\n\n_MODEL_INFO = {\n    \"mobilenet_v1_0.75_160_quantized.tflite\":\n        ModelSpecificInfo(\n            name=\"MobileNetV1 image classifier\",\n            version=\"v1\",\n            image_width=160,\n            image_height=160,\n            image_min=0,\n            image_max=255,\n            mean=[127.5],\n            std=[127.5],\n            num_classes=1001,\n            author=\"TensorFlow\")\n}\n\n\nclass MetadataPopulatorForImageClassifier(object):\n  \"\"\"Populates the metadata for an image classifier.\"\"\"\n\n  def __init__(self, model_file, model_info, label_file_path):\n    self.model_file = model_file\n    self.model_info = model_info\n    self.label_file_path = label_file_path\n    self.metadata_buf = None\n\n  def populate(self):\n    \"\"\"Creates metadata and then populates it for an image classifier.\"\"\"\n    self._create_metadata()\n    self._populate_metadata()\n\n  def _create_metadata(self):\n    \"\"\"Creates the metadata for an image classifier.\"\"\"\n\n    # Creates model info.\n    model_meta = _metadata_fb.ModelMetadataT()\n    model_meta.name = self.model_info.name\n    model_meta.description = (\"Identify the most prominent object in the \"\n                              \"image from a set of %d categories.\" %\n                              self.model_info.num_classes)\n    model_meta.version = self.model_info.version\n    model_meta.author = self.model_info.author\n    model_meta.license = (\"Apache License. Version 2.0 \"\n                          \"http://www.apache.org/licenses/LICENSE-2.0.\")\n\n    # Creates input info.\n    input_meta = _metadata_fb.TensorMetadataT()\n    input_meta.name = \"image\"\n    input_meta.description = (\n        \"Input image to be classified. The expected image is {0} x {1}, with \"\n        \"three channels (red, blue, and green) per pixel. Each value in the \"\n        \"tensor is a single byte between {2} and {3}.\".format(\n            self.model_info.image_width, self.model_info.image_height,\n            self.model_info.image_min, self.model_info.image_max))\n    input_meta.content = _metadata_fb.ContentT()\n    input_meta.content.contentProperties = _metadata_fb.ImagePropertiesT()\n    input_meta.content.contentProperties.colorSpace = (\n        _metadata_fb.ColorSpaceType.RGB)\n    input_meta.content.contentPropertiesType = (\n        _metadata_fb.ContentProperties.ImageProperties)\n    input_normalization = _metadata_fb.ProcessUnitT()\n    input_normalization.optionsType = (\n        _metadata_fb.ProcessUnitOptions.NormalizationOptions)\n    input_normalization.options = _metadata_fb.NormalizationOptionsT()\n    input_normalization.options.mean = self.model_info.mean\n    input_normalization.options.std = self.model_info.std\n    input_meta.processUnits = [input_normalization]\n    input_stats = _metadata_fb.StatsT()\n    input_stats.max = [self.model_info.image_max]\n    input_stats.min = [self.model_info.image_min]\n    input_meta.stats = input_stats\n\n    # Creates output info.\n    output_meta = _metadata_fb.TensorMetadataT()\n    output_meta.name = \"probability\"\n    output_meta.description = \"Probabilities of the %d labels respectively.\" % self.model_info.num_classes\n    output_meta.content = _metadata_fb.ContentT()\n    output_meta.content.content_properties = _metadata_fb.FeaturePropertiesT()\n    output_meta.content.contentPropertiesType = (\n        _metadata_fb.ContentProperties.FeatureProperties)\n    output_stats = _metadata_fb.StatsT()\n    output_stats.max = [1.0]\n    output_stats.min = [0.0]\n    output_meta.stats = output_stats\n    label_file = _metadata_fb.AssociatedFileT()\n    label_file.name = os.path.basename(self.label_file_path)\n    label_file.description = \"Labels for objects that the model can recognize.\"\n    label_file.type = _metadata_fb.AssociatedFileType.TENSOR_AXIS_LABELS\n    output_meta.associatedFiles = [label_file]\n\n    # Creates subgraph info.\n    subgraph = _metadata_fb.SubGraphMetadataT()\n    subgraph.inputTensorMetadata = [input_meta]\n    subgraph.outputTensorMetadata = [output_meta]\n    model_meta.subgraphMetadata = [subgraph]\n\n    b = flatbuffers.Builder(0)\n    b.Finish(\n        model_meta.Pack(b),\n        _metadata.MetadataPopulator.METADATA_FILE_IDENTIFIER)\n    self.metadata_buf = b.Output()\n\n  def _populate_metadata(self):\n    \"\"\"Populates metadata and label file to the model file.\"\"\"\n    populator = _metadata.MetadataPopulator.with_model_file(self.model_file)\n    populator.load_metadata_buffer(self.metadata_buf)\n    populator.load_associated_files([self.label_file_path])\n    populator.populate()\n\n\ndef main(_):\n  model_file = FLAGS.model_file\n  model_basename = os.path.basename(model_file)\n  if model_basename not in _MODEL_INFO:\n    raise ValueError(\n        \"The model info for, {0}, is not defined yet.\".format(model_basename))\n\n  export_model_path = os.path.join(FLAGS.export_directory, model_basename)\n\n  # Copies model_file to export_path.\n  tf.io.gfile.copy(model_file, export_model_path, overwrite=False)\n\n  # Generate the metadata objects and put them in the model file\n  populator = MetadataPopulatorForImageClassifier(\n      export_model_path, _MODEL_INFO.get(model_basename), FLAGS.label_file)\n  populator.populate()\n\n  # Validate the output model file by reading the metadata and produce\n  # a json file with the metadata under the export path\n  displayer = _metadata.MetadataDisplayer.with_model_file(export_model_path)\n  export_json_file = os.path.join(FLAGS.export_directory,\n                                  os.path.splitext(model_basename)[0] + \".json\")\n  json_file = displayer.get_metadata_json()\n  with open(export_json_file, \"w\") as f:\n    f.write(json_file)\n\n  print(\"Finished populating metadata and associated file to the model:\")\n  print(model_file)\n  print(\"The metadata json file has been saved to:\")\n  print(export_json_file)\n  print(\"The associated file that has been been packed to the model is:\")\n  print(displayer.get_packed_associated_file_list())\n\n\nif __name__ == \"__main__\":\n  define_flags()\n  app.run(main)\n"
  },
  {
    "path": "lite/examples/image_classification/metadata/requirements.txt",
    "content": "# Python packages required for metadata_writer_for_image_classifier.py\nabsl-py\ntensorflow\ntflite-support\nflatbuffers==1.12\n"
  },
  {
    "path": "lite/examples/image_classification/raspberry_pi/README.md",
    "content": "# TensorFlow Lite Python image classification example with Raspberry Pi.\n\nThis example uses [TensorFlow Lite](https://tensorflow.org/lite) with Python\non a Raspberry Pi to perform real-time image classification using images\nstreamed from the camera.\n\nAt the end of this page, there are extra steps to accelerate the example using\nthe Coral USB Accelerator to increase inference speed.\n\n\n## Set up your hardware\n\nBefore you begin, you need to [set up your Raspberry Pi](\nhttps://projects.raspberrypi.org/en/projects/raspberry-pi-setting-up) with\nRaspberry Pi OS (preferably updated to Buster).\n\nYou also need to [connect and configure the Pi Camera](\nhttps://www.raspberrypi.org/documentation/configuration/camera.md) if you use\nthe Pi Camera. This code also works with USB camera connect to the Raspberry Pi.\n\nAnd to see the results from the camera, you need a monitor connected\nto the Raspberry Pi. It's okay if you're using SSH to access the Pi shell\n(you don't need to use a keyboard connected to the Pi)—you only need a monitor\nattached to the Pi to see the camera stream.\n\n\n## Install the TensorFlow Lite runtime\n\nIn this project, all you need from the TensorFlow Lite API is the `Interpreter`\nclass. So instead of installing the large `tensorflow` package, we're using the\nmuch smaller `tflite_runtime` package.\n\nTo install this on your Raspberry Pi, follow the instructions in the\n[Python quickstart](https://www.tensorflow.org/lite/guide/python#install_tensorflow_lite_for_python).\n\nYou can install the TFLite runtime using this script.\n\n```\nsh setup.sh\n```\n\n## Download the example files\n\nFirst, clone this Git repo onto your Raspberry Pi like this:\n\n```\ngit clone https://github.com/tensorflow/examples --depth 1\n```\n\nThen use our script to install a couple Python packages, and\ndownload the TFLite model:\n\n```\ncd examples/lite/examples/image_classification/raspberry_pi\n\n# Run this script to install the required dependencies and download the TFLite models.\nsh setup.sh\n```\n\n## Run the example\n\n```\npython3 classify.py\n```\n*   You can optionally specify the `model` parameter to set the TensorFlow Lite\n    model to be used:\n    *   The default value is `efficientnet_lite0.tflite`\n    *   TensorFlow Lite image classification models **with metadatafrom**\n    (including models from [TensorFlow Hub](https://tfhub.dev/tensorflow/collections/lite/task-library/image-classifier/1)\n    or models trained with TensorFlow Lite Model Maker are supported.)\n*   You can optionally specify the `maxResults` parameter to limit the list of\n    classification results:\n    *   Supported value: A positive integer.\n    *   Default value: `3`.\n*   Example usage:\n\n```\npython3 classify.py \\\n  --model efficientnet_lite0.tflite \\\n  --maxResults 5\n```\n\n## Speed up the inferencing time (optional)\n\nIf you want to significantly speed up the inference time, you can attach an\nML accelerator such as the [Coral USB Accelerator](\nhttps://coral.withgoogle.com/products/accelerator)—a USB accessory that adds\nthe [Edge TPU ML accelerator](https://coral.withgoogle.com/docs/edgetpu/faq/)\nto any Linux-based system.\n\nIf you have a Coral USB Accelerator, you can run the sample with it enabled:\n\n1.  First, be sure you have completed the [USB Accelerator setup instructions](\n    https://coral.withgoogle.com/docs/accelerator/get-started/).\n\n2.  Run the image classification script using the Edge TPU TFLite model and\n    enable the Edge TPU option.\n\n```\npython3 classify.py \\\n  --model efficientnet_lite0_edgetpu.tflite \\\n  --enableEdgeTPU\n```\n\nYou should see significantly faster inference speeds.\n\nFor more information about creating and running TensorFlow Lite models with\nCoral devices, read [TensorFlow models on the Edge TPU](\nhttps://coral.withgoogle.com/docs/edgetpu/models-intro/).\n\nFor more information about executing inferences with TensorFlow Lite, read\n[TensorFlow Lite inference](https://www.tensorflow.org/lite/guide/inference).\n"
  },
  {
    "path": "lite/examples/image_classification/raspberry_pi/classify.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Main script to run image classification.\"\"\"\n\nimport argparse\nimport sys\nimport time\n\nimport cv2\nfrom tflite_support.task import core\nfrom tflite_support.task import processor\nfrom tflite_support.task import vision\n\n# Visualization parameters\n_ROW_SIZE = 20  # pixels\n_LEFT_MARGIN = 24  # pixels\n_TEXT_COLOR = (0, 0, 255)  # red\n_FONT_SIZE = 1\n_FONT_THICKNESS = 1\n_FPS_AVERAGE_FRAME_COUNT = 10\n\n\ndef run(model: str, max_results: int, score_threshold: float, num_threads: int,\n        enable_edgetpu: bool, camera_id: int, width: int, height: int) -> None:\n  \"\"\"Continuously run inference on images acquired from the camera.\n\n  Args:\n      model: Name of the TFLite image classification model.\n      max_results: Max of classification results.\n      score_threshold: The score threshold of classification results.\n      num_threads: Number of CPU threads to run the model.\n      enable_edgetpu: Whether to run the model on EdgeTPU.\n      camera_id: The camera id to be passed to OpenCV.\n      width: The width of the frame captured from the camera.\n      height: The height of the frame captured from the camera.\n  \"\"\"\n\n  # Initialize the image classification model\n  base_options = core.BaseOptions(\n      file_name=model, use_coral=enable_edgetpu, num_threads=num_threads)\n\n  # Enable Coral by this setting\n  classification_options = processor.ClassificationOptions(\n      max_results=max_results, score_threshold=score_threshold)\n  options = vision.ImageClassifierOptions(\n      base_options=base_options, classification_options=classification_options)\n\n  classifier = vision.ImageClassifier.create_from_options(options)\n\n  # Variables to calculate FPS\n  counter, fps = 0, 0\n  start_time = time.time()\n\n  # Start capturing video input from the camera\n  cap = cv2.VideoCapture(camera_id)\n  cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)\n  cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)\n\n  # Continuously capture images from the camera and run inference\n  while cap.isOpened():\n    success, image = cap.read()\n    if not success:\n      sys.exit(\n          'ERROR: Unable to read from webcam. Please verify your webcam settings.'\n      )\n\n    counter += 1\n    image = cv2.flip(image, 1)\n\n    # Convert the image from BGR to RGB as required by the TFLite model.\n    rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n\n    # Create TensorImage from the RGB image\n    tensor_image = vision.TensorImage.create_from_array(rgb_image)\n    # List classification results\n    categories = classifier.classify(tensor_image)\n\n    # Show classification results on the image\n    for idx, category in enumerate(categories.classifications[0].categories):\n      category_name = category.category_name\n      score = round(category.score, 2)\n      result_text = category_name + ' (' + str(score) + ')'\n      text_location = (_LEFT_MARGIN, (idx + 2) * _ROW_SIZE)\n      cv2.putText(image, result_text, text_location, cv2.FONT_HERSHEY_PLAIN,\n                  _FONT_SIZE, _TEXT_COLOR, _FONT_THICKNESS)\n\n    # Calculate the FPS\n    if counter % _FPS_AVERAGE_FRAME_COUNT == 0:\n      end_time = time.time()\n      fps = _FPS_AVERAGE_FRAME_COUNT / (end_time - start_time)\n      start_time = time.time()\n\n    # Show the FPS\n    fps_text = 'FPS = ' + str(int(fps))\n    text_location = (_LEFT_MARGIN, _ROW_SIZE)\n    cv2.putText(image, fps_text, text_location, cv2.FONT_HERSHEY_PLAIN,\n                _FONT_SIZE, _TEXT_COLOR, _FONT_THICKNESS)\n\n    # Stop the program if the ESC key is pressed.\n    if cv2.waitKey(1) == 27:\n      break\n    cv2.imshow('image_classification', image)\n\n  cap.release()\n  cv2.destroyAllWindows()\n\n\ndef main():\n  parser = argparse.ArgumentParser(\n      formatter_class=argparse.ArgumentDefaultsHelpFormatter)\n  parser.add_argument(\n      '--model',\n      help='Name of image classification model.',\n      required=False,\n      default='efficientnet_lite0.tflite')\n  parser.add_argument(\n      '--maxResults',\n      help='Max of classification results.',\n      required=False,\n      default=3)\n  parser.add_argument(\n      '--scoreThreshold',\n      help='The score threshold of classification results.',\n      required=False,\n      type=float,\n      default=0.0)\n  parser.add_argument(\n      '--numThreads',\n      help='Number of CPU threads to run the model.',\n      required=False,\n      default=4)\n  parser.add_argument(\n      '--enableEdgeTPU',\n      help='Whether to run the model on EdgeTPU.',\n      action='store_true',\n      required=False,\n      default=False)\n  parser.add_argument(\n      '--cameraId', help='Id of camera.', required=False, default=0)\n  parser.add_argument(\n      '--frameWidth',\n      help='Width of frame to capture from camera.',\n      required=False,\n      default=640)\n  parser.add_argument(\n      '--frameHeight',\n      help='Height of frame to capture from camera.',\n      required=False,\n      default=480)\n  args = parser.parse_args()\n\n  run(args.model, int(args.maxResults),\n      args.scoreThreshold, int(args.numThreads), bool(args.enableEdgeTPU),\n      int(args.cameraId), args.frameWidth, args.frameHeight)\n\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "lite/examples/image_classification/raspberry_pi/requirements.txt",
    "content": "argparse\nnumpy>=1.20.0  # To ensure compatibility with OpenCV on Raspberry Pi.\nopencv-python~=4.5.3.56\ntflite-support>=0.4.2\nprotobuf>=3.18.0,<4\n"
  },
  {
    "path": "lite/examples/image_classification/raspberry_pi/setup.sh",
    "content": "#!/bin/bash\n\nif [ $# -eq 0 ]; then\n  DATA_DIR=\"./\"\nelse\n  DATA_DIR=\"$1\"\nfi\n\n# Install Python dependencies.\npython3 -m pip install pip --upgrade\npython3 -m pip install -r requirements.txt\n\n# Download TF Lite model with metadata.\nFILE=${DATA_DIR}/efficientnet_lite0.tflite\nif [ ! -f \"$FILE\" ]; then\n  curl \\\n    -L 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_classification/rpi/lite-model_efficientnet_lite0_uint8_2.tflite' \\\n    -o ${FILE}\nfi\n\nFILE=${DATA_DIR}/efficientnet_lite0_edgetpu.tflite\nif [ ! -f \"$FILE\" ]; then\n  curl \\\n    -L 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_classification/rpi/efficientnet_lite0_edgetpu.tflite' \\\n    -o ${FILE}\nfi\necho -e \"Downloaded files are in ${DATA_DIR}\"\n"
  },
  {
    "path": "lite/examples/image_classification/raspberry_pi/test_data/ground_truth.csv",
    "content": "label,score\nred fox,0.77734375\nkit fox,0.10546875\ngrey fox,0.046875\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/README.md",
    "content": "# TensorFlow Lite Image Segmentation Demo\n\n### Overview\n\nThis is a camera app that continuously segments the objects in the frames\nseen by your device's back\ncamera.\n[Deeplab v3](https://tfhub.dev/tensorflow/lite-model/deeplabv3/1/metadata/2) is\na state-of-art deep\nlearning model for semantic image segmentation, where the goal is to assign\nsemantic labels (e.g.\nperson, dog, cat) to every pixel in the input image. These instructions\nwalk you through building and running the demo on an Android device.\n\nThe model files are downloaded via Gradle scripts when you build and run the\napp. You don't need todo any steps to download TFLite models into the\nproject explicitly.\n\nThis application should be run on a physical Android device.\n\n![App example showing UI controls.](screenshot1.jpg?raw=true \"Screenshot with controls\")\n\n![App example without UI controls.](screenshot2.jpg?raw=true \"Screenshot without controls\")\n\n## Build the demo using Android Studio\n\n### Prerequisites\n\n* The **[Android Studio](https://developer.android.com/studio/index.html)**\n  IDE (Android Studio 2021.2.1 or newer). This sample has been tested on\n  Android Studio Chipmunk.\n\n* A physical Android device with a minimum OS version of SDK 23 (Android 6.0 -\n  Marshmallow) with developer mode enabled. The process of enabling developer\n  mode may vary by device.\n\n### Building\n\n* Open Android Studio. From the Welcome screen, select Open an existing Android\n  Studio project.\n\n* From the Open File or Project window that appears, navigate to and select the\n  tensorflow-lite/examples/image_segmentation/android directory. Click OK.\n\n* If it asks you to do a Gradle Sync, click OK.\n\n* With your Android device connected to your computer and developer mode\n  enabled, click on the green Run arrow in Android Studio.\n\n### Models used\n\nDownloading, extraction, and placing the models into the assets folder is\nmanaged automatically by the download.gradle file.\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/build.gradle",
    "content": "plugins {\n    id 'com.android.application'\n    id 'org.jetbrains.kotlin.android'\n    id 'kotlin-kapt'\n    id 'androidx.navigation.safeargs'\n    id 'de.undercouch.download'\n}\n\nandroid {\n    compileSdk 32\n\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.imagesegmentation\"\n        minSdk 23\n        targetSdk 32\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n    kotlinOptions {\n        jvmTarget = '1.8'\n    }\n\n    buildFeatures {\n        viewBinding true\n    }\n    androidResources {\n        noCompress 'tflite'\n    }\n}\n\n// import DownloadModels task\nproject.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'\nproject.ext.TEST_ASSETS_DIR = projectDir.toString() + '/src/androidTest/assets'\n\n// Download default models; if you wish to use your own models then\n// place them in the \"assets\" directory and comment out this line.\napply from: 'download_models.gradle'\n\ndependencies {\n    // Kotlin lang\n    implementation 'androidx.core:core-ktx:1.15.0'\n\n    // App compat and UI things\n    implementation 'androidx.appcompat:appcompat:1.4.2'\n    implementation 'com.google.android.material:material:1.6.1'\n    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'\n\n    // Navigation library\n    def nav_version = \"2.4.2\"\n    implementation \"androidx.navigation:navigation-fragment-ktx:$nav_version\"\n    implementation \"androidx.navigation:navigation-ui-ktx:$nav_version\"\n\n    // CameraX core library\n    def camerax_version = '1.2.0-alpha02'\n    implementation \"androidx.camera:camera-core:$camerax_version\"\n\n    // CameraX Camera2 extensions\n    implementation \"androidx.camera:camera-camera2:$camerax_version\"\n\n    // CameraX Lifecycle library\n    implementation \"androidx.camera:camera-lifecycle:$camerax_version\"\n\n    // CameraX View class\n    implementation \"androidx.camera:camera-view:$camerax_version\"\n\n    //WindowManager\n    implementation 'androidx.window:window:1.1.0-alpha02'\n\n    // Unit testing\n    testImplementation 'junit:junit:4.13.2'\n\n    // Instrumented testing\n    androidTestImplementation 'androidx.test.ext:junit:1.1.3'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n    androidTestImplementation 'com.google.truth:truth:1.1.3'\n\n    implementation 'org.tensorflow:tensorflow-lite-task-vision:0.4.0'\n    // Import the GPU delegate plugin Library for GPU inference\n    implementation 'org.tensorflow:tensorflow-lite-gpu-delegate-plugin:0.4.0'\n    implementation 'org.tensorflow:tensorflow-lite-gpu:2.9.0'\n}\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/download_models.gradle",
    "content": "task downloadTestFile(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_segmentation/android/lite-model_deeplabv3_1_metadata_2.tflite'\n    dest project.ext.TEST_ASSETS_DIR + '/deeplabv3.tflite'\n    overwrite false\n}\n\ntask downloadModelFile(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_segmentation/android/lite-model_deeplabv3_1_metadata_2.tflite'\n    dest project.ext.ASSET_DIR + '/deeplabv3.tflite'\n    overwrite false\n}\n\npreBuild.dependsOn downloadModelFile, downloadTestFile\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/androidTest/java/org/tensorflow/lite/examples/imagesegmentation/ImageSegmentationHelperTest.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.imagesegmentation\n\nimport android.content.res.AssetManager\nimport android.graphics.Bitmap\nimport android.graphics.BitmapFactory\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.platform.app.InstrumentationRegistry\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.tensorflow.lite.task.vision.segmenter.Segmentation\nimport java.io.InputStream\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass ImageSegmentationHelperTest {\n    companion object {\n        private const val EXPECTED_MASK_TOLERANCE = 1e-2\n        private const val INPUT_IMAGE = \"input_image.jpg\"\n        private const val EXPECTED_IMAGE = \"expect_image.png\"\n        private val expectedLabelArray = arrayOf(\"background\", \"person\", \"horse\")\n    }\n\n    @Test\n    fun executeResultShouldNotChange() {\n        val imageSegmentationHelper = ImageSegmentationHelper(\n            context = InstrumentationRegistry.getInstrumentation().context,\n            imageSegmentationListener = object : ImageSegmentationHelper.SegmentationListener {\n                override fun onError(error: String) {\n                    // no op\n                }\n\n                override fun onResults(\n                    results: List<Segmentation>?,\n                    inferenceTime: Long,\n                    imageHeight: Int,\n                    imageWidth: Int\n                ) {\n                    assert(results != null && results.isNotEmpty())\n\n                    val colorLabels = results!![0].coloredLabels.mapIndexed { index, coloredLabel ->\n                        OverlayView.ColorLabel(\n                            index,\n                            coloredLabel.getlabel(),\n                            coloredLabel.argb\n                        )\n                    }\n\n                    // Create the mask bitmap with colors and the set of detected labels.\n                    val maskTensor = results[0].masks[0]\n                    val maskArray = maskTensor.buffer.array()\n                    val pixels = IntArray(maskArray.size)\n\n                    for (i in maskArray.indices) {\n                        // Set isExist flag to true if any pixel contains this color.\n                        val colorLabel = colorLabels[maskArray[i].toInt()].apply {\n                            isExist = true\n                        }\n                        val color = colorLabel.getColor()\n                        pixels[i] = color\n                    }\n\n                    val maskImage = Bitmap.createBitmap(\n                        pixels,\n                        maskTensor.width,\n                        maskTensor.height,\n                        Bitmap.Config.ARGB_8888\n                    )\n\n                    // Verify output mask bitmap.\n                    val expectedPixels = getPixels(loadImage(EXPECTED_IMAGE)!!)\n                    val resultPixels = getPixels(maskImage)\n\n                    assertThat(resultPixels.size).isEqualTo(expectedPixels.size)\n\n                    var inconsistentPixels = 0\n                    for (i in resultPixels.indices) {\n                        if (resultPixels[i] != expectedPixels[i]) {\n                            inconsistentPixels++\n                        }\n                    }\n\n                    assertThat(inconsistentPixels.toDouble() / resultPixels.size)\n                        .isLessThan(EXPECTED_MASK_TOLERANCE)\n\n                    // Verify labels.\n                    val resultLabels = HashSet<String>()\n                    colorLabels.filter { it.isExist }.forEach {\n                        resultLabels.add(it.label)\n                    }\n\n                    val expectedLabels = HashSet<String>()\n                    expectedLabelArray.forEach {\n                        expectedLabels.add(it)\n                    }\n\n                    assertThat(resultLabels).isEqualTo(expectedLabels)\n                }\n            })\n        loadImage(INPUT_IMAGE)?.let {\n            imageSegmentationHelper.segment(it, 0)\n        }\n    }\n\n    @Throws(Exception::class)\n    private fun loadImage(fileName: String): Bitmap? {\n        val assetManager: AssetManager =\n            InstrumentationRegistry.getInstrumentation().context.assets\n        val inputStream: InputStream = assetManager.open(fileName)\n        return BitmapFactory.decodeStream(inputStream)\n    }\n\n    private fun getPixels(bitmap: Bitmap): IntArray {\n        val width = bitmap.width\n        val height = bitmap.height\n        val pixels = IntArray(width * height)\n        bitmap.getPixels(pixels, 0, width, 0, 0, width, height)\n        return pixels\n    }\n}\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:dist=\"http://schemas.android.com/apk/distribution\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"org.tensorflow.lite.examples.imagesegmentation\">\n    <!-- Enable instant app support -->\n    <dist:module dist:instant=\"true\" />\n\n    <!-- Declare features -->\n    <uses-feature android:name=\"android.hardware.camera\" />\n\n    <!-- Declare permissions -->\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:taskAffinity=\"\">\n        <activity\n            android:name=\".MainActivity\"\n            android:clearTaskOnLaunch=\"true\"\n            android:configChanges=\"orientation|screenLayout|screenSize|smallestScreenSize\"\n            android:exported=\"true\"\n            android:resizeableActivity=\"true\"\n            android:rotationAnimation=\"seamless\"\n            android:theme=\"@style/AppTheme\"\n            tools:targetApi=\"O\">\n\n            <!-- Main app intent filter -->\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n\n            <!-- Declare notch support -->\n            <meta-data\n                android:name=\"android.notch_support\"\n                android:value=\"true\" />\n        </activity>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/java/org/tensorflow/lite/examples/imagesegmentation/ImageSegmentationHelper.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.imagesegmentation\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.os.Build\nimport android.os.SystemClock\nimport android.util.Log\nimport androidx.annotation.RequiresApi\nimport androidx.core.graphics.get\nimport org.tensorflow.lite.gpu.CompatibilityList\nimport org.tensorflow.lite.support.image.ImageProcessor\nimport org.tensorflow.lite.support.image.TensorImage\nimport org.tensorflow.lite.support.image.ops.Rot90Op\nimport org.tensorflow.lite.task.core.BaseOptions\nimport org.tensorflow.lite.task.vision.segmenter.ImageSegmenter\nimport org.tensorflow.lite.task.vision.segmenter.OutputType\nimport org.tensorflow.lite.task.vision.segmenter.Segmentation\nimport java.lang.Exception\nimport java.util.*\n\n/**\n * Class responsible to run the Image Segmentation model. more information about the DeepLab model\n * being used can be found here:\n * https://ai.googleblog.com/2018/03/semantic-image-segmentation-with.html\n * https://github.com/tensorflow/models/tree/master/research/deeplab\n *\n * Label names: 'background', 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat',\n * 'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person', 'pottedplant', 'sheep',\n * 'sofa', 'train', 'tv'\n */\nclass ImageSegmentationHelper(\n    var numThreads: Int = 2,\n    var currentDelegate: Int = 0,\n    val context: Context,\n    val imageSegmentationListener: SegmentationListener?\n) {\n    private var imageSegmenter: ImageSegmenter? = null\n\n    init {\n        setupImageSegmenter()\n    }\n\n    fun clearImageSegmenter() {\n        imageSegmenter = null\n    }\n\n    private fun setupImageSegmenter() {\n        // Create the base options for the segment\n        val optionsBuilder =\n            ImageSegmenter.ImageSegmenterOptions.builder()\n\n        // Set general segmentation options, including number of used threads\n        val baseOptionsBuilder = BaseOptions.builder().setNumThreads(numThreads)\n\n        // Use the specified hardware for running the model. Default to CPU\n        when (currentDelegate) {\n            DELEGATE_CPU -> {\n                // Default\n            }\n            DELEGATE_GPU -> {\n                if (CompatibilityList().isDelegateSupportedOnThisDevice) {\n                    baseOptionsBuilder.useGpu()\n                } else {\n                    imageSegmentationListener?.onError(\"GPU is not supported on this device\")\n                }\n            }\n            DELEGATE_NNAPI -> {\n                baseOptionsBuilder.useNnapi()\n            }\n        }\n\n        optionsBuilder.setBaseOptions(baseOptionsBuilder.build())\n\n        /*\n        CATEGORY_MASK is being specifically used to predict the available objects\n        based on individual pixels in this sample. The other option available for\n        OutputType, CONFIDENCE_MAP, provides a gray scale mapping of the image\n        where each pixel has a confidence score applied to it from 0.0f to 1.0f\n         */\n        optionsBuilder.setOutputType(OutputType.CATEGORY_MASK)\n        try {\n            imageSegmenter =\n                ImageSegmenter.createFromFileAndOptions(\n                    context,\n                    MODEL_DEEPLABV3,\n                    optionsBuilder.build()\n                )\n        } catch (e: IllegalStateException) {\n            imageSegmentationListener?.onError(\n                \"Image segmentation failed to initialize. See error logs for details\"\n            )\n            Log.e(TAG, \"TFLite failed to load model with error: \" + e.message)\n        }\n    }\n\n    @RequiresApi(Build.VERSION_CODES.Q)\n    fun segment(image: Bitmap, imageRotation: Int) {\n\n        if (imageSegmenter == null) {\n            setupImageSegmenter()\n        }\n\n        // Inference time is the difference between the system time at the start and finish of the\n        // process\n        var inferenceTime = SystemClock.uptimeMillis()\n\n        // Create preprocessor for the image.\n        // See https://www.tensorflow.org/lite/inference_with_metadata/\n        //            lite_support#imageprocessor_architecture\n        val imageProcessor =\n            ImageProcessor.Builder()\n                .add(Rot90Op(-imageRotation / 90))\n                .build()\n\n        // Preprocess the image and convert it into a TensorImage for segmentation.\n        val tensorImage = imageProcessor.process(TensorImage.fromBitmap(image))\n\n        val segmentResult = imageSegmenter?.segment(tensorImage)\n        inferenceTime = SystemClock.uptimeMillis() - inferenceTime\n\n        imageSegmentationListener?.onResults(\n            segmentResult,\n            inferenceTime,\n            tensorImage.height,\n            tensorImage.width\n        )\n    }\n\n    interface SegmentationListener {\n        fun onError(error: String)\n        fun onResults(\n            results: List<Segmentation>?,\n            inferenceTime: Long,\n            imageHeight: Int,\n            imageWidth: Int\n        )\n    }\n\n    companion object {\n        const val DELEGATE_CPU = 0\n        const val DELEGATE_GPU = 1\n        const val DELEGATE_NNAPI = 2\n        const val MODEL_DEEPLABV3 = \"deeplabv3.tflite\"\n\n        private const val TAG = \"Image Segmentation Helper\"\n    }\n}\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/java/org/tensorflow/lite/examples/imagesegmentation/MainActivity.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.imagesegmentation\n\nimport android.os.Build\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport org.tensorflow.lite.examples.imagesegmentation.databinding.ActivityMainBinding\n\n\nclass MainActivity : AppCompatActivity() {\n\n    private lateinit var activityMainBinding: ActivityMainBinding\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)\n        setContentView(activityMainBinding.root)\n    }\n\n    override fun onBackPressed() {\n        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {\n            // Workaround for Android Q memory leak issue in IRequestFinishCallback$Stub.\n            // (https://issuetracker.google.com/issues/139738913)\n            finishAfterTransition()\n        } else {\n            super.onBackPressed()\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/java/org/tensorflow/lite/examples/imagesegmentation/OverlayView.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.imagesegmentation\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.graphics.Canvas\nimport android.graphics.Color\nimport android.util.AttributeSet\nimport android.view.View\nimport org.tensorflow.lite.task.vision.segmenter.ColoredLabel\nimport org.tensorflow.lite.task.vision.segmenter.Segmentation\nimport kotlin.math.max\n\nclass OverlayView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {\n    companion object {\n        private const val ALPHA_COLOR = 128\n    }\n\n    private var scaleBitmap: Bitmap? = null\n    private var listener: OverlayViewListener? = null\n\n    fun setOnOverlayViewListener(listener: OverlayViewListener) {\n        this.listener = listener\n    }\n\n    fun clear() {\n        scaleBitmap = null\n        invalidate()\n    }\n\n    override fun draw(canvas: Canvas) {\n        super.draw(canvas)\n        scaleBitmap?.let {\n            canvas.drawBitmap(it, 0f, 0f, null)\n        }\n    }\n\n    fun setResults(\n        segmentResult: List<Segmentation>?,\n        imageHeight: Int,\n        imageWidth: Int,\n    ) {\n        if (segmentResult != null && segmentResult.isNotEmpty()) {\n            val colorLabels = segmentResult[0].coloredLabels.mapIndexed { index, coloredLabel ->\n                ColorLabel(\n                    index,\n                    coloredLabel.getlabel(),\n                    coloredLabel.argb\n                )\n            }\n\n            // Create the mask bitmap with colors and the set of detected labels.\n            // We only need the first mask for this sample because we are using\n            // the OutputType CATEGORY_MASK, which only provides a single mask.\n            val maskTensor = segmentResult[0].masks[0]\n            val maskArray = maskTensor.buffer.array()\n            val pixels = IntArray(maskArray.size)\n\n            for (i in maskArray.indices) {\n                // Set isExist flag to true if any pixel contains this color.\n                val colorLabel = colorLabels[maskArray[i].toInt()].apply {\n                    isExist = true\n                }\n                val color = colorLabel.getColor()\n                pixels[i] = color\n            }\n\n            val image = Bitmap.createBitmap(\n                pixels,\n                maskTensor.width,\n                maskTensor.height,\n                Bitmap.Config.ARGB_8888\n            )\n\n            // PreviewView is in FILL_START mode. So we need to scale up the bounding\n            // box to match with the size that the captured images will be displayed.\n            val scaleFactor = max(width * 1f / imageWidth, height * 1f / imageHeight)\n            val scaleWidth = (imageWidth * scaleFactor).toInt()\n            val scaleHeight = (imageHeight * scaleFactor).toInt()\n\n            scaleBitmap = Bitmap.createScaledBitmap(image, scaleWidth, scaleHeight, false)\n            listener?.onLabels(colorLabels.filter { it.isExist })\n        }\n    }\n\n    interface OverlayViewListener {\n        fun onLabels(colorLabels: List<ColorLabel>)\n    }\n\n    data class ColorLabel(\n        val id: Int,\n        val label: String,\n        val rgbColor: Int,\n        var isExist: Boolean = false\n    ) {\n\n        fun getColor(): Int {\n            // Use completely transparent for the background color.\n            return if (id == 0) Color.TRANSPARENT else Color.argb(\n                ALPHA_COLOR,\n                Color.red(rgbColor),\n                Color.green(rgbColor),\n                Color.blue(rgbColor)\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/java/org/tensorflow/lite/examples/imagesegmentation/fragments/CameraFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.imagesegmentation.fragments\n\nimport android.annotation.SuppressLint\nimport android.content.res.Configuration\nimport android.graphics.Bitmap\nimport android.os.Bundle\nimport android.util.Log\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.AdapterView\nimport android.widget.Toast\nimport androidx.camera.core.Preview\nimport androidx.camera.core.ImageAnalysis\nimport androidx.camera.core.Camera\nimport androidx.camera.core.CameraSelector\nimport androidx.camera.core.AspectRatio\nimport androidx.camera.core.ImageProxy\nimport androidx.camera.lifecycle.ProcessCameraProvider\nimport androidx.core.content.ContextCompat\nimport androidx.fragment.app.Fragment\nimport androidx.navigation.Navigation\nimport androidx.recyclerview.widget.GridLayoutManager\nimport org.tensorflow.lite.examples.imagesegmentation.ImageSegmentationHelper\nimport org.tensorflow.lite.examples.imagesegmentation.ImageSegmentationHelper.SegmentationListener\nimport org.tensorflow.lite.examples.imagesegmentation.OverlayView\nimport org.tensorflow.lite.examples.imagesegmentation.OverlayView.ColorLabel\nimport org.tensorflow.lite.examples.imagesegmentation.R\nimport org.tensorflow.lite.examples.imagesegmentation.databinding.FragmentCameraBinding\nimport org.tensorflow.lite.task.vision.segmenter.Segmentation\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Executors\n\nclass CameraFragment : Fragment(), SegmentationListener {\n\n    companion object {\n        private const val TAG = \"Image Segmentation\"\n\n    }\n\n    private var _fragmentCameraBinding: FragmentCameraBinding? = null\n\n    private val fragmentCameraBinding\n        get() = _fragmentCameraBinding!!\n\n    private lateinit var imageSegmentationHelper: ImageSegmentationHelper\n    private lateinit var bitmapBuffer: Bitmap\n    private var preview: Preview? = null\n    private var imageAnalyzer: ImageAnalysis? = null\n    private var camera: Camera? = null\n    private var cameraProvider: ProcessCameraProvider? = null\n    private val labelsAdapter: ColorLabelsAdapter by lazy { ColorLabelsAdapter() }\n\n    /** Blocking camera operations are performed using this executor */\n    private lateinit var cameraExecutor: ExecutorService\n\n    override fun onResume() {\n        super.onResume()\n        // Make sure that all permissions are still present, since the\n        // user could have removed them while the app was in paused state.\n        if (!PermissionsFragment.hasPermissions(requireContext())) {\n            Navigation.findNavController(requireActivity(), R.id.fragment_container)\n                .navigate(CameraFragmentDirections.actionCameraToPermissions())\n        }\n    }\n\n    override fun onDestroyView() {\n        _fragmentCameraBinding = null\n        super.onDestroyView()\n\n        // Shut down our background executor\n        cameraExecutor.shutdown()\n    }\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View {\n        _fragmentCameraBinding = FragmentCameraBinding.inflate(inflater, container, false)\n\n        return fragmentCameraBinding.root\n    }\n\n    @SuppressLint(\"MissingPermission\")\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n\n        imageSegmentationHelper = ImageSegmentationHelper(\n            context = requireContext(),\n            imageSegmentationListener = this\n        )\n\n        // Initialize our background executor\n        cameraExecutor = Executors.newSingleThreadExecutor()\n\n        // Wait for the views to be properly laid out\n        fragmentCameraBinding.viewFinder.post {\n            // Set up the camera and its use cases\n            setUpCamera()\n        }\n\n        // Attach listeners to UI control widgets\n        initBottomSheetControls()\n\n        with(fragmentCameraBinding.recyclerviewResults) {\n            adapter = labelsAdapter\n            layoutManager = GridLayoutManager(requireContext(), 3)\n        }\n\n        fragmentCameraBinding.overlay.setOnOverlayViewListener(object :\n            OverlayView.OverlayViewListener {\n            override fun onLabels(colorLabels: List<ColorLabel>) {\n                // update label at here\n                labelsAdapter.updateResultLabels(colorLabels)\n            }\n        })\n    }\n\n    private fun initBottomSheetControls() {\n        // When clicked, decrease the number of threads used for segmentation\n        fragmentCameraBinding.bottomSheetLayout.threadsMinus.setOnClickListener {\n            if (imageSegmentationHelper.numThreads > 1) {\n                imageSegmentationHelper.numThreads--\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, increase the number of threads used for segmentation\n        fragmentCameraBinding.bottomSheetLayout.threadsPlus.setOnClickListener {\n            if (imageSegmentationHelper.numThreads < 4) {\n                imageSegmentationHelper.numThreads++\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, change the underlying hardware used for inference. Current options are CPU\n        // GPU, and NNAPI\n        fragmentCameraBinding.bottomSheetLayout.spinnerDelegate.setSelection(0, false)\n        fragmentCameraBinding.bottomSheetLayout.spinnerDelegate.onItemSelectedListener =\n            object : AdapterView.OnItemSelectedListener {\n                override fun onItemSelected(\n                    parent: AdapterView<*>?,\n                    view: View?,\n                    position: Int,\n                    id: Long\n                ) {\n                    imageSegmentationHelper.currentDelegate = position\n                    updateControlsUi()\n                }\n\n                override fun onNothingSelected(parent: AdapterView<*>?) {\n                    /* no op */\n                }\n            }\n    }\n\n    // Update the values displayed in the bottom sheet. Reset segmenter.\n    private fun updateControlsUi() {\n        fragmentCameraBinding.bottomSheetLayout.threadsValue.text =\n            imageSegmentationHelper.numThreads.toString()\n\n        // Needs to be cleared instead of reinitialized because the GPU\n        // delegate needs to be initialized on the thread using it when applicable\n        imageSegmentationHelper.clearImageSegmenter()\n        fragmentCameraBinding.overlay.clear()\n    }\n\n    // Initialize CameraX, and prepare to bind the camera use cases\n    private fun setUpCamera() {\n        val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())\n        cameraProviderFuture.addListener(\n            {\n                // CameraProvider\n                cameraProvider = cameraProviderFuture.get()\n\n                // Build and bind the camera use cases\n                bindCameraUseCases()\n            },\n            ContextCompat.getMainExecutor(requireContext())\n        )\n    }\n\n    // Declare and bind preview, capture and analysis use cases\n    @SuppressLint(\"UnsafeOptInUsageError\")\n    private fun bindCameraUseCases() {\n\n        // CameraProvider\n        val cameraProvider =\n            cameraProvider ?: throw IllegalStateException(\"Camera initialization failed.\")\n\n        // CameraSelector - makes assumption that we're only using the back camera\n        val cameraSelector =\n            CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()\n\n        // Preview. Only using the 4:3 ratio because this is the closest to our models\n        preview =\n            Preview.Builder()\n                .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n                .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)\n                .build()\n\n        // ImageAnalysis. Using RGBA 8888 to match how our models work\n        imageAnalyzer =\n            ImageAnalysis.Builder()\n                .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n                .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)\n                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)\n                .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)\n                .build()\n                // The analyzer can then be assigned to the instance\n                .also {\n                    it.setAnalyzer(cameraExecutor) { image ->\n                        if (!::bitmapBuffer.isInitialized) {\n                            // The image rotation and RGB image buffer are initialized only once\n                            // the analyzer has started running\n                            bitmapBuffer = Bitmap.createBitmap(\n                                image.width,\n                                image.height,\n                                Bitmap.Config.ARGB_8888\n                            )\n                        }\n\n                        segmentImage(image)\n                    }\n                }\n\n        // Must unbind the use-cases before rebinding them\n        cameraProvider.unbindAll()\n\n        try {\n            // A variable number of use-cases can be passed here -\n            // camera provides access to CameraControl & CameraInfo\n            camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalyzer)\n\n            // Attach the viewfinder's surface provider to preview use case\n            preview?.setSurfaceProvider(fragmentCameraBinding.viewFinder.surfaceProvider)\n        } catch (exc: Exception) {\n            Log.e(TAG, \"Use case binding failed\", exc)\n        }\n    }\n\n    private fun segmentImage(image: ImageProxy) {\n        // Copy out RGB bits to the shared bitmap buffer\n        image.use { bitmapBuffer.copyPixelsFromBuffer(image.planes[0].buffer) }\n\n        val imageRotation = image.imageInfo.rotationDegrees\n        // Pass Bitmap and rotation to the image segmentation helper for processing and segmentation\n        imageSegmentationHelper.segment(bitmapBuffer, imageRotation)\n    }\n\n    override fun onConfigurationChanged(newConfig: Configuration) {\n        super.onConfigurationChanged(newConfig)\n        imageAnalyzer?.targetRotation = fragmentCameraBinding.viewFinder.display.rotation\n    }\n\n    // Update UI after objects have been segment. Extracts original image height/width\n    // to scale and place bounding boxes properly through OverlayView\n    override fun onResults(\n        results: List<Segmentation>?,\n        inferenceTime: Long,\n        imageHeight: Int,\n        imageWidth: Int\n    ) {\n        activity?.runOnUiThread {\n            fragmentCameraBinding.bottomSheetLayout.inferenceTimeVal.text =\n                String.format(\"%d ms\", inferenceTime)\n\n            // Pass necessary information to OverlayView for drawing on the canvas\n            fragmentCameraBinding.overlay.setResults(\n                results,\n                imageHeight,\n                imageWidth\n            )\n\n            // Force a redraw\n            fragmentCameraBinding.overlay.invalidate()\n        }\n    }\n\n    override fun onError(error: String) {\n        activity?.runOnUiThread {\n            Toast.makeText(requireContext(), error, Toast.LENGTH_SHORT).show()\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/java/org/tensorflow/lite/examples/imagesegmentation/fragments/ColorLabelsAdapter.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.imagesegmentation.fragments\n\nimport android.annotation.SuppressLint\nimport android.graphics.drawable.GradientDrawable\nimport android.view.LayoutInflater\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport org.tensorflow.lite.examples.imagesegmentation.OverlayView.ColorLabel\nimport org.tensorflow.lite.examples.imagesegmentation.databinding.ItemColorLabelsBinding\n\nclass ColorLabelsAdapter : RecyclerView.Adapter<ColorLabelsAdapter.ViewHolder>() {\n    private var coloredLabels: List<ColorLabel> = emptyList()\n\n    @SuppressLint(\"NotifyDataSetChanged\")\n    fun updateResultLabels(coloredLabels: List<ColorLabel>) {\n        this.coloredLabels = coloredLabels\n        notifyDataSetChanged()\n    }\n\n    inner class ViewHolder(private val binding: ItemColorLabelsBinding) :\n        RecyclerView.ViewHolder(binding.root) {\n        fun bind(label: String, rgbColor: Int) {\n            with(binding) {\n                tvLabel.text = label\n                val drawable = flBackgroundLabel.background.mutate() as GradientDrawable\n                drawable.setColor(rgbColor)\n                drawable.invalidateSelf()\n            }\n        }\n    }\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {\n        val binding =\n            ItemColorLabelsBinding.inflate(LayoutInflater.from(parent.context), parent, false)\n        return ViewHolder(binding)\n    }\n\n    override fun onBindViewHolder(holder: ViewHolder, position: Int) {\n        coloredLabels[position].let {\n            holder.bind(it.label, it.getColor())\n        }\n    }\n\n    override fun getItemCount(): Int = coloredLabels.size\n}\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/java/org/tensorflow/lite/examples/imagesegmentation/fragments/PermissionsFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.imagesegmentation.fragments\n\nimport android.Manifest\nimport android.content.Context\nimport android.content.pm.PackageManager\nimport android.os.Bundle\nimport android.widget.Toast\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.core.content.ContextCompat\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.lifecycleScope\nimport androidx.navigation.Navigation\nimport org.tensorflow.lite.examples.imagesegmentation.R\n\nprivate val PERMISSIONS_REQUIRED = arrayOf(Manifest.permission.CAMERA)\n\n/**\n * The sole purpose of this fragment is to request permissions and, once granted, display the\n * camera fragment to the user.\n */\nclass PermissionsFragment : Fragment() {\n\n    private val requestPermissionLauncher =\n        registerForActivityResult(\n            ActivityResultContracts.RequestPermission()\n        ) { isGranted: Boolean ->\n            if (isGranted) {\n                Toast.makeText(context, \"Permission request granted\", Toast.LENGTH_LONG).show()\n                navigateToCamera()\n            } else {\n                Toast.makeText(context, \"Permission request denied\", Toast.LENGTH_LONG).show()\n            }\n        }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        when {\n            ContextCompat.checkSelfPermission(\n                requireContext(),\n                Manifest.permission.CAMERA\n            ) == PackageManager.PERMISSION_GRANTED -> {\n                navigateToCamera()\n            }\n            else -> {\n                requestPermissionLauncher.launch(\n                    Manifest.permission.CAMERA)\n            }\n        }\n    }\n\n    private fun navigateToCamera() {\n        lifecycleScope.launchWhenStarted {\n            Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(\n                PermissionsFragmentDirections.actionPermissionsToCamera())\n        }\n    }\n\n    companion object {\n\n        /** Convenience method used to check if all permissions required by this app are granted */\n        fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all {\n            ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/res/color/selector_ic.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"true\" android:color=\"@color/icPressed\" />\n    <item android:state_focused=\"true\" android:color=\"@color/icFocused\" />\n    <item android:color=\"@color/icActive\" />\n</selector>\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/res/drawable/bg_color_labels.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <corners android:radius=\"@dimen/item_color_labels_radius\" />\n</shape>\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/res/drawable/ic_minus.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13H5v-2h14v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/res/drawable/ic_plus.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path android:pathData=\"M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"85.84757\"\n                android:endY=\"92.4963\"\n                android:startX=\"42.9492\"\n                android:startY=\"49.59793\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:background=\"@android:color/transparent\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <androidx.fragment.app.FragmentContainerView\n            android:id=\"@+id/fragment_container\"\n            android:name=\"androidx.navigation.fragment.NavHostFragment\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:background=\"@android:color/transparent\"\n            android:keepScreenOn=\"true\"\n            app:defaultNavHost=\"true\"\n            app:navGraph=\"@navigation/nav_graph\"\n            android:layout_marginTop=\"?android:attr/actionBarSize\"\n            tools:context=\".MainActivity\"/>\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:layout_alignParentTop=\"true\"\n            android:background=\"@color/toolbar_background\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/tfl_logo\" />\n        </androidx.appcompat.widget.Toolbar>\n\n    </RelativeLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/res/layout/fragment_camera.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/camera_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.camera.view.PreviewView\n        android:id=\"@+id/view_finder\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:scaleType=\"fillStart\" />\n\n    <org.tensorflow.lite.examples.imagesegmentation.OverlayView\n        android:id=\"@+id/overlay\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n    <include\n        android:id=\"@+id/bottom_sheet_layout\"\n        layout=\"@layout/info_bottom_sheet\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/recyclerview_results\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"top\"\n        android:layout_marginBottom=\"@dimen/bottom_sheet_peek_height\"\n        android:background=\"@color/bottom_sheet_background\"\n        android:clipToPadding=\"true\"\n        android:padding=\"@dimen/bottom_sheet_padding\"\n        app:layout_anchor=\"@id/bottom_sheet_layout\"\n        app:layout_anchorGravity=\"top\"\n        app:layout_behavior=\"com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior\" />\n\n    <View\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"1dp\"\n        android:background=\"@color/bounding_box_color\"\n        app:layout_anchor=\"@id/recyclerview_results\"\n        app:layout_anchorGravity=\"bottom\" />\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/res/layout/info_bottom_sheet.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.core.widget.NestedScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/bottom_sheet_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_gravity=\"center_horizontal\"\n    android:background=\"@color/bottom_sheet_background\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:padding=\"@dimen/bottom_sheet_padding\"\n    app:behavior_hideable=\"false\"\n    app:behavior_peekHeight=\"@dimen/bottom_sheet_peek_height\"\n    app:layout_behavior=\"com.google.android.material.bottomsheet.BottomSheetBehavior\">\n\n    <androidx.appcompat.widget.LinearLayoutCompat\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <!-- Chevron indicating that the bottom sheet is expandable -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerHorizontal=\"true\"\n                android:layout_centerVertical=\"true\"\n                android:contentDescription=\"@string/alt_bottom_sheet_chevron\"\n                android:src=\"@drawable/icn_chevron_up\" />\n        </RelativeLayout>\n\n        <!-- Inference time row -->\n        <androidx.appcompat.widget.LinearLayoutCompat\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/inference_time_label\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:text=\"@string/label_interence_time\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <TextView\n                android:id=\"@+id/inference_time_val\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:gravity=\"end\"\n                android:text=\"@string/tv_default_inference_time\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n        </androidx.appcompat.widget.LinearLayoutCompat>\n\n        <!-- Number of threads adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:text=\"@string/label_threads\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\">\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threads_minus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_thread_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/threads_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:gravity=\"center\"\n                    android:minEms=\"@integer/bottom_sheet_control_text_min_ems\"\n                    android:text=\"@string/tv_default_threads_value\"\n                    android:textColor=\"@color/bottom_sheet_text_color\"\n                    android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threads_plus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_thread_button_plus\"\n                    android:src=\"@drawable/ic_plus\" />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- Delegate selection row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/label_delegate\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n            <androidx.appcompat.widget.AppCompatSpinner\n                android:id=\"@+id/spinner_delegate\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:entries=\"@array/delegate_spinner_titles\"\n                android:minWidth=\"@dimen/bottom_sheet_spinner_delegate_min_width\"\n                android:spinnerMode=\"dropdown\"\n                android:theme=\"@style/BottomSheetSpinnerItemStyle\" />\n\n        </RelativeLayout>\n    </androidx.appcompat.widget.LinearLayoutCompat>\n</androidx.core.widget.NestedScrollView>\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/res/layout/item_color_labels.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/flBackgroundLabel\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_margin=\"@dimen/item_color_labels_margin\"\n    android:background=\"@drawable/bg_color_labels\"\n    android:padding=\"@dimen/item_color_labels_padding\">\n\n    <TextView\n        android:id=\"@+id/tvLabel\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\" />\n</FrameLayout>\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/res/navigation/nav_graph.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/nav_graph\"\n    app:startDestination=\"@id/permissions_fragment\">\n\n    <fragment\n        android:id=\"@+id/permissions_fragment\"\n        android:name=\"org.tensorflow.lite.examples.imagesegmentation.fragments.PermissionsFragment\"\n        android:label=\"PermissionsFragment\">\n\n        <action\n            android:id=\"@+id/action_permissions_to_camera\"\n            app:destination=\"@id/camera_fragment\"\n            app:popUpTo=\"@id/permissions_fragment\"\n            app:popUpToInclusive=\"true\" />\n\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/camera_fragment\"\n        android:name=\"org.tensorflow.lite.examples.imagesegmentation.fragments.CameraFragment\"\n        android:label=\"CameraFragment\">\n\n\n        <action\n            android:id=\"@+id/action_camera_to_permissions\"\n            app:destination=\"@id/permissions_fragment\"\n            app:popUpTo=\"@id/camera_fragment\"\n            app:popUpToInclusive=\"true\" />\n\n    </fragment>\n</navigation>\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n\n<resources>\n    <!-- TF Branding Orange -->\n    <color name=\"bounding_box_color\">#FF6F00</color>\n    <color name=\"toolbar_background\">#EEEEEE</color>\n    <color name=\"ic_launcher_background\">#FFFFFF</color>\n    <color name=\"bottom_sheet_background\">#EEEEEE</color>\n    <color name=\"bottom_sheet_text_color\">@android:color/black</color>\n    <color name=\"icActive\">#FFFFFFFF</color>\n    <color name=\"icFocused\">#DDFFFFFF</color>\n    <color name=\"icPressed\">#AAFFFFFF</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n\n    <dimen name=\"stroke_small\">4dp</dimen>\n    <dimen name=\"round_button_medium\">64dp</dimen>\n\n    <!-- Bottom Sheet -->\n    <dimen name=\"bottom_sheet_text_size\">20sp</dimen>\n    <dimen name=\"bottom_sheet_padding\">16dp</dimen>\n    <dimen name=\"bottom_sheet_peek_height\">50dp</dimen>\n    <dimen name=\"bottom_sheet_default_row_margin\">16dp</dimen>\n    <dimen name=\"bottom_sheet_control_btn_size\">48dp</dimen>\n    <dimen name=\"bottom_sheet_control_text_side_margin\">10dp</dimen>\n    <dimen name=\"bottom_sheet_spinner_delegate_min_width\">160dp</dimen>\n    <dimen name=\"bottom_sheet_spinner_model_min_width\">240dp</dimen>\n    <integer name=\"bottom_sheet_control_text_min_ems\">3</integer>\n\n    <!-- Item color labels -->\n    <dimen name=\"item_color_labels_radius\">10dp</dimen>\n    <dimen name=\"item_color_labels_margin\">5dp</dimen>\n    <dimen name=\"item_color_labels_padding\">5dp</dimen>\n</resources>\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n        ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n        ~\n        ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n        ~ you may not use this file except in compliance with the License.\n        ~ You may obtain a copy of the License at\n        ~\n        ~       http://www.apache.org/licenses/LICENSE-2.0\n        ~\n        ~ Unless required by applicable law or agreed to in writing, software\n        ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n        ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n        ~ See the License for the specific language governing permissions and\n        ~ limitations under the License.\n    -->\n<resources>\n    <string name=\"app_name\">TFLite Image Segmentation Demo</string>\n\n    <string name=\"alt_bottom_sheet_chevron\">Bottom sheet expandable indicator</string>\n\n    <string name=\"alt_bottom_sheet_thread_button_minus\">Decrease the number of threads used</string>\n    <string name=\"alt_bottom_sheet_thread_button_plus\">Increase the number of threads used</string>\n\n    <string name=\"label_interence_time\">Inference Time</string>\n    <string name=\"label_fps\">Frames per Second</string>\n    <string name=\"label_confidence_threshold\">Threshold</string>\n    <string name=\"label_max_results\">Max Results</string>\n    <string name=\"label_threads\">Number of Threads</string>\n    <string name=\"label_delegate\">Delegate</string>\n    <string name=\"label_models\">ML Model</string>\n    <string name=\"tv_default_inference_time\">0ms</string>\n    <string name=\"tv_default_threads_value\">2</string>\n\n    <string-array name=\"delegate_spinner_titles\">\n        <item>CPU</item>\n        <item>GPU</item>\n        <item>NNAPI</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n    </style>\n\n    <style name=\"BottomSheetSpinnerItemStyle\" parent=\"Widget.AppCompat.Light.DropDownItem.Spinner\">\n        <item name=\"android:textSize\">@dimen/bottom_sheet_text_size</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n    dependencies {\n        classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.5.0'\n        classpath 'de.undercouch:gradle-download-task:4.1.2'\n    }\n}\nplugins {\n    id 'com.android.application' version '7.2.0' apply false\n    id 'com.android.library' version '7.2.0' apply false\n    id 'org.jetbrains.kotlin.android' version '1.6.21' apply false\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx1536m\nandroid.enableJetifier=true\nandroid.useAndroidX=true"
  },
  {
    "path": "lite/examples/image_segmentation/android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/image_segmentation/android/settings.gradle",
    "content": "pluginManagement {\n    repositories {\n        gradlePluginPortal()\n        google()\n        mavenCentral()\n    }\n}\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\nrootProject.name = \"TFLite Image Segmentation Demo App\"\ninclude ':app'\n"
  },
  {
    "path": "lite/examples/image_segmentation/ios/ImageSegmentation/AppDelegate.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n  var window: UIWindow?\n\n  func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n  ) -> Bool {\n    return true\n  }\n\n}\n"
  },
  {
    "path": "lite/examples/image_segmentation/ios/ImageSegmentation/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\"images\":[{\"size\":\"60x60\",\"expected-size\":\"180\",\"filename\":\"180.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"40x40\",\"expected-size\":\"80\",\"filename\":\"80.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"40x40\",\"expected-size\":\"120\",\"filename\":\"120.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"60x60\",\"expected-size\":\"120\",\"filename\":\"120.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"57x57\",\"expected-size\":\"57\",\"filename\":\"57.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"58\",\"filename\":\"58.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"29x29\",\"expected-size\":\"29\",\"filename\":\"29.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"87\",\"filename\":\"87.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"57x57\",\"expected-size\":\"114\",\"filename\":\"114.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"60\",\"filename\":\"60.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"1024x1024\",\"filename\":\"1024.png\",\"expected-size\":\"1024\",\"idiom\":\"ios-marketing\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"scale\":\"1x\"},{\"size\":\"40x40\",\"expected-size\":\"80\",\"filename\":\"80.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"72x72\",\"expected-size\":\"72\",\"filename\":\"72.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"76x76\",\"expected-size\":\"152\",\"filename\":\"152.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"50x50\",\"expected-size\":\"100\",\"filename\":\"100.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"29x29\",\"expected-size\":\"58\",\"filename\":\"58.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"76x76\",\"expected-size\":\"76\",\"filename\":\"76.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"29\",\"filename\":\"29.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"50x50\",\"expected-size\":\"50\",\"filename\":\"50.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"72x72\",\"expected-size\":\"144\",\"filename\":\"144.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"40x40\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"83.5x83.5\",\"expected-size\":\"167\",\"filename\":\"167.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"20\",\"filename\":\"20.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"20x20\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"}]}"
  },
  {
    "path": "lite/examples/image_segmentation/ios/ImageSegmentation/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/image_segmentation/ios/ImageSegmentation/Assets.xcassets/photo_camera.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"photo_camera_2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"photo_camera_3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/image_segmentation/ios/ImageSegmentation/Assets.xcassets/photo_library.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"photo_library_2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"photo_library_3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/image_segmentation/ios/ImageSegmentation/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"14868\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <device id=\"retina6_1\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"14824\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"896\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" textAlignment=\"center\" lineBreakMode=\"tailTruncation\" numberOfLines=\"0\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"rpk-Mj-4RB\">\n                                <rect key=\"frame\" x=\"62\" y=\"394.5\" width=\"290\" height=\"107.5\"/>\n                                <string key=\"text\">Image Segmentation\nwith\nTensorFlow Lite</string>\n                                <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"30\"/>\n                                <color key=\"textColor\" systemColor=\"systemBlueColor\" red=\"0.0\" green=\"0.47843137254901963\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                <nil key=\"highlightedColor\"/>\n                            </label>\n                        </subviews>\n                        <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\" cocoaTouchSystemColor=\"whiteColor\"/>\n                        <constraints>\n                            <constraint firstItem=\"rpk-Mj-4RB\" firstAttribute=\"centerY\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerY\" id=\"4HS-bD-n3S\"/>\n                            <constraint firstItem=\"rpk-Mj-4RB\" firstAttribute=\"centerX\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerX\" id=\"OiY-re-5MG\"/>\n                        </constraints>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "lite/examples/image_segmentation/ios/ImageSegmentation/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"20037\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" colorMatched=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <device id=\"retina6_1\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"20020\"/>\n        <capability name=\"System colors in document resources\" minToolsVersion=\"11.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"ViewController\" customModule=\"TFL_Segmentation\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"UhJ-jg-kFY\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"TqK-8z-fNG\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"896\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <imageView clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"8lT-sf-xuk\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"94\" width=\"414\" height=\"414\"/>\n                                <color key=\"backgroundColor\" red=\"0.0\" green=\"0.47843137250000001\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"width\" secondItem=\"8lT-sf-xuk\" secondAttribute=\"height\" multiplier=\"1:1\" id=\"8XK-Qi-m6A\"/>\n                                </constraints>\n                            </imageView>\n                            <imageView clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleAspectFit\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"gBp-Cq-H5A\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"94\" width=\"414\" height=\"414\"/>\n                            </imageView>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Yg9-nM-9J0\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"44\" width=\"414\" height=\"50\"/>\n                                <subviews>\n                                    <button opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"2ym-n6-JSB\">\n                                        <rect key=\"frame\" x=\"298\" y=\"7\" width=\"36\" height=\"36\"/>\n                                        <state key=\"normal\" image=\"photo_library\"/>\n                                        <connections>\n                                            <action selector=\"onTapPhotoLibrary:\" destination=\"BYZ-38-t0r\" eventType=\"touchUpInside\" id=\"y9h-O6-v7c\"/>\n                                        </connections>\n                                    </button>\n                                    <button opaque=\"NO\" contentMode=\"scaleToFill\" enabled=\"NO\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"qim-4p-Nop\">\n                                        <rect key=\"frame\" x=\"80\" y=\"7\" width=\"36\" height=\"36\"/>\n                                        <state key=\"normal\" image=\"photo_camera\"/>\n                                        <connections>\n                                            <action selector=\"onTapOpenCamera:\" destination=\"BYZ-38-t0r\" eventType=\"touchUpInside\" id=\"BlS-nz-h4q\"/>\n                                        </connections>\n                                    </button>\n                                </subviews>\n                                <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\"/>\n                                <constraints>\n                                    <constraint firstItem=\"qim-4p-Nop\" firstAttribute=\"leading\" secondItem=\"Yg9-nM-9J0\" secondAttribute=\"leading\" constant=\"80\" id=\"6RE-N1-q5w\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"2ym-n6-JSB\" secondAttribute=\"trailing\" constant=\"80\" id=\"8c9-0O-Xow\"/>\n                                    <constraint firstItem=\"2ym-n6-JSB\" firstAttribute=\"centerY\" secondItem=\"Yg9-nM-9J0\" secondAttribute=\"centerY\" id=\"OI2-kw-GhF\"/>\n                                    <constraint firstAttribute=\"height\" constant=\"50\" id=\"VjL-51-Jtg\"/>\n                                    <constraint firstItem=\"2ym-n6-JSB\" firstAttribute=\"centerY\" secondItem=\"qim-4p-Nop\" secondAttribute=\"centerY\" id=\"yDZ-ho-qna\"/>\n                                </constraints>\n                            </view>\n                            <scrollView clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"scaleToFill\" showsHorizontalScrollIndicator=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Dy2-B8-THR\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"508\" width=\"414\" height=\"354\"/>\n                                <subviews>\n                                    <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"5dC-eb-FHY\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"143.5\"/>\n                                        <subviews>\n                                            <segmentedControl opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"left\" contentVerticalAlignment=\"top\" segmentControlStyle=\"plain\" selectedSegmentIndex=\"0\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"j88-xS-beZ\">\n                                                <rect key=\"frame\" x=\"48.5\" y=\"8\" width=\"317\" height=\"32\"/>\n                                                <segments>\n                                                    <segment title=\"Input\"/>\n                                                    <segment title=\"Segmentation\"/>\n                                                    <segment title=\"Overlay\"/>\n                                                </segments>\n                                                <connections>\n                                                    <action selector=\"onSegmentChanged:\" destination=\"BYZ-38-t0r\" eventType=\"valueChanged\" id=\"g0u-ht-D7A\"/>\n                                                </connections>\n                                            </segmentedControl>\n                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Legend\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"bLI-a1-79J\">\n                                                <rect key=\"frame\" x=\"8\" y=\"114.5\" width=\"398\" height=\"21\"/>\n                                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                <nil key=\"textColor\"/>\n                                                <nil key=\"highlightedColor\"/>\n                                            </label>\n                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Crop To Square\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Tis-0d-4Zb\">\n                                                <rect key=\"frame\" x=\"8\" y=\"52\" width=\"118\" height=\"21\"/>\n                                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                <nil key=\"textColor\"/>\n                                                <nil key=\"highlightedColor\"/>\n                                            </label>\n                                            <switch opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" on=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"jhc-xp-ef8\">\n                                                <rect key=\"frame\" x=\"357\" y=\"47\" width=\"51\" height=\"31\"/>\n                                                <connections>\n                                                    <action selector=\"onCropSwitchValueChanged:\" destination=\"BYZ-38-t0r\" eventType=\"valueChanged\" id=\"B6c-bK-JLb\"/>\n                                                </connections>\n                                            </switch>\n                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Inference Time\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" numberOfLines=\"0\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"kaC-2f-KHe\">\n                                                <rect key=\"frame\" x=\"8\" y=\"86\" width=\"398\" height=\"20.5\"/>\n                                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                <nil key=\"textColor\"/>\n                                                <nil key=\"highlightedColor\"/>\n                                            </label>\n                                        </subviews>\n                                        <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\"/>\n                                        <constraints>\n                                            <constraint firstItem=\"j88-xS-beZ\" firstAttribute=\"top\" secondItem=\"5dC-eb-FHY\" secondAttribute=\"top\" constant=\"8\" id=\"3rz-j2-W5a\"/>\n                                            <constraint firstItem=\"kaC-2f-KHe\" firstAttribute=\"top\" secondItem=\"jhc-xp-ef8\" secondAttribute=\"bottom\" constant=\"8\" id=\"9J3-58-w55\"/>\n                                            <constraint firstAttribute=\"trailing\" secondItem=\"jhc-xp-ef8\" secondAttribute=\"trailing\" constant=\"8\" id=\"9MM-76-aeD\"/>\n                                            <constraint firstItem=\"j88-xS-beZ\" firstAttribute=\"centerX\" secondItem=\"5dC-eb-FHY\" secondAttribute=\"centerX\" id=\"9mf-4R-87Z\"/>\n                                            <constraint firstItem=\"Tis-0d-4Zb\" firstAttribute=\"leading\" secondItem=\"5dC-eb-FHY\" secondAttribute=\"leading\" constant=\"8\" id=\"Qmt-vM-XIw\"/>\n                                            <constraint firstAttribute=\"trailing\" secondItem=\"bLI-a1-79J\" secondAttribute=\"trailing\" constant=\"8\" id=\"RRr-yj-For\"/>\n                                            <constraint firstItem=\"jhc-xp-ef8\" firstAttribute=\"leading\" relation=\"greaterThanOrEqual\" secondItem=\"Tis-0d-4Zb\" secondAttribute=\"trailing\" priority=\"250\" constant=\"8\" symbolic=\"YES\" id=\"UgQ-5H-XsD\"/>\n                                            <constraint firstAttribute=\"trailing\" secondItem=\"kaC-2f-KHe\" secondAttribute=\"trailing\" constant=\"8\" id=\"dt2-3d-fEP\"/>\n                                            <constraint firstItem=\"bLI-a1-79J\" firstAttribute=\"top\" secondItem=\"kaC-2f-KHe\" secondAttribute=\"bottom\" constant=\"8\" id=\"gQq-7U-5ZK\"/>\n                                            <constraint firstItem=\"jhc-xp-ef8\" firstAttribute=\"centerY\" secondItem=\"Tis-0d-4Zb\" secondAttribute=\"centerY\" id=\"kdw-VI-dzG\"/>\n                                            <constraint firstItem=\"kaC-2f-KHe\" firstAttribute=\"leading\" secondItem=\"5dC-eb-FHY\" secondAttribute=\"leading\" constant=\"8\" id=\"lVP-jd-uIL\"/>\n                                            <constraint firstItem=\"jhc-xp-ef8\" firstAttribute=\"top\" secondItem=\"j88-xS-beZ\" secondAttribute=\"bottom\" constant=\"8\" id=\"ryW-jY-8dC\"/>\n                                            <constraint firstItem=\"bLI-a1-79J\" firstAttribute=\"leading\" secondItem=\"5dC-eb-FHY\" secondAttribute=\"leading\" constant=\"8\" id=\"tkW-gF-sJA\"/>\n                                            <constraint firstAttribute=\"bottom\" secondItem=\"bLI-a1-79J\" secondAttribute=\"bottom\" constant=\"8\" id=\"wf4-fz-y1V\"/>\n                                        </constraints>\n                                    </view>\n                                </subviews>\n                                <constraints>\n                                    <constraint firstItem=\"5dC-eb-FHY\" firstAttribute=\"top\" secondItem=\"Dy2-B8-THR\" secondAttribute=\"top\" id=\"60V-CR-lmW\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"5dC-eb-FHY\" secondAttribute=\"trailing\" id=\"HEL-dd-aAp\"/>\n                                    <constraint firstItem=\"5dC-eb-FHY\" firstAttribute=\"leading\" secondItem=\"Dy2-B8-THR\" secondAttribute=\"leading\" id=\"IBJ-PS-Hai\"/>\n                                    <constraint firstItem=\"5dC-eb-FHY\" firstAttribute=\"centerX\" secondItem=\"Dy2-B8-THR\" secondAttribute=\"centerX\" id=\"P0L-pO-SKh\"/>\n                                    <constraint firstAttribute=\"bottom\" secondItem=\"5dC-eb-FHY\" secondAttribute=\"bottom\" id=\"waS-Zf-hWh\"/>\n                                </constraints>\n                            </scrollView>\n                        </subviews>\n                        <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\"/>\n                        <constraints>\n                            <constraint firstItem=\"Yg9-nM-9J0\" firstAttribute=\"leading\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"leading\" id=\"4am-fJ-WFG\"/>\n                            <constraint firstItem=\"Dy2-B8-THR\" firstAttribute=\"leading\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"leading\" id=\"5Uw-2Z-NVX\"/>\n                            <constraint firstItem=\"Dy2-B8-THR\" firstAttribute=\"bottom\" secondItem=\"TqK-8z-fNG\" secondAttribute=\"top\" id=\"DXp-6Z-iaO\"/>\n                            <constraint firstItem=\"8lT-sf-xuk\" firstAttribute=\"trailing\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"trailing\" id=\"EoR-E5-PFR\"/>\n                            <constraint firstItem=\"8lT-sf-xuk\" firstAttribute=\"leading\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"leading\" id=\"IVR-CW-0dc\"/>\n                            <constraint firstItem=\"Yg9-nM-9J0\" firstAttribute=\"centerX\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"centerX\" id=\"LGc-SG-v5K\"/>\n                            <constraint firstItem=\"Yg9-nM-9J0\" firstAttribute=\"top\" secondItem=\"UhJ-jg-kFY\" secondAttribute=\"bottom\" id=\"Qz8-jA-3YQ\"/>\n                            <constraint firstItem=\"gBp-Cq-H5A\" firstAttribute=\"width\" secondItem=\"8lT-sf-xuk\" secondAttribute=\"width\" id=\"SJn-jT-j54\"/>\n                            <constraint firstItem=\"Dy2-B8-THR\" firstAttribute=\"trailing\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"trailing\" id=\"VW3-nz-Wcv\"/>\n                            <constraint firstItem=\"gBp-Cq-H5A\" firstAttribute=\"height\" secondItem=\"8lT-sf-xuk\" secondAttribute=\"height\" id=\"h17-0N-foh\"/>\n                            <constraint firstItem=\"Yg9-nM-9J0\" firstAttribute=\"trailing\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"trailing\" id=\"jid-Zu-De7\"/>\n                            <constraint firstItem=\"Dy2-B8-THR\" firstAttribute=\"top\" secondItem=\"8lT-sf-xuk\" secondAttribute=\"bottom\" id=\"pVj-cm-G2G\"/>\n                            <constraint firstItem=\"gBp-Cq-H5A\" firstAttribute=\"top\" secondItem=\"8lT-sf-xuk\" secondAttribute=\"top\" id=\"q9O-kq-Wwo\"/>\n                            <constraint firstItem=\"gBp-Cq-H5A\" firstAttribute=\"leading\" secondItem=\"8lT-sf-xuk\" secondAttribute=\"leading\" id=\"wqY-gr-RFs\"/>\n                            <constraint firstItem=\"8lT-sf-xuk\" firstAttribute=\"top\" secondItem=\"Yg9-nM-9J0\" secondAttribute=\"bottom\" id=\"zzN-vb-ut1\"/>\n                        </constraints>\n                    </view>\n                    <navigationItem key=\"navigationItem\" id=\"iE9-Ll-efq\"/>\n                    <connections>\n                        <outlet property=\"cropSwitch\" destination=\"jhc-xp-ef8\" id=\"ndw-B2-Vj9\"/>\n                        <outlet property=\"inferenceStatusLabel\" destination=\"kaC-2f-KHe\" id=\"e71-zS-dky\"/>\n                        <outlet property=\"inputImageView\" destination=\"8lT-sf-xuk\" id=\"BtB-ts-BTv\"/>\n                        <outlet property=\"legendLabel\" destination=\"bLI-a1-79J\" id=\"GT7-Zl-dKO\"/>\n                        <outlet property=\"photoCameraButton\" destination=\"qim-4p-Nop\" id=\"y1o-Th-yVd\"/>\n                        <outlet property=\"resultImageView\" destination=\"gBp-Cq-H5A\" id=\"sbE-J0-sVD\"/>\n                        <outlet property=\"segmentedControl\" destination=\"j88-xS-beZ\" id=\"lHG-1F-UD5\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"1047.8260869565217\" y=\"109.82142857142857\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"photo_camera\" width=\"36\" height=\"36\"/>\n        <image name=\"photo_library\" width=\"36\" height=\"36\"/>\n        <systemColor name=\"systemBackgroundColor\">\n            <color white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n        </systemColor>\n    </resources>\n</document>\n"
  },
  {
    "path": "lite/examples/image_segmentation/ios/ImageSegmentation/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>NSPhotoLibraryUsageDescription</key>\n\t<string>This app allow you to pick photos from your library then run image segmentation.</string>\n\t<key>NSCameraUsageDescription</key>\n\t<string>This app allow you to take pictures then run image segmentation.</string>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UIRequiresFullScreen</key>\n\t<true/>\n\t<key>UIStatusBarHidden</key>\n\t<true/>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UIUserInterfaceStyle</key>\n\t<string>Light</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "lite/examples/image_segmentation/ios/ImageSegmentation/TFLite/ImageSegmentationHelper.swift",
    "content": "// Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport TensorFlowLiteTaskVision\nimport UIKit\n\nclass ImageSegmentationHelper {\n\n  /// TensorFlow Lite `Interpreter` object for performing inference on a given model.\n  private var segmenter: ImageSegmenter\n\n  /// Dedicated `DispatchQueue` for TF Lite operations.\n  private let tfLiteQueue: DispatchQueue\n\n  // MARK: - Initialization\n\n  /// Create a new `ImageSegmentationHelper` instance.\n  ///\n  /// The instance will be initialzed and returned via the completion handler.\n  static func newInstance(\n    completionHandler: @escaping ((Result<ImageSegmentationHelper, InitializationError>) -> Void)\n  ) {\n    // Create a dispatch queue to ensure all operations on the TFLite `ImageSegmenter` will run\n    // serially.\n    let tfLiteQueue = DispatchQueue(label: \"org.tensorflow.examples.lite.image_segmentation\")\n\n    // Run initialization in background thread to avoid UI freeze.\n    tfLiteQueue.async {\n      // Construct the path to the model file.\n      guard\n        let modelPath = Bundle.main.path(\n          forResource: Constants.modelFileName,\n          ofType: Constants.modelFileExtension\n        )\n      else {\n        print(\n          \"Failed to load the model file with name: \"\n            + \"\\(Constants.modelFileName).\\(Constants.modelFileExtension)\")\n        DispatchQueue.main.async {\n          completionHandler(\n            .failure(\n              InitializationError.invalidModel(\n                \"\\(Constants.modelFileName).\\(Constants.modelFileExtension)\"\n              )))\n        }\n        return\n      }\n\n      // Specify the options for the `ImageSegmenter`.\n      let options = ImageSegmenterOptions(modelPath: modelPath)\n\n      do {\n        let segmenter = try ImageSegmenter.segmenter(options: options)\n\n        // Create an ImageSegmentationHelper instance and return.\n        let segmentationHelper = ImageSegmentationHelper(\n          tfLiteQueue: tfLiteQueue,\n          segmenter: segmenter\n        )\n        DispatchQueue.main.async {\n          completionHandler(.success(segmentationHelper))\n        }\n      } catch let error {\n        print(\"Failed to create the interpreter with error: \\(error.localizedDescription)\")\n        DispatchQueue.main.async {\n          completionHandler(.failure(InitializationError.internalError(error)))\n        }\n        return\n      }\n    }\n  }\n\n  /// Initialize Image Segmentator instance.\n  fileprivate init(\n    tfLiteQueue: DispatchQueue,\n    segmenter: ImageSegmenter\n  ) {\n    // Store TF Lite intepreter\n    self.segmenter = segmenter\n\n    // Store the dedicated DispatchQueue for TFLite.\n    self.tfLiteQueue = tfLiteQueue\n  }\n\n  // MARK: - Image Segmentation\n\n  /// Run segmentation on a given image.\n  ///\n  /// - Parameters:\n  ///   - image: the target image.\n  ///   - completion: the callback to receive segmentation result.\n  func runSegmentation(\n    _ image: UIImage,\n    completion: @escaping ((Result<ImageSegmentationResult, SegmentationError>) -> Void)\n  ) {\n    tfLiteQueue.async {\n      [weak self] in\n      guard let strongSelf = self else {\n        print(\"The app is in an invalid state.\")\n        DispatchQueue.main.async {\n          completion(.failure(SegmentationError.invalidState))\n        }\n        return\n      }\n      let segmentationResult: SegmentationResult\n      var inferenceTime: TimeInterval = 0\n      var postprocessingTime: TimeInterval = 0\n\n      do {\n        // Preprocessing: Convert the input UIImage to MLImage.\n        let startTime = Date()\n        guard let mlImage = MLImage(image: image) else {\n          print(\"The input image is invalid.\")\n          DispatchQueue.main.async {\n            completion(.failure(SegmentationError.invalidImage))\n          }\n          return\n        }\n\n        // Run segmentation\n        segmentationResult = try strongSelf.segmenter.segment(mlImage: mlImage)\n\n        // Calculate segmentation time.\n        inferenceTime = Date().timeIntervalSince(startTime)\n      } catch let error {\n        print(\"Failed to invoke TFLite with error: \\(error.localizedDescription)\")\n        DispatchQueue.main.async {\n          completion(.failure(SegmentationError.internalError(error)))\n        }\n        return\n      }\n\n      /// Postprocessing: Visualize the `SegmentationResult` object.\n      let startTime = Date()\n\n      guard\n        let (resultImage, colorLegend) = strongSelf.parseOutput(\n          segmentationResult: segmentationResult)\n      else {\n        print(\"Failed to parse model output.\")\n        DispatchQueue.main.async {\n          completion(.failure(SegmentationError.postProcessingError))\n        }\n        return\n      }\n      // Calculate postprocessing time.\n      // Note: You may find postprocessing slow when running the sample app with the Debug build.\n      // You will see significant speed up if you switch to using Release build, or change\n      // Optimization Level in the project's Build Settings to the same value as the Release build.\n      postprocessingTime = Date().timeIntervalSince(startTime)\n\n      // Create a representative object that contains the segmentation result.\n      let result = ImageSegmentationResult(\n        resultImage: resultImage,\n        colorLegend: colorLegend,\n        inferenceTime: inferenceTime,\n        postProcessingTime: postprocessingTime\n      )\n\n      // Return the segmentation result.\n      DispatchQueue.main.async {\n        completion(.success(result))\n      }\n    }\n  }\n\n  // MARK: - Image Segmentation Parse\n\n  /// Run segmentation map and color for each pixel, if can't get `categoryMask` -> return nil.\n  /// - Parameter segmentationResult: The result received from image segmentation process\n  private func parseOutput(segmentationResult: SegmentationResult) -> (UIImage, [String: UIColor])?\n  {\n    guard let segmentation = segmentationResult.segmentations.first,\n      let categoryMask = segmentation.categoryMask\n    else { return nil }\n    let mask = categoryMask.mask\n    let results = [UInt8](\n      UnsafeMutableBufferPointer(\n        start: mask,\n        count: categoryMask.width * categoryMask.height))\n\n    // Create a visualization of the segmentation image.\n    let alphaChannel: UInt32 = 255\n    let classColorsUInt32: [UInt32] = segmentation.coloredLabels.map({\n      let colorAsUInt32 =\n        alphaChannel << 24  // alpha channel\n        + UInt32($0.r) << 16 + UInt32($0.g) << 8 + UInt32($0.b)\n      return colorAsUInt32\n    })\n    let segmentationImagePixels: [UInt32] = results.map({ classColorsUInt32[Int($0)] })\n    guard\n      let resultImage = UIImage.fromSRGBColorArray(\n        pixels: segmentationImagePixels,\n        size: CGSize(width: categoryMask.width, height: categoryMask.height)\n      )\n    else { return nil }\n\n    // Calculate the list of classes found in the image and its visualization color.\n    let classFoundInImageList = IndexSet(Set(results).map({ Int($0) }))\n    let filteredColorLabels = classFoundInImageList.map({ segmentation.coloredLabels[$0] })\n    let colorLegend = [String: UIColor](\n      uniqueKeysWithValues: filteredColorLabels.map { colorLabel in\n        let color = UIColor(\n          red: CGFloat(colorLabel.r) / 255.0,\n          green: CGFloat(colorLabel.g) / 255.0,\n          blue: CGFloat(colorLabel.b) / 255.0,\n          alpha: CGFloat(alphaChannel) / 255.0)\n        return (colorLabel.label, color)\n      })\n\n    return (resultImage, colorLegend)\n  }\n}\n\n// MARK: - Types\n\nstruct ImageSegmentationResult {\n  /// Visualization of the segmentation result.\n  let resultImage: UIImage\n\n  /// Dictionary of classes found in the image, and the color used to represent the class in\n  /// the segmentation result visualization.\n  let colorLegend: [String: UIColor]\n\n  /// Processing time.\n  let inferenceTime: TimeInterval\n  let postProcessingTime: TimeInterval\n}\n\n/// The errors that could happen in the initialization of this class\nenum InitializationError: Error {\n  // The app is in an invalid state\n  case invalidState\n\n  // Invalid TF Lite model\n  case invalidModel(String)\n\n  // TF Lite internal Error when initializing\n  case internalError(Error)\n}\n\n/// The errors that could happen in when doing image segmentation\nenum SegmentationError: Error {\n  // The app is in an invalid state\n  case invalidState\n\n  // Invalid input image\n  case invalidImage\n\n  // TF Lite internal Error when running inference\n  case internalError(Error)\n\n  // Error when processing the TFLite model output\n  case postProcessingError\n\n  // Error when visualizing the segmentation result\n  case resultVisualizationError\n}\n\n// MARK: - Constants\nprivate enum Constants {\n  /// The TF Lite segmentation model file\n  static let modelFileName = \"deeplabv3\"\n  static let modelFileExtension = \"tflite\"\n}\n"
  },
  {
    "path": "lite/examples/image_segmentation/ios/ImageSegmentation/TFLite/TFLiteExtension.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport CoreGraphics\nimport Foundation\nimport UIKit\n\n// MARK: - UIImage\n\n/// Extension of iOS classes that is useful for working with TensorFlow Lite computer vision models.\nextension UIImage {\n\n  /// Make the same image with orientation being `.up`.\n  /// - Returns:  A copy of the image with .up orientation or `nil` if the image could not be\n  /// rotated.\n  func transformOrientationToUp() -> UIImage? {\n    // Check if the image orientation is already .up and don't need any rotation.\n    guard imageOrientation != UIImage.Orientation.up else {\n      // No rotation needed so return a copy of this image.\n      return self.copy() as? UIImage\n    }\n\n    // Make sure that this image has an CGImage attached.\n    guard let cgImage = self.cgImage else { return nil }\n\n    // Create a CGContext to draw the rotated image to.\n    guard let colorSpace = cgImage.colorSpace,\n      let context = CGContext(\n        data: nil,\n        width: Int(size.width),\n        height: Int(size.height),\n        bitsPerComponent: cgImage.bitsPerComponent,\n        bytesPerRow: 0,\n        space: colorSpace,\n        bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue\n      )\n    else { return nil }\n\n    var transform: CGAffineTransform = CGAffineTransform.identity\n\n    // Calculate the transformation matrix that needed to bring the image orientation to .up\n    switch imageOrientation {\n    case .down, .downMirrored:\n      transform = transform.translatedBy(x: size.width, y: size.height)\n      transform = transform.rotated(by: CGFloat.pi)\n      break\n    case .left, .leftMirrored:\n      transform = transform.translatedBy(x: size.width, y: 0)\n      transform = transform.rotated(by: CGFloat.pi / 2.0)\n      break\n    case .right, .rightMirrored:\n      transform = transform.translatedBy(x: 0, y: size.height)\n      transform = transform.rotated(by: CGFloat.pi / -2.0)\n      break\n    case .up, .upMirrored:\n      break\n    @unknown default:\n      break\n    }\n\n    // If the image is mirrored then flip it.\n    switch imageOrientation {\n    case .upMirrored, .downMirrored:\n      transform.translatedBy(x: size.width, y: 0)\n      transform.scaledBy(x: -1, y: 1)\n      break\n    case .leftMirrored, .rightMirrored:\n      transform.translatedBy(x: size.height, y: 0)\n      transform.scaledBy(x: -1, y: 1)\n    case .up, .down, .left, .right:\n      break\n    @unknown default:\n      break\n    }\n\n    // Apply transformation matrix to the CGContext.\n    context.concatenate(transform)\n\n    switch imageOrientation {\n    case .left, .leftMirrored, .right, .rightMirrored:\n      context.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.height, height: size.width))\n    default:\n      context.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))\n      break\n    }\n\n    // Create a CGImage from the context.\n    guard let newCGImage = context.makeImage() else { return nil }\n\n    // Convert it to UIImage.\n    return UIImage.init(cgImage: newCGImage, scale: 1, orientation: .up)\n  }\n}\n"
  },
  {
    "path": "lite/examples/image_segmentation/ios/ImageSegmentation/UIKitExtension.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n/// Helper functions for the UIImage class that is useful for this sample app.\nextension UIImage {\n\n  /// Helper function to center-crop image.\n  ///\n  /// - Returns: Center-cropped copy of this image\n  func cropCenter() -> UIImage? {\n    let isPortrait = size.height > size.width\n    let isLandscape = size.width > size.height\n    let breadth = min(size.width, size.height)\n    let breadthSize = CGSize(width: breadth, height: breadth)\n    let breadthRect = CGRect(origin: .zero, size: breadthSize)\n\n    UIGraphicsBeginImageContextWithOptions(breadthSize, false, scale)\n    let croppingOrigin = CGPoint(\n      x: isLandscape ? floor((size.width - size.height) / 2) : 0,\n      y: isPortrait ? floor((size.height - size.width) / 2) : 0\n    )\n    guard let cgImage = cgImage?.cropping(to: CGRect(origin: croppingOrigin, size: breadthSize))\n    else { return nil }\n    UIImage(cgImage: cgImage).draw(in: breadthRect)\n    let croppedImage = UIGraphicsGetImageFromCurrentImageContext()\n    UIGraphicsEndImageContext()\n\n    return croppedImage\n  }\n\n  /// Create an `UIImage` from the given pixel array.\n  ///\n  /// - Parameters\n  ///   - pixels: The pixel array to create an image from.\n  ///   - size: The target image's size.\n  ///\n  /// - Returns: The `UIImage` object or `nil` if the image could not be drawn.\n  static func fromSRGBColorArray(pixels: [UInt32], size: CGSize) -> UIImage? {\n    guard size.width > 0 && size.height > 0 else {\n      print(\"ERROR: The target image size must be positive.\")\n      return nil\n    }\n\n    // Fails if the size of the target doesn't match with the total pixels in the SRGB array.\n    guard pixels.count == Int(size.width * size.height) else {\n      print(\n        \"ERROR: The size of the target image (\\(size)) doesn't match with the total number of \",\n        \"pixels (\\(pixels.count)) in the SRGB array.\"\n      )\n      return nil\n    }\n\n    // Make a mutable copy.\n    var data = pixels\n\n    // Convert array of pixels to a `CGImage` instance.\n    let cgImage = data.withUnsafeMutableBytes { (ptr) -> CGImage in\n      let ctx = CGContext(\n        data: ptr.baseAddress,\n        width: Int(size.width),\n        height: Int(size.height),\n        bitsPerComponent: 8,\n        bytesPerRow: MemoryLayout<UInt32>.size * Int(size.width),\n        space: CGColorSpace(name: CGColorSpace.sRGB)!,\n        bitmapInfo: CGBitmapInfo.byteOrder32Little.rawValue\n          + CGImageAlphaInfo.premultipliedFirst.rawValue\n      )!\n      return ctx.makeImage()!\n    }\n\n    // Convert the `CGImage` instance to an `UIImage` instance.\n    return UIImage(cgImage: cgImage)\n  }\n}\n\n/// Helper functions for the `UIKit` class that is useful for this sample app.\nextension UIColor {\n\n  // Check if the color is light or dark, as defined by the injected lightness threshold.\n  // A `nil` value is returned if the lightness couldn't be determined.\n  func isLight(threshold: Float = 0.5) -> Bool? {\n    let originalCGColor = self.cgColor\n\n    // Convert the color to the RGB colorspace as some color such as `UIColor.white` and `.black`\n    // are grayscale.\n    let RGBCGColor = originalCGColor.converted(\n      to: CGColorSpaceCreateDeviceRGB(), intent: .defaultIntent, options: nil)\n\n    guard let components = RGBCGColor?.components else { return nil }\n    guard components.count >= 3 else { return nil }\n\n    // Calculate color brightness according to Digital ITU BT.601.\n    let brightness = Float(\n      ((components[0] * 299) + (components[1] * 587) + (components[2] * 114)) / 1000\n    )\n\n    return (brightness > threshold)\n  }\n}\n"
  },
  {
    "path": "lite/examples/image_segmentation/ios/ImageSegmentation/ViewController.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\nclass ViewController: UIViewController {\n\n  /// Image picker for accessing the photo library or camera.\n  private var imagePicker = UIImagePickerController()\n\n  /// Image segmentator instance that runs image segmentation.\n  private var imageSegmentationHelper: ImageSegmentationHelper?\n\n  /// Target image to run image segmentation on.\n  private var targetImage: UIImage?\n\n  /// Processed (e.g center cropped)) image from targetImage that is fed to imageSegmentator.\n  private var segmentationInput: UIImage?\n\n  /// Image segmentation result.\n  private var segmentationResult: ImageSegmentationResult?\n\n  /// UI elements\n  @IBOutlet weak var inputImageView: UIImageView!\n  @IBOutlet weak var resultImageView: UIImageView!\n\n  @IBOutlet weak var photoCameraButton: UIButton!\n  @IBOutlet weak var segmentedControl: UISegmentedControl!\n  @IBOutlet weak var cropSwitch: UISwitch!\n  @IBOutlet weak var inferenceStatusLabel: UILabel!\n  @IBOutlet weak var legendLabel: UILabel!\n\n  override func viewDidLoad() {\n    super.viewDidLoad()\n\n    // Setup image picker.\n    imagePicker.delegate = self\n    imagePicker.sourceType = .photoLibrary\n\n    // Enable camera option only if current device has camera.\n    let isCameraAvailable =\n      UIImagePickerController.isCameraDeviceAvailable(.front)\n      || UIImagePickerController.isCameraDeviceAvailable(.rear)\n    if isCameraAvailable {\n      photoCameraButton.isEnabled = true\n    }\n\n    // Initialize an image segmentator instance.\n    ImageSegmentationHelper.newInstance { result in\n      switch result {\n      case let .success(segmentationHelper):\n        // Store the initialized instance for use.\n        self.imageSegmentationHelper = segmentationHelper\n\n        // Run image segmentation on a demo image.\n        self.showDemoSegmentation()\n      case .failure(_):\n        print(\"Failed to initialize.\")\n      }\n    }\n  }\n\n  /// Open camera to allow user taking photo.\n  @IBAction func onTapOpenCamera(_ sender: Any) {\n    guard\n      UIImagePickerController.isCameraDeviceAvailable(.front)\n        || UIImagePickerController.isCameraDeviceAvailable(.rear)\n    else {\n      return\n    }\n\n    imagePicker.sourceType = .camera\n    present(imagePicker, animated: true)\n  }\n\n  /// Open photo library for user to choose an image from.\n  @IBAction func onTapPhotoLibrary(_ sender: Any) {\n    imagePicker.sourceType = .photoLibrary\n    present(imagePicker, animated: true)\n  }\n\n  /// Handle tapping on different display mode: Input, Segmentation, Overlay\n  @IBAction func onSegmentChanged(_ sender: Any) {\n    // `resultImageView` is placed on top of the `inputImageView`. The visibility (alpha value) of\n    // `resultImageView` is adjusted depending on the display mode to show/hide/overlay the\n    // underlying `inputImageView`.\n    switch segmentedControl.selectedSegmentIndex {\n    case 0:\n      // Mode 0: Show input image\n      resultImageView.alpha = 0\n    case 1:\n      // Mode 1: Show visualization of segmentation result.\n      resultImageView.alpha = 1\n    case 2:\n      // Mode 2: Show overlay of segmentation result on input image.\n      resultImageView.alpha = 0.5\n    default:\n      break\n    }\n  }\n\n  /// Handle changing center crop setting.\n  @IBAction func onCropSwitchValueChanged(_ sender: Any) {\n    // Make sure that cached segmentation target image is available.\n    guard targetImage != nil else {\n      self.inferenceStatusLabel.text = \"ERROR: Input image is nil.\"\n      return\n    }\n\n    // Re-run the segmentation upon center-crop setting changed.\n    runSegmentation(targetImage!)\n  }\n}\n\n// MARK: - Image Segmentation\n\nextension ViewController {\n  /// Run image segmentation on the given image, and show result on screen.\n  ///  - Parameter image: The target image for segmentation.\n  func runSegmentation(_ image: UIImage) {\n    clearResults()\n\n    // Rotate target image to .up orientation to avoid potential orientation misalignment.\n    guard let targetImage = image.transformOrientationToUp() else {\n      inferenceStatusLabel.text = \"ERROR: Image orientation couldn't be fixed.\"\n      return\n    }\n\n    // Make sure that image segmentator is initialized.\n    guard let imageSegmentator = imageSegmentationHelper else {\n      inferenceStatusLabel.text = \"ERROR: Image Segmentator is not ready.\"\n      return\n    }\n\n    // Cache the target image.\n    self.targetImage = targetImage\n\n    // Center-crop the target image if the user has enabled the option.\n    let willCenterCrop = cropSwitch.isOn\n    guard let inputImage = willCenterCrop ? targetImage.cropCenter() : targetImage else {\n      inferenceStatusLabel.text = \"ERROR: Image could not be cropped.\"\n      return\n    }\n\n    // Cache the potentially cropped image as input to the segmentation model.\n    segmentationInput = inputImage\n\n    // Show the potentially cropped image on screen.\n    inputImageView.image = inputImage\n\n    // Lock the crop switch while segmentation is running.\n    cropSwitch.isEnabled = false\n\n    // Run image segmentation.\n    imageSegmentator.runSegmentation(\n      inputImage,\n      completion: { result in\n        // Unlock the crop switch\n        self.cropSwitch.isEnabled = true\n\n        // Show the segmentation result on screen\n        switch result {\n        case let .success(segmentationResult):\n          self.segmentationResult = segmentationResult\n\n          // Change to show segmentation overlay result\n          self.segmentedControl.selectedSegmentIndex = 2\n          self.onSegmentChanged(self)\n\n          // Show result metadata\n          self.showInferenceTime(segmentationResult)\n          self.showClassLegend(segmentationResult)\n\n          // Show segmentation result\n          self.resultImageView.image = segmentationResult.resultImage\n\n          // Enable switching between different display mode: input, segmentation, overlay\n          self.segmentedControl.isEnabled = true\n        case let .failure(error):\n          self.inferenceStatusLabel.text = error.localizedDescription\n        }\n      })\n  }\n\n  /// Clear result from previous run to prepare for new segmentation run.\n  private func clearResults() {\n    inferenceStatusLabel.text = \"Running inference with TensorFlow Lite...\"\n    legendLabel.text = nil\n    segmentedControl.isEnabled = false\n    segmentedControl.selectedSegmentIndex = 0\n  }\n\n  /// Demo image segmentation with a bundled image.\n  private func showDemoSegmentation() {\n    if let filePath = Bundle.main.path(forResource: \"boy\", ofType: \"jpg\"),\n      let image = UIImage(contentsOfFile: filePath)\n    {\n      runSegmentation(image)\n    }\n  }\n\n  /// Show segmentation latency on screen.\n  private func showInferenceTime(_ segmentationResult: ImageSegmentationResult) {\n    let timeString =\n      \"Model inference: \\(Int(segmentationResult.inferenceTime * 1000))ms.\\n\"\n      + \"Postprocessing: \\(Int(segmentationResult.postProcessingTime * 1000))ms.\\n\"\n\n    inferenceStatusLabel.text = timeString\n  }\n\n  /// Show color legend of each class found in the image.\n  private func showClassLegend(_ segmentationResult: ImageSegmentationResult) {\n    let legendText = NSMutableAttributedString()\n\n    // Loop through the classes founded in the image.\n    segmentationResult.colorLegend.forEach { (className, color) in\n      // If the color legend is light, use black text font. If not, use white text font.\n      let textColor = color.isLight() ?? true ? UIColor.black : UIColor.white\n\n      // Construct the legend text for current class.\n      let attributes = [\n        NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .headline),\n        NSAttributedString.Key.backgroundColor: color,\n        NSAttributedString.Key.foregroundColor: textColor,\n      ]\n      let string = NSAttributedString(string: \" \\(className) \", attributes: attributes)\n\n      // Add class legend to string to show on the screen.\n      legendText.append(string)\n      legendText.append(NSAttributedString(string: \"  \"))\n    }\n\n    // Show the class legends on the screen.\n    legendLabel.attributedText = legendText\n  }\n}\n\n// MARK: - UIImagePickerControllerDelegate\n\nextension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {\n\n  func imagePickerController(\n    _ picker: UIImagePickerController,\n    didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]\n  ) {\n\n    if let pickedImage = info[.originalImage] as? UIImage {\n      runSegmentation(pickedImage)\n    }\n\n    dismiss(animated: true)\n  }\n}\n"
  },
  {
    "path": "lite/examples/image_segmentation/ios/ImageSegmentation.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t36D37A0F03D127BD5B014289 /* Pods_ImageSegmentation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D23CF8BF5D33ABF1A65D8568 /* Pods_ImageSegmentation.framework */; };\n\t\t7F67811F23405195002A02F2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F67811E23405195002A02F2 /* AppDelegate.swift */; };\n\t\t7F67812323405195002A02F2 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F67812223405195002A02F2 /* ViewController.swift */; };\n\t\t7F67812623405195002A02F2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7F67812423405195002A02F2 /* Main.storyboard */; };\n\t\t7F67812823405196002A02F2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7F67812723405196002A02F2 /* Assets.xcassets */; };\n\t\t7F67812B23405196002A02F2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7F67812923405196002A02F2 /* LaunchScreen.storyboard */; };\n\t\t7F9738DD234071180058C202 /* ImageSegmentationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F9738DC234071180058C202 /* ImageSegmentationHelper.swift */; };\n\t\t7F9738E22341EFDD0058C202 /* TFLiteExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F9738E12341EFDD0058C202 /* TFLiteExtension.swift */; };\n\t\t7F9738E72343CE9F0058C202 /* UIKitExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F9738E62343CE9F0058C202 /* UIKitExtension.swift */; };\n\t\t7FA4B36F2345BC73005784A9 /* boy.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 7FA4B36E2345BC73005784A9 /* boy.jpg */; };\n\t\t7FC68171285339A4007F7A3D /* deeplabv3.tflite in Resources */ = {isa = PBXBuildFile; fileRef = 7FC68170285339A4007F7A3D /* deeplabv3.tflite */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\t7F67811B23405195002A02F2 /* TFL Segmentation.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = \"TFL Segmentation.app\"; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t7F67811E23405195002A02F2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t7F67812223405195002A02F2 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = \"<group>\"; };\n\t\t7F67812523405195002A02F2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t7F67812723405196002A02F2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t7F67812A23405196002A02F2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t7F67812C23405196002A02F2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t7F9738DC234071180058C202 /* ImageSegmentationHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageSegmentationHelper.swift; sourceTree = \"<group>\"; };\n\t\t7F9738E12341EFDD0058C202 /* TFLiteExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TFLiteExtension.swift; sourceTree = \"<group>\"; };\n\t\t7F9738E62343CE9F0058C202 /* UIKitExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitExtension.swift; sourceTree = \"<group>\"; };\n\t\t7FA4B36E2345BC73005784A9 /* boy.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = boy.jpg; sourceTree = \"<group>\"; };\n\t\t7FC68170285339A4007F7A3D /* deeplabv3.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = deeplabv3.tflite; sourceTree = \"<group>\"; };\n\t\t8857CE6BF260FD2EBF0CDB36 /* Pods-ImageSegmentation.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-ImageSegmentation.release.xcconfig\"; path = \"Pods/Target Support Files/Pods-ImageSegmentation/Pods-ImageSegmentation.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tAFA20F57D312CC344EAF47D5 /* Pods-ImageSegmentation.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-ImageSegmentation.debug.xcconfig\"; path = \"Pods/Target Support Files/Pods-ImageSegmentation/Pods-ImageSegmentation.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tD23CF8BF5D33ABF1A65D8568 /* Pods_ImageSegmentation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ImageSegmentation.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t7F67811823405195002A02F2 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t36D37A0F03D127BD5B014289 /* Pods_ImageSegmentation.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t0E0FBFF1E117E3C7957BA74A /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD23CF8BF5D33ABF1A65D8568 /* Pods_ImageSegmentation.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t28EAC581E919024130F07576 /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAFA20F57D312CC344EAF47D5 /* Pods-ImageSegmentation.debug.xcconfig */,\n\t\t\t\t8857CE6BF260FD2EBF0CDB36 /* Pods-ImageSegmentation.release.xcconfig */,\n\t\t\t);\n\t\t\tname = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7F67811223405195002A02F2 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t7F67811D23405195002A02F2 /* ImageSegmentation */,\n\t\t\t\t7F67811C23405195002A02F2 /* Products */,\n\t\t\t\t28EAC581E919024130F07576 /* Pods */,\n\t\t\t\t0E0FBFF1E117E3C7957BA74A /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7F67811C23405195002A02F2 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t7F67811B23405195002A02F2 /* TFL Segmentation.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7F67811D23405195002A02F2 /* ImageSegmentation */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t7F67811E23405195002A02F2 /* AppDelegate.swift */,\n\t\t\t\t7F67812223405195002A02F2 /* ViewController.swift */,\n\t\t\t\t7F9738E62343CE9F0058C202 /* UIKitExtension.swift */,\n\t\t\t\t7F9738E823448E750058C202 /* TFLite */,\n\t\t\t\t7FA4B36E2345BC73005784A9 /* boy.jpg */,\n\t\t\t\t7F67812423405195002A02F2 /* Main.storyboard */,\n\t\t\t\t7F67812723405196002A02F2 /* Assets.xcassets */,\n\t\t\t\t7F67812923405196002A02F2 /* LaunchScreen.storyboard */,\n\t\t\t\t7F67812C23405196002A02F2 /* Info.plist */,\n\t\t\t);\n\t\t\tpath = ImageSegmentation;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7F9738E823448E750058C202 /* TFLite */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t7FC68170285339A4007F7A3D /* deeplabv3.tflite */,\n\t\t\t\t7F9738DC234071180058C202 /* ImageSegmentationHelper.swift */,\n\t\t\t\t7F9738E12341EFDD0058C202 /* TFLiteExtension.swift */,\n\t\t\t);\n\t\t\tpath = TFLite;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t7F67811A23405195002A02F2 /* ImageSegmentation */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 7F67812F23405196002A02F2 /* Build configuration list for PBXNativeTarget \"ImageSegmentation\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t9C4626EB48450F23C89E4443 /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t7FA4B3692345B949005784A9 /* Download TensorFlow Lite model */,\n\t\t\t\t7F67811723405195002A02F2 /* Sources */,\n\t\t\t\t7F67811823405195002A02F2 /* Frameworks */,\n\t\t\t\t7F67811923405195002A02F2 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = ImageSegmentation;\n\t\t\tproductName = ImageSegmentation;\n\t\t\tproductReference = 7F67811B23405195002A02F2 /* TFL Segmentation.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t7F67811323405195002A02F2 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 1100;\n\t\t\t\tLastUpgradeCheck = 1100;\n\t\t\t\tORGANIZATIONNAME = \"TensorFlow Authors\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t7F67811A23405195002A02F2 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 11.0;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 7F67811623405195002A02F2 /* Build configuration list for PBXProject \"ImageSegmentation\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 7F67811223405195002A02F2;\n\t\t\tproductRefGroup = 7F67811C23405195002A02F2 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t7F67811A23405195002A02F2 /* ImageSegmentation */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t7F67811923405195002A02F2 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t7FC68171285339A4007F7A3D /* deeplabv3.tflite in Resources */,\n\t\t\t\t7FA4B36F2345BC73005784A9 /* boy.jpg in Resources */,\n\t\t\t\t7F67812B23405196002A02F2 /* LaunchScreen.storyboard in Resources */,\n\t\t\t\t7F67812823405196002A02F2 /* Assets.xcassets in Resources */,\n\t\t\t\t7F67812623405195002A02F2 /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t7FA4B3692345B949005784A9 /* Download TensorFlow Lite model */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Download TensorFlow Lite model\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"# Download TF Lite model from the internet if it's not exist.\\nTFLITE_FILE=${SRCROOT}/ImageSegmentation/TFLite/deeplabv3.tflite\\nif test -f \\\"$TFLITE_FILE\\\"; then\\n    echo \\\"INFO: TF Lite model already existed. Skip downloading and use the local model.\\\"\\nelse\\n    curl -L -o ${TFLITE_FILE} https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_segmentation/ios/lite-model_deeplabv3_1_metadata_2.tflite\\n    echo \\\"INFO: Downloaded TensorFlow Lite model to $TFLITE_FILE .\\\"\\nfi\\n\";\n\t\t};\n\t\t9C4626EB48450F23C89E4443 /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-ImageSegmentation-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t7F67811723405195002A02F2 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t7F67812323405195002A02F2 /* ViewController.swift in Sources */,\n\t\t\t\t7F9738DD234071180058C202 /* ImageSegmentationHelper.swift in Sources */,\n\t\t\t\t7F9738E22341EFDD0058C202 /* TFLiteExtension.swift in Sources */,\n\t\t\t\t7F9738E72343CE9F0058C202 /* UIKitExtension.swift in Sources */,\n\t\t\t\t7F67811F23405195002A02F2 /* AppDelegate.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\t7F67812423405195002A02F2 /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t7F67812523405195002A02F2 /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7F67812923405196002A02F2 /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t7F67812A23405196002A02F2 /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t7F67812D23405196002A02F2 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t7F67812E23405196002A02F2 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = s;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t7F67813023405196002A02F2 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = AFA20F57D312CC344EAF47D5 /* Pods-ImageSegmentation.debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_IDENTITY = \"Apple Development\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tEXCLUDED_ARCHS = \"\";\n\t\t\t\t\"EXCLUDED_ARCHS[sdk=iphonesimulator*]\" = arm64;\n\t\t\t\tINFOPLIST_FILE = ImageSegmentation/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.tensorflow.examples.ImageSegmentation;\n\t\t\t\tPRODUCT_NAME = \"TFL Segmentation\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t7F67813123405196002A02F2 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 8857CE6BF260FD2EBF0CDB36 /* Pods-ImageSegmentation.release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_IDENTITY = \"Apple Development\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tEXCLUDED_ARCHS = \"\";\n\t\t\t\t\"EXCLUDED_ARCHS[sdk=iphonesimulator*]\" = arm64;\n\t\t\t\tINFOPLIST_FILE = ImageSegmentation/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.tensorflow.examples.ImageSegmentation;\n\t\t\t\tPRODUCT_NAME = \"TFL Segmentation\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t7F67811623405195002A02F2 /* Build configuration list for PBXProject \"ImageSegmentation\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t7F67812D23405196002A02F2 /* Debug */,\n\t\t\t\t7F67812E23405196002A02F2 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t7F67812F23405196002A02F2 /* Build configuration list for PBXNativeTarget \"ImageSegmentation\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t7F67813023405196002A02F2 /* Debug */,\n\t\t\t\t7F67813123405196002A02F2 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 7F67811323405195002A02F2 /* Project object */;\n}\n"
  },
  {
    "path": "lite/examples/image_segmentation/ios/ImageSegmentation.xcodeproj/xcshareddata/xcschemes/ImageSegmentation.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1340\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"7F67811A23405195002A02F2\"\n               BuildableName = \"TFL Segmentation.app\"\n               BlueprintName = \"ImageSegmentation\"\n               ReferencedContainer = \"container:ImageSegmentation.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Release\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"7F67811A23405195002A02F2\"\n            BuildableName = \"TFL Segmentation.app\"\n            BlueprintName = \"ImageSegmentation\"\n            ReferencedContainer = \"container:ImageSegmentation.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"7F67811A23405195002A02F2\"\n            BuildableName = \"TFL Segmentation.app\"\n            BlueprintName = \"ImageSegmentation\"\n            ReferencedContainer = \"container:ImageSegmentation.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "lite/examples/image_segmentation/ios/Podfile",
    "content": "# Uncomment the next line to define a global platform for your project\nplatform :ios, '12.0'\n\ntarget 'ImageSegmentation' do\n  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks\n  use_frameworks!\n\n  # Pods for ImageSegmentation\n  pod 'TensorFlowLiteTaskVision'\n\nend\n"
  },
  {
    "path": "lite/examples/image_segmentation/ios/README.md",
    "content": "# Image Segmentation iOS sample\n\n![Screenshot](http://download.tensorflow.org/models/tflite/screenshots/image_segmentation_screenshot_gpu.png)\n\n*Image from [Pixabay](https://pixabay.com/photos/cap-boy-smile-tomboy-emotions-2923682/).*\n\n## Requirements\n\n*  Xcode 10.3 (installed on a Mac machine)\n*  An iOS Simulator running iOS 12 or above\n*  Xcode command-line tools (run ```xcode-select --install```)\n*  CocoaPods (run ```sudo gem install cocoapods```)\n\n## Build and run\n\n1. Clone the TensorFlow examples GitHub repository to your computer to get the\ndemo\napplication.<br/>\n```git clone https://github.com/tensorflow/examples```\n1. Install the pod to generate the workspace file:<br/>\n```cd examples/lite/examples/image_segmentation/ios && pod install```<br/>\nNote: If you have installed this pod before and that command doesn't work, try ```pod update```.<br/>\nAt the end of this step you should have a directory called ```ImageSegmentation.xcworkspace```.\n1. Open the project in Xcode with the following command:<br/>\n```open ImageSegmentation.xcworkspace```<br/>\nThis launches Xcode and opens the ```ImageSegmentation``` project.\n1. Select `Product -> Run` to install the app on an iOS Simulator or a physical\ndevice.\n"
  },
  {
    "path": "lite/examples/image_segmentation/raspberry_pi/README.md",
    "content": "# TensorFlow Lite Python image segmentation example with Raspberry Pi.\n\nThis example uses [TensorFlow Lite](https://tensorflow.org/lite) with Python on\na Raspberry Pi to perform real-time image segmentation using images streamed\nfrom the camera.\n\nAt the end of this page, there are extra steps to accelerate the example using\nthe Coral USB Accelerator to increase inference speed.\n\n## Set up your hardware\n\nBefore you begin, you need to\n[set up your Raspberry Pi](https://projects.raspberrypi.org/en/projects/raspberry-pi-setting-up)\nwith Raspberry Pi OS (preferably updated to Buster).\n\nYou also need to\n[connect and configure the Pi Camera](https://www.raspberrypi.org/documentation/configuration/camera.md)\nif you use the Pi Camera. This code also works with USB camera connect to the\nRaspberry Pi.\n\nAnd to see the results from the camera, you need a monitor connected to the\nRaspberry Pi. It's okay if you're using SSH to access the Pi shell (you don't\nneed to use a keyboard connected to the Pi)—you only need a monitor attached to\nthe Pi to see the camera stream.\n\n## Install the TensorFlow Lite runtime\n\nIn this project, all you need from the TensorFlow Lite API is the `Interpreter`\nclass. So instead of installing the large `tensorflow` package, we're using the\nmuch smaller `tflite_runtime` package.\n\nTo install this on your Raspberry Pi, follow the instructions in the\n[Python quickstart](https://www.tensorflow.org/lite/guide/python#install_tensorflow_lite_for_python).\n\nYou can install the TFLite runtime using this script.\n\n```\nsh setup.sh\n```\n\n## Download the example files\n\nFirst, clone this Git repo onto your Raspberry Pi like this:\n\n```\ngit clone https://github.com/tensorflow/examples --depth 1\n```\n\nThen use our script to install a couple Python packages, and download the\n`Deeplabv3` model:\n\n```\ncd examples/lite/examples/image_segmentation/raspberry_pi\n\n# The script install the required dependencies and download the TFLite models.\nsh setup.sh\n```\n\n## Run the example\n\n```\npython3 segment.py\n```\n\n*   You can optionally specify the `model` parameter to set the TensorFlow Lite\n    model to be used:\n    *   The default value is `deeplabv3.tflite`\n    *   Image segmentation models from TensorFlow Hub **with metadata** are\n        supported.\n*   You can optionally specify the `displayMode` parameter to change how the\n    segmentation result is displayed:\n    *   Use values: `overlay`, `side-by-side`.\n    *   The default value is `overlay`.\n*   Example usage:\n\n```\npython3 main.py\n  --model somemodel.tflite\n  --displayMode side-by-side\n```\n\n**Overlay mode** ![Overlay Image](overlay_mode.png)\n\n**Side-by-side mode** ![Side-by-side Image](sidebyside_mode.png)\n\nFor more information about executing inferences with TensorFlow Lite, read\n[TensorFlow Lite inference](https://www.tensorflow.org/lite/guide/inference).\n\n## Speed up model inference (optional)\n\nIf you want to significantly speed up the inference time, you can attach an\n[Coral USB Accelerator](https://coral.withgoogle.com/products/accelerator)—a USB\naccessory that adds the\n[Edge TPU ML accelerator](https://coral.withgoogle.com/docs/edgetpu/faq/) to any\nLinux-based system.\n\nIf you have a Coral USB Accelerator, you can run the sample with it enabled:\n\n1.  First, be sure you have completed the\n    [USB Accelerator setup instructions](https://coral.withgoogle.com/docs/accelerator/get-started/).\n\n2.  Run the image segmentation script using the EdgeTPU TFLite model and enable\n    the EdgeTPU option.\n\n```\npython3 main.py \\\n  --enableEdgeTPU\n  --model deeplabv3_edgetpu.tflite\n```\n\nYou should see significantly faster inference speeds.\n\nFor more information about creating and running TensorFlow Lite models with\nCoral devices, read\n[TensorFlow models on the Edge TPU](https://coral.withgoogle.com/docs/edgetpu/models-intro/).\n"
  },
  {
    "path": "lite/examples/image_segmentation/raspberry_pi/requirements.txt",
    "content": "argparse\nnumpy>=1.20.0  # To ensure compatibility with OpenCV on Raspberry Pi.\nopencv-python~=4.5.3.56\ntflite-support>=0.4.2\nprotobuf>=3.18.0,<4\n"
  },
  {
    "path": "lite/examples/image_segmentation/raspberry_pi/segment.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Main script to run image segmentation.\"\"\"\n\nimport argparse\nimport sys\nimport time\nfrom typing import List\n\nimport cv2\nimport numpy as np\nfrom tflite_support.task import core\nfrom tflite_support.task import processor\nfrom tflite_support.task import vision\nimport utils\n\n# Visualization parameters\n_FPS_AVERAGE_FRAME_COUNT = 10\n_FPS_LEFT_MARGIN = 24  # pixels\n_LEGEND_TEXT_COLOR = (0, 0, 255)  # red\n_LEGEND_BACKGROUND_COLOR = (255, 255, 255)  # white\n_LEGEND_FONT_SIZE = 1\n_LEGEND_FONT_THICKNESS = 1\n_LEGEND_ROW_SIZE = 20  # pixels\n_LEGEND_RECT_SIZE = 16  # pixels\n_LABEL_MARGIN = 10\n_OVERLAY_ALPHA = 0.5\n_PADDING_WIDTH_FOR_LEGEND = 150  # pixels\n\n\ndef run(model: str, display_mode: str, num_threads: int, enable_edgetpu: bool,\n        camera_id: int, width: int, height: int) -> None:\n  \"\"\"Continuously run inference on images acquired from the camera.\n\n  Args:\n    model: Name of the TFLite image segmentation model.\n    display_mode: Name of mode to display image segmentation.\n    num_threads: Number of CPU threads to run the model.\n    enable_edgetpu: Whether to run the model on EdgeTPU.\n    camera_id: The camera id to be passed to OpenCV.\n    width: The width of the frame captured from the camera.\n    height: The height of the frame captured from the camera.\n  \"\"\"\n\n  # Initialize the image segmentation model.\n  base_options = core.BaseOptions(\n      file_name=model, use_coral=enable_edgetpu, num_threads=num_threads)\n  segmentation_options = processor.SegmentationOptions(\n      output_type=processor.OutputType.CATEGORY_MASK)\n  options = vision.ImageSegmenterOptions(\n      base_options=base_options, segmentation_options=segmentation_options)\n\n  segmenter = vision.ImageSegmenter.create_from_options(options)\n\n  # Variables to calculate FPS\n  counter, fps = 0, 0\n  start_time = time.time()\n\n  # Start capturing video input from the camera\n  cap = cv2.VideoCapture(camera_id)\n  cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)\n  cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)\n\n  # Continuously capture images from the camera and run inference.\n  while cap.isOpened():\n    success, image = cap.read()\n    if not success:\n      sys.exit(\n          'ERROR: Unable to read from webcam. Please verify your webcam settings.'\n      )\n\n    counter += 1\n    image = cv2.flip(image, 1)\n\n    # Convert the image from BGR to RGB as required by the TFLite model.\n    rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n\n    # Create TensorImage from the RGB image\n    tensor_image = vision.TensorImage.create_from_array(rgb_image)\n    # Segment with each frame from camera.\n    segmentation_result = segmenter.segment(tensor_image)\n\n    # Convert the segmentation result into an image.\n    seg_map_img, found_colored_labels = utils.segmentation_map_to_image(\n        segmentation_result)\n\n    # Resize the segmentation mask to be the same shape as input image.\n    seg_map_img = cv2.resize(\n        seg_map_img,\n        dsize=(image.shape[1], image.shape[0]),\n        interpolation=cv2.INTER_NEAREST)\n\n    # Visualize segmentation result on image.\n    overlay = visualize(image, seg_map_img, display_mode, fps,\n                        found_colored_labels)\n\n    # Calculate the FPS\n    if counter % _FPS_AVERAGE_FRAME_COUNT == 0:\n      end_time = time.time()\n      fps = _FPS_AVERAGE_FRAME_COUNT / (end_time - start_time)\n      start_time = time.time()\n\n    # Stop the program if the ESC key is pressed.\n    if cv2.waitKey(1) == 27:\n      break\n    cv2.imshow('image_segmentation', overlay)\n\n  cap.release()\n  cv2.destroyAllWindows()\n\n\ndef visualize(input_image: np.ndarray, segmentation_map_image: np.ndarray,\n              display_mode: str, fps: float,\n              colored_labels: List[processor.ColoredLabel]) -> np.ndarray:\n  \"\"\"Visualize segmentation result on image.\n\n  Args:\n    input_image: The [height, width, 3] RGB input image.\n    segmentation_map_image: The [height, width, 3] RGB segmentation map image.\n    display_mode: How the segmentation map should be shown. 'overlay' or\n      'side-by-side'.\n    fps: Value of fps.\n    colored_labels: List of colored labels found in the segmentation result.\n\n  Returns:\n    Input image overlaid with segmentation result.\n  \"\"\"\n  # Show the input image and the segmentation map image.\n  if display_mode == 'overlay':\n    # Overlay mode.\n    overlay = cv2.addWeighted(input_image, _OVERLAY_ALPHA,\n                              segmentation_map_image, _OVERLAY_ALPHA, 0)\n  elif display_mode == 'side-by-side':\n    # Side by side mode.\n    overlay = cv2.hconcat([input_image, segmentation_map_image])\n  else:\n    sys.exit(f'ERROR: Unsupported display mode: {display_mode}.')\n\n  # Show the FPS\n  fps_text = 'FPS = ' + str(int(fps))\n  text_location = (_FPS_LEFT_MARGIN, _LEGEND_ROW_SIZE)\n  cv2.putText(overlay, fps_text, text_location, cv2.FONT_HERSHEY_PLAIN,\n              _LEGEND_FONT_SIZE, _LEGEND_TEXT_COLOR, _LEGEND_FONT_THICKNESS)\n\n  # Initialize the origin coordinates of the label.\n  legend_x = overlay.shape[1] + _LABEL_MARGIN\n  legend_y = overlay.shape[0] // _LEGEND_ROW_SIZE + _LABEL_MARGIN\n\n  # Expand the frame to show the label.\n  overlay = cv2.copyMakeBorder(overlay, 0, 0, 0, _PADDING_WIDTH_FOR_LEGEND,\n                               cv2.BORDER_CONSTANT, None,\n                               _LEGEND_BACKGROUND_COLOR)\n\n  # Show the label on right-side frame.\n  for colored_label in colored_labels:\n    rect_color = colored_label.color\n    start_point = (legend_x, legend_y)\n    end_point = (legend_x + _LEGEND_RECT_SIZE, legend_y + _LEGEND_RECT_SIZE)\n    cv2.rectangle(overlay, start_point, end_point, rect_color,\n                  -_LEGEND_FONT_THICKNESS)\n\n    label_location = legend_x + _LEGEND_RECT_SIZE + _LABEL_MARGIN, legend_y + _LABEL_MARGIN\n    cv2.putText(overlay, colored_label.category_name, label_location,\n                cv2.FONT_HERSHEY_PLAIN, _LEGEND_FONT_SIZE, _LEGEND_TEXT_COLOR,\n                _LEGEND_FONT_THICKNESS)\n    legend_y += (_LEGEND_RECT_SIZE + _LABEL_MARGIN)\n\n  return overlay\n\n\ndef main():\n  parser = argparse.ArgumentParser(\n      formatter_class=argparse.ArgumentDefaultsHelpFormatter)\n  parser.add_argument(\n      '--model',\n      help='Name of image segmentation model.',\n      required=False,\n      default='deeplabv3.tflite')\n  parser.add_argument(\n      '--displayMode',\n      help='Mode to display image segmentation.',\n      required=False,\n      default='overlay')\n  parser.add_argument(\n      '--numThreads',\n      help='Number of CPU threads to run the model.',\n      required=False,\n      default=4)\n  parser.add_argument(\n      '--enableEdgeTPU',\n      help='Whether to run the model on EdgeTPU.',\n      action='store_true',\n      required=False,\n      default=False)\n  parser.add_argument(\n      '--cameraId', help='Id of camera.', required=False, default=0)\n  parser.add_argument(\n      '--frameWidth',\n      help='Width of frame to capture from camera.',\n      required=False,\n      default=640)\n  parser.add_argument(\n      '--frameHeight',\n      help='Height of frame to capture from camera.',\n      required=False,\n      default=480)\n  args = parser.parse_args()\n\n  run(args.model, args.displayMode, int(args.numThreads),\n      bool(args.enableEdgeTPU), int(args.cameraId), args.frameWidth,\n      args.frameHeight)\n\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "lite/examples/image_segmentation/raspberry_pi/setup.sh",
    "content": "#!/bin/bash\n\nif [ $# -eq 0 ]; then\n  DATA_DIR=\"./\"\nelse\n  DATA_DIR=\"$1\"\nfi\n\n# Install Python dependencies\npython3 -m pip install pip --upgrade\npython3 -m pip install -r requirements.txt\n\n# Download TF Lite models with metadata.\nFILE=${DATA_DIR}/deeplabv3.tflite\nif [ ! -f \"$FILE\" ]; then\n  curl \\\n    -L 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_segmentation/rpi/lite-model_deeplabv3_1_metadata_2.tflite' \\\n    -o ${FILE}\nfi\n\nFILE=${DATA_DIR}/deeplabv3_edgetpu.tflite\nif [ ! -f \"$FILE\" ]; then\n  curl \\\n    -L 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/image_segmentation/rpi/deeplabv3_edgetpu.tflite' \\\n    -o ${FILE}\nfi\n\necho -e \"Downloaded files are in ${DATA_DIR}\"\n"
  },
  {
    "path": "lite/examples/image_segmentation/raspberry_pi/test_data/ground_truth_label.txt",
    "content": "background\nperson\nhorse"
  },
  {
    "path": "lite/examples/image_segmentation/raspberry_pi/utils.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Util functions to visualize image segmentation model output.\"\"\"\n\nfrom typing import List\n\nimport numpy as np\nfrom tflite_support.task import processor\n\n\ndef segmentation_map_to_image(\n    segmentation: processor.SegmentationResult\n) -> (np.ndarray, List[processor.ColoredLabel]):\n  \"\"\"Convert the SegmentationResult into a RGB image.\n\n  Args:\n    segmentation: An output of a image segmentation model.\n\n  Returns:\n    seg_map_img: The visualized segmentation result as an RGB image.\n    found_colored_labels: The list of ColoredLabels found in the image.\n  \"\"\"\n  segmentation = segmentation.segmentations[0]\n  # Get the list of unique labels from the model output.\n  masks = np.frombuffer(segmentation.category_mask, dtype=np.uint8)\n  found_label_indices, inverse_map, counts = np.unique(\n      masks, return_inverse=True, return_counts=True)\n  count_dict = dict(zip(found_label_indices, counts))\n\n  # Sort the list of unique label so that the class with the most pixel comes\n  # first.\n  sorted_label_indices = sorted(\n      found_label_indices, key=lambda index: count_dict[index], reverse=True)\n  found_colored_labels = [\n      segmentation.colored_labels[idx] for idx in sorted_label_indices\n  ]\n\n  # Convert segmentation map into RGB image of the same size as the input image.\n  # Note: We use the inverse map to avoid running the heavy loop in Python and\n  # pass it over to Numpy's C++ implementation to improve performance.\n  found_colors = [item.color for item in found_colored_labels]\n  output_shape = [segmentation.width, segmentation.height, 3]\n  seg_map_img = np.array(found_colors)[inverse_map].reshape(\n      output_shape).astype(np.uint8)\n\n  return seg_map_img, found_colored_labels\n"
  },
  {
    "path": "lite/examples/model_personalization/README.md",
    "content": "# TensorFlow Lite Example On-device Model Personalization\n\nThis example illustrates a way of personalizing a TFLite model\non-device without sending any data to the server. It builds\non top of existing TFLite functionality, and can be adapted\nfor various tasks and models.\n\n## Quickstart\n\nPre-requisites:\n\n- [Android Studio](https://developer.android.com/studio).\n- [Python 3.7+](https://www.python.org/downloads/).\n- [`virtualenv`](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#installing-virtualenv)\n  (usually comes preinstalled with Python).\n- Physical Android device with camera.\n\n### Prepare the TfLite model\n\nFor the following workflow, a Python virtual environment setup is optional but\nrecommended. We assume Python 3.5+ by default.\n\n```shell\npushd transfer_learning\n\n# Create a virtualenv.\npython3 -m venv env\n# Activate the created virtualenv.\nsource env/bin/activate\n\n# Install the package requirements.\npip install -r requirements.txt\n\n# Generate the model flatbuffer file `model.tflite` in the current directory.\npython generate_training_model.py\npopd\n\n# Copy over the flatbuffer file to the `android` assets directory.\ncp transfer_learning/model.tflite android/app/src/main/assets/model/model.tflite\n```\n\n### Install and run the application\n\nYou can either build and run the application inside Android Studio\nor run command line to do so.\n\nIf you want to use Android Studio, first import the\nproject into Android Studio (point it to the top-level `build.gradle`\nfile), connect your Android device to your machine, and use the\n`Run` button in Android Studio. If the `Run` button is inactive,\nfirst add an Android application build configuration for the `app`\nmodule.\n\nIf you want to build and install on Linux directly, you can first\nswitch to the `android` folder, then execute\n\n```shell\ngradle wrapper\n./gradlew build\n```\n\nThen you can run the following command to install the apk\n\n```shell\nadb install ./app/build/outputs/apk/debug/app-debug.apk\n```\n\nIf you need to install gradle, you can reference this [link](https://docs.gradle.org/current/userguide/installation.html)\n\nThe running application will look like\n\n<p align=\"center\">\n  <img src=\"app_screenshot.png\" alt=\"app_screenshot\" width=\"350\"/>\n</p>\n\nYou should now have the app started on your device. Please refer to the `Help`\nmenu for more detailed instruction.\n\nThe buttons at the bottom of the screen correspond to classes that the app\nlearns to distinguish between. Initially, the confidence scores\n(numbers on the buttons) for all classes will be either random\nor constant depending on the model.\n\nTo start training the classifier, you need at least one image captured.\nTo take a photo and associate it with a class, press the corresponding\nclass button. For better results and accuracy, please take at least 10\nimages per class, ideally with different background and/or object orientation.\n\nAfter collecting at least 1 sample photos, the \"Train\" button should\nbecome active. Press it, and wait for a few seconds until the loss\ngoes down. Then press \"Pause\", and switch to the inference\nmode in the top-right corner.  The classifier should now attempt\nto predict the class of the camera input class in real time.\n\n## Structure\n\nThere are three main parts of this project:\n\n- Model Generation: a Python CLI that allows you to\n  define and generate your personalizable model. The code\n  lives under `transfer_learning` directory.\n\n- Android library: a library that allows you to use models\n  generated by the convert from an Android app. The code\n  lives under `android/transfer_api` directory in a separate\n  Gradle module.\n\n- Android classifier app: an application that illustrates\n  how to use the model personalization library. The code lives\n  under `android/app`.\n\n### Customizing the model\n\nFirst, you need to pick the models that you want to use with model\npersonalization. A TFLite on-device personalization model has two parts:\nthe base model, which is typically trained for a generic data-rich task,\nand the head model, which will be trained on device. Base model weights\nare fixed during conversion, and cannot be changed later. You need\nto pick two models for both parts respectively.\n\nAs an example of TensorFlow models, we provide one example in the\n`transfer_learning/generate_training_model.py` file, with the following\ncomponents:\n\n- Base Model: `tf.keras.applications.MobileNetV2`, a pre-trained\n  MobileNetV2 model, which is a good fit for image recognition tasks.\n\n- Head Model: a single dense layer followed by softmax activation.\n\n- Optimizer: `tf.keras.optimizers.Adam` with the default setting\n  `learning_rate=0.001`.\n\nFeel free to create/modify the Transfer Learning model structure and\nconfigurations.\n"
  },
  {
    "path": "lite/examples/model_personalization/android/README.md",
    "content": "# TensorFlow Lite Model Personalization Demo\n\n### Overview\n\nThis is a camera app that continuously classifies the objects in the frames seen\nby your device's back camera. This example illustrates a way of personalizing a\nTFLite model on-device without sending any data to the server. It can be adapted\nfor various tasks and models. These instructions walk you through building and\nrunning the demo on an Android device.\n\nThe model files are downloaded via Gradle scripts when you build and run the\napp. You don't need to do any steps to download TFLite models into the project\nexplicitly.\n\nThis application should be run on a physical Android device.\n\n![App example showing UI controls. Training mode.](screenshot1.jpg?raw=true\n\"Training mode\")\n\n![App example without UI controls. Inference mode.](screenshot2.jpg?raw=true\n\"Inference mode\")\n\n## Build the demo using Android Studio\n\n### Prerequisites\n\n* The **[Android Studio](https://developer.android.com/studio/index.html)**\n  IDE (Android Studio 2021.2.1 or newer). This sample has been tested on Android\n  Studio Chipmunk.\n\n* A physical Android device with a minimum OS version of SDK 23 (Android 6.0 -\n  Marshmallow) with developer mode enabled. The process of enabling developer\n  mode may vary by device.\n\n### Building\n\n* Open Android Studio. From the Welcome screen, select Open an existing Android\n  Studio project.\n\n* From the Open File or Project window that appears, navigate to and select the\n  tensorflow-lite/examples/model_personalization/android directory. Click OK.\n\n* If it asks you to do a Gradle Sync, click OK.\n\n* With your Android device connected to your computer and developer mode\n  enabled, click on the green Run arrow in Android Studio.\n\n### Models used\n\nDownloading, extraction, and placing the models into the assets folder is\nmanaged automatically by the download.gradle file.\n\n### Generate your model\n\nTo generate or customize your model you can read [here](../README.md).\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/build.gradle",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nplugins {\n    id 'com.android.application'\n    id 'org.jetbrains.kotlin.android'\n    id 'kotlin-kapt'\n    id 'androidx.navigation.safeargs'\n    id 'de.undercouch.download'\n}\n\nandroid {\n    compileSdk 32\n\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.modelpersonalization\"\n        minSdk 23\n        targetSdk 32\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n    kotlinOptions {\n        jvmTarget = '1.8'\n    }\n\n    buildFeatures {\n        viewBinding true\n    }\n\n    androidResources {\n        noCompress 'tflite'\n    }\n}\n\n// import DownloadModels task\nproject.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'\n\n// Download default models; if you wish to use your own models then\n// place them in the \"assets\" directory and comment out this line.\napply from: 'download_models.gradle'\n\ndependencies {\n\n    // Kotlin lang\n    implementation 'androidx.core:core-ktx:1.15.0'\n    implementation \"androidx.fragment:fragment-ktx:1.8.5\"\n\n    // App compat and UI things\n    implementation 'androidx.appcompat:appcompat:1.5.0'\n    implementation 'com.google.android.material:material:1.6.1'\n    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'\n\n    // Navigation library\n    def nav_version = \"2.5.1\"\n    implementation \"androidx.navigation:navigation-fragment-ktx:$nav_version\"\n    implementation \"androidx.navigation:navigation-ui-ktx:$nav_version\"\n\n    // CameraX core library\n    def camerax_version = '1.2.0-alpha04'\n    implementation \"androidx.camera:camera-core:$camerax_version\"\n\n    // CameraX Camera2 extensions\n    implementation \"androidx.camera:camera-camera2:$camerax_version\"\n\n    // CameraX Lifecycle library\n    implementation \"androidx.camera:camera-lifecycle:$camerax_version\"\n\n    // CameraX View class\n    implementation \"androidx.camera:camera-view:$camerax_version\"\n\n    //WindowManager\n    implementation 'androidx.window:window:1.1.0-alpha03'\n\n    // Unit testing\n    testImplementation 'junit:junit:4.13.2'\n\n    // Instrumented testing\n    androidTestImplementation 'androidx.test.ext:junit:1.1.3'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n\n    // Tensorflow lite dependencies\n    implementation 'org.tensorflow:tensorflow-lite:2.9.0'\n    implementation 'org.tensorflow:tensorflow-lite-gpu:2.9.0'\n    implementation 'org.tensorflow:tensorflow-lite-support:0.4.2'\n    implementation 'org.tensorflow:tensorflow-lite-select-tf-ops:2.9.0'\n}\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/download_models.gradle",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\ntask downloadModelFile(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/model_personalization/android/model.tflite'\n    dest project.ext.ASSET_DIR + '/model.tflite'\n    overwrite false\n}\n\npreBuild.dependsOn downloadModelFile\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:dist=\"http://schemas.android.com/apk/distribution\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"org.tensorflow.lite.examples.modelpersonalization\">\n\n    <!-- Enable instant app support -->\n    <dist:module dist:instant=\"true\" />\n\n    <!-- Declare features -->\n    <uses-feature android:name=\"android.hardware.camera\" />\n\n    <!-- Declare permissions -->\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:taskAffinity=\"\"\n        tools:ignore=\"AllowBackup\">\n\n        <activity\n            android:name=\".MainActivity\"\n            android:clearTaskOnLaunch=\"true\"\n            android:configChanges=\"orientation|screenLayout|screenSize|smallestScreenSize\"\n            android:exported=\"true\"\n            android:resizeableActivity=\"true\"\n            android:rotationAnimation=\"seamless\"\n            android:theme=\"@style/AppTheme\"\n            tools:targetApi=\"O\">\n\n            <!-- Main app intent filter -->\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n\n            <!-- Declare notch support -->\n            <meta-data\n                android:name=\"android.notch_support\"\n                android:value=\"true\" />\n        </activity>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/java/org/tensorflow/lite/examples/modelpersonalization/MainActivity.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.modelpersonalization\n\nimport android.os.Build\nimport androidx.appcompat.app.AppCompatActivity\nimport android.os.Bundle\nimport android.widget.Toast\nimport androidx.activity.viewModels\nimport org.tensorflow.lite.examples.modelpersonalization.databinding.ActivityMainBinding\nimport org.tensorflow.lite.examples.modelpersonalization.fragments.HelperDialog\nimport org.tensorflow.lite.examples.modelpersonalization.fragments.SettingFragment\n\nclass MainActivity : AppCompatActivity() {\n    private lateinit var activityMainBinding: ActivityMainBinding\n    private val viewModel: MainViewModel by viewModels()\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)\n        setContentView(activityMainBinding.root)\n\n        activityMainBinding.imgSetting.setOnClickListener {\n            if (viewModel.getCaptureMode() == true) {\n                SettingFragment().show(\n                    supportFragmentManager,\n                    SettingFragment.TAG\n                )\n            } else {\n                Toast.makeText(\n                    this, \"Change the setting only available in \" +\n                            \"training mode\", Toast.LENGTH_LONG\n                ).show()\n            }\n        }\n        activityMainBinding.tvHelper.setOnClickListener {\n            HelperDialog().show(supportFragmentManager, HelperDialog.TAG)\n        }\n    }\n\n    override fun onBackPressed() {\n        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {\n            // Workaround for Android Q memory leak issue in IRequestFinishCallback$Stub.\n            // (https://issuetracker.google.com/issues/139738913)\n            finishAfterTransition()\n        } else {\n            super.onBackPressed()\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/java/org/tensorflow/lite/examples/modelpersonalization/MainViewModel.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.modelpersonalization\n\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.ViewModel\nimport java.util.TreeMap\n\nclass MainViewModel : ViewModel() {\n    private val _numThread = MutableLiveData<Int>()\n    val numThreads get() = _numThread\n\n    private val _trainingState =\n        MutableLiveData(TrainingState.PREPARE)\n    val trainingState get() = _trainingState\n\n    private val _captureMode = MutableLiveData(true)\n    val captureMode get() = _captureMode\n\n    private val _numberOfSamples = MutableLiveData(TreeMap<String, Int>())\n    val numberOfSamples get() = _numberOfSamples\n\n    fun configModel(numThreads: Int) {\n        _numThread.value = numThreads\n    }\n\n    fun getNumThreads() = numThreads.value\n\n    fun setTrainingState(state: TrainingState) {\n        _trainingState.value = state\n    }\n\n    fun getTrainingState() = trainingState.value\n\n    fun setCaptureMode(isCapture: Boolean) {\n        _captureMode.value = isCapture\n    }\n\n    fun getCaptureMode() = captureMode.value\n\n    fun increaseNumberOfSample(className: String) {\n        val map: TreeMap<String, Int> = _numberOfSamples.value!!\n        val currentNumber: Int = if (map.containsKey(className)) {\n            map[className]!!\n        } else {\n            0\n        }\n        map[className] = currentNumber + 1\n        _numberOfSamples.postValue(map)\n    }\n\n    fun getNumberOfSample() = numberOfSamples.value\n\n    enum class TrainingState {\n        PREPARE, TRAINING, PAUSE\n    }\n}\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/java/org/tensorflow/lite/examples/modelpersonalization/TransferLearningHelper.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.modelpersonalization\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.os.Handler\nimport android.os.Looper\nimport android.os.SystemClock\nimport android.util.Log\nimport org.tensorflow.lite.DataType\nimport org.tensorflow.lite.Interpreter\nimport org.tensorflow.lite.support.common.FileUtil\nimport org.tensorflow.lite.support.common.ops.NormalizeOp\nimport org.tensorflow.lite.support.image.ImageProcessor\nimport org.tensorflow.lite.support.image.TensorImage\nimport org.tensorflow.lite.support.image.ops.ResizeOp\nimport org.tensorflow.lite.support.image.ops.ResizeWithCropOrPadOp\nimport org.tensorflow.lite.support.image.ops.Rot90Op\nimport org.tensorflow.lite.support.label.Category\nimport org.tensorflow.lite.support.label.TensorLabel\nimport org.tensorflow.lite.support.tensorbuffer.TensorBuffer\nimport java.io.IOException\nimport java.nio.FloatBuffer\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Executors\nimport kotlin.math.max\nimport kotlin.math.min\n\n\nclass TransferLearningHelper(\n    var numThreads: Int = 2,\n    val context: Context,\n    val classifierListener: ClassifierListener?\n) {\n\n    private var interpreter: Interpreter? = null\n    private val trainingSamples: MutableList<TrainingSample> = mutableListOf()\n    private var executor: ExecutorService? = null\n\n    //This lock guarantees that only one thread is performing training and\n    //inference at any point in time.\n    private val lock = Any()\n    private var targetWidth: Int = 0\n    private var targetHeight: Int = 0\n    private val handler = Handler(Looper.getMainLooper())\n\n    init {\n        if (setupModelPersonalization()) {\n            targetWidth = interpreter!!.getInputTensor(0).shape()[2]\n            targetHeight = interpreter!!.getInputTensor(0).shape()[1]\n        } else {\n            classifierListener?.onError(\"TFLite failed to init.\")\n        }\n    }\n\n    fun close() {\n        executor?.shutdownNow()\n        executor = null\n        interpreter = null\n    }\n\n    fun pauseTraining() {\n        executor?.shutdownNow()\n    }\n\n    private fun setupModelPersonalization(): Boolean {\n        val options = Interpreter.Options()\n        options.numThreads = numThreads\n        return try {\n            val modelFile = FileUtil.loadMappedFile(context, \"model.tflite\")\n            interpreter = Interpreter(modelFile, options)\n            true\n        } catch (e: IOException) {\n            classifierListener?.onError(\n                \"Model personalization failed to \" +\n                        \"initialize. See error logs for details\"\n            )\n            Log.e(TAG, \"TFLite failed to load model with error: \" + e.message)\n            false\n        }\n    }\n\n    // Process input image and add the output into list samples which are\n    // ready for training.\n    fun addSample(image: Bitmap, className: String, rotation: Int) {\n        synchronized(lock) {\n            if (interpreter == null) {\n                setupModelPersonalization()\n            }\n            processInputImage(image, rotation)?.let { tensorImage ->\n                val bottleneck = loadBottleneck(tensorImage)\n                trainingSamples.add(\n                    TrainingSample(\n                        bottleneck,\n                        encoding(classes.getValue(className))\n                    )\n                )\n            }\n        }\n    }\n\n    // Start training process\n    fun startTraining() {\n        if (interpreter == null) {\n            setupModelPersonalization()\n        }\n\n        // Create new thread for training process.\n        executor = Executors.newSingleThreadExecutor()\n        val trainBatchSize = getTrainBatchSize()\n\n        if (trainingSamples.size < trainBatchSize) {\n            throw RuntimeException(\n                String.format(\n                    \"Too few samples to start training: need %d, got %d\",\n                    trainBatchSize, trainingSamples.size\n                )\n            )\n        }\n\n        executor?.execute {\n            synchronized(lock) {\n                var avgLoss: Float\n\n                // Keep training until the helper pause or close.\n                while (executor?.isShutdown == false) {\n                    var totalLoss = 0f\n                    var numBatchesProcessed = 0\n\n                    // Shuffle training samples to reduce overfitting and\n                    // variance.\n                    trainingSamples.shuffle()\n\n                    trainingBatches(trainBatchSize)\n                        .forEach { trainingSamples ->\n                            val trainingBatchBottlenecks =\n                                MutableList(trainBatchSize) {\n                                    FloatArray(\n                                        BOTTLENECK_SIZE\n                                    )\n                                }\n\n                            val trainingBatchLabels =\n                                MutableList(trainBatchSize) {\n                                    FloatArray(\n                                        classes.size\n                                    )\n                                }\n\n                            // Copy a training sample list into two different\n                            // input training lists.\n                            trainingSamples.forEachIndexed { index, trainingSample ->\n                                trainingBatchBottlenecks[index] =\n                                    trainingSample.bottleneck\n                                trainingBatchLabels[index] =\n                                    trainingSample.label\n                            }\n\n                            val loss = training(\n                                trainingBatchBottlenecks,\n                                trainingBatchLabels\n                            )\n                            totalLoss += loss\n                            numBatchesProcessed++\n                        }\n\n                    // Calculate the average loss after training all batches.\n                    avgLoss = totalLoss / numBatchesProcessed\n                    handler.post {\n                        classifierListener?.onLossResults(avgLoss)\n                    }\n                }\n            }\n        }\n    }\n\n    // Runs one training step with the given bottleneck batches and labels\n    // and return the loss number.\n    private fun training(\n        bottlenecks: MutableList<FloatArray>,\n        labels: MutableList<FloatArray>\n    ): Float {\n        val inputs: MutableMap<String, Any> = HashMap()\n        inputs[TRAINING_INPUT_BOTTLENECK_KEY] = bottlenecks.toTypedArray()\n        inputs[TRAINING_INPUT_LABELS_KEY] = labels.toTypedArray()\n\n        val outputs: MutableMap<String, Any> = HashMap()\n        val loss = FloatBuffer.allocate(1)\n        outputs[TRAINING_OUTPUT_KEY] = loss\n\n        interpreter?.runSignature(inputs, outputs, TRAINING_KEY)\n        return loss.get(0)\n    }\n\n    // Invokes inference on the given image batches.\n    fun classify(bitmap: Bitmap, rotation: Int) {\n        processInputImage(bitmap, rotation)?.let { image ->\n            synchronized(lock) {\n                if (interpreter == null) {\n                    setupModelPersonalization()\n                }\n\n                // Inference time is the difference between the system time at the start and finish of the\n                // process\n                var inferenceTime = SystemClock.uptimeMillis()\n\n                val inputs: MutableMap<String, Any> = HashMap()\n                inputs[INFERENCE_INPUT_KEY] = image.buffer\n\n                val outputs: MutableMap<String, Any> = HashMap()\n                val output = TensorBuffer.createFixedSize(\n                    intArrayOf(1, 4),\n                    DataType.FLOAT32\n                )\n                outputs[INFERENCE_OUTPUT_KEY] = output.buffer\n\n                interpreter?.runSignature(inputs, outputs, INFERENCE_KEY)\n                val tensorLabel = TensorLabel(classes.keys.toList(), output)\n                val result = tensorLabel.categoryList\n\n                inferenceTime = SystemClock.uptimeMillis() - inferenceTime\n\n                classifierListener?.onResults(result, inferenceTime)\n            }\n        }\n    }\n\n    // Loads the bottleneck feature from the given image array.\n    private fun loadBottleneck(image: TensorImage): FloatArray {\n        val inputs: MutableMap<String, Any> = HashMap()\n        inputs[LOAD_BOTTLENECK_INPUT_KEY] = image.buffer\n        val outputs: MutableMap<String, Any> = HashMap()\n        val bottleneck = Array(1) { FloatArray(BOTTLENECK_SIZE) }\n        outputs[LOAD_BOTTLENECK_OUTPUT_KEY] = bottleneck\n        interpreter?.runSignature(inputs, outputs, LOAD_BOTTLENECK_KEY)\n        return bottleneck[0]\n    }\n\n    // Preprocess the image and convert it into a TensorImage for classification.\n    private fun processInputImage(\n        image: Bitmap,\n        imageRotation: Int\n    ): TensorImage? {\n        val height = image.height\n        val width = image.width\n        val cropSize = min(height, width)\n        val imageProcessor = ImageProcessor.Builder()\n            .add(Rot90Op(-imageRotation / 90))\n            .add(ResizeWithCropOrPadOp(cropSize, cropSize))\n            .add(\n                ResizeOp(\n                    targetHeight,\n                    targetWidth,\n                    ResizeOp.ResizeMethod.BILINEAR\n                )\n            )\n            .add(NormalizeOp(0f, 255f))\n            .build()\n        val tensorImage = TensorImage(DataType.FLOAT32)\n        tensorImage.load(image)\n        return imageProcessor.process(tensorImage)\n    }\n\n    // encode the classes name to float array\n    private fun encoding(id: Int): FloatArray {\n        val classEncoded = FloatArray(4) { 0f }\n        classEncoded[id] = 1f\n        return classEncoded\n    }\n\n    // Training model expected batch size.\n    private fun getTrainBatchSize(): Int {\n        return min(\n            max( /* at least one sample needed */1, trainingSamples.size),\n            EXPECTED_BATCH_SIZE\n        )\n    }\n\n    // Constructs an iterator that iterates over training sample batches.\n    private fun trainingBatches(trainBatchSize: Int): Iterator<List<TrainingSample>> {\n        return object : Iterator<List<TrainingSample>> {\n            private var nextIndex = 0\n\n            override fun hasNext(): Boolean {\n                return nextIndex < trainingSamples.size\n            }\n\n            override fun next(): List<TrainingSample> {\n                val fromIndex = nextIndex\n                val toIndex: Int = nextIndex + trainBatchSize\n                nextIndex = toIndex\n                return if (toIndex >= trainingSamples.size) {\n                    // To keep batch size consistent, last batch may include some elements from the\n                    // next-to-last batch.\n                    trainingSamples.subList(\n                        trainingSamples.size - trainBatchSize,\n                        trainingSamples.size\n                    )\n                } else {\n                    trainingSamples.subList(fromIndex, toIndex)\n                }\n            }\n        }\n    }\n\n    interface ClassifierListener {\n        fun onError(error: String)\n        fun onResults(results: List<Category>?, inferenceTime: Long)\n        fun onLossResults(lossNumber: Float)\n    }\n\n    companion object {\n        const val CLASS_ONE = \"1\"\n        const val CLASS_TWO = \"2\"\n        const val CLASS_THREE = \"3\"\n        const val CLASS_FOUR = \"4\"\n        private val classes = mapOf(\n            CLASS_ONE to 0,\n            CLASS_TWO to 1,\n            CLASS_THREE to 2,\n            CLASS_FOUR to 3\n        )\n        private const val LOAD_BOTTLENECK_INPUT_KEY = \"feature\"\n        private const val LOAD_BOTTLENECK_OUTPUT_KEY = \"bottleneck\"\n        private const val LOAD_BOTTLENECK_KEY = \"load\"\n\n        private const val TRAINING_INPUT_BOTTLENECK_KEY = \"bottleneck\"\n        private const val TRAINING_INPUT_LABELS_KEY = \"label\"\n        private const val TRAINING_OUTPUT_KEY = \"loss\"\n        private const val TRAINING_KEY = \"train\"\n\n        private const val INFERENCE_INPUT_KEY = \"feature\"\n        private const val INFERENCE_OUTPUT_KEY = \"output\"\n        private const val INFERENCE_KEY = \"infer\"\n\n        private const val BOTTLENECK_SIZE = 1 * 7 * 7 * 1280\n        private const val EXPECTED_BATCH_SIZE = 20\n        private const val TAG = \"ModelPersonalizationHelper\"\n    }\n\n    data class TrainingSample(val bottleneck: FloatArray, val label: FloatArray)\n}\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/java/org/tensorflow/lite/examples/modelpersonalization/fragments/CameraFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.modelpersonalization.fragments\n\nimport android.annotation.SuppressLint\nimport android.content.res.Configuration\nimport android.graphics.Bitmap\nimport android.os.Bundle\nimport android.os.Handler\nimport android.os.Looper\nimport android.os.SystemClock\nimport android.util.Log\nimport android.view.LayoutInflater\nimport android.view.MotionEvent\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.TextView\nimport android.widget.Toast\nimport androidx.appcompat.content.res.AppCompatResources\nimport androidx.camera.core.Preview\nimport androidx.camera.core.ImageAnalysis\nimport androidx.camera.core.Camera\nimport androidx.camera.core.CameraSelector\nimport androidx.camera.core.AspectRatio\nimport androidx.camera.core.ImageProxy\nimport androidx.camera.lifecycle.ProcessCameraProvider\nimport androidx.core.content.ContextCompat\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.activityViewModels\nimport androidx.navigation.Navigation\nimport org.tensorflow.lite.examples.modelpersonalization.MainViewModel\nimport org.tensorflow.lite.examples.modelpersonalization.R\nimport org.tensorflow.lite.examples.modelpersonalization.TransferLearningHelper\nimport org.tensorflow.lite.examples.modelpersonalization.TransferLearningHelper.Companion.CLASS_FOUR\nimport org.tensorflow.lite.examples.modelpersonalization.TransferLearningHelper.Companion.CLASS_ONE\nimport org.tensorflow.lite.examples.modelpersonalization.TransferLearningHelper.Companion.CLASS_THREE\nimport org.tensorflow.lite.examples.modelpersonalization.TransferLearningHelper.Companion.CLASS_TWO\nimport org.tensorflow.lite.examples.modelpersonalization.databinding.FragmentCameraBinding\nimport org.tensorflow.lite.support.label.Category\nimport java.util.Locale\nimport java.util.concurrent.ConcurrentLinkedQueue\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Executors\n\nclass CameraFragment : Fragment(),\n    TransferLearningHelper.ClassifierListener {\n\n    companion object {\n        private const val TAG = \"Model Personalization\"\n        private const val LONG_PRESS_DURATION = 500\n        private const val SAMPLE_COLLECTION_DELAY = 300\n    }\n\n    private var _fragmentCameraBinding: FragmentCameraBinding? = null\n    private val fragmentCameraBinding\n        get() = _fragmentCameraBinding!!\n\n    private lateinit var transferLearningHelper: TransferLearningHelper\n    private lateinit var bitmapBuffer: Bitmap\n\n    private val viewModel: MainViewModel by activityViewModels()\n    private var preview: Preview? = null\n    private var imageAnalyzer: ImageAnalysis? = null\n    private var camera: Camera? = null\n    private var cameraProvider: ProcessCameraProvider? = null\n    private var previousClass: String? = null\n\n    /** Blocking camera operations are performed using this executor */\n    private lateinit var cameraExecutor: ExecutorService\n\n    // When the user presses the \"add sample\" button for some class,\n    // that class will be added to this queue. It is later extracted by\n    // InferenceThread and processed.\n    private val addSampleRequests = ConcurrentLinkedQueue<String>()\n\n    private var sampleCollectionButtonPressedTime: Long = 0\n    private var isCollectingSamples = false\n    private val sampleCollectionHandler = Handler(Looper.getMainLooper())\n    private val onAddSampleTouchListener =\n        View.OnTouchListener { view, motionEvent ->\n            when (motionEvent.action) {\n                MotionEvent.ACTION_DOWN -> {\n                    isCollectingSamples = true\n                    sampleCollectionButtonPressedTime =\n                        SystemClock.uptimeMillis()\n                    sampleCollectionHandler.post(object : Runnable {\n                        override fun run() {\n                            val timePressed =\n                                SystemClock.uptimeMillis() - sampleCollectionButtonPressedTime\n                            view.findViewById<View>(view.id).performClick()\n                            if (timePressed < LONG_PRESS_DURATION) {\n                                sampleCollectionHandler.postDelayed(\n                                    this,\n                                    LONG_PRESS_DURATION.toLong()\n                                )\n                            } else if (isCollectingSamples) {\n                                val className: String =\n                                    getClassNameFromResourceId(view.id)\n                                addSampleRequests.add(className)\n                                sampleCollectionHandler.postDelayed(\n                                    this,\n                                    SAMPLE_COLLECTION_DELAY.toLong()\n                                )\n                            }\n                        }\n                    })\n                }\n                MotionEvent.ACTION_UP -> {\n                    sampleCollectionHandler.removeCallbacksAndMessages(null)\n                    isCollectingSamples = false\n                }\n            }\n            true\n        }\n\n    override fun onResume() {\n        super.onResume()\n\n        if (!PermissionsFragment.hasPermissions(requireContext())) {\n            Navigation.findNavController(\n                requireActivity(),\n                R.id.fragment_container\n            ).navigate(CameraFragmentDirections.actionCameraToPermissions())\n        }\n    }\n\n    override fun onDestroyView() {\n        _fragmentCameraBinding = null\n        super.onDestroyView()\n\n        // Shut down our background executor\n        cameraExecutor.shutdown()\n    }\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View {\n        _fragmentCameraBinding =\n            FragmentCameraBinding.inflate(inflater, container, false)\n\n        return fragmentCameraBinding.root\n    }\n\n    @SuppressLint(\"MissingPermission\", \"ClickableViewAccessibility\")\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n\n        transferLearningHelper = TransferLearningHelper(\n            context = requireContext(),\n            classifierListener = this\n        )\n\n        cameraExecutor = Executors.newSingleThreadExecutor()\n\n        viewModel.numThreads.observe(viewLifecycleOwner) {\n            transferLearningHelper.numThreads = it\n            transferLearningHelper.close()\n            if (viewModel.getTrainingState() != MainViewModel.TrainingState.PREPARE) {\n                // If the model is training, continue training with old image\n                // sets.\n                viewModel.setTrainingState(MainViewModel.TrainingState.TRAINING)\n                transferLearningHelper.startTraining()\n            }\n        }\n\n        viewModel.trainingState.observe(viewLifecycleOwner) {\n            updateTrainingButtonState()\n        }\n\n        viewModel.captureMode.observe(viewLifecycleOwner) { isCaptureMode ->\n            if (isCaptureMode) {\n                viewModel.getNumberOfSample()?.let {\n                    updateNumberOfSample(it)\n                }\n                // Unhighlight all class buttons\n                highlightResult(null)\n            }\n\n            // Update the UI after switch to training mode.\n            updateTrainingButtonState()\n        }\n\n        viewModel.numberOfSamples.observe(viewLifecycleOwner) {\n            // Update the number of samples\n            updateNumberOfSample(it)\n            updateTrainingButtonState()\n        }\n\n        with(fragmentCameraBinding) {\n            if (viewModel.getCaptureMode()!!) {\n                btnTrainingMode.isChecked = true\n            } else {\n                btnInferenceMode.isChecked = true\n            }\n            llClassOne.setOnClickListener {\n                addSampleRequests.add(CLASS_ONE)\n            }\n            llClassTwo.setOnClickListener {\n                addSampleRequests.add(CLASS_TWO)\n            }\n            llClassThree.setOnClickListener {\n                addSampleRequests.add(CLASS_THREE)\n            }\n            llClassFour.setOnClickListener {\n                addSampleRequests.add(CLASS_FOUR)\n            }\n            llClassOne.setOnTouchListener(onAddSampleTouchListener)\n            llClassTwo.setOnTouchListener(onAddSampleTouchListener)\n            llClassThree.setOnTouchListener(onAddSampleTouchListener)\n            llClassFour.setOnTouchListener(onAddSampleTouchListener)\n            btnPauseTrain.setOnClickListener {\n                viewModel.setTrainingState(MainViewModel.TrainingState.PAUSE)\n                transferLearningHelper.pauseTraining()\n            }\n            btnResumeTrain.setOnClickListener {\n                viewModel.setTrainingState(MainViewModel.TrainingState.TRAINING)\n                transferLearningHelper.startTraining()\n            }\n            btnStartTrain.setOnClickListener {\n                // Start training process\n                viewModel.setTrainingState(MainViewModel.TrainingState.TRAINING)\n                transferLearningHelper.startTraining()\n            }\n            radioButton.setOnCheckedChangeListener { _, checkedId ->\n                if (checkedId == R.id.btnTrainingMode) {\n                    // Switch to training mode.\n                    viewModel.setCaptureMode(true)\n                } else {\n                    if (viewModel.getTrainingState() == MainViewModel.TrainingState.PREPARE) {\n                        fragmentCameraBinding.btnTrainingMode.isChecked = true\n                        fragmentCameraBinding.btnInferenceMode.isChecked = false\n\n                        Toast.makeText(\n                            requireContext(), \"Inference can only \" +\n                                    \"start after training is done.\", Toast\n                                .LENGTH_LONG\n                        ).show()\n                    } else {\n                        // Pause the training process and switch to inference mode.\n                        transferLearningHelper.pauseTraining()\n                        viewModel.setTrainingState(MainViewModel.TrainingState.PAUSE)\n                        viewModel.setCaptureMode(false)\n                    }\n                }\n            }\n\n            viewFinder.post {\n                // Set up the camera and its use cases\n                setUpCamera()\n            }\n        }\n    }\n\n    // Initialize CameraX, and prepare to bind the camera use cases\n    private fun setUpCamera() {\n        val cameraProviderFuture =\n            ProcessCameraProvider.getInstance(requireContext())\n        cameraProviderFuture.addListener(\n            {\n                // CameraProvider\n                cameraProvider = cameraProviderFuture.get()\n\n                // Build and bind the camera use cases\n                bindCameraUseCases()\n            },\n            ContextCompat.getMainExecutor(requireContext())\n        )\n    }\n\n    override fun onConfigurationChanged(newConfig: Configuration) {\n        super.onConfigurationChanged(newConfig)\n        imageAnalyzer?.targetRotation =\n            fragmentCameraBinding.viewFinder.display.rotation\n    }\n\n    // Declare and bind preview, capture and analysis use cases\n    @SuppressLint(\"UnsafeOptInUsageError\")\n    private fun bindCameraUseCases() {\n\n        // CameraProvider\n        val cameraProvider =\n            cameraProvider\n                ?: throw IllegalStateException(\"Camera initialization failed.\")\n\n        // CameraSelector - makes assumption that we're only using the back camera\n        val cameraSelector =\n            CameraSelector.Builder()\n                .requireLensFacing(CameraSelector.LENS_FACING_BACK).build()\n\n        // Preview. Only using the 4:3 ratio because this is the closest to our models\n        preview =\n            Preview.Builder()\n                .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n                .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)\n                .build()\n\n        // ImageAnalysis. Using RGBA 8888 to match how our models work\n        imageAnalyzer =\n            ImageAnalysis.Builder()\n                .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n                .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)\n                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)\n                .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)\n                .build()\n                // The analyzer can then be assigned to the instance\n                .also {\n                    it.setAnalyzer(cameraExecutor) { image ->\n                        if (!::bitmapBuffer.isInitialized) {\n                            // The image rotation and RGB image buffer are initialized only once\n                            // the analyzer has started running\n                            bitmapBuffer = Bitmap.createBitmap(\n                                image.width,\n                                image.height,\n                                Bitmap.Config.ARGB_8888\n                            )\n                        }\n\n                        val sampleClass = addSampleRequests.poll()\n                        if (sampleClass != null) {\n                            addSample(image, sampleClass)\n                            viewModel.increaseNumberOfSample(sampleClass)\n                        } else {\n                            if (viewModel.getCaptureMode() == false) {\n                                classifyImage(image)\n                            }\n                        }\n                        image.close()\n                    }\n                }\n\n        // Must unbind the use-cases before rebinding them\n        cameraProvider.unbindAll()\n\n        try {\n            // A variable number of use-cases can be passed here -\n            // camera provides access to CameraControl & CameraInfo\n            camera = cameraProvider.bindToLifecycle(\n                this,\n                cameraSelector,\n                preview,\n                imageAnalyzer\n            )\n\n            // Attach the viewfinder's surface provider to preview use case\n            preview?.setSurfaceProvider(fragmentCameraBinding.viewFinder.surfaceProvider)\n        } catch (exc: Exception) {\n            Log.e(TAG, \"Use case binding failed\", exc)\n        }\n    }\n\n    private fun classifyImage(image: ImageProxy) {\n        // Copy out RGB bits to the shared bitmap buffer\n        image.use { bitmapBuffer.copyPixelsFromBuffer(image.planes[0].buffer) }\n\n        val imageRotation = image.imageInfo.rotationDegrees\n        // Pass Bitmap and rotation to the transfer learning helper for\n        // processing and classification.\n        transferLearningHelper.classify(bitmapBuffer, imageRotation)\n    }\n\n    private fun addSample(image: ImageProxy, className: String) {\n        // Copy out RGB bits to the shared bitmap buffer\n        image.use { bitmapBuffer.copyPixelsFromBuffer(image.planes[0].buffer) }\n\n        val imageRotation = image.imageInfo.rotationDegrees\n        // Pass Bitmap and rotation to the transfer learning helper for\n        // processing and prepare training data.\n        transferLearningHelper.addSample(bitmapBuffer, className, imageRotation)\n    }\n\n    @SuppressLint(\"NotifyDataSetChanged\")\n    override fun onError(error: String) {\n        activity?.runOnUiThread {\n            Toast.makeText(requireContext(), error, Toast.LENGTH_SHORT).show()\n        }\n    }\n\n    @SuppressLint(\"NotifyDataSetChanged\")\n    override fun onResults(\n        results: List<Category>?,\n        inferenceTime: Long\n    ) {\n        activity?.runOnUiThread {\n            // Update the result in inference mode.\n            if (viewModel.getCaptureMode() == false) {\n                // Show result\n                results?.let { list ->\n                    // Highlight the class which is highest score.\n                    list.maxByOrNull { it.score }?.let {\n                        highlightResult(it.label)\n                    }\n                    updateScoreClasses(list)\n                }\n\n                fragmentCameraBinding.tvInferenceTime.text =\n                    String.format(\"%d ms\", inferenceTime)\n            }\n        }\n    }\n\n    // Show the loss number after each training.\n    override fun onLossResults(lossNumber: Float) {\n        String.format(\n            Locale.US,\n            \"Loss: %.3f\", lossNumber\n        ).let {\n            fragmentCameraBinding.tvLossConsumerPause.text = it\n            fragmentCameraBinding.tvLossConsumerResume.text = it\n        }\n    }\n\n    // Show the accurate score of each class.\n    private fun updateScoreClasses(categories: List<Category>) {\n        categories.forEach {\n            val view = getClassButtonScore(it.label)\n            (view as? TextView)?.text = String.format(\n                Locale.US, \"%.1f\", it.score\n            )\n        }\n    }\n\n    // Get the class button which represent for the label\n    private fun getClassButton(label: String): View? {\n        return when (label) {\n            CLASS_ONE -> fragmentCameraBinding.llClassOne\n            CLASS_TWO -> fragmentCameraBinding.llClassTwo\n            CLASS_THREE -> fragmentCameraBinding.llClassThree\n            CLASS_FOUR -> fragmentCameraBinding.llClassFour\n            else -> null\n        }\n    }\n\n    // Get the class button score which represent for the label\n    private fun getClassButtonScore(label: String): View? {\n        return when (label) {\n            CLASS_ONE -> fragmentCameraBinding.tvNumberClassOne\n            CLASS_TWO -> fragmentCameraBinding.tvNumberClassTwo\n            CLASS_THREE -> fragmentCameraBinding.tvNumberClassThree\n            CLASS_FOUR -> fragmentCameraBinding.tvNumberClassFour\n            else -> null\n        }\n    }\n\n    // Get the class name from resource id\n    private fun getClassNameFromResourceId(id: Int): String {\n        return when (id) {\n            fragmentCameraBinding.llClassOne.id -> CLASS_ONE\n            fragmentCameraBinding.llClassTwo.id -> CLASS_TWO\n            fragmentCameraBinding.llClassThree.id -> CLASS_THREE\n            fragmentCameraBinding.llClassFour.id -> CLASS_FOUR\n            else -> {\n                \"\"\n            }\n        }\n    }\n\n    // Highlight the current label and unhighlight the previous label\n    private fun highlightResult(label: String?) {\n        // skip the previous position if it is no position.\n        previousClass?.let {\n            setClassButtonHighlight(getClassButton(it), false)\n        }\n        if (label != null) {\n            setClassButtonHighlight(getClassButton(label), true)\n        }\n        previousClass = label\n    }\n\n    private fun setClassButtonHighlight(view: View?, isHighlight: Boolean) {\n        view?.run {\n            background = AppCompatResources.getDrawable(\n                context,\n                if (isHighlight) R.drawable.btn_default_highlight else R.drawable.btn_default\n            )\n        }\n    }\n\n    // Update the number of samples. If there are no label in the samples,\n    // set it 0.\n    private fun updateNumberOfSample(numberOfSamples: Map<String, Int>) {\n        fragmentCameraBinding.tvNumberClassOne.text = if (numberOfSamples\n                .containsKey(CLASS_ONE)\n        ) numberOfSamples.getValue(CLASS_ONE)\n            .toString()\n        else \"0\"\n        fragmentCameraBinding.tvNumberClassTwo.text = if (numberOfSamples\n                .containsKey(CLASS_TWO)\n        ) numberOfSamples.getValue(CLASS_TWO)\n            .toString()\n        else \"0\"\n        fragmentCameraBinding.tvNumberClassThree.text = if (numberOfSamples\n                .containsKey(CLASS_THREE)\n        ) numberOfSamples.getValue(CLASS_THREE)\n            .toString()\n        else \"0\"\n        fragmentCameraBinding.tvNumberClassFour.text = if (numberOfSamples\n                .containsKey(CLASS_FOUR)\n        ) numberOfSamples.getValue(CLASS_FOUR)\n            .toString()\n        else \"0\"\n    }\n\n    private fun updateTrainingButtonState() {\n        with(fragmentCameraBinding) {\n            tvInferenceTime.visibility = if (viewModel\n                    .getCaptureMode() == true\n            ) View.GONE else View.VISIBLE\n\n            btnCollectSample.visibility = if (\n                viewModel.getTrainingState() == MainViewModel.TrainingState.PREPARE &&\n                (viewModel.getNumberOfSample()?.size ?: 0) == 0 && viewModel\n                    .getCaptureMode() == true\n            ) View.VISIBLE else View.GONE\n\n            btnStartTrain.visibility = if (\n                viewModel.getTrainingState() == MainViewModel.TrainingState.PREPARE &&\n                (viewModel.getNumberOfSample()?.size ?: 0) > 0 && viewModel\n                    .getCaptureMode() == true\n            ) View.VISIBLE else View.GONE\n\n            btnPauseTrain.visibility =\n                if (viewModel.getTrainingState() == MainViewModel\n                        .TrainingState.TRAINING && viewModel\n                        .getCaptureMode() == true\n                ) View.VISIBLE else View.GONE\n\n            btnResumeTrain.visibility =\n                if (viewModel.getTrainingState() == MainViewModel\n                        .TrainingState.PAUSE && viewModel\n                        .getCaptureMode() == true\n                ) View.VISIBLE else View.GONE\n\n            // Disable adding button when it is training or in inference mode.\n            (viewModel.getCaptureMode() == true && viewModel.getTrainingState() !=\n                    MainViewModel.TrainingState.TRAINING).let { enable ->\n                llClassOne.isClickable = enable\n                llClassTwo.isClickable = enable\n                llClassThree.isClickable = enable\n                llClassFour.isClickable = enable\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/java/org/tensorflow/lite/examples/modelpersonalization/fragments/HelperDialog.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.modelpersonalization.fragments\n\nimport android.app.Dialog\nimport android.os.Bundle\nimport androidx.appcompat.app.AlertDialog\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport org.tensorflow.lite.examples.modelpersonalization.R\n\nclass HelperDialog : AppCompatDialogFragment() {\n    companion object {\n        const val TAG = \"Helper Dialog\"\n    }\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        return AlertDialog.Builder(requireActivity()).apply {\n            setTitle(requireActivity().getString(R.string.helper_dialog_title))\n            setMessage(\n                requireActivity().getString(\n                    R.string\n                        .helper_dialog_content\n                )\n            )\n            setPositiveButton(\"ok\") { _, _ ->\n                // no-op\n            }\n        }.create()\n    }\n}\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/java/org/tensorflow/lite/examples/modelpersonalization/fragments/PermissionsFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.modelpersonalization.fragments\n\nimport android.Manifest\nimport android.content.Context\nimport android.content.pm.PackageManager\nimport android.os.Bundle\nimport android.widget.Toast\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.core.content.ContextCompat\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.lifecycleScope\nimport androidx.navigation.Navigation\nimport org.tensorflow.lite.examples.modelpersonalization.R\n\nprivate val PERMISSIONS_REQUIRED = arrayOf(Manifest.permission.CAMERA)\n\nclass PermissionsFragment : Fragment() {\n\n    private val requestPermissionLauncher =\n        registerForActivityResult(\n            ActivityResultContracts.RequestPermission()\n        ) { isGranted: Boolean ->\n            if (isGranted) {\n                Toast.makeText(context, \"Permission request granted\", Toast.LENGTH_LONG).show()\n                navigateToCamera()\n            } else {\n                Toast.makeText(context, \"Permission request denied\", Toast.LENGTH_LONG).show()\n            }\n        }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        when {\n            ContextCompat.checkSelfPermission(\n                requireContext(),\n                Manifest.permission.CAMERA\n            ) == PackageManager.PERMISSION_GRANTED -> {\n                navigateToCamera()\n            }\n            else -> {\n                requestPermissionLauncher.launch(\n                    Manifest.permission.CAMERA\n                )\n            }\n        }\n    }\n\n    private fun navigateToCamera() {\n        lifecycleScope.launchWhenStarted {\n            Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(\n                PermissionsFragmentDirections.actionPermissionsToCamera()\n            )\n        }\n    }\n\n    companion object {\n\n        /** Convenience method used to check if all permissions required by this app are granted */\n        fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all {\n            ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/java/org/tensorflow/lite/examples/modelpersonalization/fragments/SettingFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.modelpersonalization.fragments\n\nimport android.app.Dialog\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.fragment.app.DialogFragment\nimport androidx.fragment.app.activityViewModels\nimport org.tensorflow.lite.examples.modelpersonalization.MainViewModel\nimport org.tensorflow.lite.examples.modelpersonalization.databinding.FragmentSettingBinding\n\n\nclass SettingFragment : DialogFragment() {\n    companion object {\n        const val TAG = \"SettingDialogFragment\"\n    }\n\n    private var _fragmentSettingBinding: FragmentSettingBinding? = null\n    private val fragmentSettingBinding\n        get() = _fragmentSettingBinding!!\n\n    private var numThreads = 2\n    private val viewModel: MainViewModel by activityViewModels()\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        return super.onCreateDialog(savedInstanceState).apply {\n            setCancelable(false)\n            setCanceledOnTouchOutside(false)\n        }\n    }\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View {\n\n        _fragmentSettingBinding = FragmentSettingBinding.inflate(\n            inflater,\n            container,\n            false\n        )\n        return fragmentSettingBinding.root\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n\n        viewModel.getNumThreads()?.let {\n            numThreads = it\n            updateDialogUi()\n        }\n\n        initDialogControls()\n\n        fragmentSettingBinding.btnConfirm.setOnClickListener {\n            viewModel.configModel(numThreads)\n            dismiss()\n        }\n        fragmentSettingBinding.btnCancel.setOnClickListener {\n            dismiss()\n        }\n    }\n\n    private fun initDialogControls() {\n        // When clicked, decrease the number of threads used for classification\n        fragmentSettingBinding.threadsMinus.setOnClickListener {\n            if (numThreads > 1) {\n                numThreads--\n                updateDialogUi()\n            }\n        }\n\n        // When clicked, increase the number of threads used for classification\n        fragmentSettingBinding.threadsPlus.setOnClickListener {\n            if (numThreads < 4) {\n                numThreads++\n                updateDialogUi()\n            }\n        }\n    }\n\n    //  Update the values displayed in the dialog.\n    private fun updateDialogUi() {\n        fragmentSettingBinding.threadsValue.text =\n            numThreads.toString()\n    }\n}\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/color/selector_ic.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"true\" android:color=\"@color/icPressed\" />\n    <item android:state_focused=\"true\" android:color=\"@color/icFocused\" />\n    <item android:color=\"@color/icActive\" />\n</selector>"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/drawable/btn_big_gray.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n\n    <solid android:color=\"@color/btn_gray\" />\n\n    <padding\n        android:bottom=\"@dimen/fr_camera_btn_big_padding\"\n        android:left=\"@dimen/fr_camera_btn_big_padding\"\n        android:right=\"@dimen/fr_camera_btn_big_padding\"\n        android:top=\"@dimen/fr_camera_btn_big_padding\" />\n\n    <corners android:radius=\"@dimen/fr_camera_view_radius\" />\n\n</shape>\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/drawable/btn_big_green.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n\n    <solid android:color=\"@color/btn_green\" />\n\n    <padding\n        android:bottom=\"@dimen/fr_camera_btn_big_padding\"\n        android:left=\"@dimen/fr_camera_btn_big_padding\"\n        android:right=\"@dimen/fr_camera_btn_big_padding\"\n        android:top=\"@dimen/fr_camera_btn_big_padding\" />\n\n    <corners android:radius=\"@dimen/fr_camera_view_radius\" />\n\n</shape>\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/drawable/btn_big_yellow.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n\n    <solid android:color=\"@color/btn_yellow\" />\n\n    <padding\n        android:bottom=\"@dimen/fr_camera_btn_big_padding\"\n        android:left=\"@dimen/fr_camera_btn_big_padding\"\n        android:right=\"@dimen/fr_camera_btn_big_padding\"\n        android:top=\"@dimen/fr_camera_btn_big_padding\" />\n\n    <corners android:radius=\"@dimen/fr_camera_view_radius\" />\n\n</shape>\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/drawable/btn_default.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <item android:state_enabled=\"false\">\n    <shape android:shape=\"rectangle\">\n      <corners android:radius=\"2dp\" />\n      <solid android:color=\"@color/material_grey_100\"/>\n    </shape>\n  </item>\n\n  <item android:state_enabled=\"true\">\n    <shape android:shape=\"rectangle\">\n      <corners android:radius=\"2dp\" />\n      <solid android:color=\"@color/material_grey_50\"/>\n    </shape>\n  </item>\n</selector>\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/drawable/btn_default_highlight.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <corners android:radius=\"2dp\" />\n    <solid android:color=\"@color/tfl_color_orange_light\" />\n    <stroke\n        android:width=\"2dp\"\n        android:color=\"@color/tfl_color_orange\" />\n</shape>\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/drawable/ic_baseline_settings.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<vector android:height=\"24dp\" android:tint=\"#000000\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/drawable/ic_minus.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13H5v-2h14v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/drawable/ic_plus.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/drawable/tf_out_line.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n  <corners android:radius=\"7dp\" />\n  <solid android:color=\"#80000000\" />\n  <stroke\n      android:width=\"1dp\"\n      android:color=\"@color/tfl_color_orange\" />\n</shape>"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/drawable/toggle_widget_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <item android:state_checked=\"true\">\n    <shape android:shape=\"rectangle\">\n      <corners android:radius=\"@dimen/fr_camera_view_radius\" />\n      <solid android:color=\"@color/tfl_color_orange\"/>\n    </shape>\n  </item>\n  <item android:drawable=\"@android:color/transparent\" android:state_pressed=\"true\" />\n  <item android:drawable=\"@android:color/transparent\" />\n</selector>\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path android:pathData=\"M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"85.84757\"\n                android:endY=\"92.4963\"\n                android:startX=\"42.9492\"\n                android:startY=\"49.59793\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/transparent\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <androidx.fragment.app.FragmentContainerView\n            android:id=\"@+id/fragment_container\"\n            android:name=\"androidx.navigation.fragment.NavHostFragment\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginTop=\"?android:attr/actionBarSize\"\n            android:background=\"@android:color/transparent\"\n            android:keepScreenOn=\"true\"\n            app:defaultNavHost=\"true\"\n            app:navGraph=\"@navigation/nav_graph\"\n            tools:context=\".MainActivity\" />\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:layout_alignParentTop=\"true\"\n            android:background=\"@color/toolbar_background\"\n            app:contentInsetStart=\"0dp\">\n\n            <androidx.constraintlayout.widget.ConstraintLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\">\n\n                <ImageView\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"wrap_content\"\n                    android:adjustViewBounds=\"true\"\n                    android:contentDescription=\"@null\"\n                    android:src=\"@drawable/tfl_logo\"\n                    app:layout_constraintEnd_toStartOf=\"@id/tvHelper\"\n                    app:layout_constraintStart_toStartOf=\"parent\"\n                    app:layout_constraintTop_toTopOf=\"parent\" />\n\n                <TextView\n                    android:id=\"@+id/tvHelper\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"match_parent\"\n                    android:layout_gravity=\"start\"\n                    android:gravity=\"center\"\n                    android:paddingStart=\"@dimen/fr_camera_toolbar_icon_padding\"\n                    android:paddingEnd=\"@dimen/fr_camera_toolbar_icon_padding\"\n                    android:text=\"@string/tv_icon_helper\"\n                    android:textColor=\"@android:color/black\"\n                    android:textSize=\"@dimen/fr_camera_helper_icon_size\"\n                    android:textStyle=\"bold\"\n                    app:layout_constraintEnd_toStartOf=\"@id/imgSetting\"\n                    app:layout_constraintTop_toTopOf=\"parent\"\n                    tools:ignore=\"SpUsage\" />\n\n                <ImageView\n                    android:id=\"@+id/imgSetting\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"match_parent\"\n                    android:layout_gravity=\"end\"\n                    android:contentDescription=\"@null\"\n                    android:paddingStart=\"@dimen/fr_camera_toolbar_icon_padding\"\n                    android:paddingEnd=\"@dimen/fr_camera_toolbar_icon_padding\"\n                    android:src=\"@drawable/ic_baseline_settings\"\n                    app:layout_constraintEnd_toEndOf=\"parent\"\n                    app:layout_constraintTop_toTopOf=\"parent\" />\n            </androidx.constraintlayout.widget.ConstraintLayout>\n        </androidx.appcompat.widget.Toolbar>\n\n    </RelativeLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/layout/fragment_camera.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/camera_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.camera.view.PreviewView\n        android:id=\"@+id/view_finder\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:scaleType=\"fillStart\" />\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"@dimen/fr_camera_control_layout_margin_top\"\n        android:orientation=\"vertical\">\n\n        <RadioGroup\n            android:id=\"@+id/radioButton\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@drawable/tf_out_line\"\n            android:orientation=\"horizontal\"\n            android:weightSum=\"2\"\n            app:layout_constraintTop_toTopOf=\"parent\">\n\n            <RadioButton\n                android:id=\"@+id/btnTrainingMode\"\n                style=\"@style/Widget.AppCompat.CompoundButton.RadioButton\"\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:background=\"@drawable/toggle_widget_background\"\n                android:button=\"@null\"\n                android:gravity=\"center\"\n                android:padding=\"@dimen/fr_camera_btn_mode_padding\"\n                android:text=\"@string/radio_btn_training_mode\"\n                android:textAllCaps=\"true\"\n                android:textColor=\"@android:color/white\"\n                android:textSize=\"@dimen/fr_camera_btn_mode_text_size\" />\n\n            <RadioButton\n                android:id=\"@+id/btnInferenceMode\"\n                style=\"@style/Widget.AppCompat.CompoundButton.RadioButton\"\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:background=\"@drawable/toggle_widget_background\"\n                android:button=\"@null\"\n                android:gravity=\"center\"\n                android:padding=\"@dimen/fr_camera_btn_mode_padding\"\n                android:text=\"@string/radio_btn_inference_mode\"\n                android:textAllCaps=\"true\"\n                android:textColor=\"@android:color/white\"\n                android:textSize=\"@dimen/fr_camera_btn_mode_text_size\" />\n        </RadioGroup>\n\n        <TextView\n            android:id=\"@+id/tvInferenceTime\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"@dimen/fr_camera_tv_inference_time_margin\"\n            android:textColor=\"@color/tfl_color_orange\"\n            app:layout_constraintBottom_toTopOf=\"@id/optionsLayout\" />\n\n        <androidx.constraintlayout.widget.ConstraintLayout\n            android:id=\"@+id/optionsLayout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginBottom=\"@dimen/fr_camera_cl_button_classes_margin\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintStart_toStartOf=\"parent\">\n\n            <LinearLayout\n                android:id=\"@+id/llClassOne\"\n                style=\"@style/ClassButton\"\n                android:layout_width=\"0dp\"\n                android:layout_height=\"0dp\"\n                android:gravity=\"center\"\n                android:orientation=\"vertical\"\n                app:layout_constraintDimensionRatio=\"1:1\"\n                app:layout_constraintEnd_toStartOf=\"@id/llClassTwo\"\n                app:layout_constraintHorizontal_weight=\"1\"\n                app:layout_constraintStart_toStartOf=\"parent\"\n                app:layout_constraintTop_toTopOf=\"parent\"\n                tools:text=\"1\">\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/tv_class_one\"\n                    android:textSize=\"@dimen/fr_camera_tv_classes_text_size\"\n                    android:textStyle=\"bold\" />\n\n                <TextView\n                    android:id=\"@+id/tvNumberClassOne\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:id=\"@+id/llClassTwo\"\n                style=\"@style/ClassButton\"\n                android:layout_width=\"0dp\"\n                android:layout_height=\"0dp\"\n                android:gravity=\"center\"\n                android:orientation=\"vertical\"\n                app:layout_constraintDimensionRatio=\"1:1\"\n                app:layout_constraintEnd_toStartOf=\"@id/llClassThree\"\n                app:layout_constraintHorizontal_weight=\"1\"\n                app:layout_constraintStart_toEndOf=\"@id/llClassOne\"\n                app:layout_constraintTop_toTopOf=\"parent\"\n                tools:text=\"2\">\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/tv_class_two\"\n                    android:textSize=\"@dimen/fr_camera_tv_classes_text_size\"\n                    android:textStyle=\"bold\" />\n\n                <TextView\n                    android:id=\"@+id/tvNumberClassTwo\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:id=\"@+id/llClassThree\"\n                style=\"@style/ClassButton\"\n                android:layout_width=\"0dp\"\n                android:layout_height=\"0dp\"\n                android:gravity=\"center\"\n                android:orientation=\"vertical\"\n                app:layout_constraintDimensionRatio=\"1:1\"\n                app:layout_constraintEnd_toStartOf=\"@id/llClassFour\"\n                app:layout_constraintHorizontal_weight=\"1\"\n                app:layout_constraintStart_toEndOf=\"@id/llClassTwo\"\n                app:layout_constraintTop_toTopOf=\"parent\"\n                tools:text=\"3\">\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/tv_class_three\"\n                    android:textSize=\"@dimen/fr_camera_tv_classes_text_size\"\n                    android:textStyle=\"bold\" />\n\n                <TextView\n                    android:id=\"@+id/tvNumberClassThree\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:id=\"@+id/llClassFour\"\n                style=\"@style/ClassButton\"\n                android:layout_width=\"0dp\"\n                android:layout_height=\"0dp\"\n                android:gravity=\"center\"\n                android:orientation=\"vertical\"\n                app:layout_constraintDimensionRatio=\"1:1\"\n                app:layout_constraintEnd_toEndOf=\"parent\"\n                app:layout_constraintHorizontal_weight=\"1\"\n                app:layout_constraintStart_toEndOf=\"@id/llClassThree\"\n                app:layout_constraintTop_toTopOf=\"parent\">\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/tv_class_four\"\n                    android:textSize=\"@dimen/fr_camera_tv_classes_text_size\"\n                    android:textStyle=\"bold\" />\n\n                <TextView\n                    android:id=\"@+id/tvNumberClassFour\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\" />\n            </LinearLayout>\n\n        </androidx.constraintlayout.widget.ConstraintLayout>\n\n        <LinearLayout\n            android:id=\"@+id/btnStartTrain\"\n            style=\"@style/BigButton\"\n            android:background=\"@drawable/btn_big_green\"\n            android:gravity=\"center\"\n            android:orientation=\"vertical\"\n            app:layout_constraintBottom_toTopOf=\"@+id/optionsLayout\"\n            app:layout_constraintLeft_toLeftOf=\"parent\"\n            app:layout_constraintRight_toRightOf=\"parent\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/tv_start_training\"\n                android:textAllCaps=\"true\"\n                android:textColor=\"@color/text_color\"\n                android:textSize=\"@dimen/fr_camera_tv_training_state_text_size_large\"\n                android:textStyle=\"bold\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:id=\"@+id/btnPauseTrain\"\n            style=\"@style/BigButton\"\n            android:background=\"@drawable/btn_big_yellow\"\n            android:gravity=\"center\"\n            android:orientation=\"vertical\"\n            app:layout_constraintBottom_toTopOf=\"@+id/optionsLayout\"\n            app:layout_constraintLeft_toLeftOf=\"parent\"\n            app:layout_constraintRight_toRightOf=\"parent\">\n\n            <TextView\n                android:id=\"@+id/tvPauseTitle\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/tv_pause_training\"\n                android:textAllCaps=\"true\"\n                android:textColor=\"@color/text_color\"\n                android:textSize=\"@dimen/fr_camera_tv_training_state_text_size_medium\"\n                android:textStyle=\"bold\" />\n\n            <TextView\n                android:id=\"@+id/tv_loss_consumer_pause\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"center\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:id=\"@+id/btnResumeTrain\"\n            style=\"@style/BigButton\"\n            android:background=\"@drawable/btn_big_green\"\n            android:gravity=\"center\"\n            android:orientation=\"vertical\"\n            app:layout_constraintBottom_toTopOf=\"@+id/optionsLayout\"\n            app:layout_constraintLeft_toLeftOf=\"parent\"\n            app:layout_constraintRight_toRightOf=\"parent\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/tv_resume_training\"\n                android:textAllCaps=\"true\"\n                android:textColor=\"@color/text_color\"\n                android:textSize=\"@dimen/fr_camera_tv_training_state_text_size_medium\"\n                android:textStyle=\"bold\" />\n\n            <TextView\n                android:id=\"@+id/tvLossConsumerResume\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"center\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:id=\"@+id/btnCollectSample\"\n            style=\"@style/BigButton\"\n            android:background=\"@drawable/btn_big_gray\"\n            android:gravity=\"center\"\n            android:orientation=\"vertical\"\n            app:layout_constraintBottom_toTopOf=\"@+id/optionsLayout\"\n            app:layout_constraintLeft_toLeftOf=\"parent\"\n            app:layout_constraintRight_toRightOf=\"parent\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/tv_collect_sample\"\n                android:textAllCaps=\"true\"\n                android:textColor=\"@color/text_color_white\"\n                android:textSize=\"@dimen/fr_camera_tv_training_state_text_size_large\"\n                android:textStyle=\"bold\" />\n        </LinearLayout>\n    </androidx.constraintlayout.widget.ConstraintLayout>\n\n    <View\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"1dp\"\n        android:background=\"@color/tfl_color_orange\"\n        app:layout_anchorGravity=\"bottom\" />\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/layout/fragment_setting.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:layout_gravity=\"center_horizontal\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:padding=\"@dimen/setting_dialog_padding\">\n\n\n    <!-- Number of threads adjustment row -->\n    <RelativeLayout\n        android:id=\"@+id/rlThreads\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"@dimen/setting_dialog_default_row_margin\"\n        android:gravity=\"center\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:text=\"@string/label_threads\"\n            android:textColor=\"@color/text_color\"\n            android:textSize=\"@dimen/setting_dialog_text_size\" />\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentRight=\"true\"\n            android:gravity=\"center_vertical\"\n            android:orientation=\"horizontal\">\n\n            <androidx.appcompat.widget.AppCompatImageButton\n                android:id=\"@+id/threads_minus\"\n                android:layout_width=\"@dimen/setting_dialog_control_btn_size\"\n                android:layout_height=\"@dimen/setting_dialog_control_btn_size\"\n                android:contentDescription=\"@string/control_dialog_thread_button_minus\"\n                android:src=\"@drawable/ic_minus\" />\n\n            <TextView\n                android:id=\"@+id/threads_value\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"@dimen/setting_dialog_control_text_side_margin\"\n                android:layout_marginRight=\"@dimen/setting_dialog_control_text_side_margin\"\n                android:gravity=\"center\"\n                android:minEms=\"@integer/setting_dialog_text_min_ems\"\n                android:text=\"@string/tv_number_of_threads_default\"\n                android:textColor=\"@color/text_color\"\n                android:textSize=\"@dimen/setting_dialog_text_size\" />\n\n            <androidx.appcompat.widget.AppCompatImageButton\n                android:id=\"@+id/threads_plus\"\n                android:layout_width=\"@dimen/setting_dialog_control_btn_size\"\n                android:layout_height=\"@dimen/setting_dialog_control_btn_size\"\n                android:contentDescription=\"@string/control_dialog_thread_button_plus\"\n                android:src=\"@drawable/ic_plus\" />\n        </LinearLayout>\n    </RelativeLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/rlThreads\"\n        android:layout_marginTop=\"@dimen/setting_dialog_button_bar_margin_top\">\n\n        <Button\n            android:id=\"@+id/btnCancel\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"@string/btn_setting_dialog_cancel\"\n            style=\"?android:attr/buttonBarButtonStyle\" />\n\n        <Button\n            style=\"?android:attr/buttonBarButtonStyle\"\n            android:id=\"@+id/btnConfirm\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"@string/btn_setting_dialog_confirm\" />\n    </LinearLayout>\n\n</RelativeLayout>\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/navigation/nav_graph.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/nav_graph\"\n    app:startDestination=\"@id/permissions_fragment\">\n\n    <fragment\n        android:id=\"@+id/permissions_fragment\"\n        android:name=\"org.tensorflow.lite.examples.modelpersonalization.fragments.PermissionsFragment\"\n        android:label=\"PermissionsFragment\">\n\n        <action\n            android:id=\"@+id/action_permissions_to_camera\"\n            app:destination=\"@id/camera_fragment\"\n            app:popUpTo=\"@id/permissions_fragment\"\n            app:popUpToInclusive=\"true\" />\n\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/camera_fragment\"\n        android:name=\"org.tensorflow.lite.examples.modelpersonalization.fragments.CameraFragment\"\n        android:label=\"CameraFragment\">\n\n        <action\n            android:id=\"@+id/action_camera_to_permissions\"\n            app:destination=\"@id/permissions_fragment\"\n            app:popUpTo=\"@id/camera_fragment\"\n            app:popUpToInclusive=\"true\" />\n    </fragment>\n</navigation>\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n    <!-- TF Branding Orange -->\n    <color name=\"tfl_color_orange\">#FF6F00</color>\n    <color name=\"tfl_color_orange_light\">#FFFFA800</color>\n    <color name=\"toolbar_background\">#EEEEEE</color>\n    <color name=\"ic_launcher_background\">#FFFFFF</color>\n    <color name=\"text_color\">@android:color/black</color>\n    <color name=\"text_color_white\">@android:color/white</color>\n    <color name=\"icActive\">#FFFFFFFF</color>\n    <color name=\"icFocused\">#DDFFFFFF</color>\n    <color name=\"icPressed\">#AAFFFFFF</color>\n    <color name=\"btn_green\">#00db8b</color>\n    <color name=\"btn_gray\">#7c8890</color>\n    <color name=\"btn_yellow\">#ede27e</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n    <dimen name=\"stroke_small\">4dp</dimen>\n    <dimen name=\"round_button_medium\">64dp</dimen>\n    <dimen name=\"setting_dialog_text_size\">14sp</dimen>\n    <dimen name=\"setting_dialog_padding\">5dp</dimen>\n    <dimen name=\"setting_dialog_default_row_margin\">16dp</dimen>\n    <dimen name=\"setting_dialog_control_btn_size\">40dp</dimen>\n    <dimen name=\"setting_dialog_control_text_side_margin\">5dp</dimen>\n    <dimen name=\"setting_dialog_button_bar_margin_top\">20dp</dimen>\n    <dimen name=\"fr_camera_toolbar_icon_padding\">15dp</dimen>\n    <dimen name=\"fr_camera_helper_icon_size\">30dp</dimen>\n    <dimen name=\"fr_camera_control_layout_margin_top\">10dp</dimen>\n    <dimen name=\"fr_camera_btn_mode_padding\">10dp</dimen>\n    <dimen name=\"fr_camera_btn_mode_text_size\">20sp</dimen>\n    <dimen name=\"fr_camera_tv_inference_time_margin\">10dp</dimen>\n    <dimen name=\"fr_camera_cl_button_classes_margin\">5dp</dimen>\n    <dimen name=\"fr_camera_tv_classes_text_size\">25sp</dimen>\n    <dimen name=\"fr_camera_tv_training_state_text_size_large\">25sp</dimen>\n    <dimen name=\"fr_camera_tv_training_state_text_size_medium\">20sp</dimen>\n    <dimen name=\"fr_camera_btn_big_padding\">5dp</dimen>\n    <dimen name=\"fr_camera_view_radius\">7dp</dimen>\n    <integer name=\"setting_dialog_text_min_ems\">3</integer>\n</resources>\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n    <string name=\"app_name\">TFLite Model Personalization Demo App</string>\n    <string name=\"control_dialog_thread_button_minus\">Decrease the number of threads used</string>\n    <string name=\"control_dialog_thread_button_plus\">Increase the number of threads used</string>\n    <string name=\"label_inference_time\">Inference Time</string>\n    <string name=\"label_threads\">Number of Threads</string>\n    <string name=\"helper_dialog_title\">How to use the app</string>\n    <string name=\"helper_dialog_content\">\n        1. There are 4 classes in the app represented by the 4 numbers\n        (1..4)\\n\\n\n        2. Each number can be associated with a category of an object(e.g.\n        car, fruit, etc…)\\n\\n\n        3. Take at least 1 image to enable training. For the intended usage,\n        please take at least 100 images per category before training.\\n\\n\n        4. Use the number button while pointing the camera at an object to\n        collect the image.\\n\\n\n        If you click the number button once, it will collect a sample of the\n        object and save it. If you long-press the shape button, it will\n        continuously collect samples until you release it.\\n\\n\n        5. Press the train button to start training. Once the loss becomes\n        small enough (e.g. \\uFE641.0), press the pause button. If the loss\n        does not converge to a small number, then press the pause button, add\n        more images for each category, and press the resume button.\\n\\n\n        6. Switch to the inference mode and point the camera to some object.\n        Each class button will display the probability of which the object\n        falls into that category and highlight the most probable one.\\n\\n\n        If training has been done well, the correct category will be\n        highlighted. If the object does not fall into any of the four\n        categories trained, then the app will still try to highlight any one\n        of the closest categories with its best guess.\\n\\n\n        7. You can always switch back to the training mode and capture more\n        images.\n    </string>\n    <string name=\"tv_number_of_threads_default\">2</string>\n    <string name=\"btn_setting_dialog_cancel\">Cancel</string>\n    <string name=\"btn_setting_dialog_confirm\">Confirm</string>\n    <string name=\"radio_btn_training_mode\">Training</string>\n    <string name=\"radio_btn_inference_mode\">Inference</string>\n    <string name=\"tv_class_one\">1</string>\n    <string name=\"tv_class_two\">2</string>\n    <string name=\"tv_class_three\">3</string>\n    <string name=\"tv_class_four\">4</string>\n    <string name=\"tv_start_training\">Train</string>\n    <string name=\"tv_pause_training\">Pause</string>\n    <string name=\"tv_resume_training\">Resume</string>\n    <string name=\"tv_collect_sample\">Collect sample</string>\n    <string name=\"tv_icon_helper\">\\?</string>\n</resources>\n"
  },
  {
    "path": "lite/examples/model_personalization/android/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n    </style>\n\n    <style name=\"ClassButton\" parent=\"@style/Widget.AppCompat.Button\">\n        <item name=\"android:layout_margin\">10dp</item>\n        <item name=\"android:paddingLeft\">4dp</item>\n        <item name=\"android:paddingRight\">4dp</item>\n        <item name=\"android:paddingTop\">12dp</item>\n        <item name=\"android:paddingBottom\">12dp</item>\n        <item name=\"android:background\">@drawable/btn_default</item>\n        <item name=\"android:foreground\">?android:attr/selectableItemBackground</item>\n    </style>\n\n    <style name=\"BigButton\" parent=\"@style/Widget.AppCompat.Button\">\n        <item name=\"android:layout_width\">300dp</item>\n        <item name=\"android:layout_height\">68dp</item>\n        <item name=\"android:layout_margin\">8dp</item>\n        <item name=\"android:orientation\">vertical</item>\n        <item name=\"android:padding\">10dp</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "lite/examples/model_personalization/android/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n    dependencies {\n        classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.5.1'\n        classpath 'de.undercouch:gradle-download-task:4.1.2'\n    }\n}\nplugins {\n    id 'com.android.application' version '7.2.0' apply false\n    id 'com.android.library' version '7.2.0' apply false\n    id 'org.jetbrains.kotlin.android' version '1.6.21' apply false\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}"
  },
  {
    "path": "lite/examples/model_personalization/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/model_personalization/android/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app\"s APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official\n# Enables namespacing of each library's R class so that its R class includes only the\n# resources declared in the library itself and none from the library's dependencies,\n# thereby reducing the size of the R class for that library\nandroid.nonTransitiveRClass=true"
  },
  {
    "path": "lite/examples/model_personalization/android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/model_personalization/android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/model_personalization/android/settings.gradle",
    "content": "pluginManagement {\n    repositories {\n        gradlePluginPortal()\n        google()\n        mavenCentral()\n    }\n}\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\nrootProject.name = \"TFLite Model Personalization Demo App\"\ninclude ':app'\n"
  },
  {
    "path": "lite/examples/model_personalization/transfer_learning/generate_training_model.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"CLI wrapper for tflite_transfer_converter.\n\nConverts a TF model to a TFLite transfer learning model.\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport tensorflow as tf\n\nIMG_SIZE = 224\nNUM_FEATURES = 7 * 7 * 1280\nNUM_CLASSES = 4\n\n\nclass TransferLearningModel(tf.Module):\n  \"\"\"TF Transfer Learning model class.\"\"\"\n\n  def __init__(self, learning_rate=0.001):\n    \"\"\"Initializes a transfer learning model instance.\n\n    Args:\n      learning_rate: A learning rate for the optimzer.\n    \"\"\"\n    self.num_features = NUM_FEATURES\n    self.num_classes = NUM_CLASSES\n\n    # trainable weights and bias for softmax\n    self.ws = tf.Variable(\n        tf.zeros((self.num_features, self.num_classes)),\n        name='ws',\n        trainable=True)\n    self.bs = tf.Variable(\n        tf.zeros((1, self.num_classes)), name='bs', trainable=True)\n\n    # base model\n    self.base = tf.keras.applications.MobileNetV2(\n        input_shape=(IMG_SIZE, IMG_SIZE, 3),\n        alpha=1.0,\n        include_top=False,\n        weights='imagenet')\n    # loss function and optimizer\n    self.loss_fn = tf.keras.losses.CategoricalCrossentropy()\n    self.optimizer = tf.keras.optimizers.Adam(learning_rate=learning_rate)\n\n  @tf.function(input_signature=[\n      tf.TensorSpec([None, IMG_SIZE, IMG_SIZE, 3], tf.float32),\n  ])\n  def load(self, feature):\n    \"\"\"Generates and loads bottleneck features from the given image batch.\n\n    Args:\n      feature: A tensor of image feature batch to generate the bottleneck from.\n\n    Returns:\n      Map of the bottleneck.\n    \"\"\"\n    x = tf.keras.applications.mobilenet_v2.preprocess_input(\n        tf.multiply(feature, 255))\n    bottleneck = tf.reshape(\n        self.base(x, training=False), (-1, self.num_features))\n    return {'bottleneck': bottleneck}\n\n  @tf.function(input_signature=[\n      tf.TensorSpec([None, NUM_FEATURES], tf.float32),\n      tf.TensorSpec([None, NUM_CLASSES], tf.float32),\n  ])\n  def train(self, bottleneck, label):\n    \"\"\"Runs one training step with the given bottleneck features and labels.\n\n    Args:\n      bottleneck: A tensor of bottleneck features generated from the base model.\n      label: A tensor of class labels for the given batch.\n\n    Returns:\n      Map of the training loss.\n    \"\"\"\n    with tf.GradientTape() as tape:\n      logits = tf.matmul(bottleneck, self.ws) + self.bs\n      prediction = tf.nn.softmax(logits)\n      loss = self.loss_fn(prediction, label)\n    gradients = tape.gradient(loss, [self.ws, self.bs])\n    self.optimizer.apply_gradients(zip(gradients, [self.ws, self.bs]))\n    result = {'loss': loss}\n    for grad in gradients:\n      result[grad.name] = grad\n    return result\n\n  @tf.function(input_signature=[\n      tf.TensorSpec([None, IMG_SIZE, IMG_SIZE, 3], tf.float32)\n  ])\n  def infer(self, feature):\n    \"\"\"Invokes an inference on the given feature.\n\n    Args:\n      feature: A tensor of image feature batch to invoke an inference on.\n\n    Returns:\n      Map of the softmax output.\n    \"\"\"\n    x = tf.keras.applications.mobilenet_v2.preprocess_input(\n        tf.multiply(feature, 255))\n    bottleneck = tf.reshape(\n        self.base(x, training=False), (-1, self.num_features))\n    logits = tf.matmul(bottleneck, self.ws) + self.bs\n    return {'output': tf.nn.softmax(logits)}\n\n  @tf.function(input_signature=[tf.TensorSpec(shape=[], dtype=tf.string)])\n  def save(self, checkpoint_path):\n    \"\"\"Saves the trainable weights to the given checkpoint file.\n\n    Args:\n      checkpoint_path: A file path to save the model.\n\n    Returns:\n      Map of the checkpoint file path.\n    \"\"\"\n    tensor_names = [self.ws.name, self.bs.name]\n    tensors_to_save = [self.ws.read_value(), self.bs.read_value()]\n    tf.raw_ops.Save(\n        filename=checkpoint_path,\n        tensor_names=tensor_names,\n        data=tensors_to_save,\n        name='save')\n    return {'checkpoint_path': checkpoint_path}\n\n  @tf.function(input_signature=[tf.TensorSpec(shape=[], dtype=tf.string)])\n  def restore(self, checkpoint_path):\n    \"\"\"Restores the serialized trainable weights from the given checkpoint file.\n\n    Args:\n      checkpoint_path: A path to a saved checkpoint file.\n\n    Returns:\n      Map of restored weight and bias.\n    \"\"\"\n    restored_tensors = {}\n    restored = tf.raw_ops.Restore(\n        file_pattern=checkpoint_path,\n        tensor_name=self.ws.name,\n        dt=np.float32,\n        name='restore')\n    self.ws.assign(restored)\n    restored_tensors['ws'] = restored\n    restored = tf.raw_ops.Restore(\n        file_pattern=checkpoint_path,\n        tensor_name=self.bs.name,\n        dt=np.float32,\n        name='restore')\n    self.bs.assign(restored)\n    restored_tensors['bs'] = restored\n    return restored_tensors\n\n  @tf.function(input_signature=[])\n  def initialize_weights(self):\n    \"\"\"Initializes the weights and bias of the head model.\n\n    Returns:\n      Map of initialized weight and bias.\n    \"\"\"\n    self.ws.assign(tf.random.uniform((self.num_features, self.num_classes)))\n    self.bs.assign(tf.random.uniform((1, self.num_classes)))\n    return {'ws': self.ws, 'bs': self.bs}\n\n\ndef convert_and_save(saved_model_dir='saved_model'):\n  \"\"\"Converts and saves the TFLite Transfer Learning model.\n\n  Args:\n    saved_model_dir: A directory path to save a converted model.\n  \"\"\"\n  model = TransferLearningModel()\n\n  tf.saved_model.save(\n      model,\n      saved_model_dir,\n      signatures={\n          'load': model.load.get_concrete_function(),\n          'train': model.train.get_concrete_function(),\n          'infer': model.infer.get_concrete_function(),\n          'save': model.save.get_concrete_function(),\n          'restore': model.restore.get_concrete_function(),\n          'initialize': model.initialize_weights.get_concrete_function(),\n      })\n\n  # Convert the model\n  converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)\n  converter.target_spec.supported_ops = [\n      tf.lite.OpsSet.TFLITE_BUILTINS,  # enable TensorFlow Lite ops.\n      tf.lite.OpsSet.SELECT_TF_OPS  # enable TensorFlow ops.\n  ]\n  converter.experimental_enable_resource_variables = True\n  tflite_model = converter.convert()\n\n  model_file_path = os.path.join('model.tflite')\n  with open(model_file_path, 'wb') as model_file:\n    model_file.write(tflite_model)\n\n\nif __name__ == '__main__':\n  convert_and_save()\n"
  },
  {
    "path": "lite/examples/model_personalization/transfer_learning/requirements.txt",
    "content": "tensorflow>=2.7.*"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    namespace 'com.google.samples.gms.tflite.c'\n    compileSdk 33\n\n    defaultConfig {\n        applicationId 'com.google.samples.gms.tflite.c'\n        minSdk 21\n        targetSdk 33\n        versionCode 1\n        versionName '1.0'\n\n        testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'\n    }\n\n    buildTypes {\n        all {\n            proguardFiles 'proguard-rules.pro'\n        }\n        release {\n            minifyEnabled true\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    buildFeatures {\n        prefab true\n    }\n\n    externalNativeBuild {\n        cmake {\n            path file('src/main/cpp/CMakeLists.txt')\n        }\n    }\n\n    aaptOptions {\n        noCompress 'bin'\n    }\n}\n\njava {\n    toolchain {\n        languageVersion.set(JavaLanguageVersion.of(11))\n    }\n}\n\ndependencies {\n\n    implementation 'androidx.appcompat:appcompat:1.6.0'\n    implementation 'com.google.android.material:material:1.8.0'\n\n    androidTestImplementation 'androidx.test:rules:1.4.0'\n    androidTestImplementation 'androidx.test:runner:1.4.0'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n    androidTestImplementation 'androidx.test.ext:junit:1.1.3'\n    androidTestImplementation 'com.google.truth:truth:1.1.3'\n\n    implementation 'com.google.android.gms:play-services-tflite-java:16.4.0'\n    implementation 'com.google.android.gms:play-services-tflite-gpu:16.4.0'\n}\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/app/proguard-rules.pro",
    "content": "-keepclasseswithmembers class com.google.samples.gms.tflite.c.MainActivity { *; }\n-keepclasseswithmembers class com.google.samples.gms.tflite.c.TfLiteJni { *; }\n-keepclasseswithmembers class android.support.** { *; }\n-keepclasseswithmembers class androidx.** { *; }\n\n# The tests use Tasks.await, but the instrumented code doesn't\n# Without the appropriate Proguard config, the method would be pruned\n-keepclasseswithmembers class com.google.android.gms.tasks.** { *; }\n# The tests also uses TfLiteNative.initialize(context)\n-keepclasseswithmembers class com.google.android.gms.tflite.java.TfLiteNative { *; }\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/app/src/androidTest/java/com/google/samples/gms/tflite/c/instrumentation/BasicScenarioTest.java",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.samples.gms.tflite.c.instrumentation;\n\nimport static com.google.common.truth.Truth.assertThat;\n\nimport android.content.Context;\nimport android.util.Log;\nimport androidx.test.core.app.ApplicationProvider;\nimport androidx.test.ext.junit.runners.AndroidJUnit4;\nimport com.google.android.gms.tasks.Tasks;\nimport com.google.android.gms.tflite.java.TfLiteNative;\nimport com.google.samples.gms.tflite.c.TfLiteJni;\nimport java.util.concurrent.ExecutionException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\n/** Instrumentation tests for the TFLite Native API. */\n@RunWith(AndroidJUnit4.class)\npublic class BasicScenarioTest {\n  private static final String TAG = \"BasicScenarioTest\";\n\n  @Test\n  public void basicScenario() throws ExecutionException, InterruptedException {\n    Context context = ApplicationProvider.getApplicationContext();\n    Tasks.await(TfLiteNative.initialize(context));\n    TfLiteJni jni = new TfLiteJni(message -> Log.e(TAG, message));\n\n    jni.loadModel(context.getAssets(), \"add.tflite\");\n    float[] output = jni.runInference(new float[] {1.f, 3.f});\n    jni.destroy();\n\n    assertThat(output).isEqualTo(new float[] {3.f, 9.f});\n  }\n}\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/app/src/androidTest/java/com/google/samples/gms/tflite/c/instrumentation/TfLiteNativeGPUAccelerationTest.java",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.samples.gms.tflite.c.instrumentation;\n\nimport static com.google.common.truth.Truth.assertThat;\n\nimport android.content.Context;\nimport android.util.Log;\nimport androidx.test.core.app.ApplicationProvider;\nimport androidx.test.ext.junit.runners.AndroidJUnit4;\nimport com.google.android.gms.tasks.Tasks;\nimport com.google.android.gms.tflite.client.TfLiteInitializationOptions;\nimport com.google.android.gms.tflite.gpu.support.TfLiteGpu;\nimport com.google.android.gms.tflite.java.TfLiteNative;\nimport com.google.samples.gms.tflite.c.TfLiteJni;\nimport java.util.concurrent.ExecutionException;\nimport org.junit.Assume;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\n/** Instrumentation tests for the TFLite Native Acceleration API. */\n@RunWith(AndroidJUnit4.class)\npublic class TfLiteNativeGPUAccelerationTest {\n  private static final String TAG = \"TfLiteNativeGPUAccelerationTest\";\n\n  private static final TfLiteInitializationOptions options =\n      TfLiteInitializationOptions.builder().setEnableGpuDelegateSupport(true).build();\n\n  private Context context;\n\n  @Before\n  public void setUp() throws ExecutionException, InterruptedException {\n    context = ApplicationProvider.getApplicationContext();\n    boolean gpuAvailable = Tasks.await(TfLiteGpu.isGpuDelegateAvailable(context));\n    Assume.assumeTrue(\"GPU acceleration is unavailable on this device.\", gpuAvailable);\n    Tasks.await(TfLiteNative.initialize(context, options));\n  }\n\n  @Test\n  public void doInferenceWithAcceleration() {\n    TfLiteJni jni = new TfLiteJni(message -> Log.e(TAG, message));\n    jni.initGpuAcceleration();\n    jni.loadModel(context.getAssets(), \"add.tflite\");\n    float[] output = jni.runInference(new float[] {1.f, 3.f});\n    jni.destroy();\n    assertThat(output).isEqualTo(new float[] {3.f, 9.f});\n  }\n}\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n\n  <application\n      android:allowBackup=\"true\"\n      android:icon=\"@mipmap/ic_launcher\"\n      android:label=\"@string/app_name\"\n      android:taskAffinity=\"\"\n      android:theme=\"@style/AppTheme.TFLite\">\n    <activity\n        android:name=\"com.google.samples.gms.tflite.c.MainActivity\"\n        android:exported=\"true\">\n      <intent-filter>\n        <action android:name=\"android.intent.action.MAIN\" />\n        <category android:name=\"android.intent.category.LAUNCHER\" />\n      </intent-filter>\n    </activity>\n  </application>\n\n  <queries>\n    <package android:name=\"com.google.android.gms.policy_tflite_dynamite_dynamite\" />\n  </queries>\n\n</manifest>\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/app/src/main/cpp/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.16.3)\n\nproject(\"tflite-c-sample\")\n\nfind_package(tensorflowlite_jni_gms_client REQUIRED CONFIG)\n\ninclude(FetchContent)\nFetchContent_Declare(\n        flatbuffers\n        GIT_REPOSITORY https://github.com/google/flatbuffers.git\n        # Keep in sync with the FlatBuffers version in the most recent version\n        # of TF Lite (Lite RT) for Play services published on maven.google.com\n        # <https://maven.google.com/web/index.html#com.google.android.gms:play-services-tflite-java>\n        # and in particular as determined by the `static_assert` in the file\n        # `tensorflow/lite/acceleration/configuration/configuration_generated.h`\n        # in the `prefab/modules/tensorflowlite_jni_gms_client/include/` directory\n        # of the `play-services-tflite-java-<version>.aar` file there.\n        #\n        # That version is currently \"GIT_TAG v24.3.25\",\n        # but due to a build error in v24.3.25, we use a snapshot that is three PRs\n        # later (all bug fixes), which fixes the build error.\n        # See <https://github.com/google/flatbuffers/commit/e6463926479bd6b330cbcf673f7e917803fd5831>.\n        GIT_TAG e6463926479bd6b330cbcf673f7e917803fd5831\n)\nset(FLATBUFFERS_BUILD_FLATC OFF)\nset(FLATBUFFERS_BUILD_TESTS OFF)\nset(FLATBUFFERS_INSTALL OFF)\nFetchContent_MakeAvailable(flatbuffers)\n\nadd_compile_definitions(TFLITE_IN_GMSCORE)\nadd_compile_definitions(TFLITE_WITH_STABLE_ABI)\nadd_compile_definitions(TFLITE_USE_OPAQUE_DELEGATE)\n\nadd_library(tflite-c-sample-jni SHARED\n        com_google_samples_gms_tflite_c_TfLiteJni.cc\n        logging_assert.h\n        java_interop.h)\n\ntarget_link_libraries(tflite-c-sample-jni\n        tensorflowlite_jni_gms_client::tensorflowlite_jni_gms_client\n        flatbuffers\n        android\n        log)\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/app/src/main/cpp/com_google_samples_gms_tflite_c_TfLiteJni.cc",
    "content": "/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#include <android/asset_manager.h>\n#include <android/asset_manager_jni.h>\n#include <android/log.h>\n#include <jni.h>\n\n#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n\n#include \"flatbuffers/flatbuffers.h\"\n#include \"java_interop.h\"    // NOLINT (build/include)\n#include \"logging_assert.h\"  // NOLINT (build/include)\n#include \"tensorflow/lite/abi/tflite.h\"\n#include \"tensorflow/lite/acceleration/configuration/c/gpu_plugin.h\"\n#include \"tensorflow/lite/acceleration/configuration/configuration_generated.h\"\n#include \"tensorflow/lite/c/c_api.h\"\n\nnamespace {\nAAsset* g_model_asset = nullptr;\nTfLiteInterpreter* g_interpreter = nullptr;\nTfLiteOpaqueDelegate* g_gpu_delegate = nullptr;\n\nTfLiteInterpreter* CreateInterpreterFromModel(JNIEnv* env, jobject callback,\n                                              TfLiteModel* model);\nTfLiteTensor* GetAndValidateInputTensor(JNIEnv* env, jobject tfliteJni,\n                                        TfLiteInterpreter* interpreter,\n                                        size_t input_size);\nconst TfLiteTensor* GetAndValidateOutputTensor(JNIEnv* env, jobject tfliteJni,\n                                               TfLiteInterpreter* interpreter);\n\nvoid LogToCallback(JNIEnv* env, jobject tfliteJni, const char* messageFormat,\n                   ...) {\n  static jmethodID printMethodId = util::java::GetMethodIdFromObject(\n      env, tfliteJni, \"sendLogMessage\", \"(Ljava/lang/String;)V\");\n\n  char message[1024];\n  va_list args;\n  va_start(args, messageFormat);\n  vsnprintf(message, sizeof(message), messageFormat, args);\n  va_end(args);\n\n  auto jmessage = util::java::NewStringUTF(env, message);\n  env->CallVoidMethod(tfliteJni, printMethodId, jmessage.get());\n}\n\n}  // namespace\n\nextern \"C\" void\nJava_com_google_samples_gms_tflite_c_TfLiteJni_initGpuAcceleration(\n    JNIEnv* env, jobject tfliteJni) {\n  if (!GmsTfLiteCheckInitializedOrThrow(env)) return;\n\n  flatbuffers::FlatBufferBuilder fbb;\n  tflite::TFLiteSettingsBuilder builder(fbb);\n  const tflite::TFLiteSettings* tflite_settings =\n      flatbuffers::GetTemporaryPointer(fbb, builder.Finish());\n  ASSERT_NE(env, tflite_settings, nullptr);\n\n  const TfLiteOpaqueDelegatePlugin* pluginCApi = TfLiteGpuDelegatePluginCApi();\n  ASSERT_NE(env, pluginCApi, nullptr);\n  g_gpu_delegate = pluginCApi->create(tflite_settings);\n  ASSERT_NE(env, g_gpu_delegate, nullptr);\n  LogToCallback(env, tfliteJni, \"GPU acceleration initialized\");\n}\n\nextern \"C\" void Java_com_google_samples_gms_tflite_c_TfLiteJni_loadModel(\n    JNIEnv* env, jobject tfliteJni, jobject asset_manager, jstring asset_name) {\n  if (!GmsTfLiteCheckInitializedOrThrow(env)) return;\n  // Create model.\n  AAssetManager* aAssetManager = AAssetManager_fromJava(env, asset_manager);\n  g_model_asset = AAssetManager_open(\n      aAssetManager, util::java::StringFromJString(env, asset_name).c_str(),\n      AASSET_MODE_BUFFER);\n  TfLiteModel* model = TfLiteModelCreate(AAsset_getBuffer(g_model_asset),\n                                         AAsset_getLength(g_model_asset));\n  ASSERT_NE(env, model, nullptr);\n  LogToCallback(env, tfliteJni, \"  Model created\");\n\n  g_interpreter = CreateInterpreterFromModel(env, tfliteJni, model);\n\n  ASSERT_EQ(env, TfLiteInterpreterAllocateTensors(g_interpreter), kTfLiteOk);\n}\n\nextern \"C\" jfloatArray\nJava_com_google_samples_gms_tflite_c_TfLiteJni_runInference(JNIEnv* env,\n                                                            jobject tfliteJni,\n                                                            jfloatArray input) {\n  jsize input_size;\n  auto cinput = util::java::FloatArrayFromJFloatArray(env, input, &input_size);\n\n  // Copy input to g_interpreter.\n  TfLiteTensor* input_tensor =\n      GetAndValidateInputTensor(env, tfliteJni, g_interpreter, input_size);\n  ASSERT_EQ(env,\n            TfLiteTensorCopyFromBuffer(input_tensor, cinput.get(),\n                                       input_size * sizeof(float)),\n            kTfLiteOk);\n  LogToCallback(env, tfliteJni, \"  Input copied\");\n\n  // Run inference.\n  ASSERT_EQ(env, TfLiteInterpreterInvoke(g_interpreter), kTfLiteOk);\n  LogToCallback(env, tfliteJni, \"  Inference executed\");\n\n  // Get output from g_interpreter.\n  const TfLiteTensor* output_tensor =\n      GetAndValidateOutputTensor(env, tfliteJni, g_interpreter);\n  float c_array[2];\n  ASSERT_EQ(env,\n            TfLiteTensorCopyToBuffer(output_tensor, c_array, 2 * sizeof(float)),\n            kTfLiteOk);\n  LogToCallback(env, tfliteJni, \"  Output values copied\");\n  return util::java::JfloatArrayFromFloatArray(env, c_array, 2);\n}\n\nextern \"C\" void Java_com_google_samples_gms_tflite_c_TfLiteJni_destroy(\n    JNIEnv* env, jobject tfliteJni) {\n  TfLiteInterpreterDelete(g_interpreter);\n  g_interpreter = nullptr;\n  // Some of the resources in the g_interpreter, e.g. the tensor names, are\n  // backed by the g_model_asset storage, so we should not close the\n  // g_model_asset until after we're done with the g_interpreter.\n  AAsset_close(g_model_asset);\n  g_model_asset = nullptr;\n  if (g_gpu_delegate) {\n    const auto pluginCApi = TfLiteGpuDelegatePluginCApi();\n    ASSERT_NE(env, pluginCApi, nullptr);\n    pluginCApi->destroy(g_gpu_delegate);\n    g_gpu_delegate = nullptr;\n  }\n}\n\nnamespace {\n\nTfLiteInterpreter* CreateInterpreterFromModel(JNIEnv* env, jobject callback,\n                                              TfLiteModel* model) {\n  // Set up options\n  TfLiteInterpreterOptions* options = TfLiteInterpreterOptionsCreate();\n  ASSERT_NE(env, options, nullptr);\n  TfLiteInterpreterOptionsSetNumThreads(options, 2);\n  if (g_gpu_delegate) {\n    TfLiteInterpreterOptionsAddDelegate(options, g_gpu_delegate);\n    LogToCallback(env, callback, \"  GPU delegate added to options\");\n  }\n  LogToCallback(env, callback, \"  Options created\");\n\n  // Create interpreter.\n  TfLiteInterpreter* interpreter = TfLiteInterpreterCreate(model, options);\n  ASSERT_NE(env, interpreter, nullptr);\n  LogToCallback(env, callback, \"  Interpreter created\");\n\n  // The options/model can be deleted immediately after interpreter creation.\n  TfLiteInterpreterOptionsDelete(options);\n  TfLiteModelDelete(model);\n  LogToCallback(env, callback, \"  Options/model closed/deleted\");\n  return interpreter;\n}\n\nTfLiteTensor* GetAndValidateInputTensor(JNIEnv* env, jobject tfliteJni,\n                                        TfLiteInterpreter* interpreter,\n                                        size_t input_size) {\n  ASSERT_EQ(env, TfLiteInterpreterGetInputTensorCount(interpreter), 1);\n\n  int input_dims[1] = {static_cast<int>(input_size)};\n  ASSERT_EQ(env,\n            TfLiteInterpreterResizeInputTensor(interpreter, 0, input_dims, 1),\n            kTfLiteOk);\n  ASSERT_EQ(env, TfLiteInterpreterAllocateTensors(interpreter), kTfLiteOk);\n  LogToCallback(env, tfliteJni, \"  Input dims checked\");\n\n  TfLiteTensor* input_tensor = TfLiteInterpreterGetInputTensor(interpreter, 0);\n  ASSERT_NE(env, input_tensor, nullptr);\n  ASSERT_EQ(env, TfLiteTensorType(input_tensor), kTfLiteFloat32);\n  ASSERT_EQ(env, TfLiteTensorNumDims(input_tensor), 1);\n  ASSERT_EQ(env, TfLiteTensorDim(input_tensor, 0), input_size);\n  ASSERT_EQ(env, TfLiteTensorByteSize(input_tensor),\n            sizeof(float) * input_size);\n  ASSERT_NE(env, TfLiteTensorData(input_tensor), nullptr);\n  ASSERT_STREQ(env, TfLiteTensorName(input_tensor), \"input\");\n  LogToCallback(env, tfliteJni, \"  Input tensor params checked\");\n\n  auto input_params = TfLiteTensorQuantizationParams(input_tensor);\n  ASSERT_EQ(env, input_params.scale, 0.f);\n  ASSERT_EQ(env, input_params.zero_point, 0);\n  LogToCallback(env, tfliteJni, \"  Input quantization params checked\");\n\n  return input_tensor;\n}\n\nconst TfLiteTensor* GetAndValidateOutputTensor(JNIEnv* env, jobject tfliteJni,\n                                               TfLiteInterpreter* interpreter) {\n  ASSERT_EQ(env, TfLiteInterpreterGetOutputTensorCount(interpreter), 1);\n\n  const TfLiteTensor* output_tensor =\n      TfLiteInterpreterGetOutputTensor(interpreter, 0);\n  ASSERT_NE(env, output_tensor, nullptr);\n  ASSERT_EQ(env, TfLiteTensorType(output_tensor), kTfLiteFloat32);\n  ASSERT_EQ(env, TfLiteTensorNumDims(output_tensor), 1);\n  ASSERT_EQ(env, TfLiteTensorDim(output_tensor, 0), 2);\n  ASSERT_EQ(env, TfLiteTensorByteSize(output_tensor), sizeof(float) * 2);\n  ASSERT_NE(env, TfLiteTensorData(output_tensor), nullptr);\n  ASSERT_STREQ(env, TfLiteTensorName(output_tensor), \"output\");\n  LogToCallback(env, tfliteJni, \"  Output tensor params checked\");\n\n  auto output_params = TfLiteTensorQuantizationParams(output_tensor);\n  ASSERT_EQ(env, output_params.scale, 0.f);\n  ASSERT_EQ(env, output_params.zero_point, 0);\n  LogToCallback(env, tfliteJni, \"  Output quantization params checked\");\n\n  return output_tensor;\n}\n\n}  // namespace\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/app/src/main/cpp/java_interop.h",
    "content": "/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#ifndef JAVA_INTEROP_H_  // NOLINT(build/header_guard)\n#define JAVA_INTEROP_H_\n\n#include <jni.h>\n\n#include <string>\n\n#include \"logging_assert.h\"  // NOLINT (build/include)\n\nnamespace util {\nnamespace java {\n\nclass LocalRefDeleter {\n public:\n  // Style guide violating implicit constructor so that the LocalRefDeleter\n  // is implicitly constructed from the second argument to ScopedLocalRef.\n  // TODO: remove NOLINT and the comment itself before publishing.\n  LocalRefDeleter(JNIEnv* env) : env_(env) {}  // NOLINT(runtime/explicit)\n\n  LocalRefDeleter(const LocalRefDeleter& orig) = default;\n\n  // Copy assignment to allow move semantics in ScopedLocalRef.\n  LocalRefDeleter& operator=(const LocalRefDeleter& rhs) { return *this; }\n\n  void operator()(jobject o) const { env_->DeleteLocalRef(o); }\n\n private:\n  JNIEnv* const env_;\n};\n\ntemplate <typename T>\nusing ScopedLocalRef =\n    std::unique_ptr<typename std::remove_pointer<T>::type, LocalRefDeleter>;\n\n// Convenient wrappers that prevent from common JNI usage mistakes.\n// We explicitly handle only the happy path of the JNI calls itself.\n// The JNI error handling is out of scope for this sample app.\n\nScopedLocalRef<jclass> GetObjectClass(JNIEnv* env, jobject obj) {\n  return ScopedLocalRef<jclass>(env->GetObjectClass(obj), env);\n}\n\njmethodID GetMethodIdFromObject(JNIEnv* env, jobject obj, const char* name,\n                                const char* sig) {\n  auto clazz = GetObjectClass(env, obj);\n  return env->GetMethodID(clazz.get(), name, sig);\n}\n\nScopedLocalRef<jstring> NewStringUTF(JNIEnv* env, const char* mutf8_bytes) {\n  return ScopedLocalRef<jstring>(env->NewStringUTF(mutf8_bytes), env);\n}\n\nstd::string StringFromJString(JNIEnv* env, jstring value) {\n  const char* data = env->GetStringUTFChars(value, nullptr);\n  std::string copy(data);\n  env->ReleaseStringUTFChars(value, data);\n  return copy;\n}\n\nstd::unique_ptr<jfloat[]> FloatArrayFromJFloatArray(JNIEnv* env,\n                                                    jfloatArray array,\n                                                    jsize* length_out_arg) {\n  jint length = env->GetArrayLength(array);\n  if (length_out_arg != nullptr) {\n    *length_out_arg = length;\n  }\n  std::unique_ptr<jfloat[]> result(new jfloat[length]);\n  env->GetFloatArrayRegion(array, 0, length, result.get());\n  return result;\n}\n\njfloatArray JfloatArrayFromFloatArray(JNIEnv* env, jfloat* array, jsize size) {\n  jfloatArray result = env->NewFloatArray(size);\n  env->SetFloatArrayRegion(result, 0, size, array);\n  return result;\n}\n\n}  // namespace java\n}  // namespace util\n#endif  // JAVA_INTEROP_H_\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/app/src/main/cpp/logging_assert.h",
    "content": "/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#ifndef LOGGING_ASSERT_H_  // NOLINT(build/header_guard)\n#define LOGGING_ASSERT_H_\n\n#include <android/log.h>\n#include <stdio.h>\n\n// We use an extra level of macro indirection here to ensure that the\n// macro arguments get evaluated, so that in a call to CHECK_CONDITION(foo),\n// the call to STRINGIZE(condition) in the definition of the CHECK_CONDITION\n// macro results in the string \"foo\" rather than the string \"condition\".\n#define STRINGIZE(expression) STRINGIZE2(expression)\n#define STRINGIZE2(expression) #expression\n\n// Will log the result to Android's logcat.\n#define CHECK_CONDITION(jni_env, condition)                            \\\n  do {                                                                 \\\n    ((condition)                                                       \\\n     ? logging::CheckSucceeded(STRINGIZE(condition), __func__)         \\\n     : logging::CheckFailed(jni_env, STRINGIZE(condition), __func__)); \\\n  } while (false)\n#define ASSERT_EQ(jni_env, expected, actual) \\\n  CHECK_CONDITION(jni_env, (expected) == (actual))\n#define ASSERT_NE(jni_env, expected, actual) \\\n  CHECK_CONDITION(jni_env, (expected) != (actual))\n#define ASSERT_STREQ(jni_env, expected, actual) \\\n  ASSERT_EQ(jni_env, 0, strcmp((expected), (actual)))\n\nnamespace logging {\n\nvoid CheckSucceeded(const char* expression, const char* function) {\n  char message[1024];\n  snprintf(message, sizeof(message), \"SUCCESS: CHECK passed: %s: %s\", function,\n           expression);\n  __android_log_print(ANDROID_LOG_INFO, \"TFLJNI\", \"%s\\n\", message);\n}\n\nvoid CheckFailed(JNIEnv* env, const char* expression, const char* function) {\n  char message[1024];\n  snprintf(message, sizeof(message), \"ERROR: CHECK failed: %s: %s\", function,\n           expression);\n  __android_log_print(ANDROID_LOG_ERROR, \"TFLJNI\", \"%s\\n\", message);\n  env->FatalError(message);\n  // no return\n  abort();\n}\n}  // namespace logging\n\n#endif  // LOGGING_ASSERT_H_\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/app/src/main/java/com/google/samples/gms/tflite/c/MainActivity.java",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.samples.gms.tflite.c;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.widget.TextView;\nimport androidx.annotation.Nullable;\nimport com.google.android.gms.tasks.Task;\nimport com.google.android.gms.tasks.Tasks;\nimport com.google.android.gms.tflite.client.TfLiteInitializationOptions;\nimport com.google.android.gms.tflite.gpu.support.TfLiteGpu;\nimport com.google.android.gms.tflite.java.TfLiteNative;\nimport java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.Arrays;\nimport java.util.Date;\n\n/** Sample activity to test the TFLite C API. */\npublic class MainActivity extends Activity {\n\n  private static final String TAG = \"MainActivity\";\n\n  private volatile boolean isGpuAvailable = false;\n\n  private TextView logView;\n\n  @Override\n  protected void onCreate(@Nullable Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    setContentView(R.layout.activity_main);\n\n    logView = findViewById(R.id.log_text);\n\n    findViewById(android.R.id.button1).setOnClickListener(v -> runScenario());\n  }\n\n  @Override\n  protected void onStart() {\n    super.onStart();\n    runScenario();\n  }\n\n  private void runScenario() {\n    String currentTime = SimpleDateFormat.getTimeInstance(DateFormat.SHORT).format(new Date());\n    logView.setText(String.format(\"Scenario started at %s\\n\", currentTime));\n    isGpuAvailable = false;\n\n    logEvent(\"Checking GPU acceleration availability...\");\n\n    Task<Void> tfLiteHandleTask =\n        TfLiteGpu.isGpuDelegateAvailable(this)\n            .onSuccessTask(\n                gpuAvailable -> {\n                  isGpuAvailable = gpuAvailable;\n                  logEvent(\"GPU acceleration is \" + (isGpuAvailable ? \"available\" : \"unavailable\"));\n                  TfLiteInitializationOptions options =\n                      TfLiteInitializationOptions.builder()\n                          .setEnableGpuDelegateSupport(isGpuAvailable)\n                          .build();\n                  return TfLiteNative.initialize(this, options);\n                });\n\n    tfLiteHandleTask\n        .onSuccessTask(\n            unused -> {\n              logEvent(\"Running inference on \" + (isGpuAvailable ? \"GPU\" : \"CPU\"));\n              TfLiteJni jni = new TfLiteJni(this::logEvent);\n              if (isGpuAvailable) {\n                jni.initGpuAcceleration();\n              }\n              logEvent(\"TfLiteJni created\");\n              jni.loadModel(getAssets(), \"add.tflite\");\n              logEvent(\"Model loaded\");\n              float[] output = jni.runInference(new float[] {1.f, 3.f});\n              logEvent(\n                  \"Ran inference, expected: [3.0, 9.0], got output: \" + Arrays.toString(output));\n              jni.destroy();\n              logEvent(\"TfLiteJni destroyed\");\n              return Tasks.forResult(output);\n            })\n        .addOnSuccessListener(unused -> logEvent(\"Scenario successful!\"))\n        .addOnFailureListener(e -> logEvent(\"Scenario failed\", e));\n  }\n\n  private void logEvent(String message) {\n    logEvent(message, null);\n  }\n\n  private void logEvent(String message, @Nullable Throwable throwable) {\n    Log.e(TAG, message, throwable);\n    logView.append(\"• \");\n    logView.append(String.valueOf(message));\n    logView.append(\"\\n\");\n    if (throwable != null) {\n      logView.append(throwable.getClass().getCanonicalName() + \": \" + throwable.getMessage());\n      logView.append(\"\\n\");\n      logView.append(Arrays.toString(throwable.getStackTrace()));\n      logView.append(\"\\n\");\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/app/src/main/java/com/google/samples/gms/tflite/c/TfLiteJni.java",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.samples.gms.tflite.c;\n\nimport android.content.res.AssetManager;\n\n/** JNI bridge to forward the calls to the native code, where we can invoke the TFLite C API. */\npublic class TfLiteJni {\n\n  private final LoggingCallback loggingCallback;\n\n  /**\n   * This interface gets called when the JNI wants to print a message (used for debugging purposes).\n   */\n  public interface LoggingCallback {\n    void printLogMessage(String message);\n  }\n\n  static {\n    System.loadLibrary(\"tflite-c-sample-jni\");\n  }\n\n  public TfLiteJni(LoggingCallback loggingCallback) {\n    this.loggingCallback = loggingCallback;\n  }\n\n  private void sendLogMessage(String message) {\n    if (loggingCallback != null) {\n      loggingCallback.printLogMessage(message);\n    }\n  }\n\n  /** Creates GPU delegate that will be used for the inference. */\n  public native void initGpuAcceleration();\n\n  /**\n   * Loads the model and creates the Interpreter. GPU delegate is applied if {@link\n   * TfLiteJni#initGpuAcceleration} was previously called.\n   */\n  public native void loadModel(AssetManager assetManager, String assetName);\n\n  /** Runs the inference using the Interpreter created by {@link TfLiteJni#loadModel}. */\n  public native float[] runInference(float[] input);\n\n  /** Unloads the assets and clears all the Interpreter's resources. */\n  public native void destroy();\n}\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:orientation=\"vertical\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n  <Button\n      android:id=\"@android:id/button1\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:text=\"@string/tap_me\" />\n\n\n  <ScrollView\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\">\n    <TextView\n        android:id=\"@+id/log_text\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\" />\n  </ScrollView>\n\n</LinearLayout>\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <color name=\"tfe_color_primary\">#ffa800</color>\n  <color name=\"tfe_color_primary_dark\">#ff6f00</color>\n  <color name=\"tfe_color_accent\">#425066</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <dimen name=\"tfe_sr_activity_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <string name=\"app_name\">TFLite sample</string>\n  <string name=\"tap_me\">Tap me</string>\n</resources>\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n  <!-- Base application theme. -->\n  <style name=\"AppTheme.TFLite\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n    <!-- Customize your theme here. -->\n    <item name=\"colorPrimary\">@color/tfe_color_primary</item>\n    <item name=\"colorPrimaryDark\">@color/tfe_color_primary_dark</item>\n    <item name=\"colorAccent\">@color/tfe_color_accent</item>\n  </style>\n\n</resources>\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nplugins {\n    id 'com.android.application' version '8.1.1' apply false\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app's APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Enables namespacing of each library's R class so that its R class includes only the\n# resources declared in the library itself and none from the library's dependencies,\n# thereby reducing the size of the R class for that library\nandroid.nonTransitiveRClass=true\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/c_api/settings.gradle",
    "content": "pluginManagement {\n    repositories {\n        gradlePluginPortal()\n        google()\n        mavenCentral()\n    }\n}\n\nplugins {\n    // for java { toolchain { languageVersion.set() } }\n    id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0'\n}\n\nrootProject.name = 'tflite-c-sample'\ninclude ':app'\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/app/build.gradle",
    "content": "apply plugin: 'com.android.application'\napply from: 'tflite-java-extract-cpp-sdk.gradle'\n\nandroid {\n    namespace 'com.google.samples.gms.tflite.cc'\n    compileSdk 33\n\n    defaultConfig {\n        applicationId 'com.google.samples.gms.tflite.cc'\n        minSdk 21\n        targetSdk 33\n        versionCode 1\n        versionName '1.0'\n\n        testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'\n    }\n\n    buildTypes {\n        all {\n            proguardFiles 'proguard-rules.pro'\n        }\n        release {\n            minifyEnabled true\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    buildFeatures {\n        prefab true\n    }\n\n    externalNativeBuild {\n        cmake {\n            path file('src/main/cpp/CMakeLists.txt')\n        }\n    }\n\n    aaptOptions {\n        noCompress 'bin'\n    }\n}\n\njava {\n    toolchain {\n        languageVersion.set(JavaLanguageVersion.of(11))\n    }\n}\n\ndependencies {\n\n    implementation 'androidx.appcompat:appcompat:1.6.0'\n    implementation 'com.google.android.material:material:1.8.0'\n\n    androidTestImplementation 'androidx.test:rules:1.4.0'\n    androidTestImplementation 'androidx.test:runner:1.4.0'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n    androidTestImplementation 'androidx.test.ext:junit:1.1.3'\n    androidTestImplementation 'com.google.truth:truth:1.1.3'\n\n    implementation 'com.google.android.gms:play-services-tflite-java:16.4.0'\n    implementation 'com.google.android.gms:play-services-tflite-gpu:16.4.0'\n}\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/app/proguard-rules.pro",
    "content": "-keepclasseswithmembers class com.google.samples.gms.tflite.cc.MainActivity { *; }\n-keepclasseswithmembers class com.google.samples.gms.tflite.cc.TfLiteJni { *; }\n-keepclasseswithmembers class android.support.** { *; }\n-keepclasseswithmembers class androidx.** { *; }\n\n# The tests use Tasks.await, but the instrumented code doesn't\n# Without the appropriate Proguard config, the method would be pruned\n-keepclasseswithmembers class com.google.android.gms.tasks.** { *; }\n# The tests also uses TfLiteNative.initialize(context)\n-keepclasseswithmembers class com.google.android.gms.tflite.java.TfLiteNative { *; }\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/app/src/androidTest/java/com/google/samples/gms/tflite/c/instrumentation/BasicScenarioTest.java",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.samples.gms.tflite.c.instrumentation;\n\nimport static com.google.common.truth.Truth.assertThat;\n\nimport android.content.Context;\nimport android.util.Log;\nimport androidx.test.core.app.ApplicationProvider;\nimport androidx.test.ext.junit.runners.AndroidJUnit4;\nimport com.google.android.gms.tasks.Tasks;\nimport com.google.android.gms.tflite.java.TfLiteNative;\nimport com.google.samples.gms.tflite.c.TfLiteJni;\nimport java.util.concurrent.ExecutionException;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\n/** Instrumentation tests for the TFLite Native API. */\n@RunWith(AndroidJUnit4.class)\npublic class BasicScenarioTest {\n  private static final String TAG = \"BasicScenarioTest\";\n\n  @Test\n  public void basicScenario() throws ExecutionException, InterruptedException {\n    Context context = ApplicationProvider.getApplicationContext();\n    Tasks.await(TfLiteNative.initialize(context));\n    TfLiteJni jni = new TfLiteJni(message -> Log.e(TAG, message));\n\n    jni.loadModel(context.getAssets(), \"add.tflite\");\n    float[] output = jni.runInference(new float[] {1.f, 3.f});\n    jni.destroy();\n\n    assertThat(output).isEqualTo(new float[] {3.f, 9.f});\n  }\n}\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/app/src/androidTest/java/com/google/samples/gms/tflite/c/instrumentation/TfLiteNativeGPUAccelerationTest.java",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.samples.gms.tflite.c.instrumentation;\n\nimport static com.google.common.truth.Truth.assertThat;\n\nimport android.content.Context;\nimport android.util.Log;\nimport androidx.test.core.app.ApplicationProvider;\nimport androidx.test.ext.junit.runners.AndroidJUnit4;\nimport com.google.android.gms.tasks.Tasks;\nimport com.google.android.gms.tflite.client.TfLiteInitializationOptions;\nimport com.google.android.gms.tflite.gpu.support.TfLiteGpu;\nimport com.google.android.gms.tflite.java.TfLiteNative;\nimport com.google.samples.gms.tflite.c.TfLiteJni;\nimport java.util.concurrent.ExecutionException;\nimport org.junit.Assume;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\n/** Instrumentation tests for the TFLite Native Acceleration API. */\n@RunWith(AndroidJUnit4.class)\npublic class TfLiteNativeGPUAccelerationTest {\n  private static final String TAG = \"TfLiteNativeGPUAccelerationTest\";\n\n  private static final TfLiteInitializationOptions options =\n      TfLiteInitializationOptions.builder().setEnableGpuDelegateSupport(true).build();\n\n  private Context context;\n\n  @Before\n  public void setUp() throws ExecutionException, InterruptedException {\n    context = ApplicationProvider.getApplicationContext();\n    boolean gpuAvailable = Tasks.await(TfLiteGpu.isGpuDelegateAvailable(context));\n    Assume.assumeTrue(\"GPU acceleration is unavailable on this device.\", gpuAvailable);\n    Tasks.await(TfLiteNative.initialize(context, options));\n  }\n\n  @Test\n  public void doInferenceWithAcceleration() {\n    TfLiteJni jni = new TfLiteJni(message -> Log.e(TAG, message));\n    jni.initGpuAcceleration();\n    jni.loadModel(context.getAssets(), \"add.tflite\");\n    float[] output = jni.runInference(new float[] {1.f, 3.f});\n    jni.destroy();\n    assertThat(output).isEqualTo(new float[] {3.f, 9.f});\n  }\n}\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n\n  <application\n      android:allowBackup=\"true\"\n      android:icon=\"@mipmap/ic_launcher\"\n      android:label=\"@string/app_name\"\n      android:taskAffinity=\"\"\n      android:theme=\"@style/AppTheme.TFLite\">\n    <activity\n        android:name=\"com.google.samples.gms.tflite.cc.MainActivity\"\n        android:exported=\"true\">\n      <intent-filter>\n        <action android:name=\"android.intent.action.MAIN\" />\n        <category android:name=\"android.intent.category.LAUNCHER\" />\n      </intent-filter>\n    </activity>\n  </application>\n\n  <queries>\n    <package android:name=\"com.google.android.gms.policy_tflite_dynamite_dynamite\" />\n  </queries>\n\n</manifest>\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/app/src/main/cpp/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.16.3)\n\nproject(\"tflite-cc-sample\")\n\n#-----------------------------------------------------------------------------\n# Set up FlatBuffers dependency.\n# (tflite_cc_api requires absl, and absl requires flatbuffers.)\n\ninclude(FetchContent)\nFetchContent_Declare(\n        flatbuffers\n        GIT_REPOSITORY https://github.com/google/flatbuffers.git\n        # Keep in sync with the FlatBuffers version in the most recent version\n        # of LiteRT (TF Lite) for Play services published on maven.google.com\n        # <https://maven.google.com/web/index.html#com.google.android.gms:play-services-tflite-java>\n        # and in particular as determined by the `static_assert` in the file\n        # `tensorflow/lite/acceleration/configuration/configuration_generated.h`\n        # in the `prefab/modules/tensorflowlite_jni_gms_client/include/` directory\n        # of the `play-services-tflite-java-<version>.aar` file there.\n        #\n        # That version is currently \"GIT_TAG v24.3.25\",\n        # but due to a build error in v24.3.25, we use a snapshot that is three PRs\n        # later (all bug fixes), which fixes the build error.\n        # See <https://github.com/google/flatbuffers/commit/e6463926479bd6b330cbcf673f7e917803fd5831>.\n        GIT_TAG e6463926479bd6b330cbcf673f7e917803fd5831\n)\nset(FLATBUFFERS_BUILD_FLATC OFF)\nset(FLATBUFFERS_BUILD_TESTS OFF)\nset(FLATBUFFERS_INSTALL OFF)\nFetchContent_MakeAvailable(flatbuffers)\n\n#-----------------------------------------------------------------------------\n# Set up ABSL dependency.\n\nFetchContent_Declare(\n        absl\n        GIT_REPOSITORY https://github.com/abseil/abseil-cpp.git\n        GIT_TAG 20240722.0 # Released on Aug 1, 2024\n)\nFetchContent_MakeAvailable(absl)\n\n#-----------------------------------------------------------------------------\n# Set up LiteRT in Play services C API (tensorflowlite_jni_gms_client) dependency.\n\nfind_package(tensorflowlite_jni_gms_client REQUIRED CONFIG)\n\n#-----------------------------------------------------------------------------\n# Set up LiteRT in Play services C++ API (tflite_cc_api) dependency.\n\nlist(PREPEND CMAKE_MODULE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/Modules\")\n\nfind_package(tflite_cc_api REQUIRED MODULE)\ninclude_directories(${tflite_cc_api_INCLUDE_DIR})\nadd_subdirectory(${tflite_cc_api_DIR} tflite_cc_api_build)\n\n#-----------------------------------------------------------------------------\n# Set up compile definitions to enable use of LiteRT in Play services\n# (rather than regular LiteRT bundled with the app).\n\nadd_compile_definitions(TFLITE_IN_GMSCORE)\nadd_compile_definitions(TFLITE_WITH_STABLE_ABI)\nadd_compile_definitions(TFLITE_USE_OPAQUE_DELEGATE)\n\n#-----------------------------------------------------------------------------\n# Define how to build this sample app's native code,\n# which is embedded in a JNI library so that it can be\n# called from the sample app's Java code.\n\nadd_library(tflite-cc-sample-jni SHARED\n        com_google_samples_gms_tflite_cc_TfLiteJni.cc\n        logging_assert.h\n        java_interop.h)\n\ntarget_link_libraries(tflite-cc-sample-jni\n        tensorflowlite_jni_gms_client::tensorflowlite_jni_gms_client\n        tflite_cc_api::tflite_cc_api\n        flatbuffers\n        android\n        log)\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/app/src/main/cpp/Modules/Findtflite_cc_api.cmake",
    "content": "# This CMake code fragment determines the location of the tflite_cc_api package.\n\nset(gradle_src_root \"${CMAKE_SOURCE_DIR}/../../..\")\n\n# tflite_cc_api_DIR is the directory containing the CMakeLists.txt file\n# for building the TFLite-in-Play-services C++ API client SDK source files.\nif (NOT EXISTS \"${gradle_src_root}/build/tflite_cc_sdk/CMakeLists.txt\")\n        message(SEND_ERROR \"CMakeLists.txt not found in ${gradle_src_root}/build/tflite_cc_sdk\")\nelseif (NOT EXISTS \"${gradle_src_root}/build/tflite_cc_sdk/tensorflow/lite/abi/cc/interpreter.cc\")\n        message(SEND_ERROR \"tensorflow/lite/abi/cc/interpreter.cc not found in ${gradle_src_root}/build/tflite_cc_sdk\")\nelse()\n        set(tflite_cc_api_DIR \"${gradle_src_root}/build/tflite_cc_sdk\")\nendif()\n\n# tflite_cc_api_INCLUDE_DIR is the include directory containing the header\n# files for the TFLite-in-Play-services C++ API.\nif (NOT EXISTS \"${gradle_src_root}/build/tflite_cc_sdk/tensorflow/lite/interpreter.h\")\n        message(SEND_ERROR \"tensorflow/lite/interpreter.h not found in ${gradle_src_root}/build/tflite_cc_sdk\")\nelseif (NOT EXISTS \"${gradle_src_root}/build/tflite_cc_sdk/tensorflow/lite/abi/cc/interpreter.h\")\n        message(SEND_ERROR \"tensorflow/lite/abi/cc/interpreter.h not found in ${gradle_src_root}/build/tflite_cc_sdk\")\nelse()\n        set(tflite_cc_api_INCLUDE_DIR \"${gradle_src_root}/tflite_cc_sdk\")\nendif()\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(tflite_cc_api DEFAULT_MSG\n                                  tflite_cc_api_DIR tflite_cc_api_INCLUDE_DIR)\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/app/src/main/cpp/com_google_samples_gms_tflite_cc_TfLiteJni.cc",
    "content": "/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#include <android/asset_manager.h>\n#include <android/asset_manager_jni.h>\n#include <android/log.h>\n#include <jni.h>\n\n#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n#include <functional>\n\n#include \"flatbuffers/flatbuffers.h\"\n#include \"java_interop.h\"    // NOLINT (build/include)\n#include \"logging_assert.h\"  // NOLINT (build/include)\n#include \"tensorflow/lite/abi/tflite.h\"\n#include \"tensorflow/lite/acceleration/configuration/c/gpu_plugin.h\"\n#include \"tensorflow/lite/acceleration/configuration/configuration_generated.h\"\n#include \"tensorflow/lite/c/c_api.h\"        // For TfLiteTensorCopyToBuffer.\n#include \"tensorflow/lite/c/c_api_types.h\"  // For TfLiteOpaqueDelegate.\n#include \"tensorflow/lite/interpreter.h\"\n#include \"tensorflow/lite/interpreter_builder.h\"\n#include \"tensorflow/lite/model_builder.h\"\n\nnamespace {\nAAsset* g_model_asset = nullptr;\nstd::unique_ptr<tflite::Interpreter> g_interpreter = nullptr;\nusing OpaqueDelegateDeleter = std::function<void(TfLiteOpaqueDelegate*)>;\nusing OpaqueDelegatePtr =\n    std::unique_ptr<TfLiteOpaqueDelegate, OpaqueDelegateDeleter>;\nOpaqueDelegatePtr g_gpu_delegate = nullptr;\n\nstd::unique_ptr<tflite::Interpreter> CreateInterpreterFromModel(\n    JNIEnv* env, jobject callback,\n    std::unique_ptr<tflite::FlatBufferModel> model);\nTfLiteTensor* GetAndValidateInputTensor(JNIEnv* env, jobject tfliteJni,\n                                        tflite::Interpreter* interpreter,\n                                        size_t input_size);\nconst TfLiteTensor* GetAndValidateOutputTensor(\n    JNIEnv* env, jobject tfliteJni, tflite::Interpreter* interpreter);\n\nvoid LogToCallback(JNIEnv* env, jobject tfliteJni, const char* messageFormat,\n                   ...) {\n  static jmethodID printMethodId = util::java::GetMethodIdFromObject(\n      env, tfliteJni, \"sendLogMessage\", \"(Ljava/lang/String;)V\");\n\n  char message[1024];\n  va_list args;\n  va_start(args, messageFormat);\n  vsnprintf(message, sizeof(message), messageFormat, args);\n  va_end(args);\n\n  auto jmessage = util::java::NewStringUTF(env, message);\n  env->CallVoidMethod(tfliteJni, printMethodId, jmessage.get());\n}\n\n}  // namespace\n\nextern \"C\" void\nJava_com_google_samples_gms_tflite_cc_TfLiteJni_initGpuAcceleration(\n    JNIEnv* env, jobject tfliteJni) {\n  if (!GmsTfLiteCheckInitializedOrThrow(env)) return;\n\n  flatbuffers::FlatBufferBuilder fbb;\n  tflite::TFLiteSettingsBuilder builder(fbb);\n  const tflite::TFLiteSettings* tflite_settings =\n      flatbuffers::GetTemporaryPointer(fbb, builder.Finish());\n  ASSERT_NE(env, tflite_settings, nullptr);\n\n  // Note that the API for creating the GPU delegate used here is a C API.\n  // TODO: use the C++ delegate plugin registry instead,\n  // when support for that is added to LiteRT in Play services.\n  const TfLiteOpaqueDelegatePlugin* pluginCApi = TfLiteGpuDelegatePluginCApi();\n  ASSERT_NE(env, pluginCApi, nullptr);\n  g_gpu_delegate = std::move(OpaqueDelegatePtr{\n      pluginCApi->create(tflite_settings),\n      [=](TfLiteOpaqueDelegate* delegate) { pluginCApi->destroy(delegate); }});\n  ASSERT_NE(env, g_gpu_delegate, nullptr);\n  LogToCallback(env, tfliteJni, \"GPU acceleration initialized\");\n}\n\nextern \"C\" void Java_com_google_samples_gms_tflite_cc_TfLiteJni_loadModel(\n    JNIEnv* env, jobject tfliteJni, jobject asset_manager, jstring asset_name) {\n  if (!GmsTfLiteCheckInitializedOrThrow(env)) return;\n  // Create model.\n  AAssetManager* aAssetManager = AAssetManager_fromJava(env, asset_manager);\n  g_model_asset = AAssetManager_open(\n      aAssetManager, util::java::StringFromJString(env, asset_name).c_str(),\n      AASSET_MODE_BUFFER);\n  std::unique_ptr<tflite::FlatBufferModel> model =\n      tflite::FlatBufferModel::VerifyAndBuildFromBuffer(\n          static_cast<const char*>(AAsset_getBuffer(g_model_asset)),\n          AAsset_getLength(g_model_asset));\n  ASSERT_NE(env, model, nullptr);\n  LogToCallback(env, tfliteJni, \"  Model created\");\n\n  g_interpreter = CreateInterpreterFromModel(env, tfliteJni, std::move(model));\n\n  ASSERT_EQ(env, g_interpreter->AllocateTensors(), kTfLiteOk);\n}\n\nextern \"C\" jfloatArray\nJava_com_google_samples_gms_tflite_cc_TfLiteJni_runInference(\n    JNIEnv* env, jobject tfliteJni, jfloatArray input) {\n  jsize input_size;\n  auto cinput = util::java::FloatArrayFromJFloatArray(env, input, &input_size);\n\n  // Copy input to g_interpreter.\n  TfLiteTensor* input_tensor = GetAndValidateInputTensor(\n      env, tfliteJni, g_interpreter.get(), input_size);\n  ASSERT_EQ(env,\n            TfLiteTensorCopyFromBuffer(input_tensor, cinput.get(),\n                                       input_size * sizeof(float)),\n            kTfLiteOk);\n  LogToCallback(env, tfliteJni, \"  Input copied\");\n\n  // Run inference.\n  ASSERT_EQ(env, g_interpreter->Invoke(), kTfLiteOk);\n  LogToCallback(env, tfliteJni, \"  Inference executed\");\n\n  // Get output from g_interpreter.\n  const TfLiteTensor* output_tensor =\n      GetAndValidateOutputTensor(env, tfliteJni, g_interpreter.get());\n  float c_array[2];\n  ASSERT_EQ(env,\n            TfLiteTensorCopyToBuffer(output_tensor, c_array, 2 * sizeof(float)),\n            kTfLiteOk);\n  LogToCallback(env, tfliteJni, \"  Output values copied\");\n  return util::java::JfloatArrayFromFloatArray(env, c_array, 2);\n}\n\nextern \"C\" void Java_com_google_samples_gms_tflite_cc_TfLiteJni_destroy(\n    JNIEnv* env, jobject tfliteJni) {\n  g_interpreter = nullptr;\n  // Some of the resources in the g_interpreter, e.g. the tensor names, are\n  // backed by the g_model_asset storage, so we should not close the\n  // g_model_asset until after we're done with the g_interpreter.\n  AAsset_close(g_model_asset);\n  g_model_asset = nullptr;\n  if (g_gpu_delegate) {\n    const auto pluginCApi = TfLiteGpuDelegatePluginCApi();\n    ASSERT_NE(env, pluginCApi, nullptr);\n    pluginCApi->destroy(g_gpu_delegate.release());\n    g_gpu_delegate = nullptr;\n  }\n}\n\nnamespace {\n\nstd::unique_ptr<tflite::Interpreter> CreateInterpreterFromModel(\n    JNIEnv* env, jobject callback,\n    std::unique_ptr<tflite::FlatBufferModel> model) {\n  // Set up the interpreter builder.\n#if TFLITE_IN_GMSCORE\n  tflite::InterpreterBuilder builder(*model);\n#else\n  tflite::ops::builtin::BuiltinOpResolver resolver;\n  tflite::InterpreterBuilder(*model, resolver);\n#endif\n  builder.SetNumThreads(2);\n  if (g_gpu_delegate) {\n    builder.AddDelegate(g_gpu_delegate.get());\n    LogToCallback(env, callback, \"  GPU delegate added to InterpreterBuilder\");\n  }\n  LogToCallback(env, callback, \"  InterpreterBuilder created\");\n\n  // Create interpreter.\n  std::unique_ptr<tflite::Interpreter> interpreter;\n  TfLiteStatus result = builder(&interpreter);\n  if (result != kTfLiteOk) {\n    LogToCallback(env, callback, \"  Interpreter creation failed: status %d\",\n                  static_cast<int>(result));\n    return nullptr;\n  }\n  ASSERT_NE(env, interpreter, nullptr);\n  LogToCallback(env, callback, \"  Interpreter created\");\n\n  // The builder and model can be deallocated immediately after interpreter\n  // creation.  (Deallocation will happen automatically when those objects go\n  // out of scope at the end of this function.)\n  LogToCallback(env, callback, \"  Deallocating builder and model\");\n  return interpreter;\n}\n\nTfLiteTensor* GetAndValidateInputTensor(JNIEnv* env, jobject tfliteJni,\n                                        tflite::Interpreter* interpreter,\n                                        size_t input_size) {\n  ASSERT_EQ(env, interpreter->inputs().size(), 1);\n\n  std::vector<int> input_dims = {static_cast<int>(input_size)};\n  ASSERT_EQ(\n      env, interpreter->ResizeInputTensor(interpreter->inputs()[0], input_dims),\n      kTfLiteOk);\n  ASSERT_EQ(env, interpreter->AllocateTensors(), kTfLiteOk);\n  LogToCallback(env, tfliteJni, \"  Input dims checked\");\n\n  TfLiteTensor* input_tensor = interpreter->input_tensor(0);\n  ASSERT_NE(env, input_tensor, nullptr);\n  ASSERT_EQ(env, TfLiteTensorType(input_tensor), kTfLiteFloat32);\n  ASSERT_EQ(env, TfLiteTensorNumDims(input_tensor), 1);\n  ASSERT_EQ(env, TfLiteTensorDim(input_tensor, 0), input_size);\n  ASSERT_EQ(env, TfLiteTensorByteSize(input_tensor),\n            sizeof(float) * input_size);\n  ASSERT_NE(env, TfLiteTensorData(input_tensor), nullptr);\n  ASSERT_STREQ(env, TfLiteTensorName(input_tensor), \"input\");\n  LogToCallback(env, tfliteJni, \"  Input tensor params checked\");\n\n  auto input_params = TfLiteTensorQuantizationParams(input_tensor);\n  ASSERT_EQ(env, input_params.scale, 0.f);\n  ASSERT_EQ(env, input_params.zero_point, 0);\n  LogToCallback(env, tfliteJni, \"  Input quantization params checked\");\n\n  return input_tensor;\n}\n\nconst TfLiteTensor* GetAndValidateOutputTensor(\n    JNIEnv* env, jobject tfliteJni, tflite::Interpreter* interpreter) {\n  ASSERT_EQ(env, interpreter->outputs().size(), 1);\n\n  const TfLiteTensor* output_tensor = interpreter->output_tensor(0);\n  ASSERT_NE(env, output_tensor, nullptr);\n  ASSERT_EQ(env, TfLiteTensorType(output_tensor), kTfLiteFloat32);\n  ASSERT_EQ(env, TfLiteTensorNumDims(output_tensor), 1);\n  ASSERT_EQ(env, TfLiteTensorDim(output_tensor, 0), 2);\n  ASSERT_EQ(env, TfLiteTensorByteSize(output_tensor), sizeof(float) * 2);\n  ASSERT_NE(env, TfLiteTensorData(output_tensor), nullptr);\n  ASSERT_STREQ(env, TfLiteTensorName(output_tensor), \"output\");\n  LogToCallback(env, tfliteJni, \"  Output tensor params checked\");\n\n  auto output_params = TfLiteTensorQuantizationParams(output_tensor);\n  ASSERT_EQ(env, output_params.scale, 0.f);\n  ASSERT_EQ(env, output_params.zero_point, 0);\n  LogToCallback(env, tfliteJni, \"  Output quantization params checked\");\n\n  return output_tensor;\n}\n\n}  // namespace\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/app/src/main/cpp/java_interop.h",
    "content": "/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#ifndef JAVA_INTEROP_H_  // NOLINT(build/header_guard)\n#define JAVA_INTEROP_H_\n\n#include <jni.h>\n\n#include <string>\n\n#include \"logging_assert.h\"  // NOLINT (build/include)\n\nnamespace util {\nnamespace java {\n\nclass LocalRefDeleter {\n public:\n  // Style guide violating implicit constructor so that the LocalRefDeleter\n  // is implicitly constructed from the second argument to ScopedLocalRef.\n  // TODO: remove NOLINT and the comment itself before publishing.\n  LocalRefDeleter(JNIEnv* env) : env_(env) {}  // NOLINT(runtime/explicit)\n\n  LocalRefDeleter(const LocalRefDeleter& orig) = default;\n\n  // Copy assignment to allow move semantics in ScopedLocalRef.\n  LocalRefDeleter& operator=(const LocalRefDeleter& rhs) { return *this; }\n\n  void operator()(jobject o) const { env_->DeleteLocalRef(o); }\n\n private:\n  JNIEnv* const env_;\n};\n\ntemplate <typename T>\nusing ScopedLocalRef =\n    std::unique_ptr<typename std::remove_pointer<T>::type, LocalRefDeleter>;\n\n// Convenient wrappers that prevent from common JNI usage mistakes.\n// We explicitly handle only the happy path of the JNI calls itself.\n// The JNI error handling is out of scope for this sample app.\n\nScopedLocalRef<jclass> GetObjectClass(JNIEnv* env, jobject obj) {\n  return ScopedLocalRef<jclass>(env->GetObjectClass(obj), env);\n}\n\njmethodID GetMethodIdFromObject(JNIEnv* env, jobject obj, const char* name,\n                                const char* sig) {\n  auto clazz = GetObjectClass(env, obj);\n  return env->GetMethodID(clazz.get(), name, sig);\n}\n\nScopedLocalRef<jstring> NewStringUTF(JNIEnv* env, const char* mutf8_bytes) {\n  return ScopedLocalRef<jstring>(env->NewStringUTF(mutf8_bytes), env);\n}\n\nstd::string StringFromJString(JNIEnv* env, jstring value) {\n  const char* data = env->GetStringUTFChars(value, nullptr);\n  std::string copy(data);\n  env->ReleaseStringUTFChars(value, data);\n  return copy;\n}\n\nstd::unique_ptr<jfloat[]> FloatArrayFromJFloatArray(JNIEnv* env,\n                                                    jfloatArray array,\n                                                    jsize* length_out_arg) {\n  jint length = env->GetArrayLength(array);\n  if (length_out_arg != nullptr) {\n    *length_out_arg = length;\n  }\n  std::unique_ptr<jfloat[]> result(new jfloat[length]);\n  env->GetFloatArrayRegion(array, 0, length, result.get());\n  return result;\n}\n\njfloatArray JfloatArrayFromFloatArray(JNIEnv* env, jfloat* array, jsize size) {\n  jfloatArray result = env->NewFloatArray(size);\n  env->SetFloatArrayRegion(result, 0, size, array);\n  return result;\n}\n\n}  // namespace java\n}  // namespace util\n#endif  // JAVA_INTEROP_H_\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/app/src/main/cpp/logging_assert.h",
    "content": "/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#ifndef LOGGING_ASSERT_H_  // NOLINT(build/header_guard)\n#define LOGGING_ASSERT_H_\n\n#include <android/log.h>\n#include <stdio.h>\n\n// We use an extra level of macro indirection here to ensure that the\n// macro arguments get evaluated, so that in a call to CHECK_CONDITION(foo),\n// the call to STRINGIZE(condition) in the definition of the CHECK_CONDITION\n// macro results in the string \"foo\" rather than the string \"condition\".\n#define STRINGIZE(expression) STRINGIZE2(expression)\n#define STRINGIZE2(expression) #expression\n\n// Will log the result to Android's logcat.\n#define CHECK_CONDITION(jni_env, condition)                            \\\n  do {                                                                 \\\n    ((condition)                                                       \\\n     ? logging::CheckSucceeded(STRINGIZE(condition), __func__)         \\\n     : logging::CheckFailed(jni_env, STRINGIZE(condition), __func__)); \\\n  } while (false)\n#define ASSERT_EQ(jni_env, expected, actual) \\\n  CHECK_CONDITION(jni_env, (expected) == (actual))\n#define ASSERT_NE(jni_env, expected, actual) \\\n  CHECK_CONDITION(jni_env, (expected) != (actual))\n#define ASSERT_STREQ(jni_env, expected, actual) \\\n  ASSERT_EQ(jni_env, 0, strcmp((expected), (actual)))\n\nnamespace logging {\n\nvoid CheckSucceeded(const char* expression, const char* function) {\n  char message[1024];\n  snprintf(message, sizeof(message), \"SUCCESS: CHECK passed: %s: %s\", function,\n           expression);\n  __android_log_print(ANDROID_LOG_INFO, \"TFLJNI\", \"%s\\n\", message);\n}\n\nvoid CheckFailed(JNIEnv* env, const char* expression, const char* function) {\n  char message[1024];\n  snprintf(message, sizeof(message), \"ERROR: CHECK failed: %s: %s\", function,\n           expression);\n  __android_log_print(ANDROID_LOG_ERROR, \"TFLJNI\", \"%s\\n\", message);\n  env->FatalError(message);\n  // no return\n  abort();\n}\n}  // namespace logging\n\n#endif  // LOGGING_ASSERT_H_\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/app/src/main/java/com/google/samples/gms/tflite/cc/MainActivity.java",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.samples.gms.tflite.cc;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.widget.TextView;\nimport androidx.annotation.Nullable;\nimport com.google.android.gms.tasks.Task;\nimport com.google.android.gms.tasks.Tasks;\nimport com.google.android.gms.tflite.client.TfLiteInitializationOptions;\nimport com.google.android.gms.tflite.gpu.support.TfLiteGpu;\nimport com.google.android.gms.tflite.java.TfLiteNative;\nimport java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.Arrays;\nimport java.util.Date;\n\n/** Sample activity to test the TFLite C API. */\npublic class MainActivity extends Activity {\n\n  private static final String TAG = \"MainActivity\";\n\n  private volatile boolean isGpuAvailable = false;\n\n  private TextView logView;\n\n  @Override\n  protected void onCreate(@Nullable Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    setContentView(R.layout.activity_main);\n\n    logView = findViewById(R.id.log_text);\n\n    findViewById(android.R.id.button1).setOnClickListener(v -> runScenario());\n  }\n\n  @Override\n  protected void onStart() {\n    super.onStart();\n    runScenario();\n  }\n\n  private void runScenario() {\n    String currentTime = SimpleDateFormat.getTimeInstance(DateFormat.SHORT).format(new Date());\n    logView.setText(String.format(\"Scenario started at %s\\n\", currentTime));\n    isGpuAvailable = false;\n\n    logEvent(\"Checking GPU acceleration availability...\");\n\n    Task<Void> tfLiteHandleTask =\n        TfLiteGpu.isGpuDelegateAvailable(this)\n            .onSuccessTask(\n                gpuAvailable -> {\n                  isGpuAvailable = gpuAvailable;\n                  logEvent(\"GPU acceleration is \" + (isGpuAvailable ? \"available\" : \"unavailable\"));\n                  TfLiteInitializationOptions options =\n                      TfLiteInitializationOptions.builder()\n                          .setEnableGpuDelegateSupport(isGpuAvailable)\n                          .build();\n                  return TfLiteNative.initialize(this, options);\n                });\n\n    tfLiteHandleTask\n        .onSuccessTask(\n            unused -> {\n              logEvent(\"Running inference on \" + (isGpuAvailable ? \"GPU\" : \"CPU\"));\n              TfLiteJni jni = new TfLiteJni(this::logEvent);\n              if (isGpuAvailable) {\n                jni.initGpuAcceleration();\n              }\n              logEvent(\"TfLiteJni created\");\n              jni.loadModel(getAssets(), \"add.tflite\");\n              logEvent(\"Model loaded\");\n              float[] output = jni.runInference(new float[] {1.f, 3.f});\n              logEvent(\n                  \"Ran inference, expected: [3.0, 9.0], got output: \" + Arrays.toString(output));\n              jni.destroy();\n              logEvent(\"TfLiteJni destroyed\");\n              return Tasks.forResult(output);\n            })\n        .addOnSuccessListener(unused -> logEvent(\"Scenario successful!\"))\n        .addOnFailureListener(e -> logEvent(\"Scenario failed\", e));\n  }\n\n  private void logEvent(String message) {\n    logEvent(message, null);\n  }\n\n  private void logEvent(String message, @Nullable Throwable throwable) {\n    Log.e(TAG, message, throwable);\n    logView.append(\"• \");\n    logView.append(String.valueOf(message));\n    logView.append(\"\\n\");\n    if (throwable != null) {\n      logView.append(throwable.getClass().getCanonicalName() + \": \" + throwable.getMessage());\n      logView.append(\"\\n\");\n      logView.append(Arrays.toString(throwable.getStackTrace()));\n      logView.append(\"\\n\");\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/app/src/main/java/com/google/samples/gms/tflite/cc/TfLiteJni.java",
    "content": "/*\n * Copyright 2023 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.samples.gms.tflite.cc;\n\nimport android.content.res.AssetManager;\n\n/** JNI bridge to forward the calls to the native code, where we can invoke the TFLite C API. */\npublic class TfLiteJni {\n\n  private final LoggingCallback loggingCallback;\n\n  /**\n   * This interface gets called when the JNI wants to print a message (used for debugging purposes).\n   */\n  public interface LoggingCallback {\n    void printLogMessage(String message);\n  }\n\n  static {\n    System.loadLibrary(\"tflite-cc-sample-jni\");\n  }\n\n  public TfLiteJni(LoggingCallback loggingCallback) {\n    this.loggingCallback = loggingCallback;\n  }\n\n  private void sendLogMessage(String message) {\n    if (loggingCallback != null) {\n      loggingCallback.printLogMessage(message);\n    }\n  }\n\n  /** Creates GPU delegate that will be used for the inference. */\n  public native void initGpuAcceleration();\n\n  /**\n   * Loads the model and creates the Interpreter. GPU delegate is applied if {@link\n   * TfLiteJni#initGpuAcceleration} was previously called.\n   */\n  public native void loadModel(AssetManager assetManager, String assetName);\n\n  /** Runs the inference using the Interpreter created by {@link TfLiteJni#loadModel}. */\n  public native float[] runInference(float[] input);\n\n  /** Unloads the assets and clears all the Interpreter's resources. */\n  public native void destroy();\n}\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:orientation=\"vertical\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n  <Button\n      android:id=\"@android:id/button1\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"wrap_content\"\n      android:text=\"@string/tap_me\" />\n\n\n  <ScrollView\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\">\n    <TextView\n        android:id=\"@+id/log_text\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\" />\n  </ScrollView>\n\n</LinearLayout>\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <color name=\"tfe_color_primary\">#ffa800</color>\n  <color name=\"tfe_color_primary_dark\">#ff6f00</color>\n  <color name=\"tfe_color_accent\">#425066</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <dimen name=\"tfe_sr_activity_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <string name=\"app_name\">TFLite C++ API sample</string>\n  <string name=\"tap_me\">Tap me</string>\n</resources>\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n  <!-- Base application theme. -->\n  <style name=\"AppTheme.TFLite\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n    <!-- Customize your theme here. -->\n    <item name=\"colorPrimary\">@color/tfe_color_primary</item>\n    <item name=\"colorPrimaryDark\">@color/tfe_color_primary_dark</item>\n    <item name=\"colorAccent\">@color/tfe_color_accent</item>\n  </style>\n\n</resources>\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/app/tflite-java-extract-cpp-sdk.gradle",
    "content": "configurations {\n    tfliteCplusplusSdkDependency {\n        extendsFrom implementation\n        canBeResolved = true\n    }\n}\n\ndef LIBRARY_NAME = \"play-services-tflite-java\"\n\ntask copyAarToBuildDir(type: Copy) {\n    from configurations.tfliteCplusplusSdkDependency\n    include \"**/$LIBRARY_NAME*.aar\"\n    // Drop version suffix.\n    rename \"($LIBRARY_NAME.*)(-\\\\d+.\\\\d+.\\\\d+.*)(.aar)\", '$1$3'\n    into \"$buildDir/$LIBRARY_NAME\"\n    doLast {\n        println(\"copyAarToBuildDir: copied AAR to $buildDir/$LIBRARY_NAME\")\n    }\n}\n\ntask extractAarContent(dependsOn: copyAarToBuildDir, type: Copy) {\n    from zipTree(\"$buildDir/$LIBRARY_NAME/${LIBRARY_NAME}.aar\")\n    into \"$buildDir/$LIBRARY_NAME/content\"\n    doLast {\n        println(\"extractAarContent: extracted AAR to $buildDir/$LIBRARY_NAME/content\")\n    }\n}\n\ntask copySdkSourcesToProjectBuildDir(dependsOn: extractAarContent, type: Copy) {\n    from \"$buildDir/$LIBRARY_NAME/content/cc_sdk\"\n    into \"$projectDir/build/tflite_cc_sdk\"\n    doLast {\n        println(\"copySdkSourcesToProjectBuildDir: copied sdk to $projectDir/build/tflite_cc_sdk\")\n    }\n}\n\npreBuild.dependsOn copySdkSourcesToProjectBuildDir\n\nclean {\n    delete \"$projectDir/build/tflite_cc_sdk\"\n}\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nplugins {\n    id 'com.android.application' version '8.1.1' apply false\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app's APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Enables namespacing of each library's R class so that its R class includes only the\n# resources declared in the library itself and none from the library's dependencies,\n# thereby reducing the size of the R class for that library\nandroid.nonTransitiveRClass=true\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/native_api/android_play_services/cc_api/settings.gradle",
    "content": "pluginManagement {\n    repositories {\n        gradlePluginPortal()\n        google()\n        mavenCentral()\n    }\n}\n\nplugins {\n    // for java { toolchain { languageVersion.set() } }\n    id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0'\n}\n\nrootProject.name = 'tflite-cc-sample'\ninclude ':app'\n"
  },
  {
    "path": "lite/examples/object_detection/android/README.md",
    "content": "# TensorFlow Lite Object Detection Android Demo\n\n### Overview\n\nThis is a camera app that continuously detects the objects (bounding boxes and\nclasses) in the frames seen by your device's back camera, with the option to use\na quantized\n[MobileNet SSD](https://tfhub.dev/tensorflow/lite-model/ssd_mobilenet_v1/1/metadata/2),\n[EfficientDet Lite 0](https://tfhub.dev/tensorflow/lite-model/efficientdet/lite0/detection/metadata/1),\n[EfficientDet Lite1](https://tfhub.dev/tensorflow/lite-model/efficientdet/lite1/detection/metadata/1),\nor\n[EfficientDet Lite2](https://tfhub.dev/tensorflow/lite-model/efficientdet/lite2/detection/metadata/1)\nmodel trained on the [COCO dataset](http://cocodataset.org/). These instructions\nwalk you through building and running the demo on an Android device.\n\nThe model files are downloaded via Gradle scripts when you build and run the\napp. You don't need to do any steps to download TFLite models into the project\nexplicitly.\n\nThis application should be run on a physical Android device.\n\n![App example showing UI controls. Highlights a cat](https://storage.googleapis.com/download.tensorflow.org/tflite/examples/obj_detection_cat.gif)\n\n![App example showing UI controls. Highlights a cat, a book, and a couch.](screenshot1.png)\n\n\n## Build the demo using Android Studio\n\n### Prerequisites\n\n*   The **[Android Studio](https://developer.android.com/studio/index.html)**\n    IDE. This sample has been tested on Android Studio Bumblebee.\n\n*   A physical Android device with a minimum OS version of SDK 24 (Android 7.0 -\n    Nougat) with developer mode enabled. The process of enabling developer mode\n    may vary by device.\n\n### Building\n\n*   Open Android Studio. From the Welcome screen, select Open an existing\n    Android Studio project.\n\n*   From the Open File or Project window that appears, navigate to and select\n    the tensorflow-lite/examples/object_detection/android directory. Click OK.\n\n*   If it asks you to do a Gradle Sync, click OK.\n\n*   With your Android device connected to your computer and developer mode\n    enabled, click on the green Run arrow in Android Studio.\n\n### Models used\n\nDownloading, extraction, and placing the models into the assets folder is\nmanaged automatically by the download.gradle file.\n"
  },
  {
    "path": "lite/examples/object_detection/android/app/build.gradle",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\napply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-kapt'\napply plugin: \"androidx.navigation.safeargs\"\napply plugin: 'de.undercouch.download'\n\nandroid {\n    compileSdkVersion 32\n    defaultConfig {\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n        applicationId \"org.tensorflow.lite.examples.objectdetection\"\n        minSdkVersion 21\n        targetSdkVersion 32\n        versionCode 1\n        versionName \"1.0.0\"\n    }\n\n    dataBinding {\n        enabled = true\n    }\n\n    compileOptions {\n        sourceCompatibility rootProject.ext.java_version\n        targetCompatibility rootProject.ext.java_version\n    }\n\n    kotlinOptions {\n        jvmTarget = rootProject.ext.java_version\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    buildFeatures {\n        viewBinding true\n    }\n    androidResources {\n        noCompress 'tflite'\n    }\n\n}\n\n// import DownloadModels task\nproject.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'\nproject.ext.TEST_ASSETS_DIR = projectDir.toString() + '/src/androidTest/assets'\n\n// Download default models; if you wish to use your own models then\n// place them in the \"assets\" directory and comment out this line.\napply from:'download_models.gradle'\n\ndependencies {\n    // Kotlin lang\n    implementation 'androidx.core:core-ktx:1.6.0'\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version\"\n    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0'\n\n    // App compat and UI things\n    implementation 'androidx.appcompat:appcompat:1.3.1'\n    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'\n    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'\n    implementation 'com.google.android.material:material:1.0.0'\n    implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'\n\n    // Navigation library\n    def nav_version = \"2.3.5\"\n    implementation \"androidx.navigation:navigation-fragment-ktx:$nav_version\"\n    implementation \"androidx.navigation:navigation-ui-ktx:$nav_version\"\n\n    // CameraX core library\n    def camerax_version = '1.1.0-beta03'\n    implementation \"androidx.camera:camera-core:$camerax_version\"\n\n    // CameraX Camera2 extensions\n    implementation \"androidx.camera:camera-camera2:$camerax_version\"\n\n    // CameraX Lifecycle library\n    implementation \"androidx.camera:camera-lifecycle:$camerax_version\"\n\n    // CameraX View class\n    implementation \"androidx.camera:camera-view:$camerax_version\"\n\n    //WindowManager\n    implementation 'androidx.window:window:1.0.0-alpha09'\n\n    // Unit testing\n    testImplementation 'androidx.test.ext:junit:1.1.3'\n    testImplementation 'androidx.test:rules:1.4.0'\n    testImplementation 'androidx.test:runner:1.4.0'\n    testImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n    testImplementation 'org.robolectric:robolectric:4.4'\n\n    // Instrumented testing\n    androidTestImplementation \"androidx.test.ext:junit:1.1.3\"\n    androidTestImplementation \"androidx.test:core:1.4.0\"\n    androidTestImplementation \"androidx.test:rules:1.4.0\"\n    androidTestImplementation \"androidx.test:runner:1.4.0\"\n    androidTestImplementation \"androidx.test.espresso:espresso-core:3.4.0\"\n\n    implementation 'org.tensorflow:tensorflow-lite-task-vision:0.4.0'\n    // Import the GPU delegate plugin Library for GPU inference\n    implementation 'org.tensorflow:tensorflow-lite-gpu-delegate-plugin:0.4.0'\n    implementation 'org.tensorflow:tensorflow-lite-gpu:2.9.0'\n}\n"
  },
  {
    "path": "lite/examples/object_detection/android/app/download_models.gradle",
    "content": "task downloadModelFile(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/object_detection/android/lite-model_ssd_mobilenet_v1_1_metadata_2.tflite'\n    dest project.ext.ASSET_DIR + '/mobilenetv1.tflite'\n    overwrite false\n}\n\ntask downloadModelFile0(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/object_detection/android/lite-model_efficientdet_lite0_detection_metadata_1.tflite'\n    dest project.ext.ASSET_DIR + '/efficientdet-lite0.tflite'\n    overwrite false\n}\n\ntask downloadModelFile1(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/object_detection/android/lite-model_efficientdet_lite1_detection_metadata_1.tflite'\n    dest project.ext.ASSET_DIR + '/efficientdet-lite1.tflite'\n    overwrite false\n}\n\ntask downloadModelFile2(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/object_detection/android/lite-model_efficientdet_lite2_detection_metadata_1.tflite'\n    dest project.ext.ASSET_DIR + '/efficientdet-lite2.tflite'\n    overwrite false\n}\n\ntask copyTestModel(type: Copy, dependsOn: downloadModelFile) {\n    from project.ext.ASSET_DIR + '/mobilenetv1.tflite'\n    into project.ext.TEST_ASSETS_DIR\n}\n\npreBuild.dependsOn downloadModelFile, downloadModelFile0, downloadModelFile1, downloadModelFile2,\n        copyTestModel"
  },
  {
    "path": "lite/examples/object_detection/android/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/androidTest/java/org/tensorflow/lite/examples/objectdetection/TFObjectDetectionTest.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.objectdetection\n\nimport android.content.res.AssetManager\nimport android.graphics.Bitmap\nimport android.graphics.BitmapFactory\nimport android.graphics.RectF\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.platform.app.InstrumentationRegistry\nimport java.io.InputStream\nimport org.junit.Assert.assertEquals\nimport org.junit.Assert.assertNotNull\nimport org.junit.Assert.assertTrue\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.tensorflow.lite.support.label.Category\nimport org.tensorflow.lite.task.vision.detector.Detection\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass TFObjectDetectionTest {\n\n    val controlResults = listOf<Detection>(\n        Detection.create(RectF(69.0f, 58.0f, 227.0f, 171.0f),\n            listOf<Category>(Category.create(\"cat\", \"cat\", 0.77734375f))),\n        Detection.create(RectF(13.0f, 6.0f, 283.0f, 215.0f),\n            listOf<Category>(Category.create(\"couch\", \"couch\", 0.5859375f))),\n            Detection.create(RectF(45.0f, 27.0f, 257.0f, 184.0f),\n            listOf<Category>(Category.create(\"chair\", \"chair\", 0.55078125f)))\n    )\n\n    @Test\n    @Throws(Exception::class)\n    fun detectionResultsShouldNotChange() {\n        val objectDetectorHelper =\n            ObjectDetectorHelper(\n                context = InstrumentationRegistry.getInstrumentation().context,\n                objectDetectorListener =\n                    object : ObjectDetectorHelper.DetectorListener {\n                        override fun onError(error: String) {\n                            // no op\n                        }\n\n                        override fun onResults(\n                          results: MutableList<Detection>?,\n                          inferenceTime: Long,\n                          imageHeight: Int,\n                          imageWidth: Int\n                        ) {\n\n                            assertEquals(controlResults.size, results!!.size)\n\n                            // Loop through the detected and control data\n                            for (i in controlResults.indices) {\n                                // Verify that the bounding boxes are the same\n                                assertEquals(results[i].boundingBox, controlResults[i].boundingBox)\n\n                                // Verify that the detected data and control\n                                // data have the same number of categories\n                                assertEquals(\n                                    results[i].categories.size,\n                                    controlResults[i].categories.size\n                                )\n\n                                // Loop through the categories\n                                for (j in 0 until controlResults[i].categories.size - 1) {\n                                    // Verify that the labels are consistent\n                                    assertEquals(\n                                        results[i].categories[j].label,\n                                        controlResults[i].categories[j].label\n                                    )\n                                }\n                            }\n                        }\n                    }\n            )\n        // Create Bitmap and convert to TensorImage\n        val bitmap = loadImage(\"cat1.png\")\n        // Run the object detector on the sample image\n        objectDetectorHelper.detect(bitmap!!, 0)\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun detectedImageIsScaledWithinModelDimens() {\n        val objectDetectorHelper =\n            ObjectDetectorHelper(\n                context = InstrumentationRegistry.getInstrumentation().context,\n                objectDetectorListener =\n                    object : ObjectDetectorHelper.DetectorListener {\n                        override fun onError(error: String) {}\n\n                        override fun onResults(\n                          results: MutableList<Detection>?,\n                          inferenceTime: Long,\n                          imageHeight: Int,\n                          imageWidth: Int\n                        ) {\n                            assertNotNull(results)\n                            for (result in results!!) {\n                                assertTrue(result.boundingBox.top <= imageHeight)\n                                assertTrue(result.boundingBox.bottom <= imageHeight)\n                                assertTrue(result.boundingBox.left <= imageWidth)\n                                assertTrue(result.boundingBox.right <= imageWidth)\n                            }\n                        }\n                    }\n            )\n\n            // Create Bitmap and convert to TensorImage\n            val bitmap = loadImage(\"cat1.png\")\n            // Run the object detector on the sample image\n            objectDetectorHelper.detect(bitmap!!, 0)\n    }\n\n    @Throws(Exception::class)\n    private fun loadImage(fileName: String): Bitmap? {\n        val assetManager: AssetManager =\n            InstrumentationRegistry.getInstrumentation().context.assets\n        val inputStream: InputStream = assetManager.open(fileName)\n        return BitmapFactory.decodeStream(inputStream)\n    }\n}\n"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<manifest\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:dist=\"http://schemas.android.com/apk/distribution\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"org.tensorflow.lite.examples.objectdetection\">\n\n    <!-- Enable instant app support -->\n    <dist:module dist:instant=\"true\" />\n\n    <!-- Declare features -->\n    <uses-feature android:name=\"android.hardware.camera\" />\n\n    <!-- Declare permissions -->\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n\n    <application\n        android:icon=\"@mipmap/ic_launcher\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:label=\"@string/app_name\"\n        android:allowBackup=\"true\"\n        android:taskAffinity=\"\"\n        tools:ignore=\"AllowBackup\">\n\n        <activity\n            android:name=\".MainActivity\"\n            android:clearTaskOnLaunch=\"true\"\n            android:theme=\"@style/AppTheme\"\n            android:exported=\"true\"\n            android:icon=\"@mipmap/ic_launcher\"\n            android:rotationAnimation=\"seamless\"\n            android:resizeableActivity=\"true\"\n            android:configChanges=\"orientation|screenLayout|screenSize|smallestScreenSize\"\n            tools:targetApi=\"O\">\n\n            <!-- Main app intent filter -->\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n\n            <!-- Declare notch support -->\n            <meta-data android:name=\"android.notch_support\" android:value=\"true\"/>\n\n        </activity>\n\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/main/java/org/tensorflow/lite/examples/objectdetection/MainActivity.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.objectdetection\n\nimport android.os.Build\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport org.tensorflow.lite.examples.objectdetection.databinding.ActivityMainBinding\n\n/**\n * Main entry point into our app. This app follows the single-activity pattern, and all\n * functionality is implemented in the form of fragments.\n */\nclass MainActivity : AppCompatActivity() {\n\n    private lateinit var activityMainBinding: ActivityMainBinding\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)\n        setContentView(activityMainBinding.root)\n    }\n\n    override fun onBackPressed() {\n        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {\n            // Workaround for Android Q memory leak issue in IRequestFinishCallback$Stub.\n            // (https://issuetracker.google.com/issues/139738913)\n            finishAfterTransition()\n        } else {\n            super.onBackPressed()\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/main/java/org/tensorflow/lite/examples/objectdetection/ObjectDetectorHelper.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.objectdetection\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.os.SystemClock\nimport android.util.Log\nimport org.tensorflow.lite.gpu.CompatibilityList\nimport org.tensorflow.lite.support.image.ImageProcessor\nimport org.tensorflow.lite.support.image.TensorImage\nimport org.tensorflow.lite.support.image.ops.Rot90Op\nimport org.tensorflow.lite.task.core.BaseOptions\nimport org.tensorflow.lite.task.vision.detector.Detection\nimport org.tensorflow.lite.task.vision.detector.ObjectDetector\n\nclass ObjectDetectorHelper(\n  var threshold: Float = 0.5f,\n  var numThreads: Int = 2,\n  var maxResults: Int = 3,\n  var currentDelegate: Int = 0,\n  var currentModel: Int = 0,\n  val context: Context,\n  val objectDetectorListener: DetectorListener?\n) {\n\n    // For this example this needs to be a var so it can be reset on changes. If the ObjectDetector\n    // will not change, a lazy val would be preferable.\n    private var objectDetector: ObjectDetector? = null\n\n    init {\n        setupObjectDetector()\n    }\n\n    fun clearObjectDetector() {\n        objectDetector = null\n    }\n\n    // Initialize the object detector using current settings on the\n    // thread that is using it. CPU and NNAPI delegates can be used with detectors\n    // that are created on the main thread and used on a background thread, but\n    // the GPU delegate needs to be used on the thread that initialized the detector\n    fun setupObjectDetector() {\n        // Create the base options for the detector using specifies max results and score threshold\n        val optionsBuilder =\n            ObjectDetector.ObjectDetectorOptions.builder()\n                .setScoreThreshold(threshold)\n                .setMaxResults(maxResults)\n\n        // Set general detection options, including number of used threads\n        val baseOptionsBuilder = BaseOptions.builder().setNumThreads(numThreads)\n\n        // Use the specified hardware for running the model. Default to CPU\n        when (currentDelegate) {\n            DELEGATE_CPU -> {\n                // Default\n            }\n            DELEGATE_GPU -> {\n                if (CompatibilityList().isDelegateSupportedOnThisDevice) {\n                    baseOptionsBuilder.useGpu()\n                } else {\n                    objectDetectorListener?.onError(\"GPU is not supported on this device\")\n                }\n            }\n            DELEGATE_NNAPI -> {\n                baseOptionsBuilder.useNnapi()\n            }\n        }\n\n        optionsBuilder.setBaseOptions(baseOptionsBuilder.build())\n\n        val modelName =\n            when (currentModel) {\n                MODEL_MOBILENETV1 -> \"mobilenetv1.tflite\"\n                MODEL_EFFICIENTDETV0 -> \"efficientdet-lite0.tflite\"\n                MODEL_EFFICIENTDETV1 -> \"efficientdet-lite1.tflite\"\n                MODEL_EFFICIENTDETV2 -> \"efficientdet-lite2.tflite\"\n                else -> \"mobilenetv1.tflite\"\n            }\n\n        try {\n            objectDetector =\n                ObjectDetector.createFromFileAndOptions(context, modelName, optionsBuilder.build())\n        } catch (e: IllegalStateException) {\n            objectDetectorListener?.onError(\n                \"Object detector failed to initialize. See error logs for details\"\n            )\n            Log.e(\"Test\", \"TFLite failed to load model with error: \" + e.message)\n        }\n    }\n\n    fun detect(image: Bitmap, imageRotation: Int) {\n        if (objectDetector == null) {\n            setupObjectDetector()\n        }\n\n        // Inference time is the difference between the system time at the start and finish of the\n        // process\n        var inferenceTime = SystemClock.uptimeMillis()\n\n        // Create preprocessor for the image.\n        // See https://www.tensorflow.org/lite/inference_with_metadata/\n        //            lite_support#imageprocessor_architecture\n        val imageProcessor =\n            ImageProcessor.Builder()\n                .add(Rot90Op(-imageRotation / 90))\n                .build()\n\n        // Preprocess the image and convert it into a TensorImage for detection.\n        val tensorImage = imageProcessor.process(TensorImage.fromBitmap(image))\n\n        val results = objectDetector?.detect(tensorImage)\n        inferenceTime = SystemClock.uptimeMillis() - inferenceTime\n        objectDetectorListener?.onResults(\n            results,\n            inferenceTime,\n            tensorImage.height,\n            tensorImage.width)\n    }\n\n    interface DetectorListener {\n        fun onError(error: String)\n        fun onResults(\n          results: MutableList<Detection>?,\n          inferenceTime: Long,\n          imageHeight: Int,\n          imageWidth: Int\n        )\n    }\n\n    companion object {\n        const val DELEGATE_CPU = 0\n        const val DELEGATE_GPU = 1\n        const val DELEGATE_NNAPI = 2\n        const val MODEL_MOBILENETV1 = 0\n        const val MODEL_EFFICIENTDETV0 = 1\n        const val MODEL_EFFICIENTDETV1 = 2\n        const val MODEL_EFFICIENTDETV2 = 3\n    }\n}\n"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/main/java/org/tensorflow/lite/examples/objectdetection/OverlayView.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.objectdetection\n\nimport android.content.Context\nimport android.graphics.Canvas\nimport android.graphics.Color\nimport android.graphics.Paint\nimport android.graphics.Rect\nimport android.graphics.RectF\nimport android.util.AttributeSet\nimport android.view.View\nimport androidx.core.content.ContextCompat\nimport java.util.LinkedList\nimport kotlin.math.max\nimport org.tensorflow.lite.task.vision.detector.Detection\n\nclass OverlayView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {\n\n    private var results: List<Detection> = LinkedList<Detection>()\n    private var boxPaint = Paint()\n    private var textBackgroundPaint = Paint()\n    private var textPaint = Paint()\n\n    private var scaleFactor: Float = 1f\n\n    private var bounds = Rect()\n\n    init {\n        initPaints()\n    }\n\n    fun clear() {\n        textPaint.reset()\n        textBackgroundPaint.reset()\n        boxPaint.reset()\n        invalidate()\n        initPaints()\n    }\n\n    private fun initPaints() {\n        textBackgroundPaint.color = Color.BLACK\n        textBackgroundPaint.style = Paint.Style.FILL\n        textBackgroundPaint.textSize = 50f\n\n        textPaint.color = Color.WHITE\n        textPaint.style = Paint.Style.FILL\n        textPaint.textSize = 50f\n\n        boxPaint.color = ContextCompat.getColor(context!!, R.color.bounding_box_color)\n        boxPaint.strokeWidth = 8F\n        boxPaint.style = Paint.Style.STROKE\n    }\n\n    override fun draw(canvas: Canvas) {\n        super.draw(canvas)\n\n        for (result in results) {\n            val boundingBox = result.boundingBox\n\n            val top = boundingBox.top * scaleFactor\n            val bottom = boundingBox.bottom * scaleFactor\n            val left = boundingBox.left * scaleFactor\n            val right = boundingBox.right * scaleFactor\n\n            // Draw bounding box around detected objects\n            val drawableRect = RectF(left, top, right, bottom)\n            canvas.drawRect(drawableRect, boxPaint)\n\n            // Create text to display alongside detected objects\n            val drawableText =\n                result.categories[0].label + \" \" +\n                        String.format(\"%.2f\", result.categories[0].score)\n\n            // Draw rect behind display text\n            textBackgroundPaint.getTextBounds(drawableText, 0, drawableText.length, bounds)\n            val textWidth = bounds.width()\n            val textHeight = bounds.height()\n            canvas.drawRect(\n                left,\n                top,\n                left + textWidth + Companion.BOUNDING_RECT_TEXT_PADDING,\n                top + textHeight + Companion.BOUNDING_RECT_TEXT_PADDING,\n                textBackgroundPaint\n            )\n\n            // Draw text for detected object\n            canvas.drawText(drawableText, left, top + bounds.height(), textPaint)\n        }\n    }\n\n    fun setResults(\n      detectionResults: MutableList<Detection>,\n      imageHeight: Int,\n      imageWidth: Int,\n    ) {\n        results = detectionResults\n\n        // PreviewView is in FILL_START mode. So we need to scale up the bounding box to match with\n        // the size that the captured images will be displayed.\n        scaleFactor = max(width * 1f / imageWidth, height * 1f / imageHeight)\n    }\n\n    companion object {\n        private const val BOUNDING_RECT_TEXT_PADDING = 8\n    }\n}\n"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/main/java/org/tensorflow/lite/examples/objectdetection/fragments/CameraFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.objectdetection.fragments\n\nimport android.annotation.SuppressLint\nimport android.content.res.Configuration\nimport android.graphics.Bitmap\nimport android.os.Bundle\nimport android.util.Log\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.AdapterView\nimport android.widget.Toast\nimport androidx.camera.core.AspectRatio\nimport androidx.camera.core.Camera\nimport androidx.camera.core.CameraSelector\nimport androidx.camera.core.ImageAnalysis\nimport androidx.camera.core.ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888\nimport androidx.camera.core.ImageProxy\nimport androidx.camera.core.Preview\nimport androidx.camera.lifecycle.ProcessCameraProvider\nimport androidx.core.content.ContextCompat\nimport androidx.fragment.app.Fragment\nimport androidx.navigation.Navigation\nimport java.util.LinkedList\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Executors\nimport org.tensorflow.lite.examples.objectdetection.ObjectDetectorHelper\nimport org.tensorflow.lite.examples.objectdetection.R\nimport org.tensorflow.lite.examples.objectdetection.databinding.FragmentCameraBinding\nimport org.tensorflow.lite.task.vision.detector.Detection\n\nclass CameraFragment : Fragment(), ObjectDetectorHelper.DetectorListener {\n\n    private val TAG = \"ObjectDetection\"\n\n    private var _fragmentCameraBinding: FragmentCameraBinding? = null\n\n    private val fragmentCameraBinding\n        get() = _fragmentCameraBinding!!\n\n    private lateinit var objectDetectorHelper: ObjectDetectorHelper\n    private lateinit var bitmapBuffer: Bitmap\n    private var preview: Preview? = null\n    private var imageAnalyzer: ImageAnalysis? = null\n    private var camera: Camera? = null\n    private var cameraProvider: ProcessCameraProvider? = null\n\n    /** Blocking camera operations are performed using this executor */\n    private lateinit var cameraExecutor: ExecutorService\n\n    override fun onResume() {\n        super.onResume()\n        // Make sure that all permissions are still present, since the\n        // user could have removed them while the app was in paused state.\n        if (!PermissionsFragment.hasPermissions(requireContext())) {\n            Navigation.findNavController(requireActivity(), R.id.fragment_container)\n                .navigate(CameraFragmentDirections.actionCameraToPermissions())\n        }\n    }\n\n    override fun onDestroyView() {\n        _fragmentCameraBinding = null\n        super.onDestroyView()\n\n        // Shut down our background executor\n        cameraExecutor.shutdown()\n    }\n\n    override fun onCreateView(\n      inflater: LayoutInflater,\n      container: ViewGroup?,\n      savedInstanceState: Bundle?\n    ): View {\n        _fragmentCameraBinding = FragmentCameraBinding.inflate(inflater, container, false)\n\n        return fragmentCameraBinding.root\n    }\n\n    @SuppressLint(\"MissingPermission\")\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n\n        objectDetectorHelper = ObjectDetectorHelper(\n            context = requireContext(),\n            objectDetectorListener = this)\n\n        // Initialize our background executor\n        cameraExecutor = Executors.newSingleThreadExecutor()\n\n        // Wait for the views to be properly laid out\n        fragmentCameraBinding.viewFinder.post {\n            // Set up the camera and its use cases\n            setUpCamera()\n        }\n\n        // Attach listeners to UI control widgets\n        initBottomSheetControls()\n    }\n\n    private fun initBottomSheetControls() {\n        // When clicked, lower detection score threshold floor\n        fragmentCameraBinding.bottomSheetLayout.thresholdMinus.setOnClickListener {\n            if (objectDetectorHelper.threshold >= 0.1) {\n                objectDetectorHelper.threshold -= 0.1f\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, raise detection score threshold floor\n        fragmentCameraBinding.bottomSheetLayout.thresholdPlus.setOnClickListener {\n            if (objectDetectorHelper.threshold <= 0.8) {\n                objectDetectorHelper.threshold += 0.1f\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, reduce the number of objects that can be detected at a time\n        fragmentCameraBinding.bottomSheetLayout.maxResultsMinus.setOnClickListener {\n            if (objectDetectorHelper.maxResults > 1) {\n                objectDetectorHelper.maxResults--\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, increase the number of objects that can be detected at a time\n        fragmentCameraBinding.bottomSheetLayout.maxResultsPlus.setOnClickListener {\n            if (objectDetectorHelper.maxResults < 5) {\n                objectDetectorHelper.maxResults++\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, decrease the number of threads used for detection\n        fragmentCameraBinding.bottomSheetLayout.threadsMinus.setOnClickListener {\n            if (objectDetectorHelper.numThreads > 1) {\n                objectDetectorHelper.numThreads--\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, increase the number of threads used for detection\n        fragmentCameraBinding.bottomSheetLayout.threadsPlus.setOnClickListener {\n            if (objectDetectorHelper.numThreads < 4) {\n                objectDetectorHelper.numThreads++\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, change the underlying hardware used for inference. Current options are CPU\n        // GPU, and NNAPI\n        fragmentCameraBinding.bottomSheetLayout.spinnerDelegate.setSelection(0, false)\n        fragmentCameraBinding.bottomSheetLayout.spinnerDelegate.onItemSelectedListener =\n            object : AdapterView.OnItemSelectedListener {\n                override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {\n                    objectDetectorHelper.currentDelegate = p2\n                    updateControlsUi()\n                }\n\n                override fun onNothingSelected(p0: AdapterView<*>?) {\n                    /* no op */\n                }\n            }\n\n        // When clicked, change the underlying model used for object detection\n        fragmentCameraBinding.bottomSheetLayout.spinnerModel.setSelection(0, false)\n        fragmentCameraBinding.bottomSheetLayout.spinnerModel.onItemSelectedListener =\n            object : AdapterView.OnItemSelectedListener {\n                override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {\n                    objectDetectorHelper.currentModel = p2\n                    updateControlsUi()\n                }\n\n                override fun onNothingSelected(p0: AdapterView<*>?) {\n                    /* no op */\n                }\n            }\n    }\n\n    // Update the values displayed in the bottom sheet. Reset detector.\n    private fun updateControlsUi() {\n        fragmentCameraBinding.bottomSheetLayout.maxResultsValue.text =\n            objectDetectorHelper.maxResults.toString()\n        fragmentCameraBinding.bottomSheetLayout.thresholdValue.text =\n            String.format(\"%.2f\", objectDetectorHelper.threshold)\n        fragmentCameraBinding.bottomSheetLayout.threadsValue.text =\n            objectDetectorHelper.numThreads.toString()\n\n        // Needs to be cleared instead of reinitialized because the GPU\n        // delegate needs to be initialized on the thread using it when applicable\n        objectDetectorHelper.clearObjectDetector()\n        fragmentCameraBinding.overlay.clear()\n    }\n\n    // Initialize CameraX, and prepare to bind the camera use cases\n    private fun setUpCamera() {\n        val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())\n        cameraProviderFuture.addListener(\n            {\n                // CameraProvider\n                cameraProvider = cameraProviderFuture.get()\n\n                // Build and bind the camera use cases\n                bindCameraUseCases()\n            },\n            ContextCompat.getMainExecutor(requireContext())\n        )\n    }\n\n    // Declare and bind preview, capture and analysis use cases\n    @SuppressLint(\"UnsafeOptInUsageError\")\n    private fun bindCameraUseCases() {\n\n        // CameraProvider\n        val cameraProvider =\n            cameraProvider ?: throw IllegalStateException(\"Camera initialization failed.\")\n\n        // CameraSelector - makes assumption that we're only using the back camera\n        val cameraSelector =\n            CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()\n\n        // Preview. Only using the 4:3 ratio because this is the closest to our models\n        preview =\n            Preview.Builder()\n                .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n                .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)\n                .build()\n\n        // ImageAnalysis. Using RGBA 8888 to match how our models work\n        imageAnalyzer =\n            ImageAnalysis.Builder()\n                .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n                .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)\n                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)\n                .setOutputImageFormat(OUTPUT_IMAGE_FORMAT_RGBA_8888)\n                .build()\n                // The analyzer can then be assigned to the instance\n                .also {\n                    it.setAnalyzer(cameraExecutor) { image ->\n                        if (!::bitmapBuffer.isInitialized) {\n                            // The image rotation and RGB image buffer are initialized only once\n                            // the analyzer has started running\n                            bitmapBuffer = Bitmap.createBitmap(\n                              image.width,\n                              image.height,\n                              Bitmap.Config.ARGB_8888\n                            )\n                        }\n\n                        detectObjects(image)\n                    }\n                }\n\n        // Must unbind the use-cases before rebinding them\n        cameraProvider.unbindAll()\n\n        try {\n            // A variable number of use-cases can be passed here -\n            // camera provides access to CameraControl & CameraInfo\n            camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalyzer)\n\n            // Attach the viewfinder's surface provider to preview use case\n            preview?.setSurfaceProvider(fragmentCameraBinding.viewFinder.surfaceProvider)\n        } catch (exc: Exception) {\n            Log.e(TAG, \"Use case binding failed\", exc)\n        }\n    }\n\n    private fun detectObjects(image: ImageProxy) {\n        // Copy out RGB bits to the shared bitmap buffer\n        image.use { bitmapBuffer.copyPixelsFromBuffer(image.planes[0].buffer) }\n\n        val imageRotation = image.imageInfo.rotationDegrees\n        // Pass Bitmap and rotation to the object detector helper for processing and detection\n        objectDetectorHelper.detect(bitmapBuffer, imageRotation)\n    }\n\n    override fun onConfigurationChanged(newConfig: Configuration) {\n        super.onConfigurationChanged(newConfig)\n        imageAnalyzer?.targetRotation = fragmentCameraBinding.viewFinder.display.rotation\n    }\n\n    // Update UI after objects have been detected. Extracts original image height/width\n    // to scale and place bounding boxes properly through OverlayView\n    override fun onResults(\n      results: MutableList<Detection>?,\n      inferenceTime: Long,\n      imageHeight: Int,\n      imageWidth: Int\n    ) {\n        activity?.runOnUiThread {\n            fragmentCameraBinding.bottomSheetLayout.inferenceTimeVal.text =\n                            String.format(\"%d ms\", inferenceTime)\n\n            // Pass necessary information to OverlayView for drawing on the canvas\n            fragmentCameraBinding.overlay.setResults(\n                results ?: LinkedList<Detection>(),\n                imageHeight,\n                imageWidth\n            )\n\n            // Force a redraw\n            fragmentCameraBinding.overlay.invalidate()\n        }\n    }\n\n    override fun onError(error: String) {\n        activity?.runOnUiThread {\n            Toast.makeText(requireContext(), error, Toast.LENGTH_SHORT).show()\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/main/java/org/tensorflow/lite/examples/objectdetection/fragments/PermissionsFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.objectdetection.fragments\n\nimport android.Manifest\nimport android.content.Context\nimport android.content.pm.PackageManager\nimport android.os.Bundle\nimport android.widget.Toast\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.core.content.ContextCompat\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.lifecycleScope\nimport androidx.navigation.Navigation\nimport org.tensorflow.lite.examples.objectdetection.R\n\nprivate val PERMISSIONS_REQUIRED = arrayOf(Manifest.permission.CAMERA)\n\n/**\n * The sole purpose of this fragment is to request permissions and, once granted, display the\n * camera fragment to the user.\n */\nclass PermissionsFragment : Fragment() {\n\n    private val requestPermissionLauncher =\n        registerForActivityResult(ActivityResultContracts.RequestPermission()\n        ) { isGranted: Boolean ->\n            if (isGranted) {\n                Toast.makeText(context, \"Permission request granted\", Toast.LENGTH_LONG).show()\n                navigateToCamera()\n            } else {\n                Toast.makeText(context, \"Permission request denied\", Toast.LENGTH_LONG).show()\n            }\n        }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        when {\n            ContextCompat.checkSelfPermission(\n                requireContext(),\n                Manifest.permission.CAMERA\n            ) == PackageManager.PERMISSION_GRANTED -> {\n                navigateToCamera()\n            }\n            else -> {\n                requestPermissionLauncher.launch(\n                    Manifest.permission.CAMERA)\n            }\n        }\n    }\n\n    private fun navigateToCamera() {\n        lifecycleScope.launchWhenStarted {\n            Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(\n                PermissionsFragmentDirections.actionPermissionsToCamera())\n        }\n    }\n\n    companion object {\n\n        /** Convenience method used to check if all permissions required by this app are granted */\n        fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all {\n            ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/main/res/color/selector_ic.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"true\" android:color=\"@color/icPressed\" />\n    <item android:state_focused=\"true\" android:color=\"@color/icFocused\" />\n    <item android:color=\"@color/icActive\" />\n</selector>"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/main/res/drawable/ic_minus.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13H5v-2h14v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/main/res/drawable/ic_plus.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path android:pathData=\"M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"85.84757\"\n                android:endY=\"92.4963\"\n                android:startX=\"42.9492\"\n                android:startY=\"49.59793\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:background=\"@android:color/transparent\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n    <androidx.fragment.app.FragmentContainerView\n        android:id=\"@+id/fragment_container\"\n        android:name=\"androidx.navigation.fragment.NavHostFragment\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"@android:color/transparent\"\n        android:keepScreenOn=\"true\"\n        app:defaultNavHost=\"true\"\n        app:navGraph=\"@navigation/nav_graph\"\n        android:layout_marginTop=\"?android:attr/actionBarSize\"\n        tools:context=\".MainActivity\"/>\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:layout_alignParentTop=\"true\"\n            android:background=\"@color/toolbar_background\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/tfl_logo\" />\n        </androidx.appcompat.widget.Toolbar>\n\n    </RelativeLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/main/res/layout/fragment_camera.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/camera_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.camera.view.PreviewView\n        android:id=\"@+id/view_finder\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:scaleType=\"fillStart\"/>\n\n    <org.tensorflow.lite.examples.objectdetection.OverlayView\n        android:id=\"@+id/overlay\"\n        android:layout_height=\"match_parent\"\n        android:layout_width=\"match_parent\" />\n\n    <include\n        android:id=\"@+id/bottom_sheet_layout\"\n        layout=\"@layout/info_bottom_sheet\" />\n</androidx.coordinatorlayout.widget.CoordinatorLayout>"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/main/res/layout/info_bottom_sheet.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.core.widget.NestedScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/bottom_sheet_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_gravity=\"center_horizontal\"\n    android:background=\"@color/bottom_sheet_background\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:padding=\"@dimen/bottom_sheet_padding\"\n    android:layout_height=\"wrap_content\"\n    app:behavior_hideable=\"false\"\n    app:behavior_peekHeight=\"@dimen/bottom_sheet_peek_height\"\n    app:layout_behavior=\"com.google.android.material.bottomsheet.BottomSheetBehavior\">\n\n    <androidx.appcompat.widget.LinearLayoutCompat\n        android:orientation=\"vertical\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <!-- Chevron indicating that the bottom sheet is expandable -->\n        <RelativeLayout\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\">\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerHorizontal=\"true\"\n                android:layout_centerVertical=\"true\"\n                android:src=\"@drawable/icn_chevron_up\"\n                android:contentDescription=\"@string/alt_bottom_sheet_chevron\" />\n        </RelativeLayout>\n\n        <!-- Inference time row -->\n        <androidx.appcompat.widget.LinearLayoutCompat\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:orientation=\"horizontal\">\n            <TextView\n                android:id=\"@+id/inference_time_label\"\n                android:text=\"@string/label_interence_time\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n            <TextView\n                android:id=\"@+id/inference_time_val\"\n                android:text=\"0ms\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"end\"\n                android:layout_centerVertical=\"true\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n        </androidx.appcompat.widget.LinearLayoutCompat>\n\n        <!-- ML confidence threshold adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/label_confidence_threshold\"\n                android:layout_centerVertical=\"true\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\"\n                android:textColor=\"@color/bottom_sheet_text_color\" />\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\"\n                >\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threshold_minus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_threshold_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/threshold_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:gravity=\"center\"\n                    android:minEms=\"@integer/bottom_sheet_control_text_min_ems\"\n                    android:text=\"0.50\"\n                    android:textColor=\"@color/bottom_sheet_text_color\"\n                    android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threshold_plus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_threshold_button_plus\"\n                    android:src=\"@drawable/ic_plus\"  />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- ML max results adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/label_max_results\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\"\n                android:layout_centerVertical=\"true\"\n                android:textColor=\"@color/bottom_sheet_text_color\" />\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center\"\n                android:orientation=\"horizontal\">\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/max_results_minus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_max_results_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n                <TextView\n                    android:id=\"@+id/max_results_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:minEms=\"@integer/bottom_sheet_control_text_min_ems\"\n                    android:gravity=\"center\"\n                    android:layout_marginLeft=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:text=\"3\"\n                    android:textColor=\"@color/bottom_sheet_text_color\"\n                    android:textSize=\"@dimen/bottom_sheet_text_size\" />\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/max_results_plus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_max_results_button_plus\"\n                    android:src=\"@drawable/ic_plus\" />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- Number of threads adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/label_threads\"\n                android:layout_centerVertical=\"true\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\"\n                android:textColor=\"@color/bottom_sheet_text_color\" />\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\"\n                >\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threads_minus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_thread_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/threads_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:gravity=\"center\"\n                    android:minEms=\"@integer/bottom_sheet_control_text_min_ems\"\n                    android:text=\"2\"\n                    android:textColor=\"@color/bottom_sheet_text_color\"\n                    android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threads_plus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_thread_button_plus\"\n                    android:src=\"@drawable/ic_plus\"  />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- Delegate selection row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\">\n\n            <TextView\n                android:text=\"@string/label_delegate\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\"\n                android:textColor=\"@color/bottom_sheet_text_color\" />\n\n            <androidx.appcompat.widget.AppCompatSpinner\n                android:id=\"@+id/spinner_delegate\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:minWidth=\"@dimen/bottom_sheet_spinner_delegate_min_width\"\n                android:spinnerMode=\"dropdown\"\n                android:theme=\"@style/BottomSheetSpinnerItemStyle\"\n                android:layout_alignParentRight=\"true\"\n                android:entries=\"@array/delegate_spinner_titles\"/>\n\n        </RelativeLayout>\n\n        <!-- Model selection row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\">\n\n            <TextView\n                android:text=\"@string/label_models\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\"\n                android:textColor=\"@color/bottom_sheet_text_color\" />\n\n            <androidx.appcompat.widget.AppCompatSpinner\n                android:id=\"@+id/spinner_model\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:minWidth=\"@dimen/bottom_sheet_spinner_model_min_width\"\n                android:spinnerMode=\"dropdown\"\n                android:theme=\"@style/BottomSheetSpinnerItemStyle\"\n                android:layout_alignParentRight=\"true\"\n                android:entries=\"@array/models_spinner_titles\"/>\n\n        </RelativeLayout>\n    </androidx.appcompat.widget.LinearLayoutCompat>\n</androidx.core.widget.NestedScrollView>"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/main/res/navigation/nav_graph.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<navigation\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/nav_graph\"\n    app:startDestination=\"@id/permissions_fragment\">\n\n    <fragment\n        android:id=\"@+id/permissions_fragment\"\n        android:name=\"org.tensorflow.lite.examples.objectdetection.fragments.PermissionsFragment\"\n        android:label=\"PermissionsFragment\" >\n\n        <action\n            android:id=\"@+id/action_permissions_to_camera\"\n            app:destination=\"@id/camera_fragment\"\n            app:popUpTo=\"@id/permissions_fragment\"\n            app:popUpToInclusive=\"true\" />\n\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/camera_fragment\"\n        android:name=\"org.tensorflow.lite.examples.objectdetection.fragments.CameraFragment\"\n        android:label=\"CameraFragment\" >\n\n\n        <action\n            android:id=\"@+id/action_camera_to_permissions\"\n            app:destination=\"@id/permissions_fragment\"\n            app:popUpTo=\"@id/camera_fragment\"\n            app:popUpToInclusive=\"true\"/>\n\n    </fragment>\n</navigation>"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n\n<resources>\n    <!-- TF Branding Orange -->\n    <color name=\"bounding_box_color\">#FF6F00</color>\n    <color name=\"toolbar_background\">#EEEEEE</color>\n    <color name=\"ic_launcher_background\">#FFFFFF</color>\n    <color name=\"bottom_sheet_background\">#EEEEEE</color>\n    <color name=\"bottom_sheet_text_color\">@android:color/black</color>\n    <color name=\"icActive\">#FFFFFFFF</color>\n    <color name=\"icFocused\">#DDFFFFFF</color>\n    <color name=\"icPressed\">#AAFFFFFF</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n\n    <dimen name=\"stroke_small\">4dp</dimen>\n    <dimen name=\"round_button_medium\">64dp</dimen>\n\n    <!-- Bottom Sheet -->\n    <dimen name=\"bottom_sheet_text_size\">20sp</dimen>\n    <dimen name=\"bottom_sheet_padding\">16dp</dimen>\n    <dimen name=\"bottom_sheet_peek_height\">50dp</dimen>\n    <dimen name=\"bottom_sheet_default_row_margin\">16dp</dimen>\n    <dimen name=\"bottom_sheet_control_btn_size\">48dp</dimen>\n    <dimen name=\"bottom_sheet_control_text_side_margin\">10dp</dimen>\n    <dimen name=\"bottom_sheet_spinner_delegate_min_width\">160dp</dimen>\n    <dimen name=\"bottom_sheet_spinner_model_min_width\">240dp</dimen>\n\n    <integer name=\"bottom_sheet_control_text_min_ems\">3</integer>\n</resources>"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n    <string name=\"app_name\">TFLite Object Detection</string>\n\n    <string name=\"alt_bottom_sheet_chevron\">Bottom sheet expandable indicator</string>\n\n    <string name=\"alt_bottom_sheet_max_results_button_minus\">Decreasing maxium detected results\n        button</string>\n    <string name=\"alt_bottom_sheet_max_results_button_plus\">Increasing maxium detected results\n        button</string>\n    <string name=\"alt_bottom_sheet_threshold_button_minus\">Decreasing threshold of detected object\n        results button</string>\n    <string name=\"alt_bottom_sheet_threshold_button_plus\">Increasing threshold of detected object\n        results button</string>\n    <string name=\"alt_bottom_sheet_thread_button_minus\">Decrease the number of threads used</string>\n    <string name=\"alt_bottom_sheet_thread_button_plus\">Increase the number of threads used</string>\n\n    <string name=\"label_interence_time\">Inference Time</string>\n    <string name=\"label_fps\">Frames per Second</string>\n    <string name=\"label_confidence_threshold\">Threshold</string>\n    <string name=\"label_max_results\">Max Results</string>\n    <string name=\"label_threads\">Number of Threads</string>\n    <string name=\"label_delegate\">Delegate</string>\n    <string name=\"label_models\">ML Model</string>\n\n    <string-array name=\"delegate_spinner_titles\">\n        <item>CPU</item>\n        <item>GPU</item>\n        <item>NNAPI</item>\n    </string-array>\n\n    <string-array name=\"models_spinner_titles\">\n        <item>MobileNet V1</item>\n        <item>EfficientDet Lite0</item>\n        <item>EfficientDet Lite1</item>\n        <item>EfficientDet Lite2</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "lite/examples/object_detection/android/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n    </style>\n\n    <style name=\"BottomSheetSpinnerItemStyle\" parent=\"Widget.AppCompat.Light.DropDownItem.Spinner\">\n        <item name=\"android:textSize\">@dimen/bottom_sheet_text_size</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "lite/examples/object_detection/android/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    // Top-level variables used for versioning\n    ext.kotlin_version = '1.5.21'\n    ext.java_version = JavaVersion.VERSION_1_8\n\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:7.1.2'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n        classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.4.1'\n        classpath 'de.undercouch:gradle-download-task:4.1.2'\n\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "lite/examples/object_detection/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/object_detection/android/gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx1536m\nandroid.enableJetifier=true\nandroid.useAndroidX=true"
  },
  {
    "path": "lite/examples/object_detection/android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/object_detection/android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/object_detection/android/settings.gradle",
    "content": "include ':app'"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/README.md",
    "content": "# TensorFlow Lite Object Detection Android Demo\n\n### Overview\n\nThis is a camera app that continuously detects the objects (bounding boxes and\nclasses) in the frames seen by your device's back camera, with the option to use\na quantized\n[MobileNet SSD](https://tfhub.dev/tensorflow/lite-model/ssd_mobilenet_v1/1/metadata/2),\n[EfficientDet Lite 0](https://tfhub.dev/tensorflow/lite-model/efficientdet/lite0/detection/metadata/1),\n[EfficientDet Lite1](https://tfhub.dev/tensorflow/lite-model/efficientdet/lite1/detection/metadata/1),\nor\n[EfficientDet Lite2](https://tfhub.dev/tensorflow/lite-model/efficientdet/lite2/detection/metadata/1)\nmodel trained on the [COCO dataset](http://cocodataset.org/). These instructions\nwalk you through building and running the demo on an Android device.\n\nThe model files are downloaded via Gradle scripts when you build and run the\napp. You don't need to do any steps to download TFLite models into the project\nexplicitly.\n\n**Terms:** By accessing or using TensorFlow Lite in Google Play services APIs,\nyou agree to the [Terms of Service](https://www.tensorflow.org/lite/android/play_services#tos).\nPlease read and understand all applicable terms and policies before accessing\nthe APIs.\n\nThis application should be run on a physical Android device.\n\n![App example showing UI controls. Highlights a cat](https://storage.googleapis.com/download.tensorflow.org/tflite/examples/obj_detection_cat.gif)\n\n![App example showing UI controls. Highlights a cat, a book, and a couch.](screenshot1.png)\n\n## Build the demo using Android Studio\n\n### Prerequisites\n\n*   The **[Android Studio](https://developer.android.com/studio/index.html)**\n    IDE. This sample has been tested on Android Studio Bumblebee.\n\n*   A physical Android device with a minimum OS version of SDK 24 (Android 7.0 -\n    Nougat) with developer mode enabled. The process of enabling developer mode\n    may vary by device. This device will also need to have the latest version of\n    Google Play Services installed.\n\n### Building\n\n*   Open Android Studio. From the Welcome screen, select Open an existing\n    Android Studio project.\n\n*   From the Open File or Project window that appears, navigate to and select\n    the tensorflow-lite/examples/object_detection/android_play_services\n    directory. Click OK.\n\n*   If it asks you to do a Gradle Sync, click OK.\n\n*   With your Android device connected to your computer and developer mode\n    enabled, click on the green Run arrow in Android Studio.\n\n### Models used\n\nDownloading, extraction, and placing the models into the assets folder is\nmanaged automatically by the download.gradle file."
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/build.gradle",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\napply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-kapt'\napply plugin: \"androidx.navigation.safeargs\"\napply plugin: 'de.undercouch.download'\n\nandroid {\n    compileSdkVersion 32\n    defaultConfig {\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n        applicationId \"org.tensorflow.lite.examples.objectdetection\"\n        minSdkVersion 21\n        targetSdkVersion 32\n        versionCode 1\n        versionName \"1.0.0\"\n    }\n\n    dataBinding {\n        enabled = true\n    }\n\n    compileOptions {\n        sourceCompatibility rootProject.ext.java_version\n        targetCompatibility rootProject.ext.java_version\n    }\n\n    kotlinOptions {\n        jvmTarget = rootProject.ext.java_version\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    buildFeatures {\n        viewBinding true\n    }\n    androidResources {\n        noCompress 'tflite'\n    }\n}\n\n// import DownloadModels task\nproject.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'\nproject.ext.TEST_ASSETS_DIR = projectDir.toString() + '/src/androidTest/assets'\n\n// Download default models; if you wish to use your own models then\n// place them in the \"assets\" directory and comment out this line.\napply from:'download_models.gradle'\n\ndependencies {\n    // Kotlin lang\n    implementation 'androidx.core:core-ktx:1.6.0'\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version\"\n    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0'\n\n    // App compat and UI things\n    implementation 'androidx.appcompat:appcompat:1.3.1'\n    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'\n    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'\n    implementation 'com.google.android.material:material:1.0.0'\n    implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'\n\n    // Navigation library\n    def nav_version = \"2.3.5\"\n    implementation \"androidx.navigation:navigation-fragment-ktx:$nav_version\"\n    implementation \"androidx.navigation:navigation-ui-ktx:$nav_version\"\n\n    // CameraX core library\n    def camerax_version = '1.1.0-beta03'\n    implementation \"androidx.camera:camera-core:$camerax_version\"\n\n    // CameraX Camera2 extensions\n    implementation \"androidx.camera:camera-camera2:$camerax_version\"\n\n    // CameraX Lifecycle library\n    implementation \"androidx.camera:camera-lifecycle:$camerax_version\"\n\n    // CameraX View class\n    implementation \"androidx.camera:camera-view:$camerax_version\"\n\n    //WindowManager\n    implementation 'androidx.window:window:1.0.0-alpha09'\n\n    // Unit testing\n    testImplementation 'androidx.test.ext:junit:1.1.3'\n    testImplementation 'androidx.test:rules:1.4.0'\n    testImplementation 'androidx.test:runner:1.4.0'\n    testImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n    testImplementation 'org.robolectric:robolectric:4.4'\n\n    // Instrumented testing\n    androidTestImplementation \"androidx.test.ext:junit:1.1.3\"\n    androidTestImplementation \"androidx.test:core:1.4.0\"\n    androidTestImplementation \"androidx.test:rules:1.4.0\"\n    androidTestImplementation \"androidx.test:runner:1.4.0\"\n    androidTestImplementation \"androidx.test.espresso:espresso-core:3.4.0\"\n\n    implementation 'org.tensorflow:tensorflow-lite-task-vision-play-services:0.4.2'\n    implementation 'com.google.android.gms:play-services-tflite-gpu:16.0.0'\n}"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/download_models.gradle",
    "content": "task downloadModelFile(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/object_detection/android_play_services/lite-model_ssd_mobilenet_v1_1_metadata_2.tflite'\n    dest project.ext.ASSET_DIR + '/mobilenetv1.tflite'\n    overwrite false\n}\n\ntask downloadModelFile0(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/object_detection/android_play_services/lite-model_efficientdet_lite0_detection_metadata_1.tflite'\n    dest project.ext.ASSET_DIR + '/efficientdet-lite0.tflite'\n    overwrite false\n}\n\ntask downloadModelFile1(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/object_detection/android_play_services/lite-model_efficientdet_lite1_detection_metadata_1.tflite'\n    dest project.ext.ASSET_DIR + '/efficientdet-lite1.tflite'\n    overwrite false\n}\n\ntask downloadModelFile2(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/object_detection/android_play_services/lite-model_efficientdet_lite2_detection_metadata_1.tflite'\n    dest project.ext.ASSET_DIR + '/efficientdet-lite2.tflite'\n    overwrite false\n}\n\ntask copyTestModel(type: Copy, dependsOn: downloadModelFile) {\n    from project.ext.ASSET_DIR + '/mobilenetv1.tflite'\n    into project.ext.TEST_ASSETS_DIR\n}\n\npreBuild.dependsOn downloadModelFile, downloadModelFile0, downloadModelFile1, downloadModelFile2,\n        copyTestModel"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/androidTest/java/org/tensorflow/lite/examples/objectdetection/TFObjectDetectionTest.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.objectdetection\n\nimport android.content.res.AssetManager\nimport android.graphics.Bitmap\nimport android.graphics.BitmapFactory\nimport android.graphics.RectF\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.platform.app.InstrumentationRegistry\nimport java.io.InputStream\nimport org.junit.Assert.assertEquals\nimport org.junit.Assert.assertNotNull\nimport org.junit.Assert.assertTrue\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.tensorflow.lite.support.label.Category\nimport org.tensorflow.lite.task.gms.vision.detector.Detection\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass TFObjectDetectionTest {\n\n    lateinit var objectDetectorHelper: ObjectDetectorHelper\n\n    val controlResults = listOf<Detection>(\n        Detection.create(RectF(69.0f, 58.0f, 227.0f, 171.0f),\n            listOf<Category>(Category.create(\"cat\", \"cat\", 0.77734375f))),\n        Detection.create(RectF(15.0f, 5.0f, 285.0f, 214.0f),\n            listOf<Category>(Category.create(\"couch\", \"couch\", 0.5859375f))),\n        Detection.create(RectF(45.0f, 27.0f, 257.0f, 184.0f),\n            listOf<Category>(Category.create(\"chair\", \"chair\", 0.55078125f)))\n    )\n\n    @Test\n    @Throws(Exception::class)\n    fun detectionResultsShouldNotChange() {\n        val objectDetectorListener = object : ObjectDetectorHelper.DetectorListener {\n            override fun onError(error: String) {\n                // no op\n            }\n\n            override fun onInitialized() {\n                // Create Bitmap and convert to TensorImage\n                val bitmap = loadImage(\"cat1.png\")\n                // Run the object detector on the sample image\n                objectDetectorHelper.detect(bitmap!!, 0)\n            }\n\n            override fun onResults(\n              results: MutableList<Detection>?,\n              inferenceTime: Long,\n              imageHeight: Int,\n              imageWidth: Int\n            ) {\n\n                assertEquals(controlResults.size, results!!.size)\n\n                // Loop through the detected and control data\n                for (i in controlResults.indices) {\n                    // Verify that the bounding boxes are the same\n                    assertEquals(results[i].boundingBox, controlResults[i].boundingBox)\n\n                    // Verify that the detected data and control\n                    // data have the same number of categories\n                    assertEquals(\n                        results[i].categories.size,\n                        controlResults[i].categories.size\n                    )\n\n                    // Loop through the categories\n                    for (j in 0 until controlResults[i].categories.size - 1) {\n                        // Verify that the labels are consistent\n                        assertEquals(\n                            results[i].categories[j].label,\n                            controlResults[i].categories[j].label\n                        )\n                    }\n                }\n            }\n        }\n\n        objectDetectorHelper =\n            ObjectDetectorHelper(\n                context = InstrumentationRegistry.getInstrumentation().targetContext,\n                objectDetectorListener = objectDetectorListener\n            )\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun detectedImageIsScaledWithinModelDimens() {\n        val listener = object : ObjectDetectorHelper.DetectorListener {\n            override fun onError(error: String) {}\n\n            override fun onResults(\n              results: MutableList<Detection>?,\n              inferenceTime: Long,\n              imageHeight: Int,\n              imageWidth: Int\n            ) {\n                assertNotNull(results)\n                for (result in results!!) {\n                    assertTrue(result.boundingBox.top <= imageHeight)\n                    assertTrue(result.boundingBox.bottom <= imageHeight)\n                    assertTrue(result.boundingBox.left <= imageWidth)\n                    assertTrue(result.boundingBox.right <= imageWidth)\n                }\n            }\n\n            override fun onInitialized() {\n                // Create Bitmap and convert to TensorImage\n                val bitmap = loadImage(\"cat1.png\")\n                // Run the object detector on the sample image\n                objectDetectorHelper.detect(bitmap!!, 0)\n            }\n        }\n        objectDetectorHelper =\n            ObjectDetectorHelper(\n                context = InstrumentationRegistry.getInstrumentation().targetContext,\n                objectDetectorListener = listener\n            )\n    }\n\n    @Throws(Exception::class)\n    private fun loadImage(fileName: String): Bitmap? {\n        val assetManager: AssetManager =\n            InstrumentationRegistry.getInstrumentation().context.assets\n        val inputStream: InputStream = assetManager.open(fileName)\n        return BitmapFactory.decodeStream(inputStream)\n    }\n}\n"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<manifest\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:dist=\"http://schemas.android.com/apk/distribution\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"org.tensorflow.lite.examples.objectdetection\">\n\n    <!-- Enable instant app support -->\n    <dist:module dist:instant=\"true\" />\n\n    <!-- Declare features -->\n    <uses-feature android:name=\"android.hardware.camera\" />\n\n    <!-- Declare permissions -->\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n\n    <application\n        android:icon=\"@mipmap/ic_launcher\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:label=\"@string/app_name\"\n        android:allowBackup=\"true\"\n        android:taskAffinity=\"\"\n        tools:ignore=\"AllowBackup\">\n\n        <activity\n            android:name=\".MainActivity\"\n            android:clearTaskOnLaunch=\"true\"\n            android:theme=\"@style/AppTheme\"\n            android:exported=\"true\"\n            android:icon=\"@mipmap/ic_launcher\"\n            android:rotationAnimation=\"seamless\"\n            android:resizeableActivity=\"true\"\n            android:configChanges=\"orientation|screenLayout|screenSize|smallestScreenSize\"\n            tools:targetApi=\"O\">\n\n            <!-- Main app intent filter -->\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n\n            <!-- Declare notch support -->\n            <meta-data android:name=\"android.notch_support\" android:value=\"true\"/>\n\n        </activity>\n\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/main/java/org/tensorflow/lite/examples/objectdetection/MainActivity.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.objectdetection\n\nimport android.os.Build\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport org.tensorflow.lite.examples.objectdetection.databinding.ActivityMainBinding\n\n/**\n * Main entry point into our app. This app follows the single-activity pattern, and all\n * functionality is implemented in the form of fragments.\n */\nclass MainActivity : AppCompatActivity() {\n\n    private lateinit var activityMainBinding: ActivityMainBinding\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)\n        setContentView(activityMainBinding.root)\n    }\n\n    override fun onBackPressed() {\n        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {\n            // Workaround for Android Q memory leak issue in IRequestFinishCallback$Stub.\n            // (https://issuetracker.google.com/issues/139738913)\n            finishAfterTransition()\n        } else {\n            super.onBackPressed()\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/main/java/org/tensorflow/lite/examples/objectdetection/ObjectDetectorHelper.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.objectdetection\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.os.SystemClock\nimport android.util.Log\nimport com.google.android.gms.tflite.client.TfLiteInitializationOptions\nimport com.google.android.gms.tflite.gpu.support.TfLiteGpu\nimport org.tensorflow.lite.support.image.ImageProcessor\nimport org.tensorflow.lite.support.image.TensorImage\nimport org.tensorflow.lite.support.image.ops.Rot90Op\nimport org.tensorflow.lite.task.core.BaseOptions\nimport org.tensorflow.lite.task.gms.vision.TfLiteVision\nimport org.tensorflow.lite.task.gms.vision.detector.Detection\nimport org.tensorflow.lite.task.gms.vision.detector.ObjectDetector\n\nclass ObjectDetectorHelper(\n  var threshold: Float = 0.5f,\n  var numThreads: Int = 2,\n  var maxResults: Int = 3,\n  var currentDelegate: Int = 0,\n  var currentModel: Int = 0,\n  val context: Context,\n  val objectDetectorListener: DetectorListener\n) {\n\n    private val TAG = \"ObjectDetectionHelper\"\n\n    // For this example this needs to be a var so it can be reset on changes. If the ObjectDetector\n    // will not change, a lazy val would be preferable.\n    private var objectDetector: ObjectDetector? = null\n    private var gpuSupported = false\n\n    init {\n\n        TfLiteGpu.isGpuDelegateAvailable(context).onSuccessTask { gpuAvailable: Boolean ->\n            val optionsBuilder =\n                TfLiteInitializationOptions.builder()\n            if (gpuAvailable) {\n                optionsBuilder.setEnableGpuDelegateSupport(true)\n            }\n            TfLiteVision.initialize(context, optionsBuilder.build())\n        }.addOnSuccessListener {\n            objectDetectorListener.onInitialized()\n        }.addOnFailureListener{\n            objectDetectorListener.onError(\"TfLiteVision failed to initialize: \"\n                    + it.message)\n        }\n    }\n\n    fun clearObjectDetector() {\n        objectDetector = null\n    }\n\n    // Initialize the object detector using current settings on the\n    // thread that is using it. CPU and NNAPI delegates can be used with detectors\n    // that are created on the main thread and used on a background thread, but\n    // the GPU delegate needs to be used on the thread that initialized the detector\n    fun setupObjectDetector() {\n        if (!TfLiteVision.isInitialized()) {\n            Log.e(TAG, \"setupObjectDetector: TfLiteVision is not initialized yet\")\n            return\n        }\n\n        // Create the base options for the detector using specifies max results and score threshold\n        val optionsBuilder =\n            ObjectDetector.ObjectDetectorOptions.builder()\n                .setScoreThreshold(threshold)\n                .setMaxResults(maxResults)\n\n        // Set general detection options, including number of used threads\n        val baseOptionsBuilder = BaseOptions.builder().setNumThreads(numThreads)\n\n        // Use the specified hardware for running the model. Default to CPU\n        when (currentDelegate) {\n            DELEGATE_CPU -> {\n                // Default\n            }\n            DELEGATE_GPU -> {\n                if (gpuSupported) {\n                    baseOptionsBuilder.useGpu()\n                } else {\n                    objectDetectorListener.onError(\"GPU is not supported on this device\")\n                }\n            }\n            DELEGATE_NNAPI -> {\n                baseOptionsBuilder.useNnapi()\n            }\n        }\n\n        optionsBuilder.setBaseOptions(baseOptionsBuilder.build())\n\n        val modelName =\n            when (currentModel) {\n                MODEL_MOBILENETV1 -> \"mobilenetv1.tflite\"\n                MODEL_EFFICIENTDETV0 -> \"efficientdet-lite0.tflite\"\n                MODEL_EFFICIENTDETV1 -> \"efficientdet-lite1.tflite\"\n                MODEL_EFFICIENTDETV2 -> \"efficientdet-lite2.tflite\"\n                else -> \"mobilenetv1.tflite\"\n            }\n\n        try {\n            objectDetector =\n                ObjectDetector.createFromFileAndOptions(context, modelName, optionsBuilder.build())\n        } catch (e: Exception) {\n            objectDetectorListener.onError(\n                \"Object detector failed to initialize. See error logs for details\"\n            )\n            Log.e(TAG, \"TFLite failed to load model with error: \" + e.message)\n        }\n    }\n\n    fun detect(image: Bitmap, imageRotation: Int) {\n        if (!TfLiteVision.isInitialized()) {\n            Log.e(TAG, \"detect: TfLiteVision is not initialized yet\")\n            return\n        }\n\n        if (objectDetector == null) {\n            setupObjectDetector()\n        }\n\n        // Inference time is the difference between the system time at the start and finish of the\n        // process\n        var inferenceTime = SystemClock.uptimeMillis()\n\n        // Create preprocessor for the image.\n        // See https://www.tensorflow.org/lite/inference_with_metadata/\n        //            lite_support#imageprocessor_architecture\n        val imageProcessor = ImageProcessor.Builder().add(Rot90Op(-imageRotation / 90)).build()\n\n        // Preprocess the image and convert it into a TensorImage for detection.\n        val tensorImage = imageProcessor.process(TensorImage.fromBitmap(image))\n\n        val results = objectDetector?.detect(tensorImage)\n        inferenceTime = SystemClock.uptimeMillis() - inferenceTime\n        objectDetectorListener.onResults(\n          results,\n          inferenceTime,\n          tensorImage.height,\n          tensorImage.width)\n    }\n\n    interface DetectorListener {\n        fun onInitialized()\n        fun onError(error: String)\n        fun onResults(\n          results: MutableList<Detection>?,\n          inferenceTime: Long,\n          imageHeight: Int,\n          imageWidth: Int\n        )\n    }\n\n    companion object {\n        const val DELEGATE_CPU = 0\n        const val DELEGATE_GPU = 1\n        const val DELEGATE_NNAPI = 2\n        const val MODEL_MOBILENETV1 = 0\n        const val MODEL_EFFICIENTDETV0 = 1\n        const val MODEL_EFFICIENTDETV1 = 2\n        const val MODEL_EFFICIENTDETV2 = 3\n    }\n}\n"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/main/java/org/tensorflow/lite/examples/objectdetection/OverlayView.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.objectdetection\n\nimport android.content.Context\nimport android.graphics.Canvas\nimport android.graphics.Color\nimport android.graphics.Paint\nimport android.graphics.Rect\nimport android.graphics.RectF\nimport android.util.AttributeSet\nimport android.view.View\nimport androidx.core.content.ContextCompat\nimport java.util.LinkedList\nimport kotlin.math.max\nimport org.tensorflow.lite.task.gms.vision.detector.Detection\n\nclass OverlayView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {\n\n    private var results: List<Detection> = LinkedList<Detection>()\n    private var boxPaint = Paint()\n    private var textBackgroundPaint = Paint()\n    private var textPaint = Paint()\n\n    private var scaleFactor: Float = 1f\n\n    private var bounds = Rect()\n\n    init {\n        initPaints()\n    }\n\n    fun clear() {\n        textPaint.reset()\n        textBackgroundPaint.reset()\n        boxPaint.reset()\n        invalidate()\n        initPaints()\n    }\n\n    private fun initPaints() {\n        textBackgroundPaint.color = Color.BLACK\n        textBackgroundPaint.style = Paint.Style.FILL\n        textBackgroundPaint.textSize = 50f\n\n        textPaint.color = Color.WHITE\n        textPaint.style = Paint.Style.FILL\n        textPaint.textSize = 50f\n\n        boxPaint.color = ContextCompat.getColor(context!!, R.color.bounding_box_color)\n        boxPaint.strokeWidth = 8F\n        boxPaint.style = Paint.Style.STROKE\n    }\n\n    override fun draw(canvas: Canvas) {\n        super.draw(canvas)\n\n        for (result in results) {\n            val boundingBox = result.boundingBox\n\n            val top = boundingBox.top * scaleFactor\n            val bottom = boundingBox.bottom * scaleFactor\n            val left = boundingBox.left * scaleFactor\n            val right = boundingBox.right * scaleFactor\n\n            // Draw bounding box around detected objects\n            val drawableRect = RectF(left, top, right, bottom)\n            canvas.drawRect(drawableRect, boxPaint)\n\n            // Create text to display alongside detected objects\n            val drawableText =\n                result.categories[0].label + \" \" +\n                        String.format(\"%.2f\", result.categories[0].score)\n\n            // Draw rect behind display text\n            textBackgroundPaint.getTextBounds(drawableText, 0, drawableText.length, bounds)\n            val textWidth = bounds.width()\n            val textHeight = bounds.height()\n            canvas.drawRect(\n                left,\n                top,\n                left + textWidth + Companion.BOUNDING_RECT_TEXT_PADDING,\n                top + textHeight + Companion.BOUNDING_RECT_TEXT_PADDING,\n                textBackgroundPaint\n            )\n\n            // Draw text for detected object\n            canvas.drawText(drawableText, left, top + bounds.height(), textPaint)\n        }\n    }\n\n    fun setResults(\n      detectionResults: MutableList<Detection>,\n      imageHeight: Int,\n      imageWidth: Int,\n    ) {\n        results = detectionResults\n\n        // PreviewView is in FILL_START mode. So we need to scale up the bounding box to match with\n        // the size that the captured images will be displayed.\n        scaleFactor = max(width * 1f / imageWidth, height * 1f / imageHeight)\n    }\n\n    companion object {\n        private const val BOUNDING_RECT_TEXT_PADDING = 8\n    }\n}\n"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/main/java/org/tensorflow/lite/examples/objectdetection/fragments/CameraFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.objectdetection.fragments\n\nimport android.annotation.SuppressLint\nimport android.content.res.Configuration\nimport android.graphics.Bitmap\nimport android.os.Bundle\nimport android.util.Log\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.AdapterView\nimport android.widget.Toast\nimport androidx.camera.core.AspectRatio\nimport androidx.camera.core.Camera\nimport androidx.camera.core.CameraSelector\nimport androidx.camera.core.ImageAnalysis\nimport androidx.camera.core.ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888\nimport androidx.camera.core.ImageProxy\nimport androidx.camera.core.Preview\nimport androidx.camera.lifecycle.ProcessCameraProvider\nimport androidx.core.content.ContextCompat\nimport androidx.fragment.app.Fragment\nimport androidx.navigation.Navigation\nimport java.util.LinkedList\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Executors\nimport org.tensorflow.lite.examples.objectdetection.ObjectDetectorHelper\nimport org.tensorflow.lite.examples.objectdetection.R\nimport org.tensorflow.lite.examples.objectdetection.databinding.FragmentCameraBinding\nimport org.tensorflow.lite.task.gms.vision.detector.Detection\n\nclass CameraFragment : Fragment(), ObjectDetectorHelper.DetectorListener {\n\n    private val TAG = \"CameraFragment\"\n\n    private var _fragmentCameraBinding: FragmentCameraBinding? = null\n\n    private val fragmentCameraBinding\n        get() = _fragmentCameraBinding!!\n\n    private lateinit var objectDetectorHelper: ObjectDetectorHelper\n    private lateinit var bitmapBuffer: Bitmap\n    private var preview: Preview? = null\n    private var imageAnalyzer: ImageAnalysis? = null\n    private var camera: Camera? = null\n    private var cameraProvider: ProcessCameraProvider? = null\n\n    /** Blocking camera operations are performed using this executor */\n    private lateinit var cameraExecutor: ExecutorService\n\n    override fun onResume() {\n        super.onResume()\n        // Make sure that all permissions are still present, since the\n        // user could have removed them while the app was in paused state.\n        if (!PermissionsFragment.hasPermissions(requireContext())) {\n            Navigation.findNavController(requireActivity(), R.id.fragment_container)\n                .navigate(CameraFragmentDirections.actionCameraToPermissions())\n        }\n    }\n\n    override fun onDestroyView() {\n        _fragmentCameraBinding = null\n        super.onDestroyView()\n\n        // Shut down our background executor\n        cameraExecutor.shutdown()\n    }\n\n    override fun onCreateView(\n      inflater: LayoutInflater,\n      container: ViewGroup?,\n      savedInstanceState: Bundle?\n    ): View {\n        _fragmentCameraBinding = FragmentCameraBinding.inflate(inflater, container, false)\n\n        return fragmentCameraBinding.root\n    }\n\n    @SuppressLint(\"MissingPermission\")\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n\n        objectDetectorHelper = ObjectDetectorHelper(\n            context = requireContext(),\n            objectDetectorListener = this)\n\n        // Attach listeners to UI control widgets\n        initBottomSheetControls()\n    }\n\n    private fun initBottomSheetControls() {\n        // When clicked, lower detection score threshold floor\n        fragmentCameraBinding.bottomSheetLayout.thresholdMinus.setOnClickListener {\n            if (objectDetectorHelper.threshold >= 0.1) {\n                objectDetectorHelper.threshold -= 0.1f\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, raise detection score threshold floor\n        fragmentCameraBinding.bottomSheetLayout.thresholdPlus.setOnClickListener {\n            if (objectDetectorHelper.threshold <= 0.8) {\n                objectDetectorHelper.threshold += 0.1f\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, reduce the number of objects that can be detected at a time\n        fragmentCameraBinding.bottomSheetLayout.maxResultsMinus.setOnClickListener {\n            if (objectDetectorHelper.maxResults > 1) {\n                objectDetectorHelper.maxResults--\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, increase the number of objects that can be detected at a time\n        fragmentCameraBinding.bottomSheetLayout.maxResultsPlus.setOnClickListener {\n            if (objectDetectorHelper.maxResults < 5) {\n                objectDetectorHelper.maxResults++\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, decrease the number of threads used for detection\n        fragmentCameraBinding.bottomSheetLayout.threadsMinus.setOnClickListener {\n            if (objectDetectorHelper.numThreads > 1) {\n                objectDetectorHelper.numThreads--\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, increase the number of threads used for detection\n        fragmentCameraBinding.bottomSheetLayout.threadsPlus.setOnClickListener {\n            if (objectDetectorHelper.numThreads < 4) {\n                objectDetectorHelper.numThreads++\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, change the underlying hardware used for inference. Current options are CPU\n        // GPU, and NNAPI\n        fragmentCameraBinding.bottomSheetLayout.spinnerDelegate.setSelection(0, false)\n        fragmentCameraBinding.bottomSheetLayout.spinnerDelegate.onItemSelectedListener =\n            object : AdapterView.OnItemSelectedListener {\n                override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {\n                    objectDetectorHelper.currentDelegate = p2\n                    updateControlsUi()\n                }\n\n                override fun onNothingSelected(p0: AdapterView<*>?) {\n                    /* no op */\n                }\n            }\n\n        // When clicked, change the underlying model used for object detection\n        fragmentCameraBinding.bottomSheetLayout.spinnerModel.setSelection(0, false)\n        fragmentCameraBinding.bottomSheetLayout.spinnerModel.onItemSelectedListener =\n            object : AdapterView.OnItemSelectedListener {\n                override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {\n                    objectDetectorHelper.currentModel = p2\n                    updateControlsUi()\n                }\n\n                override fun onNothingSelected(p0: AdapterView<*>?) {\n                    /* no op */\n                }\n            }\n    }\n\n    // Update the values displayed in the bottom sheet. Reset detector.\n    private fun updateControlsUi() {\n        fragmentCameraBinding.bottomSheetLayout.maxResultsValue.text =\n            objectDetectorHelper.maxResults.toString()\n        fragmentCameraBinding.bottomSheetLayout.thresholdValue.text =\n            String.format(\"%.2f\", objectDetectorHelper.threshold)\n        fragmentCameraBinding.bottomSheetLayout.threadsValue.text =\n            objectDetectorHelper.numThreads.toString()\n\n        // Needs to be cleared instead of reinitialized because the GPU\n        // delegate needs to be initialized on the thread using it when applicable\n        objectDetectorHelper.clearObjectDetector()\n        fragmentCameraBinding.overlay.clear()\n    }\n\n    // Initialize CameraX, and prepare to bind the camera use cases\n    private fun setUpCamera() {\n        val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())\n        cameraProviderFuture.addListener(\n            {\n                // CameraProvider\n                cameraProvider = cameraProviderFuture.get()\n\n                // Build and bind the camera use cases\n                bindCameraUseCases()\n            },\n            ContextCompat.getMainExecutor(requireContext())\n        )\n    }\n\n    // Declare and bind preview, capture and analysis use cases\n    @SuppressLint(\"UnsafeOptInUsageError\")\n    private fun bindCameraUseCases() {\n\n        // CameraProvider\n        val cameraProvider =\n            cameraProvider ?: throw IllegalStateException(\"Camera initialization failed.\")\n\n        // CameraSelector - makes assumption that we're only using the back camera\n        val cameraSelector =\n            CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()\n\n        // Preview. Only using the 4:3 ratio because this is the closest to our models\n        preview =\n            Preview.Builder()\n                .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n                .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)\n                .build()\n\n        // ImageAnalysis. Using RGBA 8888 to match how our models work\n        imageAnalyzer =\n            ImageAnalysis.Builder()\n                .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n                .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)\n                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)\n                .setOutputImageFormat(OUTPUT_IMAGE_FORMAT_RGBA_8888)\n                .build()\n                // The analyzer can then be assigned to the instance\n                .also {\n                    it.setAnalyzer(cameraExecutor) { image ->\n                        if (!::bitmapBuffer.isInitialized) {\n                            // The image rotation and RGB image buffer are initialized only once\n                            // the analyzer has started running\n                            bitmapBuffer = Bitmap.createBitmap(\n                                image.width,\n                                image.height,\n                                Bitmap.Config.ARGB_8888\n                            )\n                        }\n\n                        detectObjects(image)\n                    }\n                }\n\n        // Must unbind the use-cases before rebinding them\n        cameraProvider.unbindAll()\n\n        try {\n            // A variable number of use-cases can be passed here -\n            // camera provides access to CameraControl & CameraInfo\n            camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalyzer)\n\n            // Attach the viewfinder's surface provider to preview use case\n            preview?.setSurfaceProvider(fragmentCameraBinding.viewFinder.surfaceProvider)\n        } catch (exc: Exception) {\n            Log.e(TAG, \"Use case binding failed\", exc)\n        }\n    }\n\n    private fun detectObjects(image: ImageProxy) {\n        // Copy out RGB bits to the shared bitmap buffer\n        image.use { bitmapBuffer.copyPixelsFromBuffer(image.planes[0].buffer) }\n\n        val imageRotation = image.imageInfo.rotationDegrees\n        // Pass Bitmap and rotation to the object detector helper for processing and detection\n        objectDetectorHelper.detect(bitmapBuffer, imageRotation)\n    }\n\n    override fun onConfigurationChanged(newConfig: Configuration) {\n        super.onConfigurationChanged(newConfig)\n        imageAnalyzer?.targetRotation = fragmentCameraBinding.viewFinder.display.rotation\n    }\n\n    // Update UI after objects have been detected. Extracts original image height/width\n    // to scale and place bounding boxes properly through OverlayView\n    override fun onResults(\n      results: MutableList<Detection>?,\n      inferenceTime: Long,\n      imageHeight: Int,\n      imageWidth: Int\n    ) {\n        activity?.runOnUiThread {\n            fragmentCameraBinding.bottomSheetLayout.inferenceTimeVal.text =\n                String.format(\"%d ms\", inferenceTime)\n\n            // Pass necessary information to OverlayView for drawing on the canvas\n            fragmentCameraBinding.overlay.setResults(\n                results ?: LinkedList<Detection>(),\n                imageHeight,\n                imageWidth\n            )\n\n            // Force a redraw\n            fragmentCameraBinding.overlay.invalidate()\n        }\n    }\n\n    override fun onError(error: String) {\n        activity?.runOnUiThread {\n            Toast.makeText(requireContext(), error, Toast.LENGTH_SHORT).show()\n        }\n    }\n\n    override fun onInitialized() {\n        objectDetectorHelper.setupObjectDetector()\n        // Initialize our background executor\n        cameraExecutor = Executors.newSingleThreadExecutor()\n\n        // Wait for the views to be properly laid out\n        fragmentCameraBinding.viewFinder.post {\n            // Set up the camera and its use cases\n            setUpCamera()\n        }\n\n        fragmentCameraBinding.progressCircular.visibility = View.GONE\n    }\n}\n"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/main/java/org/tensorflow/lite/examples/objectdetection/fragments/PermissionsFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.objectdetection.fragments\n\nimport android.Manifest\nimport android.content.Context\nimport android.content.pm.PackageManager\nimport android.os.Bundle\nimport android.widget.Toast\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.core.content.ContextCompat\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.lifecycleScope\nimport androidx.navigation.Navigation\nimport org.tensorflow.lite.examples.objectdetection.R\n\nprivate val PERMISSIONS_REQUIRED = arrayOf(Manifest.permission.CAMERA)\n\n/**\n * The sole purpose of this fragment is to request permissions and, once granted, display the\n * camera fragment to the user.\n */\nclass PermissionsFragment : Fragment() {\n\n    private val requestPermissionLauncher =\n        registerForActivityResult(ActivityResultContracts.RequestPermission()\n        ) { isGranted: Boolean ->\n            if (isGranted) {\n                Toast.makeText(context, \"Permission request granted\", Toast.LENGTH_LONG).show()\n                navigateToCamera()\n            } else {\n                Toast.makeText(context, \"Permission request denied\", Toast.LENGTH_LONG).show()\n            }\n        }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        when {\n            ContextCompat.checkSelfPermission(\n                requireContext(),\n                Manifest.permission.CAMERA\n            ) == PackageManager.PERMISSION_GRANTED -> {\n                navigateToCamera()\n            }\n            else -> {\n                requestPermissionLauncher.launch(\n                    Manifest.permission.CAMERA)\n            }\n        }\n    }\n\n    private fun navigateToCamera() {\n        lifecycleScope.launchWhenStarted {\n            Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(\n                PermissionsFragmentDirections.actionPermissionsToCamera())\n        }\n    }\n\n    companion object {\n\n        /** Convenience method used to check if all permissions required by this app are granted */\n        fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all {\n            ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/main/res/color/selector_ic.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"true\" android:color=\"@color/icPressed\" />\n    <item android:state_focused=\"true\" android:color=\"@color/icFocused\" />\n    <item android:color=\"@color/icActive\" />\n</selector>"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/main/res/drawable/ic_minus.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13H5v-2h14v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/main/res/drawable/ic_plus.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path android:pathData=\"M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"85.84757\"\n                android:endY=\"92.4963\"\n                android:startX=\"42.9492\"\n                android:startY=\"49.59793\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:background=\"@android:color/transparent\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n    <androidx.fragment.app.FragmentContainerView\n        android:id=\"@+id/fragment_container\"\n        android:name=\"androidx.navigation.fragment.NavHostFragment\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"@android:color/transparent\"\n        android:keepScreenOn=\"true\"\n        app:defaultNavHost=\"true\"\n        app:navGraph=\"@navigation/nav_graph\"\n        android:layout_marginTop=\"?android:attr/actionBarSize\"\n        tools:context=\".MainActivity\"/>\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:layout_alignParentTop=\"true\"\n            android:background=\"@color/toolbar_background\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/tfl_logo\" />\n        </androidx.appcompat.widget.Toolbar>\n\n    </RelativeLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/main/res/layout/fragment_camera.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/camera_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.camera.view.PreviewView\n        android:id=\"@+id/view_finder\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:scaleType=\"fillStart\"/>\n\n    <org.tensorflow.lite.examples.objectdetection.OverlayView\n        android:id=\"@+id/overlay\"\n        android:layout_height=\"match_parent\"\n        android:layout_width=\"match_parent\" />\n\n    <androidx.core.widget.ContentLoadingProgressBar\n        android:id=\"@+id/progress_circular\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:indeterminate=\"true\"\n        android:visibility=\"visible\"\n        android:layout_gravity=\"center\"\n        style=\"?android:attr/progressBarStyleLarge\" />\n\n    <include\n        android:id=\"@+id/bottom_sheet_layout\"\n        layout=\"@layout/info_bottom_sheet\" />\n</androidx.coordinatorlayout.widget.CoordinatorLayout>"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/main/res/layout/info_bottom_sheet.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.core.widget.NestedScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/bottom_sheet_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_gravity=\"center_horizontal\"\n    android:background=\"@color/bottom_sheet_background\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:padding=\"@dimen/bottom_sheet_padding\"\n    android:layout_height=\"wrap_content\"\n    app:behavior_hideable=\"false\"\n    app:behavior_peekHeight=\"@dimen/bottom_sheet_peek_height\"\n    app:layout_behavior=\"com.google.android.material.bottomsheet.BottomSheetBehavior\">\n\n    <androidx.appcompat.widget.LinearLayoutCompat\n        android:orientation=\"vertical\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <!-- Chevron indicating that the bottom sheet is expandable -->\n        <RelativeLayout\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\">\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerHorizontal=\"true\"\n                android:layout_centerVertical=\"true\"\n                android:src=\"@drawable/icn_chevron_up\"\n                android:contentDescription=\"@string/alt_bottom_sheet_chevron\" />\n        </RelativeLayout>\n\n        <!-- Inference time row -->\n        <androidx.appcompat.widget.LinearLayoutCompat\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:orientation=\"horizontal\">\n            <TextView\n                android:id=\"@+id/inference_time_label\"\n                android:text=\"@string/label_interence_time\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n            <TextView\n                android:id=\"@+id/inference_time_val\"\n                android:text=\"0ms\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"end\"\n                android:layout_centerVertical=\"true\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n        </androidx.appcompat.widget.LinearLayoutCompat>\n\n        <!-- ML confidence threshold adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/label_confidence_threshold\"\n                android:layout_centerVertical=\"true\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\"\n                android:textColor=\"@color/bottom_sheet_text_color\" />\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\"\n                >\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threshold_minus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_threshold_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/threshold_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:gravity=\"center\"\n                    android:minEms=\"@integer/bottom_sheet_control_text_min_ems\"\n                    android:text=\"0.50\"\n                    android:textColor=\"@color/bottom_sheet_text_color\"\n                    android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threshold_plus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_threshold_button_plus\"\n                    android:src=\"@drawable/ic_plus\"  />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- ML max results adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/label_max_results\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\"\n                android:layout_centerVertical=\"true\"\n                android:textColor=\"@color/bottom_sheet_text_color\" />\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center\"\n                android:orientation=\"horizontal\">\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/max_results_minus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_max_results_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n                <TextView\n                    android:id=\"@+id/max_results_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:minEms=\"@integer/bottom_sheet_control_text_min_ems\"\n                    android:gravity=\"center\"\n                    android:layout_marginLeft=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:text=\"3\"\n                    android:textColor=\"@color/bottom_sheet_text_color\"\n                    android:textSize=\"@dimen/bottom_sheet_text_size\" />\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/max_results_plus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_max_results_button_plus\"\n                    android:src=\"@drawable/ic_plus\" />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- Number of threads adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/label_threads\"\n                android:layout_centerVertical=\"true\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\"\n                android:textColor=\"@color/bottom_sheet_text_color\" />\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\"\n                >\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threads_minus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_thread_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/threads_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/bottom_sheet_control_text_side_margin\"\n                    android:gravity=\"center\"\n                    android:minEms=\"@integer/bottom_sheet_control_text_min_ems\"\n                    android:text=\"2\"\n                    android:textColor=\"@color/bottom_sheet_text_color\"\n                    android:textSize=\"@dimen/bottom_sheet_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threads_plus\"\n                    android:layout_width=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:layout_height=\"@dimen/bottom_sheet_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_thread_button_plus\"\n                    android:src=\"@drawable/ic_plus\"  />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- Delegate selection row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\">\n\n            <TextView\n                android:text=\"@string/label_delegate\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\"\n                android:textColor=\"@color/bottom_sheet_text_color\" />\n\n            <androidx.appcompat.widget.AppCompatSpinner\n                android:id=\"@+id/spinner_delegate\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:minWidth=\"@dimen/bottom_sheet_spinner_delegate_min_width\"\n                android:spinnerMode=\"dropdown\"\n                android:theme=\"@style/BottomSheetSpinnerItemStyle\"\n                android:layout_alignParentRight=\"true\"\n                android:entries=\"@array/delegate_spinner_titles\"/>\n\n        </RelativeLayout>\n\n        <!-- Model selection row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/bottom_sheet_default_row_margin\">\n\n            <TextView\n                android:text=\"@string/label_models\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\"\n                android:textColor=\"@color/bottom_sheet_text_color\" />\n\n            <androidx.appcompat.widget.AppCompatSpinner\n                android:id=\"@+id/spinner_model\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:minWidth=\"@dimen/bottom_sheet_spinner_model_min_width\"\n                android:spinnerMode=\"dropdown\"\n                android:theme=\"@style/BottomSheetSpinnerItemStyle\"\n                android:layout_alignParentRight=\"true\"\n                android:entries=\"@array/models_spinner_titles\"/>\n\n        </RelativeLayout>\n    </androidx.appcompat.widget.LinearLayoutCompat>\n</androidx.core.widget.NestedScrollView>"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/main/res/navigation/nav_graph.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<navigation\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/nav_graph\"\n    app:startDestination=\"@id/permissions_fragment\">\n\n    <fragment\n        android:id=\"@+id/permissions_fragment\"\n        android:name=\"org.tensorflow.lite.examples.objectdetection.fragments.PermissionsFragment\"\n        android:label=\"PermissionsFragment\" >\n\n        <action\n            android:id=\"@+id/action_permissions_to_camera\"\n            app:destination=\"@id/camera_fragment\"\n            app:popUpTo=\"@id/permissions_fragment\"\n            app:popUpToInclusive=\"true\" />\n\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/camera_fragment\"\n        android:name=\"org.tensorflow.lite.examples.objectdetection.fragments.CameraFragment\"\n        android:label=\"CameraFragment\" >\n\n\n        <action\n            android:id=\"@+id/action_camera_to_permissions\"\n            app:destination=\"@id/permissions_fragment\"\n            app:popUpTo=\"@id/camera_fragment\"\n            app:popUpToInclusive=\"true\"/>\n\n    </fragment>\n</navigation>"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n\n<resources>\n    <!-- TF Branding Orange -->\n    <color name=\"bounding_box_color\">#FF6F00</color>\n    <color name=\"toolbar_background\">#EEEEEE</color>\n    <color name=\"ic_launcher_background\">#FFFFFF</color>\n    <color name=\"bottom_sheet_background\">#EEEEEE</color>\n    <color name=\"bottom_sheet_text_color\">@android:color/black</color>\n    <color name=\"icActive\">#FFFFFFFF</color>\n    <color name=\"icFocused\">#DDFFFFFF</color>\n    <color name=\"icPressed\">#AAFFFFFF</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n\n    <dimen name=\"stroke_small\">4dp</dimen>\n    <dimen name=\"round_button_medium\">64dp</dimen>\n\n    <!-- Bottom Sheet -->\n    <dimen name=\"bottom_sheet_text_size\">20sp</dimen>\n    <dimen name=\"bottom_sheet_padding\">16dp</dimen>\n    <dimen name=\"bottom_sheet_peek_height\">50dp</dimen>\n    <dimen name=\"bottom_sheet_default_row_margin\">16dp</dimen>\n    <dimen name=\"bottom_sheet_control_btn_size\">48dp</dimen>\n    <dimen name=\"bottom_sheet_control_text_side_margin\">10dp</dimen>\n    <dimen name=\"bottom_sheet_spinner_delegate_min_width\">160dp</dimen>\n    <dimen name=\"bottom_sheet_spinner_model_min_width\">240dp</dimen>\n\n    <integer name=\"bottom_sheet_control_text_min_ems\">3</integer>\n</resources>"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n    <string name=\"app_name\">TFLite Object Detection</string>\n\n    <string name=\"alt_bottom_sheet_chevron\">Bottom sheet expandable indicator</string>\n\n    <string name=\"alt_bottom_sheet_max_results_button_minus\">Decreasing maxium detected results\n        button</string>\n    <string name=\"alt_bottom_sheet_max_results_button_plus\">Increasing maxium detected results\n        button</string>\n    <string name=\"alt_bottom_sheet_threshold_button_minus\">Decreasing threshold of detected object\n        results button</string>\n    <string name=\"alt_bottom_sheet_threshold_button_plus\">Increasing threshold of detected object\n        results button</string>\n    <string name=\"alt_bottom_sheet_thread_button_minus\">Decrease the number of threads used</string>\n    <string name=\"alt_bottom_sheet_thread_button_plus\">Increase the number of threads used</string>\n\n    <string name=\"label_interence_time\">Inference Time</string>\n    <string name=\"label_fps\">Frames per Second</string>\n    <string name=\"label_confidence_threshold\">Threshold</string>\n    <string name=\"label_max_results\">Max Results</string>\n    <string name=\"label_threads\">Number of Threads</string>\n    <string name=\"label_delegate\">Delegate</string>\n    <string name=\"label_models\">ML Model</string>\n\n    <string-array name=\"delegate_spinner_titles\">\n        <item>CPU</item>\n        <item>GPU</item>\n        <item>NNAPI</item>\n    </string-array>\n\n    <string-array name=\"models_spinner_titles\">\n        <item>MobileNet V1</item>\n        <item>EfficientDet Lite0</item>\n        <item>EfficientDet Lite1</item>\n        <item>EfficientDet Lite2</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n    </style>\n\n    <style name=\"BottomSheetSpinnerItemStyle\" parent=\"Widget.AppCompat.Light.DropDownItem.Spinner\">\n        <item name=\"android:textSize\">@dimen/bottom_sheet_text_size</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    // Top-level variables used for versioning\n    ext.kotlin_version = '1.5.21'\n    ext.java_version = JavaVersion.VERSION_1_8\n\n    repositories {\n        google()\n        mavenCentral()\n    }\n\n    dependencies {\n        classpath 'com.android.tools.build:gradle:7.2.1'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n        classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.4.1'\n        classpath 'de.undercouch:gradle-download-task:4.1.2'\n\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx1536m\nandroid.enableJetifier=true\nandroid.useAndroidX=true"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/object_detection/android_play_services/settings.gradle",
    "content": "include ':app'"
  },
  {
    "path": "lite/examples/object_detection/ios/ObjectDetection/AppDelegate.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n  var window: UIWindow?\n\n  func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil\n  ) -> Bool {\n    return true\n  }\n}\n"
  },
  {
    "path": "lite/examples/object_detection/ios/ObjectDetection/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\"images\":[{\"size\":\"60x60\",\"expected-size\":\"180\",\"filename\":\"180.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"40x40\",\"expected-size\":\"80\",\"filename\":\"80.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"40x40\",\"expected-size\":\"120\",\"filename\":\"120.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"60x60\",\"expected-size\":\"120\",\"filename\":\"120.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"57x57\",\"expected-size\":\"57\",\"filename\":\"57.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"58\",\"filename\":\"58.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"29x29\",\"expected-size\":\"29\",\"filename\":\"29.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"87\",\"filename\":\"87.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"57x57\",\"expected-size\":\"114\",\"filename\":\"114.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"60\",\"filename\":\"60.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"1024x1024\",\"filename\":\"1024.png\",\"expected-size\":\"1024\",\"idiom\":\"ios-marketing\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"scale\":\"1x\"},{\"size\":\"40x40\",\"expected-size\":\"80\",\"filename\":\"80.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"72x72\",\"expected-size\":\"72\",\"filename\":\"72.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"76x76\",\"expected-size\":\"152\",\"filename\":\"152.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"50x50\",\"expected-size\":\"100\",\"filename\":\"100.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"29x29\",\"expected-size\":\"58\",\"filename\":\"58.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"76x76\",\"expected-size\":\"76\",\"filename\":\"76.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"29\",\"filename\":\"29.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"50x50\",\"expected-size\":\"50\",\"filename\":\"50.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"72x72\",\"expected-size\":\"144\",\"filename\":\"144.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"40x40\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"83.5x83.5\",\"expected-size\":\"167\",\"filename\":\"167.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"20\",\"filename\":\"20.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"20x20\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"}]}"
  },
  {
    "path": "lite/examples/object_detection/ios/ObjectDetection/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/object_detection/ios/ObjectDetection/Assets.xcassets/down_icon.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icnChevronDown.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icnChevronDown@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icnChevronDown@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/object_detection/ios/ObjectDetection/Assets.xcassets/tfl_logo.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"tfl_logo.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"tfl_logo@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"tfl_logo@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/object_detection/ios/ObjectDetection/Assets.xcassets/up_icon.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icnChevronUp.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icnChevronUp@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icnChevronUp@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/object_detection/ios/ObjectDetection/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"13122.16\" systemVersion=\"17A277\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"13104.12\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "lite/examples/object_detection/ios/ObjectDetection/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"20037\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <device id=\"retina4_7\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"20020\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"System colors in document resources\" minToolsVersion=\"11.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"ViewController\" customModule=\"ObjectDetection\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"jhC-YW-J6Q\" customClass=\"PreviewView\" customModule=\"ObjectDetection\" customModuleProvider=\"target\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"-20\" width=\"375\" height=\"687\"/>\n                                <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                            </view>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"tyX-XX-qft\" customClass=\"OverlayView\" customModule=\"ObjectDetection\" customModuleProvider=\"target\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"-20\" width=\"375\" height=\"687\"/>\n                                <subviews>\n                                    <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"RqP-HE-5qz\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"-25\" width=\"375\" height=\"100\"/>\n                                        <subviews>\n                                            <imageView userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" image=\"tfl_logo\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"SdB-Ww-kKt\">\n                                                <rect key=\"frame\" x=\"16\" y=\"56\" width=\"160\" height=\"24\"/>\n                                                <constraints>\n                                                    <constraint firstAttribute=\"width\" constant=\"160\" id=\"Hxk-Ra-r7j\"/>\n                                                    <constraint firstAttribute=\"height\" constant=\"24\" id=\"gFf-7C-Qc1\"/>\n                                                </constraints>\n                                            </imageView>\n                                        </subviews>\n                                        <color key=\"backgroundColor\" red=\"0.0\" green=\"0.0\" blue=\"0.0\" alpha=\"0.50341497319999995\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                        <constraints>\n                                            <constraint firstItem=\"SdB-Ww-kKt\" firstAttribute=\"leading\" secondItem=\"RqP-HE-5qz\" secondAttribute=\"leading\" constant=\"16\" id=\"bI9-df-ZQV\"/>\n                                            <constraint firstAttribute=\"height\" constant=\"100\" id=\"hk5-85-52j\"/>\n                                            <constraint firstAttribute=\"bottom\" secondItem=\"SdB-Ww-kKt\" secondAttribute=\"bottom\" constant=\"20\" id=\"rP5-hM-Qxu\"/>\n                                        </constraints>\n                                    </view>\n                                </subviews>\n                                <color key=\"backgroundColor\" white=\"1\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"RqP-HE-5qz\" secondAttribute=\"trailing\" id=\"Glo-oM-bi1\"/>\n                                    <constraint firstItem=\"RqP-HE-5qz\" firstAttribute=\"leading\" secondItem=\"tyX-XX-qft\" secondAttribute=\"leading\" id=\"k9E-Lh-r1l\"/>\n                                </constraints>\n                            </view>\n                            <button hidden=\"YES\" opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" buttonType=\"system\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"0vh-Hu-Nap\">\n                                <rect key=\"frame\" x=\"131\" y=\"311\" width=\"113\" height=\"40\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"height\" constant=\"40\" id=\"c2m-U6-foP\"/>\n                                </constraints>\n                                <state key=\"normal\" title=\"Resume Session\"/>\n                                <connections>\n                                    <action selector=\"onClickResumeButton:\" destination=\"BYZ-38-t0r\" eventType=\"touchUpInside\" id=\"fcT-HY-cuf\"/>\n                                </connections>\n                            </button>\n                            <label hidden=\"YES\" opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Camera Unavailable\" textAlignment=\"center\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"LAO-dO-grc\">\n                                <rect key=\"frame\" x=\"20\" y=\"281\" width=\"335\" height=\"25\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"height\" constant=\"25\" id=\"MGk-Ei-Sj1\"/>\n                                </constraints>\n                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"22\"/>\n                                <nil key=\"textColor\"/>\n                                <nil key=\"highlightedColor\"/>\n                            </label>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ro1-YL-L1d\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"357\" width=\"375\" height=\"310\"/>\n                                <subviews>\n                                    <containerView opaque=\"NO\" contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"X6N-mc-Zhj\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"60\" width=\"375\" height=\"250\"/>\n                                        <connections>\n                                            <segue destination=\"H9B-4l-MbM\" kind=\"embed\" identifier=\"EMBED\" id=\"4W9-nV-kHJ\"/>\n                                        </connections>\n                                    </containerView>\n                                    <imageView userInteractionEnabled=\"NO\" contentMode=\"center\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" image=\"down_icon\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"rK2-DF-4PP\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"60\"/>\n                                        <constraints>\n                                            <constraint firstAttribute=\"height\" constant=\"60\" id=\"ZtV-QY-HAp\"/>\n                                        </constraints>\n                                    </imageView>\n                                </subviews>\n                                <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"rK2-DF-4PP\" secondAttribute=\"trailing\" id=\"7Zg-BX-3OO\"/>\n                                    <constraint firstItem=\"X6N-mc-Zhj\" firstAttribute=\"top\" secondItem=\"rK2-DF-4PP\" secondAttribute=\"bottom\" id=\"CJP-mI-gYQ\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"X6N-mc-Zhj\" secondAttribute=\"trailing\" id=\"Qze-3r-olX\"/>\n                                    <constraint firstItem=\"rK2-DF-4PP\" firstAttribute=\"leading\" secondItem=\"ro1-YL-L1d\" secondAttribute=\"leading\" id=\"bjF-ZT-RqW\"/>\n                                    <constraint firstAttribute=\"height\" constant=\"310\" id=\"epz-Pj-7Or\"/>\n                                    <constraint firstItem=\"rK2-DF-4PP\" firstAttribute=\"top\" secondItem=\"ro1-YL-L1d\" secondAttribute=\"top\" id=\"huJ-55-o7F\"/>\n                                    <constraint firstAttribute=\"bottom\" secondItem=\"X6N-mc-Zhj\" secondAttribute=\"bottom\" id=\"jBX-gf-oIh\"/>\n                                    <constraint firstItem=\"X6N-mc-Zhj\" firstAttribute=\"leading\" secondItem=\"ro1-YL-L1d\" secondAttribute=\"leading\" id=\"wEH-FP-f0P\"/>\n                                </constraints>\n                            </view>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"mWS-40-cTU\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"667\" width=\"375\" height=\"0.0\"/>\n                                <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\"/>\n                            </view>\n                        </subviews>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <constraints>\n                            <constraint firstItem=\"tyX-XX-qft\" firstAttribute=\"bottom\" secondItem=\"jhC-YW-J6Q\" secondAttribute=\"bottom\" id=\"33b-ks-zuf\"/>\n                            <constraint firstItem=\"6Tk-OE-BBY\" firstAttribute=\"top\" secondItem=\"RqP-HE-5qz\" secondAttribute=\"top\" constant=\"45\" id=\"6EF-3u-avy\"/>\n                            <constraint firstItem=\"mWS-40-cTU\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" id=\"7B7-Sj-FvV\"/>\n                            <constraint firstItem=\"6Tk-OE-BBY\" firstAttribute=\"trailing\" secondItem=\"jhC-YW-J6Q\" secondAttribute=\"trailing\" id=\"83W-Qu-4uJ\"/>\n                            <constraint firstItem=\"tyX-XX-qft\" firstAttribute=\"leading\" secondItem=\"jhC-YW-J6Q\" secondAttribute=\"leading\" id=\"DpY-T9-f2F\"/>\n                            <constraint firstItem=\"LAO-dO-grc\" firstAttribute=\"centerY\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"centerY\" constant=\"-40\" id=\"FEO-rc-Vc3\"/>\n                            <constraint firstItem=\"ro1-YL-L1d\" firstAttribute=\"leading\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"leading\" id=\"G8h-dz-zgv\"/>\n                            <constraint firstItem=\"6Tk-OE-BBY\" firstAttribute=\"trailing\" secondItem=\"mWS-40-cTU\" secondAttribute=\"trailing\" id=\"OD4-4d-xoQ\"/>\n                            <constraint firstItem=\"0vh-Hu-Nap\" firstAttribute=\"centerX\" secondItem=\"LAO-dO-grc\" secondAttribute=\"centerX\" id=\"OyR-JO-lnp\"/>\n                            <constraint firstItem=\"mWS-40-cTU\" firstAttribute=\"top\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"bottom\" id=\"Ps0-W0-FHt\"/>\n                            <constraint firstItem=\"6Tk-OE-BBY\" firstAttribute=\"bottom\" secondItem=\"ro1-YL-L1d\" secondAttribute=\"bottom\" id=\"Pt9-ax-F66\"/>\n                            <constraint firstItem=\"6Tk-OE-BBY\" firstAttribute=\"trailing\" secondItem=\"LAO-dO-grc\" secondAttribute=\"trailing\" constant=\"20\" id=\"SqP-RR-Lae\"/>\n                            <constraint firstItem=\"tyX-XX-qft\" firstAttribute=\"trailing\" secondItem=\"jhC-YW-J6Q\" secondAttribute=\"trailing\" id=\"Tgb-iQ-X7H\"/>\n                            <constraint firstItem=\"6Tk-OE-BBY\" firstAttribute=\"bottom\" secondItem=\"jhC-YW-J6Q\" secondAttribute=\"bottom\" id=\"WOf-3d-YjE\"/>\n                            <constraint firstItem=\"jhC-YW-J6Q\" firstAttribute=\"top\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"top\" constant=\"-20\" id=\"X8h-iK-TdO\"/>\n                            <constraint firstItem=\"LAO-dO-grc\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" constant=\"20\" id=\"byg-sW-1q4\"/>\n                            <constraint firstItem=\"0vh-Hu-Nap\" firstAttribute=\"top\" secondItem=\"LAO-dO-grc\" secondAttribute=\"bottom\" constant=\"5\" id=\"cqs-kM-ItJ\"/>\n                            <constraint firstItem=\"jhC-YW-J6Q\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" id=\"g3H-D2-exL\"/>\n                            <constraint firstAttribute=\"trailing\" secondItem=\"ro1-YL-L1d\" secondAttribute=\"trailing\" id=\"oNX-Pa-cBX\"/>\n                            <constraint firstAttribute=\"bottom\" secondItem=\"mWS-40-cTU\" secondAttribute=\"bottom\" id=\"pwF-4W-qcH\"/>\n                            <constraint firstItem=\"tyX-XX-qft\" firstAttribute=\"top\" secondItem=\"jhC-YW-J6Q\" secondAttribute=\"top\" id=\"zsz-Cm-DI8\"/>\n                        </constraints>\n                    </view>\n                    <connections>\n                        <outlet property=\"bottomSheetStateImageView\" destination=\"rK2-DF-4PP\" id=\"ggN-Ph-0VK\"/>\n                        <outlet property=\"bottomSheetView\" destination=\"ro1-YL-L1d\" id=\"Hqc-5T-rzo\"/>\n                        <outlet property=\"bottomSheetViewBottomSpace\" destination=\"Pt9-ax-F66\" id=\"VIm-Zg-hkb\"/>\n                        <outlet property=\"cameraUnavailableLabel\" destination=\"LAO-dO-grc\" id=\"3qR-EM-aQ5\"/>\n                        <outlet property=\"overlayView\" destination=\"tyX-XX-qft\" id=\"h55-V9-Wep\"/>\n                        <outlet property=\"previewView\" destination=\"jhC-YW-J6Q\" id=\"tyJ-nv-oXG\"/>\n                        <outlet property=\"resumeButton\" destination=\"0vh-Hu-Nap\" id=\"QcK-hL-R17\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"38\" y=\"72\"/>\n        </scene>\n        <!--Inference View Controller-->\n        <scene sceneID=\"3Ap-WK-W08\">\n            <objects>\n                <viewController id=\"H9B-4l-MbM\" customClass=\"InferenceViewController\" customModule=\"ObjectDetection\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"rF4-XE-YOF\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"250\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <tableView clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" misplaced=\"YES\" alwaysBounceVertical=\"YES\" scrollEnabled=\"NO\" dataMode=\"prototypes\" style=\"plain\" separatorStyle=\"none\" allowsSelection=\"NO\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"28\" sectionFooterHeight=\"28\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"OmF-uK-ZDW\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"87\"/>\n                                <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                <prototypes>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" reuseIdentifier=\"INFO_CELL\" rowHeight=\"43\" id=\"uTx-Z2-72z\" customClass=\"InfoCell\" customModule=\"ObjectDetection\" customModuleProvider=\"target\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"44.5\" width=\"375\" height=\"43\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"uTx-Z2-72z\" id=\"evn-BS-Oui\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"43\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Label\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" numberOfLines=\"0\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"K0V-Lx-jBW\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"5\" width=\"36\" height=\"17\"/>\n                                                    <constraints>\n                                                        <constraint firstAttribute=\"height\" constant=\"17\" id=\"BSU-HF-GiQ\"/>\n                                                    </constraints>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" weight=\"medium\" pointSize=\"14\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                                <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Label\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"C2h-xX-LNV\">\n                                                    <rect key=\"frame\" x=\"323\" y=\"5\" width=\"36\" height=\"17\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" weight=\"medium\" pointSize=\"14\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                                <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"vDk-7g-1IF\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"42\" width=\"343\" height=\"1\"/>\n                                                    <color key=\"backgroundColor\" red=\"0.45882352939999999\" green=\"0.45882352939999999\" blue=\"0.45882352939999999\" alpha=\"0.19554016490000001\" colorSpace=\"custom\" customColorSpace=\"calibratedRGB\"/>\n                                                    <constraints>\n                                                        <constraint firstAttribute=\"height\" constant=\"1\" id=\"NSt-2w-7Bj\"/>\n                                                    </constraints>\n                                                </view>\n                                            </subviews>\n                                            <constraints>\n                                                <constraint firstItem=\"C2h-xX-LNV\" firstAttribute=\"centerY\" secondItem=\"K0V-Lx-jBW\" secondAttribute=\"centerY\" id=\"BVc-VW-fYZ\"/>\n                                                <constraint firstAttribute=\"trailing\" secondItem=\"C2h-xX-LNV\" secondAttribute=\"trailing\" constant=\"16\" id=\"JcL-Nn-KHT\"/>\n                                                <constraint firstItem=\"K0V-Lx-jBW\" firstAttribute=\"top\" secondItem=\"evn-BS-Oui\" secondAttribute=\"top\" constant=\"5\" id=\"Jlc-zT-VVX\"/>\n                                                <constraint firstItem=\"K0V-Lx-jBW\" firstAttribute=\"leading\" secondItem=\"evn-BS-Oui\" secondAttribute=\"leading\" constant=\"16\" id=\"YC9-TX-y68\"/>\n                                                <constraint firstAttribute=\"trailing\" secondItem=\"vDk-7g-1IF\" secondAttribute=\"trailing\" constant=\"16\" id=\"f4U-Vu-5dS\"/>\n                                                <constraint firstItem=\"C2h-xX-LNV\" firstAttribute=\"leading\" relation=\"greaterThanOrEqual\" secondItem=\"K0V-Lx-jBW\" secondAttribute=\"trailing\" constant=\"8\" symbolic=\"YES\" id=\"j8l-3h-r2l\"/>\n                                                <constraint firstItem=\"vDk-7g-1IF\" firstAttribute=\"top\" secondItem=\"K0V-Lx-jBW\" secondAttribute=\"bottom\" constant=\"20\" id=\"jeR-OX-qk0\"/>\n                                                <constraint firstItem=\"vDk-7g-1IF\" firstAttribute=\"leading\" secondItem=\"evn-BS-Oui\" secondAttribute=\"leading\" constant=\"16\" id=\"v5E-vu-ku7\"/>\n                                            </constraints>\n                                        </tableViewCellContentView>\n                                        <connections>\n                                            <outlet property=\"fieldNameLabel\" destination=\"K0V-Lx-jBW\" id=\"Mda-XH-W4Q\"/>\n                                            <outlet property=\"infoLabel\" destination=\"C2h-xX-LNV\" id=\"h2j-9d-PLA\"/>\n                                        </connections>\n                                    </tableViewCell>\n                                </prototypes>\n                                <connections>\n                                    <outlet property=\"dataSource\" destination=\"H9B-4l-MbM\" id=\"ZeX-gj-mOH\"/>\n                                    <outlet property=\"delegate\" destination=\"H9B-4l-MbM\" id=\"hNo-Q5-u4z\"/>\n                                </connections>\n                            </tableView>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"JY9-ih-gmV\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"90\" width=\"375\" height=\"160\"/>\n                                <subviews>\n                                    <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"c6b-6a-7dz\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"80\" width=\"375\" height=\"40\"/>\n                                        <subviews>\n                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Threads\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ViR-yL-073\">\n                                                <rect key=\"frame\" x=\"20\" y=\"11.5\" width=\"53\" height=\"17\"/>\n                                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                                <nil key=\"textColor\"/>\n                                                <nil key=\"highlightedColor\"/>\n                                            </label>\n                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"1\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" numberOfLines=\"0\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"oFM-QA-mgz\">\n                                                <rect key=\"frame\" x=\"246.5\" y=\"11.5\" width=\"6.5\" height=\"17\"/>\n                                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                                <nil key=\"textColor\"/>\n                                                <nil key=\"highlightedColor\"/>\n                                            </label>\n                                            <stepper opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" maximumValue=\"100\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"tjf-Ot-aGc\">\n                                                <rect key=\"frame\" x=\"265\" y=\"4\" width=\"94\" height=\"32\"/>\n                                                <color key=\"tintColor\" red=\"0.45882352939999999\" green=\"0.45882352939999999\" blue=\"0.45882352939999999\" alpha=\"1\" colorSpace=\"calibratedRGB\"/>\n                                                <connections>\n                                                    <action selector=\"threadStepperValueChanged:\" destination=\"H9B-4l-MbM\" eventType=\"valueChanged\" id=\"HDM-Fx-1ye\"/>\n                                                </connections>\n                                            </stepper>\n                                        </subviews>\n                                        <constraints>\n                                            <constraint firstItem=\"ViR-yL-073\" firstAttribute=\"leading\" secondItem=\"c6b-6a-7dz\" secondAttribute=\"leading\" constant=\"20\" id=\"BU1-gw-8Nn\"/>\n                                            <constraint firstItem=\"tjf-Ot-aGc\" firstAttribute=\"leading\" secondItem=\"oFM-QA-mgz\" secondAttribute=\"trailing\" constant=\"12\" id=\"OYP-HE-4oQ\"/>\n                                            <constraint firstItem=\"ViR-yL-073\" firstAttribute=\"centerY\" secondItem=\"c6b-6a-7dz\" secondAttribute=\"centerY\" id=\"Pdj-To-kJB\"/>\n                                            <constraint firstItem=\"tjf-Ot-aGc\" firstAttribute=\"centerY\" secondItem=\"c6b-6a-7dz\" secondAttribute=\"centerY\" id=\"YOr-gj-tlD\"/>\n                                            <constraint firstAttribute=\"trailing\" secondItem=\"tjf-Ot-aGc\" secondAttribute=\"trailing\" constant=\"16\" id=\"b3y-OO-sQx\"/>\n                                            <constraint firstAttribute=\"height\" constant=\"40\" id=\"gDE-m6-af1\"/>\n                                            <constraint firstItem=\"oFM-QA-mgz\" firstAttribute=\"centerY\" secondItem=\"ViR-yL-073\" secondAttribute=\"centerY\" id=\"m3y-NY-nSC\"/>\n                                            <constraint firstItem=\"oFM-QA-mgz\" firstAttribute=\"leading\" relation=\"greaterThanOrEqual\" secondItem=\"ViR-yL-073\" secondAttribute=\"trailing\" id=\"mXr-le-h26\"/>\n                                        </constraints>\n                                    </view>\n                                    <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"BM3-mq-cys\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"40\"/>\n                                        <subviews>\n                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Threshold\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"byr-fp-V4Q\">\n                                                <rect key=\"frame\" x=\"20\" y=\"11.5\" width=\"65\" height=\"17\"/>\n                                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                                <nil key=\"textColor\"/>\n                                                <nil key=\"highlightedColor\"/>\n                                            </label>\n                                            <stepper opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" value=\"0.10000000000000001\" minimumValue=\"0.10000000000000001\" maximumValue=\"0.90000000000000002\" stepValue=\"0.10000000000000001\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"hkg-IE-xIk\">\n                                                <rect key=\"frame\" x=\"265\" y=\"4\" width=\"94\" height=\"32\"/>\n                                                <connections>\n                                                    <action selector=\"thresholdStepperValueChanged:\" destination=\"H9B-4l-MbM\" eventType=\"valueChanged\" id=\"WnF-I7-JzX\"/>\n                                                </connections>\n                                            </stepper>\n                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"0.1\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"bs6-rC-zVA\">\n                                                <rect key=\"frame\" x=\"234\" y=\"11.5\" width=\"19\" height=\"17\"/>\n                                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                                <nil key=\"textColor\"/>\n                                                <nil key=\"highlightedColor\"/>\n                                            </label>\n                                        </subviews>\n                                        <constraints>\n                                            <constraint firstItem=\"hkg-IE-xIk\" firstAttribute=\"centerY\" secondItem=\"BM3-mq-cys\" secondAttribute=\"centerY\" id=\"0k9-nh-X10\"/>\n                                            <constraint firstItem=\"byr-fp-V4Q\" firstAttribute=\"centerY\" secondItem=\"BM3-mq-cys\" secondAttribute=\"centerY\" id=\"Dhd-Lu-qQX\"/>\n                                            <constraint firstItem=\"bs6-rC-zVA\" firstAttribute=\"centerY\" secondItem=\"byr-fp-V4Q\" secondAttribute=\"centerY\" id=\"PTc-28-Caq\"/>\n                                            <constraint firstItem=\"byr-fp-V4Q\" firstAttribute=\"leading\" secondItem=\"BM3-mq-cys\" secondAttribute=\"leading\" constant=\"20\" id=\"UNJ-Y4-dxW\"/>\n                                            <constraint firstItem=\"hkg-IE-xIk\" firstAttribute=\"leading\" secondItem=\"bs6-rC-zVA\" secondAttribute=\"trailing\" constant=\"12\" id=\"aNP-ON-Hf5\"/>\n                                            <constraint firstAttribute=\"trailing\" secondItem=\"hkg-IE-xIk\" secondAttribute=\"trailing\" constant=\"16\" id=\"oVi-Bu-hDH\"/>\n                                            <constraint firstAttribute=\"height\" constant=\"40\" id=\"pkH-2O-Vrw\"/>\n                                        </constraints>\n                                    </view>\n                                    <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"20M-wU-X13\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"40\" width=\"375\" height=\"40\"/>\n                                        <subviews>\n                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Max results\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"aO3-V9-0jw\">\n                                                <rect key=\"frame\" x=\"20\" y=\"11.5\" width=\"74.5\" height=\"17\"/>\n                                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                                <nil key=\"textColor\"/>\n                                                <nil key=\"highlightedColor\"/>\n                                            </label>\n                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"0\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"lhF-t1-Ric\">\n                                                <rect key=\"frame\" x=\"244\" y=\"11.5\" width=\"9\" height=\"17\"/>\n                                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                                <nil key=\"textColor\"/>\n                                                <nil key=\"highlightedColor\"/>\n                                            </label>\n                                            <stepper opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" value=\"1\" minimumValue=\"1\" maximumValue=\"10\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"bxf-E3-PB4\">\n                                                <rect key=\"frame\" x=\"265\" y=\"4\" width=\"94\" height=\"32\"/>\n                                                <connections>\n                                                    <action selector=\"maxResultStepperValueChanged:\" destination=\"H9B-4l-MbM\" eventType=\"valueChanged\" id=\"pSf-fy-rTq\"/>\n                                                </connections>\n                                            </stepper>\n                                        </subviews>\n                                        <constraints>\n                                            <constraint firstAttribute=\"height\" constant=\"40\" id=\"RDA-Jp-ecG\"/>\n                                            <constraint firstAttribute=\"trailing\" secondItem=\"bxf-E3-PB4\" secondAttribute=\"trailing\" constant=\"16\" id=\"Ufd-5i-seR\"/>\n                                            <constraint firstItem=\"bxf-E3-PB4\" firstAttribute=\"centerY\" secondItem=\"20M-wU-X13\" secondAttribute=\"centerY\" id=\"VMa-Un-SZq\"/>\n                                            <constraint firstItem=\"aO3-V9-0jw\" firstAttribute=\"centerY\" secondItem=\"20M-wU-X13\" secondAttribute=\"centerY\" id=\"ZCG-Z9-Gef\"/>\n                                            <constraint firstItem=\"bxf-E3-PB4\" firstAttribute=\"leading\" secondItem=\"lhF-t1-Ric\" secondAttribute=\"trailing\" constant=\"12\" id=\"bo3-o2-ThL\"/>\n                                            <constraint firstItem=\"aO3-V9-0jw\" firstAttribute=\"leading\" secondItem=\"20M-wU-X13\" secondAttribute=\"leading\" constant=\"20\" id=\"diD-Zu-S1A\"/>\n                                            <constraint firstItem=\"lhF-t1-Ric\" firstAttribute=\"centerY\" secondItem=\"aO3-V9-0jw\" secondAttribute=\"centerY\" id=\"qVH-AH-mrk\"/>\n                                        </constraints>\n                                    </view>\n                                    <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"zBs-fb-XKZ\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"120\" width=\"375\" height=\"40\"/>\n                                        <subviews>\n                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"ML Model\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"xyS-qD-i7t\">\n                                                <rect key=\"frame\" x=\"20\" y=\"11.5\" width=\"64\" height=\"17\"/>\n                                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                                <nil key=\"textColor\"/>\n                                                <nil key=\"highlightedColor\"/>\n                                            </label>\n                                            <textField opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"left\" contentVerticalAlignment=\"center\" borderStyle=\"roundedRect\" textAlignment=\"natural\" minimumFontSize=\"17\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"koN-u7-PyO\">\n                                                <rect key=\"frame\" x=\"219\" y=\"3\" width=\"140\" height=\"34\"/>\n                                                <constraints>\n                                                    <constraint firstAttribute=\"width\" constant=\"140\" id=\"xUZ-99-t8r\"/>\n                                                </constraints>\n                                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                                <textInputTraits key=\"textInputTraits\"/>\n                                            </textField>\n                                        </subviews>\n                                        <constraints>\n                                            <constraint firstItem=\"xyS-qD-i7t\" firstAttribute=\"leading\" secondItem=\"zBs-fb-XKZ\" secondAttribute=\"leading\" constant=\"20\" id=\"Idb-b3-oVe\"/>\n                                            <constraint firstItem=\"xyS-qD-i7t\" firstAttribute=\"centerY\" secondItem=\"zBs-fb-XKZ\" secondAttribute=\"centerY\" id=\"Ke9-ih-0Cc\"/>\n                                            <constraint firstItem=\"koN-u7-PyO\" firstAttribute=\"centerY\" secondItem=\"zBs-fb-XKZ\" secondAttribute=\"centerY\" id=\"kMM-5z-iIQ\"/>\n                                            <constraint firstAttribute=\"height\" constant=\"40\" id=\"tmb-L5-BdH\"/>\n                                            <constraint firstAttribute=\"trailing\" secondItem=\"koN-u7-PyO\" secondAttribute=\"trailing\" constant=\"16\" id=\"zfv-Kc-BcR\"/>\n                                        </constraints>\n                                    </view>\n                                </subviews>\n                                <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                <constraints>\n                                    <constraint firstItem=\"BM3-mq-cys\" firstAttribute=\"top\" secondItem=\"JY9-ih-gmV\" secondAttribute=\"top\" id=\"4qX-bn-IFp\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"BM3-mq-cys\" secondAttribute=\"trailing\" id=\"97f-VW-CT6\"/>\n                                    <constraint firstAttribute=\"bottom\" secondItem=\"zBs-fb-XKZ\" secondAttribute=\"bottom\" id=\"DgB-7Z-tyG\"/>\n                                    <constraint firstItem=\"c6b-6a-7dz\" firstAttribute=\"leading\" secondItem=\"JY9-ih-gmV\" secondAttribute=\"leading\" id=\"Fd8-dB-3IE\"/>\n                                    <constraint firstItem=\"BM3-mq-cys\" firstAttribute=\"leading\" secondItem=\"JY9-ih-gmV\" secondAttribute=\"leading\" id=\"KhM-Hq-edb\"/>\n                                    <constraint firstItem=\"zBs-fb-XKZ\" firstAttribute=\"top\" secondItem=\"c6b-6a-7dz\" secondAttribute=\"bottom\" id=\"L6B-9W-heS\"/>\n                                    <constraint firstItem=\"c6b-6a-7dz\" firstAttribute=\"top\" secondItem=\"20M-wU-X13\" secondAttribute=\"bottom\" id=\"Okf-Qc-6AD\"/>\n                                    <constraint firstItem=\"zBs-fb-XKZ\" firstAttribute=\"leading\" secondItem=\"JY9-ih-gmV\" secondAttribute=\"leading\" id=\"Rg5-Pj-Zos\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"zBs-fb-XKZ\" secondAttribute=\"trailing\" id=\"Suh-YA-eda\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"c6b-6a-7dz\" secondAttribute=\"trailing\" id=\"WcB-9k-6nu\"/>\n                                    <constraint firstItem=\"20M-wU-X13\" firstAttribute=\"top\" secondItem=\"BM3-mq-cys\" secondAttribute=\"bottom\" id=\"fJp-vj-10g\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"20M-wU-X13\" secondAttribute=\"trailing\" id=\"gye-jM-8N7\"/>\n                                    <constraint firstItem=\"20M-wU-X13\" firstAttribute=\"leading\" secondItem=\"JY9-ih-gmV\" secondAttribute=\"leading\" id=\"y6I-lE-Qgu\"/>\n                                </constraints>\n                            </view>\n                        </subviews>\n                        <viewLayoutGuide key=\"safeArea\" id=\"FWg-KA-dA9\"/>\n                        <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                        <constraints>\n                            <constraint firstItem=\"OmF-uK-ZDW\" firstAttribute=\"top\" secondItem=\"FWg-KA-dA9\" secondAttribute=\"top\" id=\"8VJ-r5-VYG\"/>\n                            <constraint firstItem=\"JY9-ih-gmV\" firstAttribute=\"trailing\" secondItem=\"FWg-KA-dA9\" secondAttribute=\"trailing\" id=\"ENR-BD-Rm9\"/>\n                            <constraint firstItem=\"OmF-uK-ZDW\" firstAttribute=\"leading\" secondItem=\"FWg-KA-dA9\" secondAttribute=\"leading\" id=\"NtT-nX-A43\"/>\n                            <constraint firstItem=\"JY9-ih-gmV\" firstAttribute=\"leading\" secondItem=\"FWg-KA-dA9\" secondAttribute=\"leading\" id=\"WXu-Pd-g8S\"/>\n                            <constraint firstItem=\"JY9-ih-gmV\" firstAttribute=\"bottom\" secondItem=\"rF4-XE-YOF\" secondAttribute=\"bottom\" id=\"Z8O-Pa-wJd\"/>\n                            <constraint firstItem=\"JY9-ih-gmV\" firstAttribute=\"top\" secondItem=\"OmF-uK-ZDW\" secondAttribute=\"bottom\" id=\"dy2-u5-DsN\"/>\n                            <constraint firstItem=\"OmF-uK-ZDW\" firstAttribute=\"trailing\" secondItem=\"FWg-KA-dA9\" secondAttribute=\"trailing\" id=\"rwX-FO-1hT\"/>\n                        </constraints>\n                    </view>\n                    <connections>\n                        <outlet property=\"maxResultLabel\" destination=\"lhF-t1-Ric\" id=\"WtO-aY-5G6\"/>\n                        <outlet property=\"maxResultStepper\" destination=\"bxf-E3-PB4\" id=\"wjG-Rq-I0F\"/>\n                        <outlet property=\"modelTextField\" destination=\"koN-u7-PyO\" id=\"hZV-l4-757\"/>\n                        <outlet property=\"tableView\" destination=\"OmF-uK-ZDW\" id=\"Amg-Wu-i1M\"/>\n                        <outlet property=\"threadStepper\" destination=\"tjf-Ot-aGc\" id=\"RSC-HE-X6G\"/>\n                        <outlet property=\"threadValueLabel\" destination=\"oFM-QA-mgz\" id=\"fB2-fI-IkF\"/>\n                        <outlet property=\"thresholdLabel\" destination=\"bs6-rC-zVA\" id=\"XTE-PG-iEv\"/>\n                        <outlet property=\"thresholdStepper\" destination=\"hkg-IE-xIk\" id=\"cWp-Gp-ePV\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"cBg-Ov-BS7\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"1480.8\" y=\"316.64167916041981\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"down_icon\" width=\"20\" height=\"6\"/>\n        <image name=\"tfl_logo\" width=\"265.5\" height=\"42.5\"/>\n        <systemColor name=\"systemBackgroundColor\">\n            <color white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n        </systemColor>\n    </resources>\n</document>\n"
  },
  {
    "path": "lite/examples/object_detection/ios/ObjectDetection/Camera Feed/CameraFeedManager.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\nimport AVFoundation\n\n// MARK: CameraFeedManagerDelegate Declaration\nprotocol CameraFeedManagerDelegate: AnyObject {\n\n  /**\n   This method delivers the pixel buffer of the current frame seen by the device's camera.\n   */\n  func didOutput(pixelBuffer: CVPixelBuffer)\n\n  /**\n   This method intimates that the camera permissions have been denied.\n   */\n  func presentCameraPermissionsDeniedAlert()\n\n  /**\n   This method intimates that there was an error in video configuration.\n   */\n  func presentVideoConfigurationErrorAlert()\n\n  /**\n   This method intimates that a session runtime error occurred.\n   */\n  func sessionRunTimeErrorOccurred()\n\n  /**\n   This method intimates that the session was interrupted.\n   */\n  func sessionWasInterrupted(canResumeManually resumeManually: Bool)\n\n  /**\n   This method intimates that the session interruption has ended.\n   */\n  func sessionInterruptionEnded()\n\n}\n\n/**\n This enum holds the state of the camera initialization.\n */\nenum CameraConfiguration {\n  case success\n  case failed\n  case permissionDenied\n}\n\n/**\n This class manages all camera related functionality\n */\nclass CameraFeedManager: NSObject {\n\n  // MARK: Camera Related Instance Variables\n  private let session: AVCaptureSession = AVCaptureSession()\n  private let previewView: PreviewView\n  private let sessionQueue = DispatchQueue(label: \"org.tensorflow.lite.sessionQueue\")\n  private var cameraConfiguration: CameraConfiguration = .failed\n  private lazy var videoDataOutput = AVCaptureVideoDataOutput()\n  private var isSessionRunning = false\n\n  // MARK: CameraFeedManagerDelegate\n  weak var delegate: CameraFeedManagerDelegate?\n\n  // MARK: Initializer\n  init(previewView: PreviewView) {\n    self.previewView = previewView\n    super.init()\n\n    // Initializes the session\n    session.sessionPreset = .high\n    self.previewView.session = session\n    self.previewView.previewLayer.connection?.videoOrientation = .portrait\n    self.previewView.previewLayer.videoGravity = .resizeAspectFill\n    self.attemptToConfigureSession()\n  }\n\n  // MARK: Session Start and End methods\n\n  /**\n   This method starts an AVCaptureSession based on whether the camera configuration was successful.\n   */\n  func checkCameraConfigurationAndStartSession() {\n    sessionQueue.async {\n      switch self.cameraConfiguration {\n      case .success:\n        self.addObservers()\n        self.startSession()\n      case .failed:\n        DispatchQueue.main.async {\n          self.delegate?.presentVideoConfigurationErrorAlert()\n        }\n      case .permissionDenied:\n        DispatchQueue.main.async {\n          self.delegate?.presentCameraPermissionsDeniedAlert()\n        }\n      }\n    }\n  }\n\n  /**\n   This method stops a running an AVCaptureSession.\n   */\n  func stopSession() {\n    self.removeObservers()\n    sessionQueue.async {\n      if self.session.isRunning {\n        self.session.stopRunning()\n        self.isSessionRunning = self.session.isRunning\n      }\n    }\n\n  }\n\n  /**\n   This method resumes an interrupted AVCaptureSession.\n   */\n  func resumeInterruptedSession(withCompletion completion: @escaping (Bool) -> ()) {\n\n    sessionQueue.async {\n      self.startSession()\n\n      DispatchQueue.main.async {\n        completion(self.isSessionRunning)\n      }\n    }\n  }\n\n  /**\n   This method starts the AVCaptureSession\n   **/\n  private func startSession() {\n    self.session.startRunning()\n    self.isSessionRunning = self.session.isRunning\n  }\n\n  // MARK: Session Configuration Methods.\n  /**\n   This method requests for camera permissions and handles the configuration of the session and stores the result of configuration.\n   */\n  private func attemptToConfigureSession() {\n    switch AVCaptureDevice.authorizationStatus(for: .video) {\n    case .authorized:\n      self.cameraConfiguration = .success\n    case .notDetermined:\n      self.sessionQueue.suspend()\n      self.requestCameraAccess(completion: { (granted) in\n        self.sessionQueue.resume()\n      })\n    case .denied:\n      self.cameraConfiguration = .permissionDenied\n    default:\n      break\n    }\n\n    self.sessionQueue.async {\n      self.configureSession()\n    }\n  }\n\n  /**\n   This method requests for camera permissions.\n   */\n  private func requestCameraAccess(completion: @escaping (Bool) -> ()) {\n    AVCaptureDevice.requestAccess(for: .video) { (granted) in\n      if !granted {\n        self.cameraConfiguration = .permissionDenied\n      }\n      else {\n        self.cameraConfiguration = .success\n      }\n      completion(granted)\n    }\n  }\n\n\n  /**\n   This method handles all the steps to configure an AVCaptureSession.\n   */\n  private func configureSession() {\n\n    guard cameraConfiguration == .success else {\n      return\n    }\n    session.beginConfiguration()\n\n    // Tries to add an AVCaptureDeviceInput.\n    guard addVideoDeviceInput() == true else {\n      self.session.commitConfiguration()\n      self.cameraConfiguration = .failed\n      return\n    }\n\n    // Tries to add an AVCaptureVideoDataOutput.\n    guard addVideoDataOutput() else {\n      self.session.commitConfiguration()\n      self.cameraConfiguration = .failed\n      return\n    }\n\n    session.commitConfiguration()\n    self.cameraConfiguration = .success\n  }\n\n  /**\n   This method tries to add an AVCaptureDeviceInput to the current AVCaptureSession.\n   */\n  private func addVideoDeviceInput() -> Bool {\n\n    /**Tries to get the default back camera.\n     */\n    guard let camera  = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else {\n      fatalError(\"Cannot find camera\")\n    }\n\n    do {\n      let videoDeviceInput = try AVCaptureDeviceInput(device: camera)\n      if session.canAddInput(videoDeviceInput) {\n        session.addInput(videoDeviceInput)\n        return true\n      }\n      else {\n        return false\n      }\n    }\n    catch {\n      fatalError(\"Cannot create video device input\")\n    }\n  }\n\n  /**\n   This method tries to add an AVCaptureVideoDataOutput to the current AVCaptureSession.\n   */\n  private func addVideoDataOutput() -> Bool {\n\n    let sampleBufferQueue = DispatchQueue(label: \"sampleBufferQueue\")\n    videoDataOutput.setSampleBufferDelegate(self, queue: sampleBufferQueue)\n    videoDataOutput.alwaysDiscardsLateVideoFrames = true\n    videoDataOutput.videoSettings = [ String(kCVPixelBufferPixelFormatTypeKey) : kCMPixelFormat_32BGRA]\n\n    if session.canAddOutput(videoDataOutput) {\n      session.addOutput(videoDataOutput)\n      videoDataOutput.connection(with: .video)?.videoOrientation = .portrait\n      return true\n    }\n    return false\n  }\n\n  // MARK: Notification Observer Handling\n  private func addObservers() {\n    NotificationCenter.default.addObserver(self, selector: #selector(CameraFeedManager.sessionRuntimeErrorOccurred(notification:)), name: NSNotification.Name.AVCaptureSessionRuntimeError, object: session)\n    NotificationCenter.default.addObserver(self, selector: #selector(CameraFeedManager.sessionWasInterrupted(notification:)), name: NSNotification.Name.AVCaptureSessionWasInterrupted, object: session)\n    NotificationCenter.default.addObserver(self, selector: #selector(CameraFeedManager.sessionInterruptionEnded), name: NSNotification.Name.AVCaptureSessionInterruptionEnded, object: session)\n  }\n\n  private func removeObservers() {\n    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVCaptureSessionRuntimeError, object: session)\n    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVCaptureSessionWasInterrupted, object: session)\n    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVCaptureSessionInterruptionEnded, object: session)\n  }\n\n  // MARK: Notification Observers\n  @objc func sessionWasInterrupted(notification: Notification) {\n\n    if let userInfoValue = notification.userInfo?[AVCaptureSessionInterruptionReasonKey] as AnyObject?,\n      let reasonIntegerValue = userInfoValue.integerValue,\n      let reason = AVCaptureSession.InterruptionReason(rawValue: reasonIntegerValue) {\n      print(\"Capture session was interrupted with reason \\(reason)\")\n\n      var canResumeManually = false\n      if reason == .videoDeviceInUseByAnotherClient {\n        canResumeManually = true\n      } else if reason == .videoDeviceNotAvailableWithMultipleForegroundApps {\n        canResumeManually = false\n      }\n\n      self.delegate?.sessionWasInterrupted(canResumeManually: canResumeManually)\n\n    }\n  }\n\n  @objc func sessionInterruptionEnded(notification: Notification) {\n\n    self.delegate?.sessionInterruptionEnded()\n  }\n\n  @objc func sessionRuntimeErrorOccurred(notification: Notification) {\n    guard let error = notification.userInfo?[AVCaptureSessionErrorKey] as? AVError else {\n      return\n    }\n\n    print(\"Capture session runtime error: \\(error)\")\n\n    if error.code == .mediaServicesWereReset {\n      sessionQueue.async {\n        if self.isSessionRunning {\n          self.startSession()\n        } else {\n          DispatchQueue.main.async {\n            self.delegate?.sessionRunTimeErrorOccurred()\n          }\n        }\n      }\n    } else {\n      self.delegate?.sessionRunTimeErrorOccurred()\n\n    }\n  }\n}\n\n\n/**\n AVCaptureVideoDataOutputSampleBufferDelegate\n */\nextension CameraFeedManager: AVCaptureVideoDataOutputSampleBufferDelegate {\n\n  /** This method delegates the CVPixelBuffer of the frame seen by the camera currently.\n   */\n  func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {\n\n    // Converts the CMSampleBuffer to a CVPixelBuffer.\n    let pixelBuffer: CVPixelBuffer? = CMSampleBufferGetImageBuffer(sampleBuffer)\n\n    guard let imagePixelBuffer = pixelBuffer else {\n      return\n    }\n\n    // Delegates the pixel buffer to the ViewController.\n    delegate?.didOutput(pixelBuffer: imagePixelBuffer)\n  }\n\n}\n"
  },
  {
    "path": "lite/examples/object_detection/ios/ObjectDetection/Camera Feed/PreviewView.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\nimport AVFoundation\n\n/**\n The camera frame is displayed on this view.\n */\nclass PreviewView: UIView {\n\n  var previewLayer: AVCaptureVideoPreviewLayer {\n    guard let layer = layer as? AVCaptureVideoPreviewLayer else {\n      fatalError(\"Layer expected is of type VideoPreviewLayer\")\n    }\n    return layer\n  }\n\n  var session: AVCaptureSession? {\n    get {\n      return previewLayer.session\n    }\n    set {\n      previewLayer.session = newValue\n    }\n  }\n\n  override class var layerClass: AnyClass {\n    return AVCaptureVideoPreviewLayer.self\n  }\n}\n"
  },
  {
    "path": "lite/examples/object_detection/ios/ObjectDetection/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>TFL Detect</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>NSCameraUsageDescription</key>\n\t<string>This app will use camera to detect objects around you.</string>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UIRequiresFullScreen</key>\n\t<true/>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UIUserInterfaceStyle</key>\n\t<string>Light</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "lite/examples/object_detection/ios/ObjectDetection/TFLite/ObjectDetectionHelper.swift",
    "content": "// Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport TensorFlowLiteTaskVision\n\n/// Stores results for a particular frame that was successfully run through the `Interpreter`.\nstruct Result {\n  let inferenceTime: Double\n  let detections: [Detection]\n}\n\n/// Information about a model file or labels file.\ntypealias FileInfo = (name: String, extension: String)\n\n/// This class handles all data preprocessing and makes calls to run inference on a given frame\n/// by invoking the `ObjectDetector`.\nclass ObjectDetectionHelper: NSObject {\n\n  // MARK: Private properties\n\n  /// TensorFlow Lite `ObjectDetector` object for performing object detection using a given model.\n  private var detector: ObjectDetector\n\n  private let colors = [\n    UIColor.black,  // 0.0 white\n    UIColor.darkGray,  // 0.333 white\n    UIColor.lightGray,  // 0.667 white\n    UIColor.white,  // 1.0 white\n    UIColor.gray,  // 0.5 white\n    UIColor.red,  // 1.0, 0.0, 0.0 RGB\n    UIColor.green,  // 0.0, 1.0, 0.0 RGB\n    UIColor.blue,  // 0.0, 0.0, 1.0 RGB\n    UIColor.cyan,  // 0.0, 1.0, 1.0 RGB\n    UIColor.yellow,  // 1.0, 1.0, 0.0 RGB\n    UIColor.magenta,  // 1.0, 0.0, 1.0 RGB\n    UIColor.orange,  // 1.0, 0.5, 0.0 RGB\n    UIColor.purple,  // 0.5, 0.0, 0.5 RGB\n    UIColor.brown,  // 0.6, 0.4, 0.2 RGB\n  ]\n\n  // MARK: - Initialization\n\n  /// A failable initializer for `ObjectDetectionHelper`.\n  ///\n  /// - Parameter modelFileInfo: The TFLite model to be used.\n  /// - Parameter:\n  ///   - threadCount: Number of threads to be used.\n  ///   - scoreThreshold: Minimum score of objects to be include in the detection result.\n  ///   - maxResults: Maximum number of objects to be include in the detection result.\n  /// - Returns: A new instance is created if the model is successfully loaded from the app's main\n  /// bundle.\n  init?(modelFileInfo: FileInfo, threadCount: Int, scoreThreshold: Float, maxResults: Int) {\n    let modelFilename = modelFileInfo.name\n\n    // Construct the path to the model file.\n    guard\n      let modelPath = Bundle.main.path(\n        forResource: modelFileInfo.name,\n        ofType: modelFileInfo.extension\n      )\n    else {\n      print(\"Failed to load the model file with name: \\(modelFilename).\")\n      return nil\n    }\n\n    // Specify the options for the `Detector`.\n    let options = ObjectDetectorOptions(modelPath: modelPath)\n    options.classificationOptions.scoreThreshold = scoreThreshold\n    options.classificationOptions.maxResults = maxResults\n    options.baseOptions.computeSettings.cpuSettings.numThreads = Int32(threadCount)\n    do {\n      // Create the `Detector`.\n      detector = try ObjectDetector.detector(options: options)\n    } catch let error {\n      print(\"Failed to create the interpreter with error: \\(error.localizedDescription)\")\n      return nil\n    }\n\n    super.init()\n  }\n\n  /// Detect objects from the given frame.\n  ///\n  /// This method handles all data preprocessing and makes calls to run inference on a given frame\n  /// through the `Detector`. It then formats the inferences obtained and returns results\n  /// for a successful inference.\n  ///\n  /// - Parameter pixelBuffer: The target frame.\n  /// - Returns: The detected objects and other metadata of the inference.\n  func detect(frame pixelBuffer: CVPixelBuffer) -> Result? {\n\n    guard let mlImage = MLImage(pixelBuffer: pixelBuffer) else { return nil }\n    // Run inference\n    do {\n      let startDate = Date()\n      let detectionResult = try detector.detect(mlImage: mlImage)\n      let interval = Date().timeIntervalSince(startDate) * 1000\n\n      // Returns the detection time and detections\n      return Result(inferenceTime: interval, detections: detectionResult.detections)\n    } catch let error {\n      print(\"Failed to invoke the interpreter with error: \\(error.localizedDescription)\")\n      return nil\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/object_detection/ios/ObjectDetection/ViewControllers/InferenceViewController.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n// MARK: InferenceViewControllerDelegate Method Declarations\nprotocol InferenceViewControllerDelegate {\n\n  /// Called when the user has changed the model settings. The delegate needs to subsequently\n  /// perform the action on the TFLite model.\n  func viewController(\n    _ viewController: InferenceViewController,\n    didReceiveAction action: InferenceViewController.Action)\n}\n\nclass InferenceViewController: UIViewController {\n\n  enum Action {\n    case changeThreadCount(Int)\n    case changeScoreThreshold(Float)\n    case changeMaxResults(Int)\n    case changeModel(ModelType)\n  }\n\n  // MARK: Sections and Information to display\n  private enum InferenceSections: Int, CaseIterable {\n    case InferenceInfo\n  }\n\n  private enum InferenceInfo: Int, CaseIterable {\n    case Resolution\n    case InferenceTime\n\n    func displayString() -> String {\n\n      var toReturn = \"\"\n\n      switch self {\n      case .Resolution:\n        toReturn = \"Resolution\"\n      case .InferenceTime:\n        toReturn = \"Inference Time\"\n\n      }\n      return toReturn\n    }\n  }\n\n  // MARK: Storyboard Outlets\n  @IBOutlet weak var tableView: UITableView!\n  @IBOutlet weak var threadStepper: UIStepper!\n  @IBOutlet weak var threadValueLabel: UILabel!\n  @IBOutlet weak var maxResultStepper: UIStepper!\n  @IBOutlet weak var maxResultLabel: UILabel!\n  @IBOutlet weak var thresholdStepper: UIStepper!\n  @IBOutlet weak var thresholdLabel: UILabel!\n  @IBOutlet weak var modelTextField: UITextField!\n\n  // MARK: Constants\n  private let normalCellHeight: CGFloat = 27.0\n  private let separatorCellHeight: CGFloat = 42.0\n  private let bottomSpacing: CGFloat = 21.0\n  private let bottomSheetButtonDisplayHeight: CGFloat = 60.0\n  private let infoTextColor = UIColor.black\n  private let lightTextInfoColor = UIColor(\n    displayP3Red: 117.0 / 255.0, green: 117.0 / 255.0, blue: 117.0 / 255.0, alpha: 1.0)\n  private let infoFont = UIFont.systemFont(ofSize: 14.0, weight: .regular)\n  private let highlightedFont = UIFont.systemFont(ofSize: 14.0, weight: .medium)\n\n  // MARK: Instance Variables\n  var inferenceTime: Double = 0\n  var resolution: CGSize = CGSize.zero\n  var currentThreadCount: Int = 0\n  var scoreThreshold: Float = 0.5\n  var maxResults: Int = 3\n  var modelSelectIndex: Int = 0\n\n  private var modelSelect: ModelType {\n    if modelSelectIndex < ModelType.allCases.count {\n      return ModelType.allCases[modelSelectIndex]\n    } else {\n      return .ssdMobileNetV1\n    }\n  }\n\n  // MARK: Delegate\n  var delegate: InferenceViewControllerDelegate?\n\n  // MARK: Computed properties\n  var collapsedHeight: CGFloat {\n    return bottomSheetButtonDisplayHeight\n\n  }\n\n  override func viewDidLoad() {\n    super.viewDidLoad()\n    setupUI()\n  }\n  // MARK: private func\n  private func setupUI() {\n\n    threadStepper.value = Double(currentThreadCount)\n    threadValueLabel.text = \"\\(currentThreadCount)\"\n\n    maxResultStepper.value = Double(maxResults)\n    maxResultLabel.text = \"\\(maxResults)\"\n\n    thresholdStepper.value = Double(scoreThreshold)\n    thresholdLabel.text = \"\\(scoreThreshold)\"\n\n    modelTextField.text = modelSelect.title\n\n    let picker = UIPickerView()\n    picker.delegate = self\n    picker.dataSource = self\n    modelTextField.inputView = picker\n\n    let doneButton = UIButton(\n      frame: CGRect(\n        x: 0,\n        y: 0,\n        width: 60,\n        height: 44))\n    doneButton.setTitle(\"Done\", for: .normal)\n    doneButton.setTitleColor(.blue, for: .normal)\n    doneButton.addTarget(\n      self, action: #selector(choseModelDoneButtonTouchUpInside(_:)), for: .touchUpInside)\n    let inputAccessoryView = UIView(\n      frame: CGRect(\n        x: 0,\n        y: 0,\n        width: UIScreen.main.bounds.size.width,\n        height: 44))\n    inputAccessoryView.backgroundColor = .gray\n    inputAccessoryView.addSubview(doneButton)\n    modelTextField.inputAccessoryView = inputAccessoryView\n  }\n\n  // MARK: Button Actions\n  /// Delegate the change of number of threads to ViewController and change the stepper display.\n\n  @IBAction func threadStepperValueChanged(_ sender: UIStepper) {\n    currentThreadCount = Int(sender.value)\n    delegate?.viewController(self, didReceiveAction: .changeThreadCount(currentThreadCount))\n    threadValueLabel.text = \"\\(currentThreadCount)\"\n  }\n\n  @IBAction func thresholdStepperValueChanged(_ sender: UIStepper) {\n    scoreThreshold = Float(sender.value)\n    delegate?.viewController(self, didReceiveAction: .changeScoreThreshold(scoreThreshold))\n    thresholdLabel.text = \"\\(scoreThreshold)\"\n  }\n\n  @IBAction func maxResultStepperValueChanged(_ sender: UIStepper) {\n    maxResults = Int(sender.value)\n    delegate?.viewController(self, didReceiveAction: .changeMaxResults(maxResults))\n    maxResultLabel.text = \"\\(maxResults)\"\n  }\n\n  @objc\n  func choseModelDoneButtonTouchUpInside(_ sender: UIButton) {\n    delegate?.viewController(self, didReceiveAction: .changeModel(modelSelect))\n    modelTextField.text = modelSelect.title\n    modelTextField.resignFirstResponder()\n  }\n\n}\n\n// MARK: UITableView Data Source\nextension InferenceViewController: UITableViewDelegate, UITableViewDataSource {\n\n  func numberOfSections(in tableView: UITableView) -> Int {\n\n    return InferenceSections.allCases.count\n  }\n\n  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {\n\n    guard let inferenceSection = InferenceSections(rawValue: section) else {\n      return 0\n    }\n\n    var rowCount = 0\n    switch inferenceSection {\n    case .InferenceInfo:\n      rowCount = InferenceInfo.allCases.count\n    }\n    return rowCount\n  }\n\n  func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {\n\n    var height: CGFloat = 0.0\n\n    guard let inferenceSection = InferenceSections(rawValue: indexPath.section) else {\n      return height\n    }\n\n    switch inferenceSection {\n    case .InferenceInfo:\n      if indexPath.row == InferenceInfo.allCases.count - 1 {\n        height = separatorCellHeight + bottomSpacing\n      } else {\n        height = normalCellHeight\n      }\n    }\n    return height\n  }\n\n  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {\n\n    let cell = tableView.dequeueReusableCell(withIdentifier: \"INFO_CELL\") as! InfoCell\n\n    guard let inferenceSection = InferenceSections(rawValue: indexPath.section) else {\n      return cell\n    }\n\n    var fieldName = \"\"\n    var info = \"\"\n\n    switch inferenceSection {\n    case .InferenceInfo:\n      let tuple = displayStringsForInferenceInfo(atRow: indexPath.row)\n      fieldName = tuple.0\n      info = tuple.1\n\n    }\n    cell.fieldNameLabel.font = infoFont\n    cell.fieldNameLabel.textColor = infoTextColor\n    cell.fieldNameLabel.text = fieldName\n    cell.infoLabel.text = info\n    return cell\n  }\n\n  // MARK: Format Display of information in the bottom sheet\n  /**\n   This method formats the display of additional information relating to the inferences.\n   */\n  func displayStringsForInferenceInfo(atRow row: Int) -> (String, String) {\n\n    var fieldName: String = \"\"\n    var info: String = \"\"\n\n    guard let inferenceInfo = InferenceInfo(rawValue: row) else {\n      return (fieldName, info)\n    }\n\n    fieldName = inferenceInfo.displayString()\n\n    switch inferenceInfo {\n    case .Resolution:\n      info = \"\\(Int(resolution.width))x\\(Int(resolution.height))\"\n    case .InferenceTime:\n\n      info = String(format: \"%.2fms\", inferenceTime)\n    }\n\n    return (fieldName, info)\n  }\n}\n\nextension InferenceViewController: UIPickerViewDelegate, UIPickerViewDataSource {\n  func numberOfComponents(in pickerView: UIPickerView) -> Int {\n    return 1\n  }\n\n  func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {\n    return ModelType.allCases.count\n  }\n\n  func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int)\n    -> String?\n  {\n    if row < ModelType.allCases.count {\n      return ModelType.allCases[row].title\n    } else {\n      return nil\n    }\n  }\n\n  func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {\n    modelSelectIndex = row\n  }\n}\n\nclass InfoCell: UITableViewCell {\n  @IBOutlet weak var fieldNameLabel: UILabel!\n  @IBOutlet weak var infoLabel: UILabel!\n}\n"
  },
  {
    "path": "lite/examples/object_detection/ios/ObjectDetection/ViewControllers/ViewController.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport TensorFlowLiteTaskVision\nimport UIKit\n\nclass ViewController: UIViewController {\n\n  // MARK: Storyboards Connections\n  @IBOutlet weak var previewView: PreviewView!\n  @IBOutlet weak var overlayView: OverlayView!\n  @IBOutlet weak var resumeButton: UIButton!\n  @IBOutlet weak var cameraUnavailableLabel: UILabel!\n\n  @IBOutlet weak var bottomSheetStateImageView: UIImageView!\n  @IBOutlet weak var bottomSheetView: UIView!\n  @IBOutlet weak var bottomSheetViewBottomSpace: NSLayoutConstraint!\n\n  // MARK: Constants\n  private let displayFont = UIFont.systemFont(ofSize: 14.0, weight: .medium)\n  private let edgeOffset: CGFloat = 2.0\n  private let labelOffset: CGFloat = 10.0\n  private let animationDuration = 0.5\n  private let collapseTransitionThreshold: CGFloat = -30.0\n  private let expandTransitionThreshold: CGFloat = 30.0\n  private let colors = [\n    UIColor.red,\n    UIColor(displayP3Red: 90.0 / 255.0, green: 200.0 / 255.0, blue: 250.0 / 255.0, alpha: 1.0),\n    UIColor.green,\n    UIColor.orange,\n    UIColor.blue,\n    UIColor.purple,\n    UIColor.magenta,\n    UIColor.yellow,\n    UIColor.cyan,\n    UIColor.brown,\n  ]\n\n  // MARK: Model config variables\n  private var threadCount: Int = 1\n  private var detectionModel: ModelType = ConstantsDefault.modelType\n  private var scoreThreshold: Float = ConstantsDefault.scoreThreshold\n  private var maxResults: Int = ConstantsDefault.maxResults\n\n  // MARK: Instance Variables\n  private var initialBottomSpace: CGFloat = 0.0\n\n  // Holds the results at any time\n  private var result: Result?\n  private let inferenceQueue = DispatchQueue(label: \"org.tensorflow.lite.inferencequeue\")\n  private var isInferenceQueueBusy = false\n\n  // MARK: Controllers that manage functionality\n  private lazy var cameraFeedManager = CameraFeedManager(previewView: previewView)\n  private var objectDetectionHelper: ObjectDetectionHelper? = ObjectDetectionHelper(\n    modelFileInfo: ConstantsDefault.modelType.modelFileInfo,\n    threadCount: ConstantsDefault.threadCount,\n    scoreThreshold: ConstantsDefault.scoreThreshold,\n    maxResults: ConstantsDefault.maxResults\n  )\n  private var inferenceViewController: InferenceViewController?\n\n  // MARK: View Handling Methods\n  override func viewDidLoad() {\n    super.viewDidLoad()\n    guard objectDetectionHelper != nil else {\n      fatalError(\"Failed to create the ObjectDetectionHelper. See the console for the error.\")\n    }\n    cameraFeedManager.delegate = self\n    overlayView.clearsContextBeforeDrawing = true\n    addPanGesture()\n  }\n\n  override func didReceiveMemoryWarning() {\n    super.didReceiveMemoryWarning()\n    // Dispose of any resources that can be recreated.\n  }\n\n  override func viewWillAppear(_ animated: Bool) {\n    super.viewWillAppear(animated)\n    changeBottomViewState()\n    cameraFeedManager.checkCameraConfigurationAndStartSession()\n  }\n\n  override func viewWillDisappear(_ animated: Bool) {\n    super.viewWillDisappear(animated)\n\n    cameraFeedManager.stopSession()\n  }\n\n  override var preferredStatusBarStyle: UIStatusBarStyle {\n    return .lightContent\n  }\n\n  // MARK: Button Actions\n  @IBAction func onClickResumeButton(_ sender: Any) {\n    cameraFeedManager.resumeInterruptedSession { (complete) in\n\n      if complete {\n        self.resumeButton.isHidden = true\n        self.cameraUnavailableLabel.isHidden = true\n      } else {\n        self.presentUnableToResumeSessionAlert()\n      }\n    }\n  }\n\n  func presentUnableToResumeSessionAlert() {\n    let alert = UIAlertController(\n      title: \"Unable to Resume Session\",\n      message: \"There was an error while attempting to resume session.\",\n      preferredStyle: .alert\n    )\n    alert.addAction(UIAlertAction(title: \"OK\", style: .default, handler: nil))\n\n    self.present(alert, animated: true)\n  }\n\n  // MARK: Storyboard Segue Handlers\n  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {\n    super.prepare(for: segue, sender: sender)\n\n    if segue.identifier == \"EMBED\" {\n      inferenceViewController = segue.destination as? InferenceViewController\n\n      inferenceViewController?.currentThreadCount = threadCount\n      inferenceViewController?.maxResults = maxResults\n      inferenceViewController?.scoreThreshold = scoreThreshold\n      inferenceViewController?.modelSelectIndex =\n        ModelType.allCases.firstIndex(where: { $0 == detectionModel }) ?? 0\n      inferenceViewController?.delegate = self\n\n      guard let tempResult = result else {\n        return\n      }\n      inferenceViewController?.inferenceTime = tempResult.inferenceTime\n    }\n  }\n}\n\n// MARK: InferenceViewControllerDelegate Methods\nextension ViewController: InferenceViewControllerDelegate {\n  func viewController(\n    _ viewController: InferenceViewController,\n    didReceiveAction action: InferenceViewController.Action\n  ) {\n    switch action {\n    case .changeThreadCount(let threadCount):\n      if self.threadCount == threadCount { return }\n      self.threadCount = threadCount\n    case .changeMaxResults(let maxResults):\n      if self.maxResults == maxResults { return }\n      self.maxResults = maxResults\n    case .changeModel(let detectionModel):\n      if self.detectionModel == detectionModel { return }\n      self.detectionModel = detectionModel\n    case .changeScoreThreshold(let scoreThreshold):\n      if self.scoreThreshold == scoreThreshold { return }\n      self.scoreThreshold = scoreThreshold\n    }\n    inferenceQueue.async {\n      self.objectDetectionHelper = ObjectDetectionHelper(\n        modelFileInfo: self.detectionModel.modelFileInfo,\n        threadCount: self.threadCount,\n        scoreThreshold: self.scoreThreshold,\n        maxResults: self.maxResults\n      )\n    }\n  }\n}\n\n// MARK: CameraFeedManagerDelegate Methods\nextension ViewController: CameraFeedManagerDelegate {\n\n  func didOutput(pixelBuffer: CVPixelBuffer) {\n    // Drop current frame if the previous frame is still being processed.\n    guard !self.isInferenceQueueBusy else { return }\n\n    inferenceQueue.async {\n      self.isInferenceQueueBusy = true\n      self.detect(pixelBuffer: pixelBuffer)\n      self.isInferenceQueueBusy = false\n    }\n  }\n\n  // MARK: Session Handling Alerts\n  func sessionRunTimeErrorOccurred() {\n    // Handles session run time error by updating the UI and providing a button if session can be manually resumed.\n    self.resumeButton.isHidden = false\n  }\n\n  func sessionWasInterrupted(canResumeManually resumeManually: Bool) {\n    // Updates the UI when session is interrupted.\n    if resumeManually {\n      self.resumeButton.isHidden = false\n    } else {\n      self.cameraUnavailableLabel.isHidden = false\n    }\n  }\n\n  func sessionInterruptionEnded() {\n    // Updates UI once session interruption has ended.\n    if !self.cameraUnavailableLabel.isHidden {\n      self.cameraUnavailableLabel.isHidden = true\n    }\n\n    if !self.resumeButton.isHidden {\n      self.resumeButton.isHidden = true\n    }\n  }\n\n  func presentVideoConfigurationErrorAlert() {\n    let alertController = UIAlertController(\n      title: \"Configuration Failed\", message: \"Configuration of camera has failed.\",\n      preferredStyle: .alert)\n    let okAction = UIAlertAction(title: \"OK\", style: .cancel, handler: nil)\n    alertController.addAction(okAction)\n\n    present(alertController, animated: true, completion: nil)\n  }\n\n  func presentCameraPermissionsDeniedAlert() {\n    let alertController = UIAlertController(\n      title: \"Camera Permissions Denied\",\n      message:\n        \"Camera permissions have been denied for this app. You can change this by going to Settings\",\n      preferredStyle: .alert)\n\n    let cancelAction = UIAlertAction(title: \"Cancel\", style: .cancel, handler: nil)\n    let settingsAction = UIAlertAction(title: \"Settings\", style: .default) { (action) in\n\n      UIApplication.shared.open(\n        URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)\n    }\n\n    alertController.addAction(cancelAction)\n    alertController.addAction(settingsAction)\n\n    present(alertController, animated: true, completion: nil)\n\n  }\n\n  /** This method runs the live camera pixelBuffer through tensorFlow to get the result.\n   */\n  func detect(pixelBuffer: CVPixelBuffer) {\n    result = self.objectDetectionHelper?.detect(frame: pixelBuffer)\n\n    guard let displayResult = result else {\n      return\n    }\n\n    let width = CVPixelBufferGetWidth(pixelBuffer)\n    let height = CVPixelBufferGetHeight(pixelBuffer)\n\n    DispatchQueue.main.async {\n\n      // Display results by handing off to the InferenceViewController\n      self.inferenceViewController?.resolution = CGSize(width: width, height: height)\n\n      var inferenceTime: Double = 0\n      if let resultInferenceTime = self.result?.inferenceTime {\n        inferenceTime = resultInferenceTime\n      }\n      self.inferenceViewController?.inferenceTime = inferenceTime\n      self.inferenceViewController?.tableView.reloadData()\n\n      // Draws the bounding boxes and displays class names and confidence scores.\n      self.drawAfterPerformingCalculations(\n        onDetections: displayResult.detections,\n        withImageSize: CGSize(width: CGFloat(width), height: CGFloat(height)))\n    }\n  }\n\n  /**\n   This method takes the results, translates the bounding box rects to the current view, draws the bounding boxes, classNames and confidence scores of inferences.\n   */\n  func drawAfterPerformingCalculations(\n    onDetections detections: [Detection], withImageSize imageSize: CGSize\n  ) {\n\n    self.overlayView.objectOverlays = []\n    self.overlayView.setNeedsDisplay()\n\n    guard !detections.isEmpty else {\n      return\n    }\n\n    var objectOverlays: [ObjectOverlay] = []\n\n    for detection in detections {\n\n      guard let category = detection.categories.first else { continue }\n\n      // Translates bounding box rect to current view.\n      var convertedRect = detection.boundingBox.applying(\n        CGAffineTransform(\n          scaleX: self.overlayView.bounds.size.width / imageSize.width,\n          y: self.overlayView.bounds.size.height / imageSize.height))\n\n      if convertedRect.origin.x < 0 {\n        convertedRect.origin.x = self.edgeOffset\n      }\n\n      if convertedRect.origin.y < 0 {\n        convertedRect.origin.y = self.edgeOffset\n      }\n\n      if convertedRect.maxY > self.overlayView.bounds.maxY {\n        convertedRect.size.height =\n          self.overlayView.bounds.maxY - convertedRect.origin.y - self.edgeOffset\n      }\n\n      if convertedRect.maxX > self.overlayView.bounds.maxX {\n        convertedRect.size.width =\n          self.overlayView.bounds.maxX - convertedRect.origin.x - self.edgeOffset\n      }\n\n      let objectDescription = String(\n        format: \"\\(category.label ?? \"Unknown\") (%.2f)\",\n        category.score)\n\n      let displayColor = colors[category.index % colors.count]\n\n      let size = objectDescription.size(withAttributes: [.font: self.displayFont])\n\n      let objectOverlay = ObjectOverlay(\n        name: objectDescription, borderRect: convertedRect, nameStringSize: size,\n        color: displayColor,\n        font: self.displayFont)\n\n      objectOverlays.append(objectOverlay)\n    }\n\n    // Hands off drawing to the OverlayView\n    self.draw(objectOverlays: objectOverlays)\n\n  }\n\n  /** Calls methods to update overlay view with detected bounding boxes and class names.\n   */\n  func draw(objectOverlays: [ObjectOverlay]) {\n\n    self.overlayView.objectOverlays = objectOverlays\n    self.overlayView.setNeedsDisplay()\n  }\n\n}\n\n// MARK: Bottom Sheet Interaction Methods\nextension ViewController {\n\n  // MARK: Bottom Sheet Interaction Methods\n  /**\n   This method adds a pan gesture to make the bottom sheet interactive.\n   */\n  private func addPanGesture() {\n    let panGesture = UIPanGestureRecognizer(\n      target: self, action: #selector(ViewController.didPan(panGesture:)))\n    bottomSheetView.addGestureRecognizer(panGesture)\n  }\n\n  /** Change whether bottom sheet should be in expanded or collapsed state.\n   */\n  private func changeBottomViewState() {\n    guard let inferenceVC = inferenceViewController else {\n      return\n    }\n\n    if bottomSheetViewBottomSpace.constant == inferenceVC.collapsedHeight\n      - bottomSheetView.bounds.size.height\n    {\n      bottomSheetViewBottomSpace.constant = 0.0\n    } else {\n      bottomSheetViewBottomSpace.constant =\n        inferenceVC.collapsedHeight - bottomSheetView.bounds.size.height\n    }\n    setImageBasedOnBottomViewState()\n  }\n\n  /**\n   Set image of the bottom sheet icon based on whether it is expanded or collapsed\n   */\n  private func setImageBasedOnBottomViewState() {\n    if bottomSheetViewBottomSpace.constant == 0.0 {\n      bottomSheetStateImageView.image = UIImage(named: \"down_icon\")\n    } else {\n      bottomSheetStateImageView.image = UIImage(named: \"up_icon\")\n    }\n  }\n\n  /**\n   This method responds to the user panning on the bottom sheet.\n   */\n  @objc func didPan(panGesture: UIPanGestureRecognizer) {\n    // Opens or closes the bottom sheet based on the user's interaction with the bottom sheet.\n    let translation = panGesture.translation(in: view)\n\n    switch panGesture.state {\n    case .began:\n      initialBottomSpace = bottomSheetViewBottomSpace.constant\n      translateBottomSheet(withVerticalTranslation: translation.y)\n    case .changed:\n      translateBottomSheet(withVerticalTranslation: translation.y)\n    case .cancelled:\n      setBottomSheetLayout(withBottomSpace: initialBottomSpace)\n    case .ended:\n      translateBottomSheetAtEndOfPan(withVerticalTranslation: translation.y)\n      setImageBasedOnBottomViewState()\n      initialBottomSpace = 0.0\n    default:\n      break\n    }\n  }\n\n  /**\n   This method sets bottom sheet translation while pan gesture state is continuously changing.\n   */\n  private func translateBottomSheet(withVerticalTranslation verticalTranslation: CGFloat) {\n    let bottomSpace = initialBottomSpace - verticalTranslation\n    guard\n      (bottomSpace <= 0.0)\n        && (bottomSpace >=\n          inferenceViewController!.collapsedHeight - bottomSheetView.bounds.size.height)\n    else {\n      return\n    }\n    setBottomSheetLayout(withBottomSpace: bottomSpace)\n  }\n\n  /**\n   This method changes bottom sheet state to either fully expanded or closed at the end of pan.\n   */\n  private func translateBottomSheetAtEndOfPan(withVerticalTranslation verticalTranslation: CGFloat)\n  {\n\n    // Changes bottom sheet state to either fully open or closed at the end of pan.\n    let bottomSpace = bottomSpaceAtEndOfPan(withVerticalTranslation: verticalTranslation)\n    setBottomSheetLayout(withBottomSpace: bottomSpace)\n  }\n\n  /**\n   Return the final state of the bottom sheet view (whether fully collapsed or expanded) that is to be retained.\n   */\n  private func bottomSpaceAtEndOfPan(withVerticalTranslation verticalTranslation: CGFloat)\n    -> CGFloat\n  {\n\n    // Calculates whether to fully expand or collapse bottom sheet when pan gesture ends.\n    var bottomSpace = initialBottomSpace - verticalTranslation\n\n    var height: CGFloat = 0.0\n    if initialBottomSpace == 0.0 {\n      height = bottomSheetView.bounds.size.height\n    } else {\n      height = inferenceViewController!.collapsedHeight\n    }\n\n    let currentHeight = bottomSheetView.bounds.size.height + bottomSpace\n\n    if currentHeight - height <= collapseTransitionThreshold {\n      bottomSpace = inferenceViewController!.collapsedHeight - bottomSheetView.bounds.size.height\n    } else if currentHeight - height >= expandTransitionThreshold {\n      bottomSpace = 0.0\n    } else {\n      bottomSpace = initialBottomSpace\n    }\n\n    return bottomSpace\n  }\n\n  /**\n   This method layouts the change of the bottom space of bottom sheet with respect to the view managed by this controller.\n   */\n  func setBottomSheetLayout(withBottomSpace bottomSpace: CGFloat) {\n    view.setNeedsLayout()\n    bottomSheetViewBottomSpace.constant = bottomSpace\n    view.setNeedsLayout()\n  }\n\n}\n\n// MARK: - Display handler function\n\n/// TFLite model types\nenum ModelType: CaseIterable {\n  case efficientDetLite0\n  case efficientDetLite1\n  case efficientDetLite2\n  case ssdMobileNetV1\n\n  var modelFileInfo: FileInfo {\n    switch self {\n    case .ssdMobileNetV1:\n      return FileInfo(\"ssd_mobilenet_v1\", \"tflite\")\n    case .efficientDetLite0:\n      return FileInfo(\"efficientdet_lite0\", \"tflite\")\n    case .efficientDetLite1:\n      return FileInfo(\"efficientdet_lite1\", \"tflite\")\n    case .efficientDetLite2:\n      return FileInfo(\"efficientdet_lite2\", \"tflite\")\n    }\n  }\n\n  var title: String {\n    switch self {\n    case .ssdMobileNetV1:\n      return \"SSD-MobileNetV1\"\n    case .efficientDetLite0:\n      return \"EfficientDet-Lite0\"\n    case .efficientDetLite1:\n      return \"EfficientDet-Lite1\"\n    case .efficientDetLite2:\n      return \"EfficientDet-Lite2\"\n    }\n  }\n}\n\n/// Default configuration\nstruct ConstantsDefault {\n  static let modelType: ModelType = .efficientDetLite0\n  static let threadCount = 1\n  static let scoreThreshold: Float = 0.5\n  static let maxResults: Int = 3\n  static let theadCountLimit = 10\n}\n"
  },
  {
    "path": "lite/examples/object_detection/ios/ObjectDetection/Views/OverlayView.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n/**\n This structure holds the display parameters for the overlay to be drawon on a detected object.\n */\nstruct ObjectOverlay {\n  let name: String\n  let borderRect: CGRect\n  let nameStringSize: CGSize\n  let color: UIColor\n  let font: UIFont\n}\n\n/**\n This UIView draws overlay on a detected object.\n */\nclass OverlayView: UIView {\n\n  var objectOverlays: [ObjectOverlay] = []\n  private let cornerRadius: CGFloat = 10.0\n  private let stringBgAlpha: CGFloat\n    = 0.7\n  private let lineWidth: CGFloat = 3\n  private let stringFontColor = UIColor.white\n  private let stringHorizontalSpacing: CGFloat = 13.0\n  private let stringVerticalSpacing: CGFloat = 7.0\n\n  override func draw(_ rect: CGRect) {\n\n    // Drawing code\n    for objectOverlay in objectOverlays {\n\n      drawBorders(of: objectOverlay)\n      drawBackground(of: objectOverlay)\n      drawName(of: objectOverlay)\n    }\n  }\n\n  /**\n   This method draws the borders of the detected objects.\n   */\n  func drawBorders(of objectOverlay: ObjectOverlay) {\n\n    let path = UIBezierPath(rect: objectOverlay.borderRect)\n    path.lineWidth = lineWidth\n    objectOverlay.color.setStroke()\n\n    path.stroke()\n  }\n\n  /**\n   This method draws the background of the string.\n   */\n  func drawBackground(of objectOverlay: ObjectOverlay) {\n\n    let stringBgRect = CGRect(x: objectOverlay.borderRect.origin.x, y: objectOverlay.borderRect.origin.y , width: 2 * stringHorizontalSpacing + objectOverlay.nameStringSize.width, height: 2 * stringVerticalSpacing + objectOverlay.nameStringSize.height\n    )\n\n    let stringBgPath = UIBezierPath(rect: stringBgRect)\n    objectOverlay.color.withAlphaComponent(stringBgAlpha).setFill()\n    stringBgPath.fill()\n  }\n\n  /**\n   This method draws the name of object overlay.\n   */\n  func drawName(of objectOverlay: ObjectOverlay) {\n\n    // Draws the string.\n    let stringRect = CGRect(x: objectOverlay.borderRect.origin.x + stringHorizontalSpacing, y: objectOverlay.borderRect.origin.y + stringVerticalSpacing, width: objectOverlay.nameStringSize.width, height: objectOverlay.nameStringSize.height)\n\n    let attributedString = NSAttributedString(string: objectOverlay.name, attributes: [NSAttributedString.Key.foregroundColor : stringFontColor, NSAttributedString.Key.font : objectOverlay.font])\n    attributedString.draw(in: stringRect)\n  }\n\n}\n"
  },
  {
    "path": "lite/examples/object_detection/ios/ObjectDetection.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t1E80B32721521BCC7B34949C /* Pods_ObjectDetection.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B7188819D4ABCA5FEB2A57A /* Pods_ObjectDetection.framework */; };\n\t\tAA119EA2217F337300DE46CF /* ObjectDetectionHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA119EA1217F337300DE46CF /* ObjectDetectionHelper.swift */; };\n\t\tAA16E59D218081AA00D9E153 /* InferenceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA16E59C218081AA00D9E153 /* InferenceViewController.swift */; };\n\t\tAA756E652150C996004BB0BC /* CameraFeedManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA756E642150C996004BB0BC /* CameraFeedManager.swift */; };\n\t\tAA756E6A2150CE89004BB0BC /* OverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA756E692150CE88004BB0BC /* OverlayView.swift */; };\n\t\tAA9AC59F212D39FC000B4E9D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA9AC59E212D39FC000B4E9D /* AppDelegate.swift */; };\n\t\tAA9AC5A4212D39FC000B4E9D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA9AC5A2212D39FC000B4E9D /* Main.storyboard */; };\n\t\tAA9AC5A6212D39FD000B4E9D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AA9AC5A5212D39FD000B4E9D /* Assets.xcassets */; };\n\t\tAA9AC5A9212D39FD000B4E9D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA9AC5A7212D39FD000B4E9D /* LaunchScreen.storyboard */; };\n\t\tAA9AC5D7212D43E0000B4E9D /* PreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA9AC5D5212D43E0000B4E9D /* PreviewView.swift */; };\n\t\tAA9AC5E2212D4520000B4E9D /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA9AC5E1212D4520000B4E9D /* ViewController.swift */; };\n\t\tBF43FFBA2845ABFD00D8FB74 /* efficientdet_lite0.tflite in Resources */ = {isa = PBXBuildFile; fileRef = BF1FBCA92844A35100E05940 /* efficientdet_lite0.tflite */; };\n\t\tBF43FFBB2845ABFD00D8FB74 /* efficientdet_lite1.tflite in Resources */ = {isa = PBXBuildFile; fileRef = BF1FBCAB2844A35100E05940 /* efficientdet_lite1.tflite */; };\n\t\tBF43FFBC2845ABFD00D8FB74 /* efficientdet_lite2.tflite in Resources */ = {isa = PBXBuildFile; fileRef = BF1FBCAA2844A35100E05940 /* efficientdet_lite2.tflite */; };\n\t\tBF43FFBD2845ABFD00D8FB74 /* ssd_mobilenet_v1.tflite in Resources */ = {isa = PBXBuildFile; fileRef = BF1FBCAC2844A35100E05940 /* ssd_mobilenet_v1.tflite */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\t2B7188819D4ABCA5FEB2A57A /* Pods_ObjectDetection.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ObjectDetection.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t5461A9602A0A3561DB9D597D /* Pods-ObjectDetection.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-ObjectDetection.debug.xcconfig\"; path = \"Target Support Files/Pods-ObjectDetection/Pods-ObjectDetection.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t661EBCB319BEACFB38192437 /* Pods-ObjectDetection.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-ObjectDetection.release.xcconfig\"; path = \"Target Support Files/Pods-ObjectDetection/Pods-ObjectDetection.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tAA119EA1217F337300DE46CF /* ObjectDetectionHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectDetectionHelper.swift; sourceTree = \"<group>\"; };\n\t\tAA16E59C218081AA00D9E153 /* InferenceViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InferenceViewController.swift; sourceTree = \"<group>\"; };\n\t\tAA756E642150C996004BB0BC /* CameraFeedManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraFeedManager.swift; sourceTree = \"<group>\"; };\n\t\tAA756E692150CE88004BB0BC /* OverlayView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OverlayView.swift; sourceTree = \"<group>\"; };\n\t\tAA9AC59B212D39FC000B4E9D /* ObjectDetection.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ObjectDetection.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tAA9AC59E212D39FC000B4E9D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\tAA9AC5A3212D39FC000B4E9D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\tAA9AC5A5212D39FD000B4E9D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tAA9AC5A8212D39FD000B4E9D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\tAA9AC5AA212D39FD000B4E9D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tAA9AC5D5212D43E0000B4E9D /* PreviewView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewView.swift; sourceTree = \"<group>\"; };\n\t\tAA9AC5E1212D4520000B4E9D /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = \"<group>\"; };\n\t\tBF1FBCA92844A35100E05940 /* efficientdet_lite0.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = efficientdet_lite0.tflite; sourceTree = \"<group>\"; };\n\t\tBF1FBCAA2844A35100E05940 /* efficientdet_lite2.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = efficientdet_lite2.tflite; sourceTree = \"<group>\"; };\n\t\tBF1FBCAB2844A35100E05940 /* efficientdet_lite1.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = efficientdet_lite1.tflite; sourceTree = \"<group>\"; };\n\t\tBF1FBCAC2844A35100E05940 /* ssd_mobilenet_v1.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = ssd_mobilenet_v1.tflite; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tAA9AC598212D39FC000B4E9D /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t1E80B32721521BCC7B34949C /* Pods_ObjectDetection.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tA35BE145980A216CC3A6A0A6 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2B7188819D4ABCA5FEB2A57A /* Pods_ObjectDetection.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAA09263B212D730700D5890F /* TFLite */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAA119EA1217F337300DE46CF /* ObjectDetectionHelper.swift */,\n\t\t\t\tBF1FBCA92844A35100E05940 /* efficientdet_lite0.tflite */,\n\t\t\t\tBF1FBCAB2844A35100E05940 /* efficientdet_lite1.tflite */,\n\t\t\t\tBF1FBCAA2844A35100E05940 /* efficientdet_lite2.tflite */,\n\t\t\t\tBF1FBCAC2844A35100E05940 /* ssd_mobilenet_v1.tflite */,\n\t\t\t);\n\t\t\tpath = TFLite;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAA756E682150CE88004BB0BC /* Views */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAA756E692150CE88004BB0BC /* OverlayView.swift */,\n\t\t\t);\n\t\t\tpath = Views;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAA9AC592212D39FC000B4E9D = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAA9AC59D212D39FC000B4E9D /* ObjectDetection */,\n\t\t\t\tAA9AC59C212D39FC000B4E9D /* Products */,\n\t\t\t\tC5C06069A877002CD7A994EB /* Pods */,\n\t\t\t\tA35BE145980A216CC3A6A0A6 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAA9AC59C212D39FC000B4E9D /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAA9AC59B212D39FC000B4E9D /* ObjectDetection.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAA9AC59D212D39FC000B4E9D /* ObjectDetection */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAA9AC5D4212D43E0000B4E9D /* Camera Feed */,\n\t\t\t\tAA9AC5D1212D43BD000B4E9D /* ViewControllers */,\n\t\t\t\tAA756E682150CE88004BB0BC /* Views */,\n\t\t\t\tAA09263B212D730700D5890F /* TFLite */,\n\t\t\t\tAA9AC59E212D39FC000B4E9D /* AppDelegate.swift */,\n\t\t\t\tAA9AC5A2212D39FC000B4E9D /* Main.storyboard */,\n\t\t\t\tAA9AC5A5212D39FD000B4E9D /* Assets.xcassets */,\n\t\t\t\tAA9AC5A7212D39FD000B4E9D /* LaunchScreen.storyboard */,\n\t\t\t\tAA9AC5AA212D39FD000B4E9D /* Info.plist */,\n\t\t\t);\n\t\t\tpath = ObjectDetection;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAA9AC5D1212D43BD000B4E9D /* ViewControllers */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAA16E59C218081AA00D9E153 /* InferenceViewController.swift */,\n\t\t\t\tAA9AC5E1212D4520000B4E9D /* ViewController.swift */,\n\t\t\t);\n\t\t\tpath = ViewControllers;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAA9AC5D4212D43E0000B4E9D /* Camera Feed */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAA756E642150C996004BB0BC /* CameraFeedManager.swift */,\n\t\t\t\tAA9AC5D5212D43E0000B4E9D /* PreviewView.swift */,\n\t\t\t);\n\t\t\tpath = \"Camera Feed\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tC5C06069A877002CD7A994EB /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t5461A9602A0A3561DB9D597D /* Pods-ObjectDetection.debug.xcconfig */,\n\t\t\t\t661EBCB319BEACFB38192437 /* Pods-ObjectDetection.release.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\tAA9AC59A212D39FC000B4E9D /* ObjectDetection */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = AA9AC5C3212D39FD000B4E9D /* Build configuration list for PBXNativeTarget \"ObjectDetection\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t0AAB246F7E1B285AB2651E60 /* [CP] Check Pods Manifest.lock */,\n\t\t\t\tAA055D8A21622F7C00B25948 /* ShellScript */,\n\t\t\t\tAA9AC597212D39FC000B4E9D /* Sources */,\n\t\t\t\tAA9AC598212D39FC000B4E9D /* Frameworks */,\n\t\t\t\tAA9AC599212D39FC000B4E9D /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = ObjectDetection;\n\t\t\tproductName = ObjectDetection;\n\t\t\tproductReference = AA9AC59B212D39FC000B4E9D /* ObjectDetection.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tAA9AC593212D39FC000B4E9D /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 0940;\n\t\t\t\tLastUpgradeCheck = 1330;\n\t\t\t\tORGANIZATIONNAME = \"Y Media Labs\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tAA9AC59A212D39FC000B4E9D = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.4.1;\n\t\t\t\t\t\tLastSwiftMigration = 1140;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = AA9AC596212D39FC000B4E9D /* Build configuration list for PBXProject \"ObjectDetection\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = AA9AC592212D39FC000B4E9D;\n\t\t\tproductRefGroup = AA9AC59C212D39FC000B4E9D /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tAA9AC59A212D39FC000B4E9D /* ObjectDetection */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\tAA9AC599212D39FC000B4E9D /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBF43FFBA2845ABFD00D8FB74 /* efficientdet_lite0.tflite in Resources */,\n\t\t\t\tBF43FFBB2845ABFD00D8FB74 /* efficientdet_lite1.tflite in Resources */,\n\t\t\t\tBF43FFBC2845ABFD00D8FB74 /* efficientdet_lite2.tflite in Resources */,\n\t\t\t\tBF43FFBD2845ABFD00D8FB74 /* ssd_mobilenet_v1.tflite in Resources */,\n\t\t\t\tAA9AC5A9212D39FD000B4E9D /* LaunchScreen.storyboard in Resources */,\n\t\t\t\tAA9AC5A6212D39FD000B4E9D /* Assets.xcassets in Resources */,\n\t\t\t\tAA9AC5A4212D39FC000B4E9D /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t0AAB246F7E1B285AB2651E60 /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-ObjectDetection-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\tAA055D8A21622F7C00B25948 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"# Type a script or drag a script file from your workspace to insert its path.\\n\\\"$SRCROOT/RunScripts/download_models.sh\\\"\\n\";\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tAA9AC597212D39FC000B4E9D /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tAA9AC5E2212D4520000B4E9D /* ViewController.swift in Sources */,\n\t\t\t\tAA756E6A2150CE89004BB0BC /* OverlayView.swift in Sources */,\n\t\t\t\tAA119EA2217F337300DE46CF /* ObjectDetectionHelper.swift in Sources */,\n\t\t\t\tAA756E652150C996004BB0BC /* CameraFeedManager.swift in Sources */,\n\t\t\t\tAA9AC59F212D39FC000B4E9D /* AppDelegate.swift in Sources */,\n\t\t\t\tAA9AC5D7212D43E0000B4E9D /* PreviewView.swift in Sources */,\n\t\t\t\tAA16E59D218081AA00D9E153 /* InferenceViewController.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\tAA9AC5A2212D39FC000B4E9D /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tAA9AC5A3212D39FC000B4E9D /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAA9AC5A7212D39FD000B4E9D /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tAA9AC5A8212D39FD000B4E9D /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\tAA9AC5C1212D39FD000B4E9D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 4.2;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tAA9AC5C2212D39FD000B4E9D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tSWIFT_VERSION = 4.2;\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tAA9AC5C4212D39FD000B4E9D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 5461A9602A0A3561DB9D597D /* Pods-ObjectDetection.debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tDEVELOPMENT_TEAM = EQHXZ8M8AV;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = ObjectDetection/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.tensorflow.ObjectDetection;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"Google Development\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tAA9AC5C5212D39FD000B4E9D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 661EBCB319BEACFB38192437 /* Pods-ObjectDetection.release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tDEVELOPMENT_TEAM = EQHXZ8M8AV;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = ObjectDetection/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.tensorflow.ObjectDetection;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"Google Development\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tAA9AC596212D39FC000B4E9D /* Build configuration list for PBXProject \"ObjectDetection\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tAA9AC5C1212D39FD000B4E9D /* Debug */,\n\t\t\t\tAA9AC5C2212D39FD000B4E9D /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tAA9AC5C3212D39FD000B4E9D /* Build configuration list for PBXNativeTarget \"ObjectDetection\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tAA9AC5C4212D39FD000B4E9D /* Debug */,\n\t\t\t\tAA9AC5C5212D39FD000B4E9D /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = AA9AC593212D39FC000B4E9D /* Project object */;\n}\n"
  },
  {
    "path": "lite/examples/object_detection/ios/Podfile",
    "content": "# Uncomment the next line to define a global platform for your project\n platform :ios, '12.0'\n\ntarget 'ObjectDetection' do\n  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks\n  use_frameworks!\n\n  # Pods for ObjectDetection\n  pod 'TensorFlowLiteTaskVision'\n\nend\n"
  },
  {
    "path": "lite/examples/object_detection/ios/README.md",
    "content": "# TensorFlow Lite Object Detection iOS Example Application\n\n**iOS Versions Supported:** iOS 12.0 and above.\n**Xcode Version Required:** 10.0 and above\n\n## Overview\n\nThis is a camera app that continuously detects the objects (bounding boxes and classes) in the frames seen by your device's back camera, using a quantized [MobileNet SSD](https://github.com/tensorflow/models/tree/master/research/object_detection) model trained on the [COCO dataset](http://cocodataset.org/). These instructions walk you through building and running the demo on an iOS device.\n\nThe model files are downloaded via scripts in Xcode when you build and run. You don't need to do any steps to download TFLite models into the project explicitly.\n\n<!-- TODO(b/124116863): Add app screenshot. -->\n\n## Prerequisites\n\n* You must have Xcode installed\n\n* You must have a valid Apple Developer ID\n\n* The demo app requires a camera and must be executed on a real iOS device. You can build it and run with the iPhone Simulator but the app raises a camera not found exception.\n\n* You don't need to build the entire TensorFlow library to run the demo, it uses CocoaPods to download the TensorFlow Lite library.\n\n* You'll also need the Xcode command-line tools:\n ```xcode-select --install```\n If this is a new install, you will need to run the Xcode application once to agree to the license before continuing.\n## Building the iOS Demo App\n\n1. Install CocoaPods if you don't have it.\n```sudo gem install cocoapods```\n\n2. Install the pod to generate the workspace file:\n```cd lite/examples/object_detection/ios/```\n```pod install```\n  If you have installed this pod before and that command doesn't work, try\n```pod update```\nAt the end of this step you should have a file called ```ObjectDetection.xcworkspace```\n\n3. Open **ObjectDetection.xcworkspace** in Xcode.\n\n4. Please change the bundle identifier to a unique identifier and select your development team in **'General->Signing'** before building the application if you are using an iOS device.\n\n5. Build and run the app in Xcode.\nYou'll have to grant permissions for the app to use the device's camera. Point the camera at various objects and enjoy seeing how the model classifies things!\n\n### Note\n_Please do not delete the empty references_ to the .tflite and .txt files after you clone the repo and open the project. These references will be fulfilled once the model and label files are downloaded when the application is built and run for the first time. If you delete the references to them, you can still find that the .tflite and .txt files are downloaded to the Model folder, the next time you build the application. You will have to add the references to these files in the bundle separately in that case.\n\n## Model Used\n\nThis app uses a MobileNet SSD model trained on [COCO dataset](http://cocodataset.org/). The input image size required is 300 X 300 X 3. You can download the model [here](https://storage.googleapis.com/download.tensorflow.org/models/tflite/coco_ssd_mobilenet_v1_1.0_quant_2018_06_29.zip). You can find more information on the research on object detection [here](https://github.com/tensorflow/models/tree/master/research/object_detection).\n\n## iOS App Details\n\nThe app is written entirely in Swift and uses the TensorFlow Lite\n[Swift library](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/swift)\nfor performing image classification.\n\nNote: Objective-C developers should use the TensorFlow Lite\n[Objective-C library](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/objc).\n"
  },
  {
    "path": "lite/examples/object_detection/ios/RunScripts/download_models.sh",
    "content": "#!/bin/bash\n# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\nset -ex\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nMOBILENETV1_SSD_URL=\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/object_detection/ios/lite-model_ssd_mobilenet_v1_1_metadata_2.tflite\"\nEFFICIENTDET_LITE0_URL=\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/object_detection/ios/lite-model_efficientdet_lite0_detection_metadata_1.tflite\"\nEFFICIENTDET_LITE1_URL=\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/object_detection/ios/lite-model_efficientdet_lite1_detection_metadata_1.tflite\"\nEFFICIENTDET_LITE2_URL=\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/object_detection/ios/lite-model_efficientdet_lite2_detection_metadata_1.tflite\"\n\nMOBILENETV1_SSD_NAME=\"ssd_mobilenet_v1.tflite\"\nEFFICIENTDET_LITE0_NAME=\"efficientdet_lite0.tflite\"\nEFFICIENTDET_LITE1_NAME=\"efficientdet_lite1.tflite\"\nEFFICIENTDET_LITE2_NAME=\"efficientdet_lite2.tflite\"\n\nDOWNLOADS_DIR=$(mktemp -d)\n\ncd \"$SCRIPT_DIR\"\n\ndownload() {\n  local usage=\"Usage: download_and_extract URL DIR\"\n  local url=\"${1:?${usage}}\"\n  local dir=\"${2:?${usage}}\"\n  local name=\"${3:?${usage}}\"\n  echo \"downloading ${url}\" >&2\n  mkdir -p \"${dir}\"\n  tempdir=$(mktemp -d)\n\n  curl -L ${url} > ${tempdir}/${name}\n  cp -R ${tempdir}/* ${dir}/\n  rm -rf ${tempdir}\n}\n\nhas_download=false\n\nif [ -f ../ObjectDetection/TFLite/${MOBILENETV1_SSD_NAME} ]\nthen\necho \"File ${MOBILENETV1_SSD_NAME} exists.\"\nelse\nhas_download=true\ndownload \"${MOBILENETV1_SSD_URL}\" \"${DOWNLOADS_DIR}/models\" \"${MOBILENETV1_SSD_NAME}\"\nfile ${DOWNLOADS_DIR}/models\nfi\n\nif [ -f ../ObjectDetection/TFLite/${EFFICIENTDET_LITE0_NAME} ]\nthen\necho \"File ${EFFICIENTDET_LITE0_NAME} exists.\"\nelse\nhas_download=true\ndownload \"${EFFICIENTDET_LITE0_URL}\" \"${DOWNLOADS_DIR}/models\" \"${EFFICIENTDET_LITE0_NAME}\"\nfile ${DOWNLOADS_DIR}/models\nfi\n\nif [ -f ../ObjectDetection/TFLite/${EFFICIENTDET_LITE1_NAME} ]\nthen\necho \"File ${EFFICIENTDET_LITE1_NAME} exists.\"\nelse\nhas_download=true\ndownload \"${EFFICIENTDET_LITE1_URL}\" \"${DOWNLOADS_DIR}/models\" \"${EFFICIENTDET_LITE1_NAME}\"\nfile ${DOWNLOADS_DIR}/models\nfi\n\nif [ -f ../ObjectDetection/TFLite/${EFFICIENTDET_LITE2_NAME} ]\nthen\necho \"File ${EFFICIENTDET_LITE2_NAME} exists.\"\nelse\nhas_download=true\ndownload \"${EFFICIENTDET_LITE2_URL}\" \"${DOWNLOADS_DIR}/models\" \"${EFFICIENTDET_LITE2_NAME}\"\nfile ${DOWNLOADS_DIR}/models\nfi\n\nif ${has_download}\nthen\ncp ${DOWNLOADS_DIR}/models/* ../ObjectDetection/TFLite\nrm -rf ${DOWNLOADS_DIR}\nfi\n\n"
  },
  {
    "path": "lite/examples/object_detection/raspberry_pi/README.md",
    "content": "# TensorFlow Lite Python object detection example with Raspberry Pi\n\nThis example uses [TensorFlow Lite](https://tensorflow.org/lite) with Python on\na Raspberry Pi to perform real-time object detection using images streamed from\nthe Pi Camera. It draws a bounding box around each detected object in the camera\npreview (when the object score is above a given threshold).\n\nAt the end of this page, there are extra steps to accelerate the example using\nthe Coral USB Accelerator to increase inference speed.\n\n## Set up your hardware\n\nBefore you begin, you need to\n[set up your Raspberry Pi](https://projects.raspberrypi.org/en/projects/raspberry-pi-setting-up)\nwith Raspberry Pi OS (preferably updated to Buster).\n\nYou also need to\n[connect and configure the Pi Camera](https://www.raspberrypi.org/documentation/configuration/camera.md)\nif you use the Pi Camera. This code also works with USB camera connect to the\nRaspberry Pi.\n\nAnd to see the results from the camera, you need a monitor connected to the\nRaspberry Pi. It's okay if you're using SSH to access the Pi shell (you don't\nneed to use a keyboard connected to the Pi)—you only need a monitor attached to\nthe Pi to see the camera stream.\n\n## Download the example files\n\nFirst, clone this Git repo onto your Raspberry Pi like this:\n\n```\ngit clone https://github.com/tensorflow/examples --depth 1\n```\n\nThen use our script to install a couple Python packages, and download the\nEfficientDet-Lite model:\n\n```\ncd examples/lite/examples/object_detection/raspberry_pi\n\n# The script install the required dependencies and download the TFLite models.\nsh setup.sh\n```\n\nIn this project, all you need from the TensorFlow Lite API is the `Interpreter`\nclass. So instead of installing the large `tensorflow` package, we're using the\nmuch smaller `tflite_runtime` package. The setup scripts automatically install\nthe TensorFlow Lite runtime.\n\n## Run the example\n\n```\npython3 detect.py \\\n  --model efficientdet_lite0.tflite\n```\n\nYou should see the camera feed appear on the monitor attached to your Raspberry\nPi. Put some objects in front of the camera, like a coffee mug or keyboard, and\nyou'll see boxes drawn around those that the model recognizes, including the\nlabel and score for each. It also prints the number of frames per second (FPS)\nat the top-left corner of the screen. As the pipeline contains some processes\nother than model inference, including visualizing the detection results, you can\nexpect a higher FPS if your inference pipeline runs in headless mode without\nvisualization.\n\nFor more information about executing inferences with TensorFlow Lite, read\n[TensorFlow Lite inference](https://www.tensorflow.org/lite/guide/inference).\n\n## Speed up model inference (optional)\n\nIf you want to significantly speed up the inference time, you can attach an\n[Coral USB Accelerator](https://coral.withgoogle.com/products/accelerator)—a USB\naccessory that adds the\n[Edge TPU ML accelerator](https://coral.withgoogle.com/docs/edgetpu/faq/) to any\nLinux-based system.\n\nIf you have a Coral USB Accelerator, you can run the sample with it enabled:\n\n1.  First, be sure you have completed the\n    [USB Accelerator setup instructions](https://coral.withgoogle.com/docs/accelerator/get-started/).\n\n2.  Run the object detection script using the EdgeTPU TFLite model and enable\n    the EdgeTPU option. Be noted that the EdgeTPU requires a specific TFLite\n    model that is different from the one used above.\n\n```\npython3 detect.py \\\n  --enableEdgeTPU\n  --model efficientdet_lite0_edgetpu.tflite\n```\n\nYou should see significantly faster inference speeds.\n\nFor more information about creating and running TensorFlow Lite models with\nCoral devices, read\n[TensorFlow models on the Edge TPU](https://coral.withgoogle.com/docs/edgetpu/models-intro/).\n"
  },
  {
    "path": "lite/examples/object_detection/raspberry_pi/detect.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Main script to run the object detection routine.\"\"\"\nimport argparse\nimport sys\nimport time\n\nimport cv2\nfrom tflite_support.task import core\nfrom tflite_support.task import processor\nfrom tflite_support.task import vision\nimport utils\n\n\ndef run(model: str, camera_id: int, width: int, height: int, num_threads: int,\n        enable_edgetpu: bool) -> None:\n  \"\"\"Continuously run inference on images acquired from the camera.\n\n  Args:\n    model: Name of the TFLite object detection model.\n    camera_id: The camera id to be passed to OpenCV.\n    width: The width of the frame captured from the camera.\n    height: The height of the frame captured from the camera.\n    num_threads: The number of CPU threads to run the model.\n    enable_edgetpu: True/False whether the model is a EdgeTPU model.\n  \"\"\"\n\n  # Variables to calculate FPS\n  counter, fps = 0, 0\n  start_time = time.time()\n\n  # Start capturing video input from the camera\n  cap = cv2.VideoCapture(camera_id)\n  cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)\n  cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)\n\n  # Visualization parameters\n  row_size = 20  # pixels\n  left_margin = 24  # pixels\n  text_color = (0, 0, 255)  # red\n  font_size = 1\n  font_thickness = 1\n  fps_avg_frame_count = 10\n\n  # Initialize the object detection model\n  base_options = core.BaseOptions(\n      file_name=model, use_coral=enable_edgetpu, num_threads=num_threads)\n  detection_options = processor.DetectionOptions(\n      max_results=3, score_threshold=0.3)\n  options = vision.ObjectDetectorOptions(\n      base_options=base_options, detection_options=detection_options)\n  detector = vision.ObjectDetector.create_from_options(options)\n\n  # Continuously capture images from the camera and run inference\n  while cap.isOpened():\n    success, image = cap.read()\n    if not success:\n      sys.exit(\n          'ERROR: Unable to read from webcam. Please verify your webcam settings.'\n      )\n\n    counter += 1\n    image = cv2.flip(image, 1)\n\n    # Convert the image from BGR to RGB as required by the TFLite model.\n    rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n\n    # Create a TensorImage object from the RGB image.\n    input_tensor = vision.TensorImage.create_from_array(rgb_image)\n\n    # Run object detection estimation using the model.\n    detection_result = detector.detect(input_tensor)\n\n    # Draw keypoints and edges on input image\n    image = utils.visualize(image, detection_result)\n\n    # Calculate the FPS\n    if counter % fps_avg_frame_count == 0:\n      end_time = time.time()\n      fps = fps_avg_frame_count / (end_time - start_time)\n      start_time = time.time()\n\n    # Show the FPS\n    fps_text = 'FPS = {:.1f}'.format(fps)\n    text_location = (left_margin, row_size)\n    cv2.putText(image, fps_text, text_location, cv2.FONT_HERSHEY_PLAIN,\n                font_size, text_color, font_thickness)\n\n    # Stop the program if the ESC key is pressed.\n    if cv2.waitKey(1) == 27:\n      break\n    cv2.imshow('object_detector', image)\n\n  cap.release()\n  cv2.destroyAllWindows()\n\n\ndef main():\n  parser = argparse.ArgumentParser(\n      formatter_class=argparse.ArgumentDefaultsHelpFormatter)\n  parser.add_argument(\n      '--model',\n      help='Path of the object detection model.',\n      required=False,\n      default='efficientdet_lite0.tflite')\n  parser.add_argument(\n      '--cameraId', help='Id of camera.', required=False, type=int, default=0)\n  parser.add_argument(\n      '--frameWidth',\n      help='Width of frame to capture from camera.',\n      required=False,\n      type=int,\n      default=640)\n  parser.add_argument(\n      '--frameHeight',\n      help='Height of frame to capture from camera.',\n      required=False,\n      type=int,\n      default=480)\n  parser.add_argument(\n      '--numThreads',\n      help='Number of CPU threads to run the model.',\n      required=False,\n      type=int,\n      default=4)\n  parser.add_argument(\n      '--enableEdgeTPU',\n      help='Whether to run the model on EdgeTPU.',\n      action='store_true',\n      required=False,\n      default=False)\n  args = parser.parse_args()\n\n  run(args.model, int(args.cameraId), args.frameWidth, args.frameHeight,\n      int(args.numThreads), bool(args.enableEdgeTPU))\n\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "lite/examples/object_detection/raspberry_pi/requirements.txt",
    "content": "argparse\nnumpy>=1.20.0  # To ensure compatibility with OpenCV on Raspberry Pi.\nopencv-python~=4.5.3.56\ntflite-support>=0.4.2\nprotobuf>=3.18.0,<4\n"
  },
  {
    "path": "lite/examples/object_detection/raspberry_pi/setup.sh",
    "content": "#!/bin/bash\n\nif [ $# -eq 0 ]; then\n  DATA_DIR=\"./\"\nelse\n  DATA_DIR=\"$1\"\nfi\n\n# Install Python dependencies\npython3 -m pip install pip --upgrade\npython3 -m pip install -r requirements.txt\n\n# Download TF Lite models\nFILE=${DATA_DIR}/efficientdet_lite0.tflite\nif [ ! -f \"$FILE\" ]; then\n  curl \\\n    -L 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/object_detection/rpi/lite-model_efficientdet_lite0_detection_metadata_1.tflite' \\\n    -o ${FILE}\nfi\n\nFILE=${DATA_DIR}/efficientdet_lite0_edgetpu.tflite\nif [ ! -f \"$FILE\" ]; then\n  curl \\\n    -L 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/object_detection/rpi/efficientdet_lite0_edgetpu_metadata.tflite' \\\n    -o ${FILE}\nfi\n\necho -e \"Downloaded files are in ${DATA_DIR}\"\n"
  },
  {
    "path": "lite/examples/object_detection/raspberry_pi/test_data/table_results.csv",
    "content": "label,left,top,right,bottom,score\ncup,63,199,205,376,0.66796875\ndining table,-5,127,622,479,0.5859375\nknife,341,250,592,419,0.45703125\nbook,94,127,402,219,0.43359375\nrefrigerator,259,0,486,123,0.27734375\n"
  },
  {
    "path": "lite/examples/object_detection/raspberry_pi/utils.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Utility functions to display the pose detection results.\"\"\"\n\nimport cv2\nimport numpy as np\nfrom tflite_support.task import processor\n\n_MARGIN = 10  # pixels\n_ROW_SIZE = 10  # pixels\n_FONT_SIZE = 1\n_FONT_THICKNESS = 1\n_TEXT_COLOR = (0, 0, 255)  # red\n\n\ndef visualize(\n    image: np.ndarray,\n    detection_result: processor.DetectionResult,\n) -> np.ndarray:\n  \"\"\"Draws bounding boxes on the input image and return it.\n\n  Args:\n    image: The input RGB image.\n    detection_result: The list of all \"Detection\" entities to be visualize.\n\n  Returns:\n    Image with bounding boxes.\n  \"\"\"\n  for detection in detection_result.detections:\n    # Draw bounding_box\n    bbox = detection.bounding_box\n    start_point = bbox.origin_x, bbox.origin_y\n    end_point = bbox.origin_x + bbox.width, bbox.origin_y + bbox.height\n    cv2.rectangle(image, start_point, end_point, _TEXT_COLOR, 3)\n\n    # Draw label and score\n    category = detection.categories[0]\n    category_name = category.category_name\n    probability = round(category.score, 2)\n    result_text = category_name + ' (' + str(probability) + ')'\n    text_location = (_MARGIN + bbox.origin_x,\n                     _MARGIN + _ROW_SIZE + bbox.origin_y)\n    cv2.putText(image, result_text, text_location, cv2.FONT_HERSHEY_PLAIN,\n                _FONT_SIZE, _TEXT_COLOR, _FONT_THICKNESS)\n\n  return image\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/README.md",
    "content": "# OCR (Optical Character Recognition) Android sample.\n\nOCR is the process of recognizing characters from images using computer vision\nand machine learning techniques. This reference app demos how to use TensorFlow\nLite to do OCR. It uses a text detection model and a text recognition model as a\npipeline to recognize texts.\n\n## Requirements\n\n*   Android Studio 4.2 (installed on a Linux, Mac or Windows machine)\n*   An Android device, or an Android Emulator\n\n## Build and run\n\n### Step 1. Clone the TensorFlow examples source code\n\nClone the TensorFlow examples GitHub repository to your computer to get the demo\napplication.\n\n```\ngit clone https://github.com/tensorflow/examples\n```\n\n### Step 2. Import the sample app to Android Studio\n\nOpen the TensorFlow source code in Android Studio. To do this, open Android\nStudio and select `Import Projects (Gradle, Eclipse ADT, etc.)`, setting the\nfolder to `examples/lite/examples/optical_character_recognition/android`\n\n### Step 3. Run the Android app\n\nConnect the Android device to the computer and be sure to approve any ADB\npermission prompts that appear on your phone. Select `Run -> Run app.` Select\nthe deployment target in the connected devices to the device on which the app\nwill be installed. This will install the app on the device.\n\nTo test the app, open the app called `TFL OCR` on your device. Re-installing the\napp may require you to uninstall the previous installations.\n\nFor gradle CLI, running `./gradlew build` can create APKs for both solutions\nunder `app/build/outputs/apk`.\n\n## Limitations\n\n*   The current\n    [text recognition model](https://tfhub.dev/tulasiram58827/lite-model/keras-ocr/float16/2)\n    is trained using synthetic data with English letters and numbers, so only\n    English is supported.\n\n*   The models are not general enough for OCR in the wild (say, random images\n    taken by a smartphone camera in a low lighting condition).\n\nSo we have chosen 3 Google product logos only to demonstrate how to do OCR with\nTensorFlow Lite. If you are looking for a ready-to-use production-grade OCR\nproduct, you should consider\n[Google ML Kit](https://developers.google.com/ml-kit/vision/text-recognition).\nML Kit, which uses TFLIte underneath, should be sufficient for most OCR use\ncases, but there are some cases where you may want to build your own OCR\nsolution with TFLite. Some examples are:\n\n*   You have your own text detection/recognition TFLite models that you would\n    like to use\n*   You have special business requirements (i.e., recognizing texts that are\n    upside down) and need to customize the OCR pipeline\n*   You want to support languages not covered by ML Kit\n*   Your target user devices don’t necessarily have Google Play services\n    installed\n\n## References\n\n*   OpenCV text detection/recognition:\n    https://github.com/opencv/opencv/blob/master/samples/dnn/text_detection.py\n*   OCR TFLite community project by commnuity contributors @Tulasi123789 and\n    @risingsayak https://github.com/tulasiram58827/ocr_tflite\n*   OpenCV text detection:\n    https://www.pyimagesearch.com/2018/08/20/opencv-text-detection-east-text-detector/\n*   Deep Learning based Text Detection Using OpenCV:\n    https://learnopencv.com/deep-learning-based-text-detection-using-opencv-c-python/\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/build.gradle",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\napply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-android-extensions'\napply plugin: 'kotlin-kapt'\n\nandroid {\n    compileSdkVersion 29\n    buildToolsVersion \"29.0.2\"\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.ocr\"\n        minSdkVersion 21\n        targetSdkVersion 29\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n    aaptOptions {\n        noCompress \"tflite\"\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n    // If you find lint problem like:\n    // * What went wrong:\n    // A problem was found with the configuration of task ':app:lint'.\n    // > No value has been specified for property 'lintClassPath'.\n    //\n    // Probably you haven't set ANDROID_HOME. To fix:\n    // export ANDROID_HOME=$HOME/Android/Sdk   # Your Android SDK path.\n    lintOptions {\n        abortOnError false\n    }\n}\n\n// App assets folder:\nproject.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'\n\n// Download TF Lite model.\napply from: 'download.gradle'\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n\n    implementation 'androidx.appcompat:appcompat:1.1.0'\n    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'\n    implementation 'androidx.coordinatorlayout:coordinatorlayout:1.1.0'\n    implementation 'androidx.legacy:legacy-support-v4:1.0.0'\n    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'\n    implementation 'androidx.lifecycle:lifecycle-viewmodel:2.2.0'\n    implementation 'androidx.recyclerview:recyclerview:1.1.0'\n    implementation 'com.github.bumptech.glide:glide:4.11.0'\n    implementation 'com.google.android.material:material:1.1.0'\n    implementation 'com.google.code.gson:gson:2.8.6'\n    implementation 'com.google.guava:guava:28.2-android'\n    implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.3.71'\n    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0'\n    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0'\n\n    implementation 'org.tensorflow:tensorflow-lite:0.0.0-nightly-SNAPSHOT'\n    implementation 'org.tensorflow:tensorflow-lite-gpu:0.0.0-nightly-SNAPSHOT'\n    implementation 'org.tensorflow:tensorflow-lite-select-tf-ops:0.0.0-nightly-SNAPSHOT'\n    implementation 'org.tensorflow:tensorflow-lite-support:0.0.0-nightly-SNAPSHOT'\n\n    implementation 'com.quickbirdstudios:opencv:4.5.3.0'\n}\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/download.gradle",
    "content": "apply plugin: 'de.undercouch.download'\n\ntask downloadTextDetectionModelFile(type: Download) {\n    src 'https://tfhub.dev/sayakpaul/lite-model/east-text-detector/fp16/1?lite-format=tflite'\n    dest project.ext.ASSET_DIR + '/text_detection.tflite'\n    overwrite false\n}\n\ntask downloadTextRecognitionModelFile(type: Download) {\n    src 'https://tfhub.dev/tulasiram58827/lite-model/keras-ocr/float16/2?lite-format=tflite'\n    dest project.ext.ASSET_DIR + '/text_recognition.tflite'\n    overwrite false\n}\n\npreBuild.dependsOn downloadTextDetectionModelFile\npreBuild.dependsOn downloadTextRecognitionModelFile"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/settings.gradle",
    "content": "rootProject.name = 'TFLite OCR Demo App'\ninclude ':app'"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          xmlns:tools=\"http://schemas.android.com/tools\"\n          package=\"org.tensorflow.lite.examples.ocr\">\n\n  <uses-sdk />\n\n  <application\n          android:allowBackup=\"true\"\n          android:icon=\"@mipmap/ic_launcher\"\n          android:label=\"@string/tfe_ocr_app_name\"\n          android:roundIcon=\"@mipmap/ic_launcher_round\"\n          android:supportsRtl=\"true\"\n          android:theme=\"@style/AppTheme.OCR\"\n          tools:ignore=\"GoogleAppIndexingWarning\">\n\n    <activity android:name=\".MainActivity\"\n              android:exported=\"true\">\n      <intent-filter>\n        <action android:name=\"android.intent.action.MAIN\"/>\n        <category android:name=\"android.intent.category.LAUNCHER\"/>\n      </intent-filter>\n    </activity>\n  </application>\n\n</manifest>\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/src/main/java/org/tensorflow/lite/examples/ocr/ImageUtils.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.ocr\n\nimport android.graphics.Bitmap\nimport android.graphics.BitmapFactory\nimport android.graphics.ImageFormat.RGB_565\nimport android.graphics.Matrix\nimport androidx.exifinterface.media.ExifInterface\nimport java.io.File\nimport org.tensorflow.lite.DataType\nimport org.tensorflow.lite.support.common.ops.NormalizeOp\nimport org.tensorflow.lite.support.image.ImageProcessor\nimport org.tensorflow.lite.support.image.TensorImage\nimport org.tensorflow.lite.support.image.ops.ResizeOp\nimport org.tensorflow.lite.support.image.ops.TransformToGrayscaleOp\n\n/** Collection of image reading and manipulation utilities in the form of static functions. */\nabstract class ImageUtils {\n  companion object {\n\n    /**\n     * Helper function used to convert an EXIF orientation enum into a transformation matrix that\n     * can be applied to a bitmap.\n     *\n     * @param orientation\n     * - One of the constants from [ExifInterface]\n     */\n    private fun decodeExifOrientation(orientation: Int): Matrix {\n      val matrix = Matrix()\n\n      // Apply transformation corresponding to declared EXIF orientation\n      when (orientation) {\n        ExifInterface.ORIENTATION_NORMAL, ExifInterface.ORIENTATION_UNDEFINED -> Unit\n        ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90F)\n        ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180F)\n        ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270F)\n        ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> matrix.postScale(-1F, 1F)\n        ExifInterface.ORIENTATION_FLIP_VERTICAL -> matrix.postScale(1F, -1F)\n        ExifInterface.ORIENTATION_TRANSPOSE -> {\n          matrix.postScale(-1F, 1F)\n          matrix.postRotate(270F)\n        }\n        ExifInterface.ORIENTATION_TRANSVERSE -> {\n          matrix.postScale(-1F, 1F)\n          matrix.postRotate(90F)\n        }\n\n        // Error out if the EXIF orientation is invalid\n        else -> throw IllegalArgumentException(\"Invalid orientation: $orientation\")\n      }\n\n      // Return the resulting matrix\n      return matrix\n    }\n\n    /**\n     * sets the Exif orientation of an image. this method is used to fix the exit of pictures taken\n     * by the camera\n     *\n     * @param filePath\n     * - The image file to change\n     * @param value\n     * - the orientation of the file\n     */\n    fun setExifOrientation(filePath: String, value: String) {\n      val exif = ExifInterface(filePath)\n      exif.setAttribute(ExifInterface.TAG_ORIENTATION, value)\n      exif.saveAttributes()\n    }\n\n    /** Transforms rotation and mirroring information into one of the [ExifInterface] constants */\n    fun computeExifOrientation(rotationDegrees: Int, mirrored: Boolean) =\n      when {\n        rotationDegrees == 0 && !mirrored -> ExifInterface.ORIENTATION_NORMAL\n        rotationDegrees == 0 && mirrored -> ExifInterface.ORIENTATION_FLIP_HORIZONTAL\n        rotationDegrees == 180 && !mirrored -> ExifInterface.ORIENTATION_ROTATE_180\n        rotationDegrees == 180 && mirrored -> ExifInterface.ORIENTATION_FLIP_VERTICAL\n        rotationDegrees == 270 && mirrored -> ExifInterface.ORIENTATION_TRANSVERSE\n        rotationDegrees == 90 && !mirrored -> ExifInterface.ORIENTATION_ROTATE_90\n        rotationDegrees == 90 && mirrored -> ExifInterface.ORIENTATION_TRANSPOSE\n        rotationDegrees == 270 && mirrored -> ExifInterface.ORIENTATION_ROTATE_270\n        rotationDegrees == 270 && !mirrored -> ExifInterface.ORIENTATION_TRANSVERSE\n        else -> ExifInterface.ORIENTATION_UNDEFINED\n      }\n\n    /**\n     * Decode a bitmap from a file and apply the transformations described in its EXIF data\n     *\n     * @param file\n     * - The image file to be read using [BitmapFactory.decodeFile]\n     */\n    fun decodeBitmap(file: File): Bitmap {\n      // First, decode EXIF data and retrieve transformation matrix\n      val exif = ExifInterface(file.absolutePath)\n      val transformation =\n        decodeExifOrientation(\n          exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_ROTATE_90)\n        )\n\n      // Read bitmap using factory methods, and transform it using EXIF data\n      val options = BitmapFactory.Options()\n      val bitmap = BitmapFactory.decodeFile(file.absolutePath, options)\n      return Bitmap.createBitmap(\n        BitmapFactory.decodeFile(file.absolutePath),\n        0,\n        0,\n        bitmap.width,\n        bitmap.height,\n        transformation,\n        true\n      )\n    }\n\n    /**\n     * Convert a bitmap to a TensorImage for the recognition model with target size and\n     * normalization\n     *\n     * @param bitmapIn\n     * - the bitmap to convert\n     * @param width\n     * - target width of the converted TensorImage\n     * @param height\n     * - target height of the converted TensorImage\n     * @param means\n     * - means of the images\n     * @param stds\n     * - stds of the images\n     */\n    fun bitmapToTensorImageForRecognition(\n      bitmapIn: Bitmap,\n      width: Int,\n      height: Int,\n      mean: Float,\n      std: Float\n    ): TensorImage {\n      val imageProcessor =\n        ImageProcessor.Builder()\n          .add(ResizeOp(height, width, ResizeOp.ResizeMethod.BILINEAR))\n          .add(TransformToGrayscaleOp())\n          .add(NormalizeOp(mean, std))\n          .build()\n      var tensorImage = TensorImage(DataType.FLOAT32)\n\n      tensorImage.load(bitmapIn)\n      tensorImage = imageProcessor.process(tensorImage)\n\n      return tensorImage\n    }\n\n    /**\n     * Convert a bitmap to a TensorImage for the detection model with target size and normalization\n     *\n     * @param bitmapIn\n     * - the bitmap to convert\n     * @param width\n     * - target width of the converted TensorImage\n     * @param height\n     * - target height of the converted TensorImage\n     * @param means\n     * - means of the images\n     * @param stds\n     * - stds of the images\n     */\n    fun bitmapToTensorImageForDetection(\n      bitmapIn: Bitmap,\n      width: Int,\n      height: Int,\n      means: FloatArray,\n      stds: FloatArray\n    ): TensorImage {\n      val imageProcessor =\n        ImageProcessor.Builder()\n          .add(ResizeOp(height, width, ResizeOp.ResizeMethod.BILINEAR))\n          .add(NormalizeOp(means, stds))\n          .build()\n      var tensorImage = TensorImage(DataType.FLOAT32)\n\n      tensorImage.load(bitmapIn)\n      tensorImage = imageProcessor.process(tensorImage)\n\n      return tensorImage\n    }\n\n    fun createEmptyBitmap(\n      imageWidth: Int,\n      imageHeigth: Int,\n      color: Int = 0,\n      imageConfig: Bitmap.Config = Bitmap.Config.RGB_565\n    ): Bitmap {\n      val ret = Bitmap.createBitmap(imageWidth, imageHeigth, imageConfig)\n      if (color != 0) {\n        ret.eraseColor(color)\n      }\n      return ret\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/src/main/java/org/tensorflow/lite/examples/ocr/MLExecutionViewModel.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.ocr\n\nimport androidx.lifecycle.ViewModel\nimport android.content.Context\nimport android.graphics.BitmapFactory\nimport android.util.Log\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MutableLiveData\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.ExecutorCoroutineDispatcher\nimport kotlinx.coroutines.Job\nimport kotlinx.coroutines.launch\n\nprivate const val TAG = \"MLExecutionViewModel\"\n\nclass MLExecutionViewModel : ViewModel() {\n\n  private val _resultingBitmap = MutableLiveData<ModelExecutionResult>()\n\n  val resultingBitmap: LiveData<ModelExecutionResult>\n    get() = _resultingBitmap\n\n  private val viewModelJob = Job()\n  private val viewModelScope = CoroutineScope(viewModelJob)\n\n  // the execution of the model has to be on the same thread where the interpreter\n  // was created\n  fun onApplyModel(\n    context: Context,\n    fileName: String,\n    ocrModel: OCRModelExecutor?,\n    inferenceThread: ExecutorCoroutineDispatcher\n  ) {\n    viewModelScope.launch(inferenceThread) {\n      val inputStream = context.assets.open(fileName)\n      val contentImage = BitmapFactory.decodeStream(inputStream)\n      try {\n        val result = ocrModel?.execute(contentImage)\n        _resultingBitmap.postValue(result)\n      } catch (e: Exception) {\n        Log.e(TAG, \"Fail to execute OCRModelExecutor: ${e.message}\")\n        _resultingBitmap.postValue(null)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/src/main/java/org/tensorflow/lite/examples/ocr/MainActivity.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.ocr\n\nimport android.annotation.SuppressLint\nimport androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory\nimport android.content.res.ColorStateList\nimport android.graphics.Bitmap\nimport android.graphics.BitmapFactory\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.appcompat.widget.Toolbar\nimport android.util.Log\nimport android.view.MotionEvent\nimport android.view.View\nimport android.widget.Button\nimport android.widget.ImageView\nimport android.widget.Switch\nimport android.widget.TextView\nimport androidx.lifecycle.Observer\nimport com.bumptech.glide.Glide\nimport com.google.android.material.chip.Chip\nimport com.google.android.material.chip.ChipGroup\nimport java.io.IOException\nimport java.io.InputStream\nimport java.util.concurrent.Executors\nimport kotlinx.coroutines.MainScope\nimport kotlinx.coroutines.asCoroutineDispatcher\nimport kotlinx.coroutines.async\nimport kotlinx.coroutines.sync.Mutex\nimport kotlinx.coroutines.sync.withLock\n\nprivate const val TAG = \"MainActivity\"\n\nclass MainActivity : AppCompatActivity() {\n\n  private val tfImageName = \"tensorflow.jpg\"\n  private val androidImageName = \"android.jpg\"\n  private val chromeImageName = \"chrome.jpg\"\n  private lateinit var viewModel: MLExecutionViewModel\n  private lateinit var resultImageView: ImageView\n  private lateinit var tfImageView: ImageView\n  private lateinit var androidImageView: ImageView\n  private lateinit var chromeImageView: ImageView\n  private lateinit var chipsGroup: ChipGroup\n  private lateinit var runButton: Button\n  private lateinit var textPromptTextView: TextView\n\n  private var useGPU = false\n  private var selectedImageName = \"tensorflow.jpg\"\n  private var ocrModel: OCRModelExecutor? = null\n  private val inferenceThread = Executors.newSingleThreadExecutor().asCoroutineDispatcher()\n  private val mainScope = MainScope()\n  private val mutex = Mutex()\n\n  override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n    setContentView(R.layout.tfe_is_activity_main)\n\n    val toolbar: Toolbar = findViewById(R.id.toolbar)\n    setSupportActionBar(toolbar)\n    supportActionBar?.setDisplayShowTitleEnabled(false)\n\n    tfImageView = findViewById(R.id.tf_imageview)\n    androidImageView = findViewById(R.id.android_imageview)\n    chromeImageView = findViewById(R.id.chrome_imageview)\n\n    val candidateImageViews = arrayOf<ImageView>(tfImageView, androidImageView, chromeImageView)\n\n    val assetManager = assets\n    try {\n      val tfInputStream: InputStream = assetManager.open(tfImageName)\n      val tfBitmap = BitmapFactory.decodeStream(tfInputStream)\n      tfImageView.setImageBitmap(tfBitmap)\n      val androidInputStream: InputStream = assetManager.open(androidImageName)\n      val androidBitmap = BitmapFactory.decodeStream(androidInputStream)\n      androidImageView.setImageBitmap(androidBitmap)\n      val chromeInputStream: InputStream = assetManager.open(chromeImageName)\n      val chromeBitmap = BitmapFactory.decodeStream(chromeInputStream)\n      chromeImageView.setImageBitmap(chromeBitmap)\n    } catch (e: IOException) {\n      Log.e(TAG, \"Failed to open a test image\")\n    }\n\n    for (iv in candidateImageViews) {\n      setInputImageViewListener(iv)\n    }\n\n    resultImageView = findViewById(R.id.result_imageview)\n    chipsGroup = findViewById(R.id.chips_group)\n    textPromptTextView = findViewById(R.id.text_prompt)\n    val useGpuSwitch: Switch = findViewById(R.id.switch_use_gpu)\n\n    viewModel = AndroidViewModelFactory(application).create(MLExecutionViewModel::class.java)\n    viewModel.resultingBitmap.observe(\n      this,\n      Observer { resultImage ->\n        if (resultImage != null) {\n          updateUIWithResults(resultImage)\n        }\n        enableControls(true)\n      }\n    )\n\n    mainScope.async(inferenceThread) { createModelExecutor(useGPU) }\n\n    useGpuSwitch.setOnCheckedChangeListener { _, isChecked ->\n      useGPU = isChecked\n      mainScope.async(inferenceThread) { createModelExecutor(useGPU) }\n    }\n\n    runButton = findViewById(R.id.rerun_button)\n    runButton.setOnClickListener {\n      enableControls(false)\n\n      mainScope.async(inferenceThread) {\n        mutex.withLock {\n          if (ocrModel != null) {\n            viewModel.onApplyModel(baseContext, selectedImageName, ocrModel, inferenceThread)\n          } else {\n            Log.d(\n              TAG,\n              \"Skipping running OCR since the ocrModel has not been properly initialized ...\"\n            )\n          }\n        }\n      }\n    }\n\n    setChipsToLogView(HashMap<String, Int>())\n    enableControls(true)\n  }\n\n  @SuppressLint(\"ClickableViewAccessibility\")\n  private fun setInputImageViewListener(iv: ImageView) {\n    iv.setOnTouchListener(\n      object : View.OnTouchListener {\n        override fun onTouch(v: View, event: MotionEvent?): Boolean {\n          if (v.equals(tfImageView)) {\n            selectedImageName = tfImageName\n            textPromptTextView.setText(getResources().getString(R.string.tfe_using_first_image))\n          } else if (v.equals(androidImageView)) {\n            selectedImageName = androidImageName\n            textPromptTextView.setText(getResources().getString(R.string.tfe_using_second_image))\n          } else if (v.equals(chromeImageView)) {\n            selectedImageName = chromeImageName\n            textPromptTextView.setText(getResources().getString(R.string.tfe_using_third_image))\n          }\n          return false\n        }\n      }\n    )\n  }\n\n  private suspend fun createModelExecutor(useGPU: Boolean) {\n    mutex.withLock {\n      if (ocrModel != null) {\n        ocrModel!!.close()\n        ocrModel = null\n      }\n      try {\n        ocrModel = OCRModelExecutor(this, useGPU)\n      } catch (e: Exception) {\n        Log.e(TAG, \"Fail to create OCRModelExecutor: ${e.message}\")\n        val logText: TextView = findViewById(R.id.log_view)\n        logText.text = e.message\n      }\n    }\n  }\n\n  private fun setChipsToLogView(itemsFound: Map<String, Int>) {\n    chipsGroup.removeAllViews()\n\n    for ((word, color) in itemsFound) {\n      val chip = Chip(this)\n      chip.text = word\n      chip.chipBackgroundColor = getColorStateListForChip(color)\n      chip.isClickable = false\n      chipsGroup.addView(chip)\n    }\n    val labelsFoundTextView: TextView = findViewById(R.id.tfe_is_labels_found)\n    if (chipsGroup.childCount == 0) {\n      labelsFoundTextView.text = getString(R.string.tfe_ocr_no_text_found)\n    } else {\n      labelsFoundTextView.text = getString(R.string.tfe_ocr_texts_found)\n    }\n    chipsGroup.parent.requestLayout()\n  }\n\n  private fun getColorStateListForChip(color: Int): ColorStateList {\n    val states =\n      arrayOf(\n        intArrayOf(android.R.attr.state_enabled), // enabled\n        intArrayOf(android.R.attr.state_pressed) // pressed\n      )\n\n    val colors = intArrayOf(color, color)\n    return ColorStateList(states, colors)\n  }\n\n  private fun setImageView(imageView: ImageView, image: Bitmap) {\n    Glide.with(baseContext).load(image).override(250, 250).fitCenter().into(imageView)\n  }\n\n  private fun updateUIWithResults(modelExecutionResult: ModelExecutionResult) {\n    setImageView(resultImageView, modelExecutionResult.bitmapResult)\n    val logText: TextView = findViewById(R.id.log_view)\n    logText.text = modelExecutionResult.executionLog\n\n    setChipsToLogView(modelExecutionResult.itemsFound)\n    enableControls(true)\n  }\n\n  private fun enableControls(enable: Boolean) {\n    runButton.isEnabled = enable\n  }\n}\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/src/main/java/org/tensorflow/lite/examples/ocr/ModelExecutionResult.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.ocr\n\nimport android.graphics.Bitmap\n\ndata class ModelExecutionResult(\n  val bitmapResult: Bitmap,\n  val executionLog: String,\n  // A map between words and colors of the items found.\n  val itemsFound: Map<String, Int>\n)\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/src/main/java/org/tensorflow/lite/examples/ocr/OCRModelExecutor.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.ocr\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.graphics.Canvas\nimport android.graphics.Color\nimport android.graphics.Paint\nimport android.util.Log\nimport java.io.FileInputStream\nimport java.io.IOException\nimport java.nio.ByteBuffer\nimport java.nio.ByteOrder\nimport java.nio.MappedByteBuffer\nimport java.nio.channels.FileChannel\nimport java.util.Random\nimport kotlin.collections.ArrayList\nimport kotlin.collections.HashMap\nimport org.opencv.android.OpenCVLoader\nimport org.opencv.android.Utils.bitmapToMat\nimport org.opencv.android.Utils.matToBitmap\nimport org.opencv.core.Mat\nimport org.opencv.core.MatOfFloat\nimport org.opencv.core.MatOfInt\nimport org.opencv.core.MatOfPoint2f\nimport org.opencv.core.MatOfRotatedRect\nimport org.opencv.core.Point\nimport org.opencv.core.RotatedRect\nimport org.opencv.core.Size\nimport org.opencv.dnn.Dnn.NMSBoxesRotated\nimport org.opencv.imgproc.Imgproc.boxPoints\nimport org.opencv.imgproc.Imgproc.getPerspectiveTransform\nimport org.opencv.imgproc.Imgproc.warpPerspective\nimport org.opencv.utils.Converters.vector_RotatedRect_to_Mat\nimport org.opencv.utils.Converters.vector_float_to_Mat\nimport org.tensorflow.lite.Interpreter\nimport org.tensorflow.lite.gpu.GpuDelegate\n\n/**\n * Class to run the OCR models. The OCR process is broken down into 2 stages: 1) Text detection\n * using [EAST model](https://tfhub.dev/sayakpaul/lite-model/east-text-detector/fp16/1) 2) Text\n * recognition using\n * [Keras OCR model](https://tfhub.dev/tulasiram58827/lite-model/keras-ocr/float16/2)\n */\nclass OCRModelExecutor(context: Context, private var useGPU: Boolean = false) : AutoCloseable {\n  private var gpuDelegate: GpuDelegate? = null\n\n  private val recognitionResult: ByteBuffer\n  private val detectionInterpreter: Interpreter\n  private val recognitionInterpreter: Interpreter\n\n  private var ratioHeight = 0.toFloat()\n  private var ratioWidth = 0.toFloat()\n  private var indicesMat: MatOfInt\n  private var boundingBoxesMat: MatOfRotatedRect\n  private var ocrResults: HashMap<String, Int>\n\n  init {\n    try {\n      if (!OpenCVLoader.initDebug()) throw Exception(\"Unable to load OpenCV\")\n      else Log.d(TAG, \"OpenCV loaded\")\n    } catch (e: Exception) {\n      val exceptionLog = \"something went wrong: ${e.message}\"\n      Log.d(TAG, exceptionLog)\n    }\n\n    detectionInterpreter = getInterpreter(context, textDetectionModel, useGPU)\n    // Recognition model requires Flex so we disable GPU delegate no matter user choice\n    recognitionInterpreter = getInterpreter(context, textRecognitionModel, false)\n\n    recognitionResult = ByteBuffer.allocateDirect(recognitionModelOutputSize * 8)\n    recognitionResult.order(ByteOrder.nativeOrder())\n    indicesMat = MatOfInt()\n    boundingBoxesMat = MatOfRotatedRect()\n    ocrResults = HashMap<String, Int>()\n  }\n\n  fun execute(data: Bitmap): ModelExecutionResult {\n    try {\n      ratioHeight = data.height.toFloat() / detectionImageHeight\n      ratioWidth = data.width.toFloat() / detectionImageWidth\n      ocrResults.clear()\n\n      detectTexts(data)\n\n      val bitmapWithBoundingBoxes = recognizeTexts(data, boundingBoxesMat, indicesMat)\n\n      return ModelExecutionResult(bitmapWithBoundingBoxes, \"OCR result\", ocrResults)\n    } catch (e: Exception) {\n      val exceptionLog = \"something went wrong: ${e.message}\"\n      Log.d(TAG, exceptionLog)\n\n      val emptyBitmap = ImageUtils.createEmptyBitmap(displayImageSize, displayImageSize)\n      return ModelExecutionResult(emptyBitmap, exceptionLog, HashMap<String, Int>())\n    }\n  }\n\n  private fun detectTexts(data: Bitmap) {\n    val detectionTensorImage =\n      ImageUtils.bitmapToTensorImageForDetection(\n        data,\n        detectionImageWidth,\n        detectionImageHeight,\n        detectionImageMeans,\n        detectionImageStds\n      )\n\n    val detectionInputs = arrayOf(detectionTensorImage.buffer.rewind())\n    val detectionOutputs: HashMap<Int, Any> = HashMap<Int, Any>()\n\n    val detectionScores =\n      Array(1) { Array(detectionOutputNumRows) { Array(detectionOutputNumCols) { FloatArray(1) } } }\n    val detectionGeometries =\n      Array(1) { Array(detectionOutputNumRows) { Array(detectionOutputNumCols) { FloatArray(5) } } }\n    detectionOutputs.put(0, detectionScores)\n    detectionOutputs.put(1, detectionGeometries)\n\n    detectionInterpreter.runForMultipleInputsOutputs(detectionInputs, detectionOutputs)\n\n    val transposeddetectionScores =\n      Array(1) { Array(1) { Array(detectionOutputNumRows) { FloatArray(detectionOutputNumCols) } } }\n    val transposedDetectionGeometries =\n      Array(1) { Array(5) { Array(detectionOutputNumRows) { FloatArray(detectionOutputNumCols) } } }\n\n    // transpose detection output tensors\n    for (i in 0 until transposeddetectionScores[0][0].size) {\n      for (j in 0 until transposeddetectionScores[0][0][0].size) {\n        for (k in 0 until 1) {\n          transposeddetectionScores[0][k][i][j] = detectionScores[0][i][j][k]\n        }\n        for (k in 0 until 5) {\n          transposedDetectionGeometries[0][k][i][j] = detectionGeometries[0][i][j][k]\n        }\n      }\n    }\n\n    val detectedRotatedRects = ArrayList<RotatedRect>()\n    val detectedConfidences = ArrayList<Float>()\n\n    for (y in 0 until transposeddetectionScores[0][0].size) {\n      val detectionScoreData = transposeddetectionScores[0][0][y]\n      val detectionGeometryX0Data = transposedDetectionGeometries[0][0][y]\n      val detectionGeometryX1Data = transposedDetectionGeometries[0][1][y]\n      val detectionGeometryX2Data = transposedDetectionGeometries[0][2][y]\n      val detectionGeometryX3Data = transposedDetectionGeometries[0][3][y]\n      val detectionRotationAngleData = transposedDetectionGeometries[0][4][y]\n\n      for (x in 0 until transposeddetectionScores[0][0][0].size) {\n        if (detectionScoreData[x] < 0.5) {\n          continue\n        }\n\n        // Compute the rotated bounding boxes and confiences (heavily based on OpenCV example):\n        // https://github.com/opencv/opencv/blob/master/samples/dnn/text_detection.py\n        val offsetX = x * 4.0\n        val offsetY = y * 4.0\n\n        val h = detectionGeometryX0Data[x] + detectionGeometryX2Data[x]\n        val w = detectionGeometryX1Data[x] + detectionGeometryX3Data[x]\n\n        val angle = detectionRotationAngleData[x]\n        val cos = Math.cos(angle.toDouble())\n        val sin = Math.sin(angle.toDouble())\n\n        val offset =\n          Point(\n            offsetX + cos * detectionGeometryX1Data[x] + sin * detectionGeometryX2Data[x],\n            offsetY - sin * detectionGeometryX1Data[x] + cos * detectionGeometryX2Data[x]\n          )\n        val p1 = Point(-sin * h + offset.x, -cos * h + offset.y)\n        val p3 = Point(-cos * w + offset.x, sin * w + offset.y)\n        val center = Point(0.5 * (p1.x + p3.x), 0.5 * (p1.y + p3.y))\n\n        val textDetection =\n          RotatedRect(\n            center,\n            Size(w.toDouble(), h.toDouble()),\n            (-1 * angle * 180.0 / Math.PI)\n          )\n        detectedRotatedRects.add(textDetection)\n        detectedConfidences.add(detectionScoreData[x])\n      }\n    }\n\n    val detectedConfidencesMat = MatOfFloat(vector_float_to_Mat(detectedConfidences))\n\n    boundingBoxesMat = MatOfRotatedRect(vector_RotatedRect_to_Mat(detectedRotatedRects))\n    NMSBoxesRotated(\n      boundingBoxesMat,\n      detectedConfidencesMat,\n      detectionConfidenceThreshold.toFloat(),\n      detectionNMSThreshold.toFloat(),\n      indicesMat\n    )\n  }\n\n  private fun recognizeTexts(\n    data: Bitmap,\n    boundingBoxesMat: MatOfRotatedRect,\n    indicesMat: MatOfInt\n  ): Bitmap {\n    val bitmapWithBoundingBoxes = data.copy(Bitmap.Config.ARGB_8888, true)\n    val canvas = Canvas(bitmapWithBoundingBoxes)\n    val paint = Paint()\n    paint.style = Paint.Style.STROKE\n    paint.strokeWidth = 10.toFloat()\n    paint.setColor(Color.GREEN)\n\n    for (i in indicesMat.toArray()) {\n      val boundingBox = boundingBoxesMat.toArray()[i]\n      val targetVertices = ArrayList<Point>()\n      targetVertices.add(Point(0.toDouble(), (recognitionImageHeight - 1).toDouble()))\n      targetVertices.add(Point(0.toDouble(), 0.toDouble()))\n      targetVertices.add(Point((recognitionImageWidth - 1).toDouble(), 0.toDouble()))\n      targetVertices.add(\n        Point((recognitionImageWidth - 1).toDouble(), (recognitionImageHeight - 1).toDouble())\n      )\n\n      val srcVertices = ArrayList<Point>()\n\n      val boundingBoxPointsMat = Mat()\n      boxPoints(boundingBox, boundingBoxPointsMat)\n      for (j in 0 until 4) {\n        srcVertices.add(\n          Point(\n            boundingBoxPointsMat.get(j, 0)[0] * ratioWidth,\n            boundingBoxPointsMat.get(j, 1)[0] * ratioHeight\n          )\n        )\n        if (j != 0) {\n          canvas.drawLine(\n            (boundingBoxPointsMat.get(j, 0)[0] * ratioWidth).toFloat(),\n            (boundingBoxPointsMat.get(j, 1)[0] * ratioHeight).toFloat(),\n            (boundingBoxPointsMat.get(j - 1, 0)[0] * ratioWidth).toFloat(),\n            (boundingBoxPointsMat.get(j - 1, 1)[0] * ratioHeight).toFloat(),\n            paint\n          )\n        }\n      }\n      canvas.drawLine(\n        (boundingBoxPointsMat.get(0, 0)[0] * ratioWidth).toFloat(),\n        (boundingBoxPointsMat.get(0, 1)[0] * ratioHeight).toFloat(),\n        (boundingBoxPointsMat.get(3, 0)[0] * ratioWidth).toFloat(),\n        (boundingBoxPointsMat.get(3, 1)[0] * ratioHeight).toFloat(),\n        paint\n      )\n\n      val srcVerticesMat =\n        MatOfPoint2f(srcVertices[0], srcVertices[1], srcVertices[2], srcVertices[3])\n      val targetVerticesMat =\n        MatOfPoint2f(targetVertices[0], targetVertices[1], targetVertices[2], targetVertices[3])\n      val rotationMatrix = getPerspectiveTransform(srcVerticesMat, targetVerticesMat)\n      val recognitionBitmapMat = Mat()\n      val srcBitmapMat = Mat()\n      bitmapToMat(data, srcBitmapMat)\n      warpPerspective(\n        srcBitmapMat,\n        recognitionBitmapMat,\n        rotationMatrix,\n        Size(recognitionImageWidth.toDouble(), recognitionImageHeight.toDouble())\n      )\n\n      val recognitionBitmap =\n        ImageUtils.createEmptyBitmap(\n          recognitionImageWidth,\n          recognitionImageHeight,\n          0,\n          Bitmap.Config.ARGB_8888\n        )\n      matToBitmap(recognitionBitmapMat, recognitionBitmap)\n\n      val recognitionTensorImage =\n        ImageUtils.bitmapToTensorImageForRecognition(\n          recognitionBitmap,\n          recognitionImageWidth,\n          recognitionImageHeight,\n          recognitionImageMean,\n          recognitionImageStd\n        )\n\n      recognitionResult.rewind()\n      recognitionInterpreter.run(recognitionTensorImage.buffer, recognitionResult)\n\n      var recognizedText = \"\"\n      for (k in 0 until recognitionModelOutputSize) {\n        var alphabetIndex = recognitionResult.getInt(k * 8)\n        if (alphabetIndex in 0..alphabets.length - 1)\n          recognizedText = recognizedText + alphabets[alphabetIndex]\n      }\n      Log.d(\"Recognition result:\", recognizedText)\n      if (recognizedText != \"\") {\n        ocrResults.put(recognizedText, getRandomColor())\n      }\n    }\n    return bitmapWithBoundingBoxes\n  }\n  // base:\n  // https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/java/demo/app/src/main/java/com/example/android/tflitecamerademo/ImageClassifier.java\n  @Throws(IOException::class)\n  private fun loadModelFile(context: Context, modelFile: String): MappedByteBuffer {\n    val fileDescriptor = context.assets.openFd(modelFile)\n    val inputStream = FileInputStream(fileDescriptor.fileDescriptor)\n    val fileChannel = inputStream.channel\n    val startOffset = fileDescriptor.startOffset\n    val declaredLength = fileDescriptor.declaredLength\n    val retFile = fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength)\n    fileDescriptor.close()\n    return retFile\n  }\n\n  @Throws(IOException::class)\n  private fun getInterpreter(\n    context: Context,\n    modelName: String,\n    useGpu: Boolean = false\n  ): Interpreter {\n    val tfliteOptions = Interpreter.Options()\n    tfliteOptions.setNumThreads(numberThreads)\n\n    gpuDelegate = null\n    if (useGpu) {\n      gpuDelegate = GpuDelegate()\n      tfliteOptions.addDelegate(gpuDelegate)\n    }\n\n    return Interpreter(loadModelFile(context, modelName), tfliteOptions)\n  }\n\n  override fun close() {\n    detectionInterpreter.close()\n    recognitionInterpreter.close()\n    if (gpuDelegate != null) {\n      gpuDelegate!!.close()\n    }\n  }\n\n  fun getRandomColor(): Int {\n    val random = Random()\n    return Color.argb(\n      (128),\n      (255 * random.nextFloat()).toInt(),\n      (255 * random.nextFloat()).toInt(),\n      (255 * random.nextFloat()).toInt()\n    )\n  }\n\n  companion object {\n    public const val TAG = \"TfLiteOCRDemo\"\n    private const val textDetectionModel = \"text_detection.tflite\"\n    private const val textRecognitionModel = \"text_recognition.tflite\"\n    private const val numberThreads = 4\n    private const val alphabets = \"0123456789abcdefghijklmnopqrstuvwxyz\"\n    private const val displayImageSize = 257\n    private const val detectionImageHeight = 320\n    private const val detectionImageWidth = 320\n    private val detectionImageMeans =\n      floatArrayOf(103.94.toFloat(), 116.78.toFloat(), 123.68.toFloat())\n    private val detectionImageStds = floatArrayOf(1.toFloat(), 1.toFloat(), 1.toFloat())\n    private val detectionOutputNumRows = 80\n    private val detectionOutputNumCols = 80\n    private val detectionConfidenceThreshold = 0.5\n    private val detectionNMSThreshold = 0.4\n    private const val recognitionImageHeight = 31\n    private const val recognitionImageWidth = 200\n    private const val recognitionImageMean = 0.toFloat()\n    private const val recognitionImageStd = 255.toFloat()\n    private const val recognitionModelOutputSize = 48\n  }\n}\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/src/main/res/drawable/bottom_sheet_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <corners\n        android:topLeftRadius=\"@dimen/tfe_bottom_sheet_corner_radius\"\n        android:topRightRadius=\"@dimen/tfe_bottom_sheet_corner_radius\" />\n    <padding android:top=\"@dimen/tfe_bottom_sheet_top_padding\" />\n    <solid android:color=\"@android:color/white\" />\n</shape>"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillColor=\"#26A69A\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n</vector>\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/src/main/res/drawable/rounded_edge.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <solid\n        android:color=\"@color/tfe_is_color_light_gray\">\n    </solid>\n    <stroke\n        android:width=\"0dp\"\n        android:color=\"#424242\">\n    </stroke>\n    <corners\n        android:topLeftRadius=\"30dp\"\n        android:topRightRadius=\"30dp\"\n        android:bottomLeftRadius=\"0dp\"\n        android:bottomRightRadius=\"0dp\">\n    </corners>\n\n</shape>\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n  <path\n      android:fillType=\"evenOdd\"\n      android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\">\n    <aapt:attr name=\"android:fillColor\">\n      <gradient\n          android:endX=\"78.5885\"\n          android:endY=\"90.9159\"\n          android:startX=\"48.7653\"\n          android:startY=\"61.0927\"\n          android:type=\"linear\">\n        <item\n            android:color=\"#44000000\"\n            android:offset=\"0.0\"/>\n        <item\n            android:color=\"#00000000\"\n            android:offset=\"1.0\"/>\n      </gradient>\n    </aapt:attr>\n  </path>\n  <path\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"nonZero\"\n      android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/src/main/res/layout/tfe_is_activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"#00000000\"\n    tools:context=\"org.tensorflow.lite.examples.ocr.MainActivity\">\n\n    <androidx.appcompat.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?attr/actionBarSize\"\n        android:background=\"@android:color/white\">\n\n        <ImageView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:src=\"@drawable/tfl2_logo_dark\" />\n    </androidx.appcompat.widget.Toolbar>\n\n    <LinearLayout\n        android:id=\"@+id/images_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:gravity=\"center_horizontal\"\n        android:paddingTop=\"?attr/actionBarSize\">\n\n        <HorizontalScrollView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\">\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"horizontal\">\n\n                <ImageView\n                    android:id=\"@+id/tf_imageview\"\n                    android:layout_width=\"@dimen/tfe_is_preview_size\"\n                    android:layout_height=\"@dimen/tfe_is_preview_size\"\n                    android:contentDescription=\"@string/tfe_tf_image_description\"\n                    android:padding=\"8dp\" />\n\n                <ImageView\n                    android:id=\"@+id/android_imageview\"\n                    android:layout_width=\"@dimen/tfe_is_preview_size\"\n                    android:layout_height=\"@dimen/tfe_is_preview_size\"\n                    android:contentDescription=\"@string/tfe_android_image_description\"\n                    android:padding=\"8dp\" />\n\n                <ImageView\n                    android:id=\"@+id/chrome_imageview\"\n                    android:layout_width=\"@dimen/tfe_is_preview_size\"\n                    android:layout_height=\"@dimen/tfe_is_preview_size\"\n                    android:contentDescription=\"@string/tfe_chrome_image_description\"\n                    android:padding=\"8dp\" />\n            </LinearLayout>\n        </HorizontalScrollView>\n\n        <TextView\n            android:id=\"@+id/text_prompt\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/tfe_using_first_image\" />\n\n        <ImageView\n            android:id=\"@+id/result_imageview\"\n            android:layout_width=\"@dimen/tfe_is_preview_size\"\n            android:layout_height=\"@dimen/tfe_is_preview_size\"\n            android:contentDescription=\"@string/tfe_result_image_description\"\n            android:padding=\"8dp\" />\n\n    </LinearLayout>\n\n    <include layout=\"@layout/tfe_is_bottom_sheet_layout\" />\n\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/src/main/res/layout/tfe_is_bottom_sheet_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/bottom_sheet\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_margin=\"8dp\"\n    android:background=\"@drawable/rounded_edge\"\n    android:orientation=\"vertical\"\n    android:paddingTop=\"10dp\"\n    android:paddingBottom=\"16dp\"\n    app:behavior_hideable=\"false\"\n    app:behavior_peekHeight=\"36dp\"\n    app:layout_behavior=\"com.google.android.material.bottomsheet.BottomSheetBehavior\"\n    tools:showIn=\"@layout/tfe_is_activity_main\">\n\n    <ImageView\n        android:id=\"@+id/bottom_sheet_arrow\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:src=\"@drawable/icn_chevron_up\" />\n\n    <Switch\n        android:id=\"@+id/switch_use_gpu\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"8dp\"\n        android:layout_marginEnd=\"8dp\"\n        android:text=\"@string/tfe_ocr_gpu\" />\n\n    <TextView\n        android:id=\"@+id/log_view\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Medium\"\n        android:padding=\"8dp\"\n        android:layout_marginStart=\"8dp\"\n        android:layout_marginEnd=\"8dp\"\n        android:textColor=\"@android:color/black\" />\n\n    <TextView\n        android:id=\"@+id/tfe_is_labels_found\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:padding=\"8dp\"\n        android:layout_marginStart=\"8dp\"\n        android:layout_marginEnd=\"8dp\"\n        android:text=\"@string/tfe_ocr_texts_found\" />\n\n    <com.google.android.material.chip.ChipGroup\n        android:id=\"@+id/chips_group\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"\n        app:chipSpacingVertical=\"4dp\" />\n\n    <Button\n        android:id=\"@+id/rerun_button\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:padding=\"8dp\"\n        android:text=\"@string/tfe_ocr_run_model\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <color name=\"tfe_color_primary\">#ffa800</color>\n  <color name=\"tfe_color_primary_dark\">#ff6f00</color>\n  <color name=\"tfe_color_accent\">#425066</color>\n  <color name=\"tfe_is_color_light_gray\">#E4E4E4</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <dimen name=\"tfe_is_preview_size\">250dp</dimen>\n    <dimen name=\"tfe_bottom_sheet_corner_radius\">15dp</dimen>\n    <dimen name=\"tfe_bottom_sheet_top_padding\">8dp</dimen>\n</resources>\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"tfe_ocr_app_name\" translatable=\"false\">TFL OCR</string>\n    <string name=\"tfe_ocr_gpu\" translatable=\"false\">GPU</string>\n    <string name=\"tfe_ocr_run_model\" translatable=\"false\">Run model</string>\n    <string name=\"tfe_ocr_texts_found\" translatable=\"false\">Text(s) Found</string>\n    <string name=\"tfe_ocr_no_text_found\" translatable=\"false\">No Text Found</string>\n    <string name=\"tfe_using_first_image\" translatable=\"false\">You are using the first image</string>\n    <string name=\"tfe_using_second_image\" translatable=\"false\">You are using the second image</string>\n    <string name=\"tfe_using_third_image\" translatable=\"false\">You are using the third image</string>\n    <string name=\"tfe_tf_image_description\" translatable=\"false\">TensorFlow image</string>\n    <string name=\"tfe_android_image_description\" translatable=\"false\">Android image</string>\n    <string name=\"tfe_chrome_image_description\" translatable=\"false\">Chrome image</string>\n    <string name=\"tfe_result_image_description\" translatable=\"false\">Result image</string>\n</resources>\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n  <!-- Base application theme. -->\n  <style name=\"AppTheme.OCR\" parent=\"Theme.MaterialComponents.Light.NoActionBar\">\n    <!-- Customize your theme here. -->\n    <item name=\"colorPrimary\">@color/tfe_color_primary</item>\n    <item name=\"colorPrimaryDark\">@color/tfe_color_primary_dark</item>\n    <item name=\"colorAccent\">@color/tfe_color_accent</item>\n  </style>\n\n</resources>\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/build.gradle",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    ext.kotlin_version = '1.5.30-M1'\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:4.2.0'\n        classpath 'de.undercouch:gradle-download-task:4.0.2'\n        classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.71'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n        maven {\n            name 'ossrh-snapshot'\n            url 'https://oss.sonatype.org/content/repositories/snapshots'\n        }\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app\"s APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Automatically convert third-party libraries to use AndroidX\nandroid.enableJetifier=true"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/optical_character_recognition/android/settings.gradle",
    "content": "rootProject.name = 'TFLite Reinforcement Learning Demo App'\ninclude ':app'\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/README.md",
    "content": "# TensorFlow Lite Pose Estimation Android Demo\n\n### Overview\nThis is an app that continuously detects the body parts in the frames seen by\nyour device's camera. These instructions walk you through building and running\nthe demo on an Android device. Camera captures are discarded immediately after\nuse, nothing is stored or saved.\n\nThe app demonstrates how to use 4 models:\n\n* Single pose models: The model can estimate the pose of only one person in the\ninput image. If the input image contains multiple persons, the detection result\ncan be largely incorrect.\n   * PoseNet\n   * MoveNet Lightning\n   * MoveNet Thunder\n* Multi pose models: The model can estimate pose of multiple persons in the\ninput image.\n   * MoveNet MultiPose: Support up to 6 persons.\n\nSee this [blog post](https://blog.tensorflow.org/2021/05/next-generation-pose-detection-with-movenet-and-tensorflowjs.html)\nfor a comparison between these models.\n\n![Demo Image](posenetimage.png)\n\n## Build the demo using Android Studio\n\n### Prerequisites\n\n* If you don't have it already, install **[Android Studio](\n https://developer.android.com/studio/index.html)** 4.2 or\n above, following the instructions on the website.\n\n* Android device and Android development environment with minimum API 21.\n\n### Building\n* Open Android Studio, and from the `Welcome` screen, select\n`Open an existing Android Studio project`.\n\n* From the `Open File or Project` window that appears, navigate to and select\n the `lite/examples/pose_estimation/android` directory from wherever you\n cloned the `tensorflow/examples` GitHub repo. Click `OK`.\n\n* If it asks you to do a `Gradle Sync`, click `OK`.\n\n* You may also need to install various platforms and tools, if you get errors\n like `Failed to find target with hash string 'android-21'` and similar. Click\n the `Run` button (the green arrow) or select `Run` > `Run 'android'` from the\n top menu. You may need to rebuild the project using `Build` > `Rebuild Project`.\n\n* If it asks you to use `Instant Run`, click `Proceed Without Instant Run`.\n\n* Also, you need to have an Android device plugged in with developer options\n enabled at this point. See **[here](\n https://developer.android.com/studio/run/device)** for more details\n on setting up developer devices.\n\n\n### Model used\nDownloading, extraction and placement in assets folder has been managed\n automatically by `download.gradle`.\n\nIf you explicitly want to download the model, you can download it from here:\n\n* [Posenet](https://storage.googleapis.com/download.tensorflow.org/models/tflite/posenet_mobilenet_v1_100_257x257_multi_kpt_stripped.tflite)\n* [Movenet Lightning](https://kaggle.com/models/google/movenet/frameworks/tfLite/variations/singlepose-lightning)\n* [Movenet Thunder](https://www.kaggle.com/models/google/movenet/frameworks/tfLite/variations/singlepose-thunder)\n* [Movenet MultiPose](https://www.kaggle.com/models/google/movenet/frameworks/tfLite/variations/multipose-lightning-tflite-float16)\n\n### Additional Note\n_Please do not delete the assets folder content_. If you explicitly deleted the\n files, then please choose `Build` > `Rebuild` from menu to re-download the\n deleted model files into assets folder.\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/.gitignore",
    "content": "/build"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/build.gradle",
    "content": "plugins {\n    id 'com.android.application'\n    id 'kotlin-android'\n}\n\nandroid {\n    compileSdkVersion 30\n    buildToolsVersion \"30.0.3\"\n\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.poseestimation\"\n        minSdkVersion 23\n        targetSdkVersion 30\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    namespace \"org.tensorflow.lite.examples.poseestimation\"\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n    kotlinOptions {\n        jvmTarget = '1.8'\n    }\n}\n\n// Download tflite model\napply from:\"download.gradle\"\n\ndependencies {\n\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version\"\n    implementation 'androidx.core:core-ktx:1.5.0'\n    implementation 'androidx.appcompat:appcompat:1.3.0'\n    implementation 'com.google.android.material:material:1.3.0'\n    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'\n    implementation \"androidx.activity:activity-ktx:1.2.3\"\n    implementation 'androidx.fragment:fragment-ktx:1.3.5'\n    implementation 'org.tensorflow:tensorflow-lite:2.14.0'\n    implementation 'org.tensorflow:tensorflow-lite-gpu:2.5.0'\n    implementation 'org.tensorflow:tensorflow-lite-support:0.3.0'\n\n    androidTestImplementation 'androidx.test.ext:junit:1.1.2'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'\n    androidTestImplementation \"com.google.truth:truth:1.1.3\"\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/download.gradle",
    "content": "task downloadPosenetModel(type: DownloadUrlTask) {\n    def modelPosenetDownloadUrl = \"https://storage.googleapis.com/download.tensorflow.org/models/tflite/posenet_mobilenet_v1_100_257x257_multi_kpt_stripped.tflite\"\n    doFirst {\n        println \"Downloading ${modelPosenetDownloadUrl}\"\n    }\n    sourceUrl = \"${modelPosenetDownloadUrl}\"\n    target = file(\"src/main/assets/posenet.tflite\")\n}\n\ntask downloadMovenetLightningModel(type: DownloadUrlTask) {\n    def modelMovenetLightningDownloadUrl = \"https://tfhub.dev/google/lite-model/movenet/singlepose/lightning/tflite/float16/4?lite-format=tflite\"\n    doFirst {\n        println \"Downloading ${modelMovenetLightningDownloadUrl}\"\n    }\n    sourceUrl = \"${modelMovenetLightningDownloadUrl}\"\n    target = file(\"src/main/assets/movenet_lightning.tflite\")\n}\n\ntask downloadMovenetThunderModel(type: DownloadUrlTask) {\n    def modelMovenetThunderDownloadUrl = \"https://tfhub.dev/google/lite-model/movenet/singlepose/thunder/tflite/float16/4?lite-format=tflite\"\n    doFirst {\n        println \"Downloading ${modelMovenetThunderDownloadUrl}\"\n    }\n    sourceUrl = \"${modelMovenetThunderDownloadUrl}\"\n    target = file(\"src/main/assets/movenet_thunder.tflite\")\n}\n\ntask downloadMovenetMultiPoseModel(type: DownloadUrlTask) {\n    def modelMovenetThunderDownloadUrl = \"https://tfhub.dev/google/lite-model/movenet/multipose/lightning/tflite/float16/1?lite-format=tflite\"\n    doFirst {\n        println \"Downloading ${modelMovenetThunderDownloadUrl}\"\n    }\n    sourceUrl = \"${modelMovenetThunderDownloadUrl}\"\n    target = file(\"src/main/assets/movenet_multipose_fp16.tflite\")\n}\n\ntask downloadPoseClassifierModel(type: DownloadUrlTask) {\n    def modelPoseClassifierDownloadUrl = \"https://storage.googleapis.com/download.tensorflow.org/models/tflite/pose_classifier/yoga_classifier.tflite\"\n    doFirst {\n        println \"Downloading ${modelPoseClassifierDownloadUrl}\"\n    }\n    sourceUrl = \"${modelPoseClassifierDownloadUrl}\"\n    target = file(\"src/main/assets/classifier.tflite\")\n}\n\ntask downloadModel {\n    dependsOn downloadPosenetModel\n    dependsOn downloadMovenetLightningModel\n    dependsOn downloadMovenetThunderModel\n    dependsOn downloadPoseClassifierModel\n    dependsOn downloadMovenetMultiPoseModel\n}\n\nclass DownloadUrlTask extends DefaultTask {\n    @Input\n    String sourceUrl\n\n    @OutputFile\n    File target\n\n    @TaskAction\n    void download() {\n        ant.get(src: sourceUrl, dest: target)\n    }\n}\n\npreBuild.dependsOn downloadModel\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/androidTest/assets/image_credits.txt",
    "content": "Image1: https://pixabay.com/illustrations/woman-stand-wait-person-shoes-1427073/\nImage2: https://pixabay.com/photos/businessman-suit-germany-black-1146791/\nImage3: https://pixabay.com/photos/tree-pose-yoga-yogini-lifestyle-4823155/\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/androidTest/assets/pose_landmark_truth.csv",
    "content": "nose_x,nose_y,left_eye_x,left_eye_y,right_eye_x,right_eye_y,left_ear_x,left_ear_y,right_ear_x,right_ear_y,left_shoulder_x,left_shoulder_y,right_shoulder_x,right_shoulder_y,left_elbow_x,left_elbow_y,right_elbow_x,right_elbow_y,left_wrist_x,left_wrist_y,right_wrist_x,right_wrist_y,left_hip_x,left_hip_y,right_hip_x,right_hip_y,left_knee_x,left_knee_y,right_knee_x,right_knee_y,left_ankle_x,left_ankle_y,right_ankle_x,right_ankle_y\n186,89,200,77,177,78,224,86,167,85,244,158,154,154,258,248,143,239,265,327,136,313,234,311,170,311,247,446,134,445,262,561,92,571\n182,84,191,73,171,74,202,75,157,77,220,119,139,136,260,192,185,230,268,209,246,217,221,288,176,294,205,421,174,421,186,538,155,564\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/androidTest/java/org/tensorflow/lite/examples/poseestimation/ml/EvaluationUtils.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.ml\n\nimport android.graphics.Bitmap\nimport android.graphics.BitmapFactory\nimport android.graphics.Canvas\nimport android.graphics.PointF\nimport androidx.test.platform.app.InstrumentationRegistry\nimport com.google.common.truth.Truth.assertThat\nimport com.google.common.truth.Truth.assertWithMessage\nimport org.tensorflow.lite.examples.poseestimation.data.BodyPart\nimport org.tensorflow.lite.examples.poseestimation.data.Person\nimport java.io.BufferedReader\nimport java.io.InputStreamReader\nimport kotlin.math.pow\n\nobject EvaluationUtils {\n\n    /**\n     * Assert whether the detected person from the image match with the expected result.\n     * Detection result is accepted as correct if it is within the acceptableError range from the\n     * expected result.\n     */\n    fun assertPoseDetectionResult(\n        person: Person,\n        expectedResult: Map<BodyPart, PointF>,\n        acceptableError: Float\n    ) {\n        // Check if the model is confident enough in detecting the person\n        assertThat(person.score).isGreaterThan(0.5f)\n\n        for ((bodyPart, expectedPointF) in expectedResult) {\n            val keypoint = person.keyPoints.firstOrNull { it.bodyPart == bodyPart }\n            assertWithMessage(\"$bodyPart must exist\").that(keypoint).isNotNull()\n\n            val detectedPointF = keypoint!!.coordinate\n            val distanceFromExpectedPointF = distance(detectedPointF, expectedPointF)\n            assertWithMessage(\"Detected $bodyPart must be close to expected result\")\n                .that(distanceFromExpectedPointF).isAtMost(acceptableError)\n        }\n    }\n\n    /**\n     * Load an image from assets folder using its name.\n     */\n    fun loadBitmapAssetByName(name: String): Bitmap {\n        val testContext = InstrumentationRegistry.getInstrumentation().context\n        val testInput = testContext.assets.open(name)\n        return BitmapFactory.decodeStream(testInput)\n    }\n\n    /**\n     * Load csv from assets folder\n     */\n    fun loadCSVAsset(name: String): List<Map<BodyPart, PointF>> {\n        val data = mutableListOf<Map<BodyPart, PointF>>()\n        val testContext = InstrumentationRegistry.getInstrumentation().context\n        val testInput = testContext.assets.open(name)\n        val inputStreamReader = InputStreamReader(testInput)\n        val reader = BufferedReader(inputStreamReader)\n        // Skip header line\n        reader.readLine()\n\n        // Read expected coordinates from each following lines\n        reader.forEachLine {\n            val listPoint = it.split(\",\")\n            val map = mutableMapOf<BodyPart, PointF>()\n            for (i in listPoint.indices step 2) {\n                map[BodyPart.fromInt(i / 2)] =\n                    PointF(listPoint[i].toFloat(), listPoint[i + 1].toFloat())\n            }\n            data.add(map)\n        }\n        return data\n    }\n\n    /**\n     * Calculate the distance between two points\n     */\n    private fun distance(point1: PointF, point2: PointF): Float {\n        return ((point1.x - point2.x).pow(2) + (point1.y - point2.y).pow(2)).pow(0.5f)\n    }\n\n    /**\n     * Concatenate images of same height horizontally\n     */\n    fun hConcat(image1: Bitmap, image2: Bitmap): Bitmap {\n        if (image1.height != image2.height) {\n            throw Exception(\"Input images are not same height.\")\n        }\n        val finalBitmap =\n            Bitmap.createBitmap(image1.width + image2.width, image1.height, Bitmap.Config.ARGB_8888)\n        val canvas = Canvas(finalBitmap)\n        canvas.drawBitmap(image1, 0f, 0f, null)\n        canvas.drawBitmap(image2, image1.width.toFloat(), 0f, null)\n        return finalBitmap\n    }\n}"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/androidTest/java/org/tensorflow/lite/examples/poseestimation/ml/MovenetLightningTest.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.ml\n\nimport android.content.Context\nimport android.graphics.PointF\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.platform.app.InstrumentationRegistry\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.tensorflow.lite.examples.poseestimation.data.BodyPart\nimport org.tensorflow.lite.examples.poseestimation.data.Device\n\n@RunWith(AndroidJUnit4::class)\nclass MovenetLightningTest {\n\n    companion object {\n        private const val TEST_INPUT_IMAGE1 = \"image1.png\"\n        private const val TEST_INPUT_IMAGE2 = \"image2.jpg\"\n        private const val ACCEPTABLE_ERROR = 21f\n    }\n\n    private lateinit var poseDetector: PoseDetector\n    private lateinit var appContext: Context\n    private lateinit var expectedDetectionResult: List<Map<BodyPart, PointF>>\n\n    @Before\n    fun setup() {\n        appContext = InstrumentationRegistry.getInstrumentation().targetContext\n        poseDetector = MoveNet.create(appContext, Device.CPU, ModelType.Lightning)\n        expectedDetectionResult =\n            EvaluationUtils.loadCSVAsset(\"pose_landmark_truth.csv\")\n    }\n\n    @Test\n    fun testPoseEstimationResultWithImage1() {\n        val input = EvaluationUtils.loadBitmapAssetByName(TEST_INPUT_IMAGE1)\n\n        // As Movenet use previous frame to optimize detection result, we run it multiple times\n        // using the same image to improve result.\n        poseDetector.estimatePoses(input)\n        poseDetector.estimatePoses(input)\n        poseDetector.estimatePoses(input)\n        val person = poseDetector.estimatePoses(input)[0]\n        EvaluationUtils.assertPoseDetectionResult(\n            person,\n            expectedDetectionResult[0],\n            ACCEPTABLE_ERROR\n        )\n    }\n\n    @Test\n    fun testPoseEstimationResultWithImage2() {\n        val input = EvaluationUtils.loadBitmapAssetByName(TEST_INPUT_IMAGE2)\n\n        // As Movenet use previous frame to optimize detection result, we run it multiple times\n        // using the same image to improve result.\n        poseDetector.estimatePoses(input)\n        poseDetector.estimatePoses(input)\n        poseDetector.estimatePoses(input)\n        val person = poseDetector.estimatePoses(input)[0]\n        EvaluationUtils.assertPoseDetectionResult(\n            person,\n            expectedDetectionResult[1],\n            ACCEPTABLE_ERROR\n        )\n    }\n}"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/androidTest/java/org/tensorflow/lite/examples/poseestimation/ml/MovenetMultiPoseTest.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.ml\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.graphics.PointF\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.platform.app.InstrumentationRegistry\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.tensorflow.lite.examples.poseestimation.data.BodyPart\nimport org.tensorflow.lite.examples.poseestimation.data.Device\nimport org.tensorflow.lite.examples.poseestimation.ml.MoveNetMultiPose\nimport org.tensorflow.lite.examples.poseestimation.ml.Type\n\n@RunWith(AndroidJUnit4::class)\nclass MovenetMultiPoseTest {\n    companion object {\n        private const val TEST_INPUT_IMAGE1 = \"image1.png\"\n        private const val TEST_INPUT_IMAGE2 = \"image2.jpg\"\n        private const val ACCEPTABLE_ERROR = 17f\n    }\n\n    private lateinit var poseDetector: MoveNetMultiPose\n    private lateinit var appContext: Context\n    private lateinit var inputFinal: Bitmap\n    private lateinit var expectedDetectionResult: List<Map<BodyPart, PointF>>\n\n    @Before\n    fun setup() {\n        appContext = InstrumentationRegistry.getInstrumentation().targetContext\n        poseDetector = MoveNetMultiPose.create(appContext, Device.CPU, Type.Dynamic)\n        val input1 = EvaluationUtils.loadBitmapAssetByName(TEST_INPUT_IMAGE1)\n        val input2 = EvaluationUtils.loadBitmapAssetByName(TEST_INPUT_IMAGE2)\n        inputFinal = EvaluationUtils.hConcat(input1, input2)\n        expectedDetectionResult =\n                EvaluationUtils.loadCSVAsset(\"pose_landmark_truth.csv\")\n\n        // update coordination of the pose_landmark_truth.csv corresponding to the new input image\n        for ((_, value) in expectedDetectionResult[1]) {\n            value.x = value.x + input1.width\n        }\n    }\n\n    @Test\n    fun testPoseEstimateResult() {\n        val persons = poseDetector.estimatePoses(inputFinal)\n        assert(persons.size == 2)\n\n        // Sort the results so that the person on the right side come first.\n        val sortedPersons = persons.sortedBy { it.boundingBox?.left }\n\n        EvaluationUtils.assertPoseDetectionResult(\n                sortedPersons[0],\n                expectedDetectionResult[0],\n                ACCEPTABLE_ERROR\n        )\n\n        EvaluationUtils.assertPoseDetectionResult(\n                sortedPersons[1],\n                expectedDetectionResult[1],\n                ACCEPTABLE_ERROR\n        )\n    }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/androidTest/java/org/tensorflow/lite/examples/poseestimation/ml/MovenetThunderTest.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.ml\n\nimport android.content.Context\nimport android.graphics.PointF\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.platform.app.InstrumentationRegistry\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.tensorflow.lite.examples.poseestimation.data.BodyPart\nimport org.tensorflow.lite.examples.poseestimation.data.Device\n\n@RunWith(AndroidJUnit4::class)\nclass MovenetThunderTest {\n\n    companion object {\n        private const val TEST_INPUT_IMAGE1 = \"image1.png\"\n        private const val TEST_INPUT_IMAGE2 = \"image2.jpg\"\n        private const val ACCEPTABLE_ERROR = 15f\n    }\n\n    private lateinit var poseDetector: PoseDetector\n    private lateinit var appContext: Context\n    private lateinit var expectedDetectionResult: List<Map<BodyPart, PointF>>\n\n    @Before\n    fun setup() {\n        appContext = InstrumentationRegistry.getInstrumentation().targetContext\n        poseDetector = MoveNet.create(appContext, Device.CPU, ModelType.Thunder)\n        expectedDetectionResult =\n            EvaluationUtils.loadCSVAsset(\"pose_landmark_truth.csv\")\n    }\n\n    @Test\n    fun testPoseEstimationResultWithImage1() {\n        val input = EvaluationUtils.loadBitmapAssetByName(TEST_INPUT_IMAGE1)\n\n        // As Movenet use previous frame to optimize detection result, we run it multiple times\n        // using the same image to improve result.\n        poseDetector.estimatePoses(input)\n        poseDetector.estimatePoses(input)\n        poseDetector.estimatePoses(input)\n        val person = poseDetector.estimatePoses(input)[0]\n        EvaluationUtils.assertPoseDetectionResult(\n            person,\n            expectedDetectionResult[0],\n            ACCEPTABLE_ERROR\n        )\n    }\n\n    @Test\n    fun testPoseEstimationResultWithImage2() {\n        val input = EvaluationUtils.loadBitmapAssetByName(TEST_INPUT_IMAGE2)\n\n        // As Movenet use previous frame to optimize detection result, we run it multiple times\n        // using the same image to improve result.\n        poseDetector.estimatePoses(input)\n        poseDetector.estimatePoses(input)\n        poseDetector.estimatePoses(input)\n        val person = poseDetector.estimatePoses(input)[0]\n        EvaluationUtils.assertPoseDetectionResult(\n            person,\n            expectedDetectionResult[1],\n            ACCEPTABLE_ERROR\n        )\n    }\n}"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/androidTest/java/org/tensorflow/lite/examples/poseestimation/ml/PoseClassifierTest.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.ml\n\nimport android.content.Context\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.platform.app.InstrumentationRegistry\nimport junit.framework.TestCase\nimport junit.framework.TestCase.assertEquals\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.tensorflow.lite.examples.poseestimation.data.Device\n\n@RunWith(AndroidJUnit4::class)\nclass PoseClassifierTest {\n\n    companion object {\n        private const val TEST_INPUT_IMAGE = \"image3.jpeg\"\n    }\n\n    private lateinit var appContext: Context\n    private lateinit var poseDetector: PoseDetector\n    private lateinit var poseClassifier: PoseClassifier\n\n    @Before\n    fun setup() {\n        appContext = InstrumentationRegistry.getInstrumentation().targetContext\n        poseDetector = MoveNet.create(appContext, Device.CPU, ModelType.Lightning)\n        poseClassifier = PoseClassifier.create(appContext)\n    }\n\n    @Test\n    fun testPoseClassifier() {\n        val input = EvaluationUtils.loadBitmapAssetByName(TEST_INPUT_IMAGE)\n        // As Movenet use previous frame to optimize detection result, we run it multiple times\n        // using the same image to improve result.\n        poseDetector.estimatePoses(input)\n        poseDetector.estimatePoses(input)\n        poseDetector.estimatePoses(input)\n        val person = poseDetector.estimatePoses(input)[0]\n        val classificationResult = poseClassifier.classify(person)\n        val predictedPose = classificationResult.maxByOrNull { it.second }?.first ?: \"n/a\"\n        assertEquals(\n            \"Predicted pose is different from ground truth.\",\n            \"tree\",\n            predictedPose\n        )\n    }\n}"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/androidTest/java/org/tensorflow/lite/examples/poseestimation/ml/PosenetTest.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.ml\n\nimport android.content.Context\nimport android.graphics.PointF\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.platform.app.InstrumentationRegistry\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.tensorflow.lite.examples.poseestimation.data.BodyPart\nimport org.tensorflow.lite.examples.poseestimation.data.Device\n\n@RunWith(AndroidJUnit4::class)\nclass PosenetTest {\n\n    companion object {\n        private const val TEST_INPUT_IMAGE1 = \"image1.png\"\n        private const val TEST_INPUT_IMAGE2 = \"image2.jpg\"\n        private const val ACCEPTABLE_ERROR = 37f\n    }\n\n    private lateinit var poseDetector: PoseDetector\n    private lateinit var appContext: Context\n    private lateinit var expectedDetectionResult: List<Map<BodyPart, PointF>>\n\n    @Before\n    fun setup() {\n        appContext = InstrumentationRegistry.getInstrumentation().targetContext\n        poseDetector = PoseNet.create(appContext, Device.CPU)\n        expectedDetectionResult =\n            EvaluationUtils.loadCSVAsset(\"pose_landmark_truth.csv\")\n    }\n\n    @Test\n    fun testPoseEstimationResultWithImage1() {\n        val input = EvaluationUtils.loadBitmapAssetByName(TEST_INPUT_IMAGE1)\n        val person = poseDetector.estimatePoses(input)[0]\n        EvaluationUtils.assertPoseDetectionResult(\n            person,\n            expectedDetectionResult[0],\n            ACCEPTABLE_ERROR\n        )\n    }\n\n    @Test\n    fun testPoseEstimationResultWithImage2() {\n        val input = EvaluationUtils.loadBitmapAssetByName(TEST_INPUT_IMAGE2)\n        val person = poseDetector.estimatePoses(input)[0]\n        EvaluationUtils.assertPoseDetectionResult(\n            person,\n            expectedDetectionResult[1],\n            ACCEPTABLE_ERROR\n        )\n    }\n}"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/androidTest/java/org/tensorflow/lite/examples/poseestimation/ml/VisualizationTest.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.ml\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.platform.app.InstrumentationRegistry\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.tensorflow.lite.examples.poseestimation.VisualizationUtils\nimport org.tensorflow.lite.examples.poseestimation.data.Device\n\n/**\n * This test is used to visually verify detection results by the models.\n * You can put a breakpoint at the end of the method, debug this method, than use the\n * \"View Bitmap\" feature of the debugger to check the visualized detection result.\n */\n@RunWith(AndroidJUnit4::class)\nclass VisualizationTest {\n\n    companion object {\n        private const val TEST_INPUT_IMAGE = \"image2.jpg\"\n    }\n\n    private lateinit var appContext: Context\n    private lateinit var inputBitmap: Bitmap\n\n    @Before\n    fun setup() {\n        appContext = InstrumentationRegistry.getInstrumentation().targetContext\n        inputBitmap = EvaluationUtils.loadBitmapAssetByName(TEST_INPUT_IMAGE)\n    }\n\n    @Test\n    fun testPosenet() {\n        val poseDetector = PoseNet.create(appContext, Device.CPU)\n        val person = poseDetector.estimatePoses(inputBitmap)[0]\n        val outputBitmap = VisualizationUtils.drawBodyKeypoints(inputBitmap, arrayListOf(person))\n        assertThat(outputBitmap).isNotNull()\n    }\n\n    @Test\n    fun testMovenetLightning() {\n        // Due to Movenet's cropping logic, we run inference several times with the same input\n        // image to improve accuracy\n        val poseDetector = MoveNet.create(appContext, Device.CPU, ModelType.Lightning)\n        poseDetector.estimatePoses(inputBitmap)\n        poseDetector.estimatePoses(inputBitmap)\n        val person2 = poseDetector.estimatePoses(inputBitmap)[0]\n        val outputBitmap2 = VisualizationUtils.drawBodyKeypoints(inputBitmap, arrayListOf(person2))\n        assertThat(outputBitmap2).isNotNull()\n    }\n\n    @Test\n    fun testMovenetThunder() {\n        // Due to Movenet's cropping logic, we run inference several times with the same input\n        // image to improve accuracy\n        val poseDetector = MoveNet.create(appContext, Device.CPU, ModelType.Thunder)\n        poseDetector.estimatePoses(inputBitmap)\n        poseDetector.estimatePoses(inputBitmap)\n        val person = poseDetector.estimatePoses(inputBitmap)[0]\n        val outputBitmap = VisualizationUtils.drawBodyKeypoints(inputBitmap, arrayListOf(person))\n        assertThat(outputBitmap).isNotNull()\n    }\n}"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/androidTest/java/org/tensorflow/lite/examples/poseestimation/tracker/BoundingBoxTrackerTest.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.tracker\n\nimport android.graphics.RectF\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport junit.framework.TestCase.assertEquals\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.tensorflow.lite.examples.poseestimation.data.Person\n\n@RunWith(AndroidJUnit4::class)\nclass BoundingBoxTrackerTest {\n    companion object {\n        private const val MAX_TRACKS = 4\n        private const val MAX_AGE = 1000 // Unit: milliseconds.\n        private const val MIN_SIMILARITY = 0.5f\n    }\n\n    private lateinit var boundingBoxTracker: BoundingBoxTracker\n\n    @Before\n    fun setup() {\n        val trackerConfig = TrackerConfig(MAX_TRACKS, MAX_AGE, MIN_SIMILARITY)\n        boundingBoxTracker = BoundingBoxTracker(trackerConfig)\n    }\n\n    @Test\n    fun testIoU() {\n        val persons = Person(\n            -1, listOf(), RectF(\n                0f,\n                0f,\n                2f / 3,\n                1f\n            ), 1f\n        )\n\n        val track =\n            Track(\n                Person(\n                    -1,\n                    listOf(),\n                    RectF(\n                        1 / 3f,\n                        0.0f,\n                        1f,\n                        1f,\n                    ), 1f\n                ), 1000000\n            )\n        val computedIoU = boundingBoxTracker.iou(persons, track.person)\n        assertEquals(\"Wrong IoU value.\", 1f / 3, computedIoU, 0.000001f)\n    }\n\n    @Test\n    fun testIoUFullOverlap() {\n        val persons = Person(\n            -1, listOf(),\n            RectF(\n                0f,\n                0f,\n                1f,\n                1f\n            ), 1f\n        )\n\n        val track =\n            Track(\n                Person(\n                    -1,\n                    listOf(),\n                    RectF(\n                        0f,\n                        0f,\n                        1f,\n                        1f,\n                    ), 1f\n                ), 1000000\n            )\n        val computedIoU = boundingBoxTracker.iou(persons, track.person)\n        assertEquals(\"Wrong IoU value.\", 1f, computedIoU, 0.000001f)\n    }\n\n    @Test\n    fun testIoUNoIntersection() {\n        val persons = Person(\n            -1, listOf(),\n            RectF(\n                0f,\n                0f,\n                0.5f,\n                0.5f\n            ), 1f\n        )\n\n        val track =\n            Track(\n                Person(\n                    -1,\n                    listOf(),\n                    RectF(\n                        0.5f,\n                        0.5f,\n                        1f,\n                        1f,\n                    ), 1f\n                ), 1000000\n            )\n        val computedIoU = boundingBoxTracker.iou(persons, track.person)\n        assertEquals(\"Wrong IoU value.\", 0f, computedIoU, 0.000001f)\n    }\n\n    @Test\n    fun testBoundingBoxTracking() {\n        // Timestamp: 0. Poses becomes the first two tracks.\n        var persons = listOf(\n            Person( // Becomes track 1.\n                -1, listOf(), RectF(\n                    0f,\n                    0f,\n                    0.5f,\n                    0.5f,\n                ), 1f\n            ),\n            Person( // Becomes track 2.\n                -1, listOf(), RectF(\n                    0f,\n                    0f,\n                    1f,\n                    1f\n                ), 1f\n            )\n        )\n        persons = boundingBoxTracker.apply(persons, 0)\n        var track = boundingBoxTracker.tracks\n        assertEquals(2, persons.size)\n        assertEquals(1, persons[0].id)\n        assertEquals(2, persons[1].id)\n        assertEquals(2, track.size)\n        assertEquals(1, track[0].person.id)\n        assertEquals(0, track[0].lastTimestamp)\n        assertEquals(2, track[1].person.id)\n        assertEquals(0, track[1].lastTimestamp)\n\n        // Timestamp: 100000. First pose is linked with track 1. Second pose spawns\n        // a new track (id = 2).\n        persons = listOf(\n            Person( // Linked with track 1.\n                -1, listOf(), RectF(\n                    0.1f,\n                    0.1f,\n                    0.5f,\n                    0.5f\n                ), 1f\n            ),\n            Person( // Becomes track 3.\n                -1, listOf(), RectF(\n                    0.2f,\n                    0.3f,\n                    0.9f,\n                    0.9f\n                ), 1f\n            )\n        )\n        persons = boundingBoxTracker.apply(persons, 100000)\n        track = boundingBoxTracker.tracks\n        assertEquals(2, persons.size)\n        assertEquals(1, persons[0].id)\n        assertEquals(3, persons[1].id)\n        assertEquals(3, track.size)\n        assertEquals(1, track[0].person.id)\n        assertEquals(100000, track[0].lastTimestamp)\n        assertEquals(3, track[1].person.id)\n        assertEquals(100000, track[1].lastTimestamp)\n        assertEquals(2, track[2].person.id)\n        assertEquals(0, track[2].lastTimestamp)\n\n        // Timestamp: 1050000. First pose is linked with track 1. Second pose is\n        // identical to track 2, but is not linked because track 2 is deleted due to\n        // age. Instead it spawns track 4.\n        persons = listOf(\n            Person( // Linked with track 1.\n                -1, listOf(), RectF(\n                    0.1f,\n                    0.1f,\n                    0.55f,\n                    0.5f\n                ), 1f\n            ),\n            Person( // Becomes track 4.\n                -1, listOf(), RectF(\n                    0f,\n                    0f,\n                    1f,\n                    1f\n                ), 1f\n            )\n        )\n        persons = boundingBoxTracker.apply(persons, 1050000)\n        track = boundingBoxTracker.tracks\n        assertEquals(2, persons.size)\n        assertEquals(1, persons[0].id)\n        assertEquals(4, persons[1].id)\n        assertEquals(3, track.size)\n        assertEquals(1, track[0].person.id)\n        assertEquals(1050000, track[0].lastTimestamp)\n        assertEquals(4, track[1].person.id)\n        assertEquals(1050000, track[1].lastTimestamp)\n        assertEquals(3, track[2].person.id)\n        assertEquals(100000, track[2].lastTimestamp)\n    }\n}"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/androidTest/java/org/tensorflow/lite/examples/poseestimation/tracker/KeyPointsTrackerTest.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.tracker\n\nimport android.graphics.PointF\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport junit.framework.Assert\nimport junit.framework.TestCase.assertEquals\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.tensorflow.lite.examples.poseestimation.data.BodyPart\nimport org.tensorflow.lite.examples.poseestimation.data.KeyPoint\nimport org.tensorflow.lite.examples.poseestimation.data.Person\nimport kotlin.math.exp\nimport kotlin.math.pow\n\n@RunWith(AndroidJUnit4::class)\nclass KeyPointsTrackerTest {\n    companion object {\n        private const val MAX_TRACKS = 4\n        private const val MAX_AGE = 1000\n        private const val MIN_SIMILARITY = 0.5f\n        private const val KEYPOINT_THRESHOLD = 0.2f\n        private const val MIN_NUM_KEYPOINT = 2\n        private val KEYPOINT_FALLOFF = listOf(0.1f, 0.1f, 0.1f, 0.1f)\n    }\n\n    private lateinit var keyPointsTracker: KeyPointsTracker\n\n    @Before\n    fun setup() {\n        val trackerConfig = TrackerConfig(\n            MAX_TRACKS, MAX_AGE, MIN_SIMILARITY,\n            KeyPointsTrackerParams(KEYPOINT_THRESHOLD, KEYPOINT_FALLOFF, MIN_NUM_KEYPOINT)\n        )\n        keyPointsTracker = KeyPointsTracker(trackerConfig)\n    }\n\n    @Test\n    fun testOks() {\n        val persons =\n            Person(\n                -1, listOf(\n                    KeyPoint(BodyPart.NOSE, PointF(0.2f, 0.2f), 1f),\n                    KeyPoint(BodyPart.RIGHT_ELBOW, PointF(0.4f, 0.4f), 0.8f),\n                    KeyPoint(BodyPart.RIGHT_KNEE, PointF(0.6f, 0.6f), 0.1f),\n                    KeyPoint(BodyPart.RIGHT_ANKLE, PointF(0.8f, 0.7f), 0.8f),\n                ), score = 1f\n            )\n        val tracks =\n            Track(\n                Person(\n                    0, listOf(\n                        KeyPoint(BodyPart.NOSE, PointF(0.2f, 0.2f), 1f),\n                        KeyPoint(BodyPart.RIGHT_ELBOW, PointF(0.4f, 0.4f), 0.8f),\n                        KeyPoint(BodyPart.RIGHT_KNEE, PointF(0.6f, 0.6f), 0.9f),\n                        KeyPoint(BodyPart.RIGHT_ANKLE, PointF(0.8f, 0.8f), 0.8f),\n                    ), score = 1f\n                ),\n                1000000,\n            )\n\n        val oks = keyPointsTracker.oks(persons, tracks.person)\n        val boxArea = (0.8f - 0.2f) * (0.8f - 0.2f)\n        val x = 2f * KEYPOINT_FALLOFF[3]\n        val d = 0.1f\n        val expectedOks: Float =\n            (1f + 1f + exp(-1f * d.pow(2) / (2f * boxArea * x.pow(2)))) / 3f\n        assertEquals(expectedOks, oks, 0.000001f)\n    }\n\n    @Test\n    fun testOksReturnZero() {\n        // Compute OKS returns 0.0 with less than 2 valid keypoints\n        val persons =\n            Person(\n                -1, listOf(\n                    KeyPoint(BodyPart.NOSE, PointF(0.2f, 0.2f), 1f),\n                    KeyPoint(BodyPart.RIGHT_ELBOW, PointF(0.4f, 0.4f), 0.1f), // Low confidence.\n                    KeyPoint(BodyPart.RIGHT_KNEE, PointF(0.6f, 0.6f), 0.9f),\n                    KeyPoint(BodyPart.RIGHT_ANKLE, PointF(0.8f, 0.8f), 0.8f),\n                ), score = 1f\n            )\n        val tracks =\n            Track(\n                Person(\n                    0, listOf(\n                        KeyPoint(BodyPart.NOSE, PointF(0.2f, 0.2f), 1f),\n                        KeyPoint(BodyPart.RIGHT_ELBOW, PointF(0.4f, 0.4f), 0.8f),\n                        KeyPoint(BodyPart.RIGHT_KNEE, PointF(0.6f, 0.6f), 0.1f),// Low confidence.\n                        KeyPoint(BodyPart.RIGHT_ANKLE, PointF(0.8f, 0.8f), 0.0f),// Low confidence.\n                    ), score = 1f\n                ), 1000000\n            )\n\n        val oks = keyPointsTracker.oks(persons, tracks.person)\n        assertEquals(0f, oks, 0.000001f)\n    }\n\n    @Test\n    fun testArea() {\n        val keyPoints = listOf(\n            KeyPoint(BodyPart.NOSE, PointF(0.1f, 0.2f), 1f),\n            KeyPoint(BodyPart.RIGHT_ELBOW, PointF(0.3f, 0.4f), 0.9f),\n            KeyPoint(BodyPart.RIGHT_KNEE, PointF(0.4f, 0.6f), 0.9f),\n            KeyPoint(BodyPart.RIGHT_ANKLE, PointF(0.7f, 0.8f), 0.1f),\n        )\n        val area = keyPointsTracker.area(keyPoints)\n        val expectedArea = (0.4f - 0.1f) * (0.6f - 0.2f)\n        assertEquals(expectedArea, area)\n    }\n\n    @Test\n    fun testKeyPointsTracker() {\n        // Timestamp: 0. Person becomes the only track.\n        var persons = listOf(\n            Person(\n                -1, listOf(\n                    KeyPoint(BodyPart.NOSE, PointF(0.2f, 0.2f), 1f),\n                    KeyPoint(BodyPart.RIGHT_ELBOW, PointF(0.4f, 0.4f), 0.8f),\n                    KeyPoint(BodyPart.RIGHT_KNEE, PointF(0.6f, 0.6f), 0.9f),\n                    KeyPoint(BodyPart.RIGHT_ANKLE, PointF(0.8f, 0.8f), 0.0f),\n                ), score = 0.9f\n            )\n        )\n        persons = keyPointsTracker.apply(persons, 0)\n        var track = keyPointsTracker.tracks\n        assertEquals(1, persons.size)\n        assertEquals(1, persons[0].id)\n        assertEquals(1, track.size)\n        assertEquals(1, track[0].person.id)\n        assertEquals(0, track[0].lastTimestamp)\n\n        // Timestamp: 100000. First person is linked with track 1. Second person spawns\n        // a new track (id = 2).\n        persons = listOf(\n            Person(\n                -1,\n                listOf(\n                    // Links with id = 1.\n                    KeyPoint(BodyPart.NOSE, PointF(0.2f, 0.2f), 1f),\n                    KeyPoint(BodyPart.RIGHT_ELBOW, PointF(0.4f, 0.4f), 0.8f),\n                    KeyPoint(BodyPart.RIGHT_KNEE, PointF(0.6f, 0.6f), 0.9f),\n                    KeyPoint(BodyPart.RIGHT_ANKLE, PointF(0.8f, 0.8f), 0.8f),\n                ),\n                score = 1f\n            ),\n            Person(\n                -1,\n                listOf(\n                    // Becomes id = 2.\n                    KeyPoint(BodyPart.NOSE, PointF(0.8f, 0.8f), 0.8f),\n                    KeyPoint(BodyPart.RIGHT_ELBOW, PointF(0.6f, 0.6f), 0.3f),\n                    KeyPoint(BodyPart.RIGHT_KNEE, PointF(0.4f, 0.4f), 0.1f),\n                    KeyPoint(BodyPart.RIGHT_ANKLE, PointF(0.2f, 0.2f), 0.8f),\n                ),\n                score = 1f\n            )\n        )\n        persons = keyPointsTracker.apply(persons, 100000)\n        track = keyPointsTracker.tracks\n        assertEquals(2, persons.size)\n        assertEquals(1, persons[0].id)\n        assertEquals(2, persons[1].id)\n        assertEquals(2, track.size)\n        assertEquals(1, track[0].person.id)\n        assertEquals(100000, track[0].lastTimestamp)\n        assertEquals(2, track[1].person.id)\n        assertEquals(100000, track[1].lastTimestamp)\n\n        // Timestamp: 900000. First person is linked with track 2. Second person spawns\n        // a new track (id = 3).\n        persons = listOf(\n            Person(\n                -1,\n                listOf(\n                    // Links with id = 2.\n                    KeyPoint(BodyPart.NOSE, PointF(0.6f, 0.7f), 0.7f),\n                    KeyPoint(BodyPart.RIGHT_ELBOW, PointF(0.5f, 0.6f), 0.7f),\n                    KeyPoint(BodyPart.RIGHT_KNEE, PointF(0.0f, 0.0f), 0.1f),\n                    KeyPoint(BodyPart.RIGHT_ANKLE, PointF(0.2f, 0.1f), 1f),\n                ),\n                score = 1f\n            ),\n            Person(\n                -1,\n                listOf(\n                    // Becomes id = 3.\n                    KeyPoint(BodyPart.NOSE, PointF(0.5f, 0.1f), 0.6f),\n                    KeyPoint(BodyPart.RIGHT_ELBOW, PointF(0.9f, 0.3f), 0.6f),\n                    KeyPoint(BodyPart.RIGHT_KNEE, PointF(0.1f, 1f), 0.9f),\n                    KeyPoint(BodyPart.RIGHT_ANKLE, PointF(0.4f, 0.4f), 0.1f),\n                ),\n                score = 1f\n            )\n        )\n        persons = keyPointsTracker.apply(persons, 900000)\n        track = keyPointsTracker.tracks\n        assertEquals(2, persons.size)\n        assertEquals(2, persons[0].id)\n        assertEquals(3, persons[1].id)\n        assertEquals(3, track.size)\n        assertEquals(2, track[0].person.id)\n        assertEquals(900000, track[0].lastTimestamp)\n        assertEquals(3, track[1].person.id)\n        assertEquals(900000, track[1].lastTimestamp)\n        assertEquals(1, track[2].person.id)\n        assertEquals(100000, track[2].lastTimestamp)\n\n        // Timestamp: 1200000. First person spawns a new track (id = 4), even though\n        // it has the same keypoints as track 1. This is because the age exceeds\n        // 1000 msec. The second person links with id 2. The third person spawns a new\n        // track (id = 5).\n        persons = listOf(\n            Person(\n                -1,\n                listOf(\n                    // Becomes id = 4.\n                    KeyPoint(BodyPart.NOSE, PointF(0.2f, 0.2f), 1f),\n                    KeyPoint(BodyPart.RIGHT_ELBOW, PointF(0.4f, 0.4f), 0.8f),\n                    KeyPoint(BodyPart.RIGHT_KNEE, PointF(0.6f, 0.6f), 0.9f),\n                    KeyPoint(BodyPart.RIGHT_ANKLE, PointF(0.8f, 0.8f), 0.8f),\n                ),\n                score = 1f\n            ),\n            Person(\n                -1,\n                listOf(\n                    // Links with id = 2.\n                    KeyPoint(BodyPart.NOSE, PointF(0.55f, 0.7f), 0.7f),\n                    KeyPoint(BodyPart.RIGHT_ELBOW, PointF(0.5f, 0.6f), 0.9f),\n                    KeyPoint(BodyPart.RIGHT_KNEE, PointF(1f, 1f), 0.1f),\n                    KeyPoint(BodyPart.RIGHT_ANKLE, PointF(0.8f, 0.1f), 0f),\n                ),\n                score = 1f\n            ),\n            Person(\n                -1,\n                listOf(\n                    // Becomes id = 5.\n                    KeyPoint(BodyPart.NOSE, PointF(0.1f, 0.1f), 0.1f),\n                    KeyPoint(BodyPart.RIGHT_ELBOW, PointF(0.2f, 0.2f), 0.9f),\n                    KeyPoint(BodyPart.RIGHT_KNEE, PointF(0.3f, 0.3f), 0.7f),\n                    KeyPoint(BodyPart.RIGHT_ANKLE, PointF(0.4f, 0.4f), 0.8f),\n                ),\n                score = 1f\n            )\n        )\n        persons = keyPointsTracker.apply(persons, 1200000)\n        track = keyPointsTracker.tracks\n        assertEquals(3, persons.size)\n        assertEquals(4, persons[0].id)\n        assertEquals(2, persons[1].id)\n        assertEquals(4, track.size)\n        assertEquals(2, track[0].person.id)\n        assertEquals(1200000, track[0].lastTimestamp)\n        assertEquals(4, track[1].person.id)\n        assertEquals(1200000, track[1].lastTimestamp)\n        assertEquals(5, track[2].person.id)\n        assertEquals(1200000, track[2].lastTimestamp)\n        assertEquals(3, track[3].person.id)\n        assertEquals(900000, track[3].lastTimestamp)\n\n        // Timestamp: 1300000. First person spawns a new track (id = 6). Since\n        // maxTracks is 4, the oldest track (id = 3) is removed.\n        persons = listOf(\n            Person(\n                -1,\n                listOf(\n                    // Becomes id = 6.\n                    KeyPoint(BodyPart.NOSE, PointF(0.1f, 0.8f), 1f),\n                    KeyPoint(BodyPart.RIGHT_ELBOW, PointF(0.2f, 0.9f), 0.6f),\n                    KeyPoint(BodyPart.RIGHT_KNEE, PointF(0.2f, 0.9f), 0.5f),\n                    KeyPoint(BodyPart.RIGHT_ANKLE, PointF(0.8f, 0.2f), 0.4f),\n                ),\n                score = 1f\n            )\n        )\n        persons = keyPointsTracker.apply(persons, 1300000)\n        track = keyPointsTracker.tracks\n        assertEquals(1, persons.size)\n        assertEquals(6, persons[0].id)\n        assertEquals(4, track.size)\n        assertEquals(6, track[0].person.id)\n        assertEquals(1300000, track[0].lastTimestamp)\n        assertEquals(2, track[1].person.id)\n        assertEquals(1200000, track[1].lastTimestamp)\n        assertEquals(4, track[2].person.id)\n        assertEquals(1200000, track[2].lastTimestamp)\n        assertEquals(5, track[3].person.id)\n        assertEquals(1200000, track[3].lastTimestamp)\n    }\n}"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.tensorflow.lite.examples.poseestimation\">\n\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n    <uses-feature\n        android:name=\"android.hardware.camera\"\n        android:required=\"true\" />\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@drawable/ic_launcher\"\n        android:label=\"@string/tfe_pe_app_name\"\n        android:roundIcon=\"@drawable/ic_launcher\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/Theme.PoseEstimation\">\n        <activity android:name=\".MainActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/assets/labels.txt",
    "content": "chair\ncobra\ndog\ntree\nwarrior"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/MainActivity.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation\n\nimport android.Manifest\nimport android.app.AlertDialog\nimport android.app.Dialog\nimport android.content.pm.PackageManager\nimport android.os.Bundle\nimport android.os.Process\nimport android.view.SurfaceView\nimport android.view.View\nimport android.view.WindowManager\nimport android.widget.*\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.appcompat.widget.SwitchCompat\nimport androidx.core.content.ContextCompat\nimport androidx.fragment.app.DialogFragment\nimport androidx.lifecycle.lifecycleScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\nimport org.tensorflow.lite.examples.poseestimation.camera.CameraSource\nimport org.tensorflow.lite.examples.poseestimation.data.Device\nimport org.tensorflow.lite.examples.poseestimation.ml.*\n\nclass MainActivity : AppCompatActivity() {\n    companion object {\n        private const val FRAGMENT_DIALOG = \"dialog\"\n    }\n\n    /** A [SurfaceView] for camera preview.   */\n    private lateinit var surfaceView: SurfaceView\n\n    /** Default pose estimation model is 1 (MoveNet Thunder)\n     * 0 == MoveNet Lightning model\n     * 1 == MoveNet Thunder model\n     * 2 == MoveNet MultiPose model\n     * 3 == PoseNet model\n     **/\n    private var modelPos = 1\n\n    /** Default device is CPU */\n    private var device = Device.CPU\n\n    private lateinit var tvScore: TextView\n    private lateinit var tvFPS: TextView\n    private lateinit var spnDevice: Spinner\n    private lateinit var spnModel: Spinner\n    private lateinit var spnTracker: Spinner\n    private lateinit var vTrackerOption: View\n    private lateinit var tvClassificationValue1: TextView\n    private lateinit var tvClassificationValue2: TextView\n    private lateinit var tvClassificationValue3: TextView\n    private lateinit var swClassification: SwitchCompat\n    private lateinit var vClassificationOption: View\n    private var cameraSource: CameraSource? = null\n    private var isClassifyPose = false\n    private val requestPermissionLauncher =\n        registerForActivityResult(\n            ActivityResultContracts.RequestPermission()\n        ) { isGranted: Boolean ->\n            if (isGranted) {\n                // Permission is granted. Continue the action or workflow in your\n                // app.\n                openCamera()\n            } else {\n                // Explain to the user that the feature is unavailable because the\n                // features requires a permission that the user has denied. At the\n                // same time, respect the user's decision. Don't link to system\n                // settings in an effort to convince the user to change their\n                // decision.\n                ErrorDialog.newInstance(getString(R.string.tfe_pe_request_permission))\n                    .show(supportFragmentManager, FRAGMENT_DIALOG)\n            }\n        }\n    private var changeModelListener = object : AdapterView.OnItemSelectedListener {\n        override fun onNothingSelected(parent: AdapterView<*>?) {\n            // do nothing\n        }\n\n        override fun onItemSelected(\n            parent: AdapterView<*>?,\n            view: View?,\n            position: Int,\n            id: Long\n        ) {\n            changeModel(position)\n        }\n    }\n\n    private var changeDeviceListener = object : AdapterView.OnItemSelectedListener {\n        override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {\n            changeDevice(position)\n        }\n\n        override fun onNothingSelected(parent: AdapterView<*>?) {\n            // do nothing\n        }\n    }\n\n    private var changeTrackerListener = object : AdapterView.OnItemSelectedListener {\n        override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {\n            changeTracker(position)\n        }\n\n        override fun onNothingSelected(parent: AdapterView<*>?) {\n            // do nothing\n        }\n    }\n\n    private var setClassificationListener =\n        CompoundButton.OnCheckedChangeListener { _, isChecked ->\n            showClassificationResult(isChecked)\n            isClassifyPose = isChecked\n            isPoseClassifier()\n        }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContentView(R.layout.activity_main)\n        // keep screen on while app is running\n        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)\n        tvScore = findViewById(R.id.tvScore)\n        tvFPS = findViewById(R.id.tvFps)\n        spnModel = findViewById(R.id.spnModel)\n        spnDevice = findViewById(R.id.spnDevice)\n        spnTracker = findViewById(R.id.spnTracker)\n        vTrackerOption = findViewById(R.id.vTrackerOption)\n        surfaceView = findViewById(R.id.surfaceView)\n        tvClassificationValue1 = findViewById(R.id.tvClassificationValue1)\n        tvClassificationValue2 = findViewById(R.id.tvClassificationValue2)\n        tvClassificationValue3 = findViewById(R.id.tvClassificationValue3)\n        swClassification = findViewById(R.id.swPoseClassification)\n        vClassificationOption = findViewById(R.id.vClassificationOption)\n        initSpinner()\n        spnModel.setSelection(modelPos)\n        swClassification.setOnCheckedChangeListener(setClassificationListener)\n        if (!isCameraPermissionGranted()) {\n            requestPermission()\n        }\n    }\n\n    override fun onStart() {\n        super.onStart()\n        openCamera()\n    }\n\n    override fun onResume() {\n        cameraSource?.resume()\n        super.onResume()\n    }\n\n    override fun onPause() {\n        cameraSource?.close()\n        cameraSource = null\n        super.onPause()\n    }\n\n    // check if permission is granted or not.\n    private fun isCameraPermissionGranted(): Boolean {\n        return checkPermission(\n            Manifest.permission.CAMERA,\n            Process.myPid(),\n            Process.myUid()\n        ) == PackageManager.PERMISSION_GRANTED\n    }\n\n    // open camera\n    private fun openCamera() {\n        if (isCameraPermissionGranted()) {\n            if (cameraSource == null) {\n                cameraSource =\n                    CameraSource(surfaceView, object : CameraSource.CameraSourceListener {\n                        override fun onFPSListener(fps: Int) {\n                            tvFPS.text = getString(R.string.tfe_pe_tv_fps, fps)\n                        }\n\n                        override fun onDetectedInfo(\n                            personScore: Float?,\n                            poseLabels: List<Pair<String, Float>>?\n                        ) {\n                            tvScore.text = getString(R.string.tfe_pe_tv_score, personScore ?: 0f)\n                            poseLabels?.sortedByDescending { it.second }?.let {\n                                tvClassificationValue1.text = getString(\n                                    R.string.tfe_pe_tv_classification_value,\n                                    convertPoseLabels(if (it.isNotEmpty()) it[0] else null)\n                                )\n                                tvClassificationValue2.text = getString(\n                                    R.string.tfe_pe_tv_classification_value,\n                                    convertPoseLabels(if (it.size >= 2) it[1] else null)\n                                )\n                                tvClassificationValue3.text = getString(\n                                    R.string.tfe_pe_tv_classification_value,\n                                    convertPoseLabels(if (it.size >= 3) it[2] else null)\n                                )\n                            }\n                        }\n\n                    }).apply {\n                        prepareCamera()\n                    }\n                isPoseClassifier()\n                lifecycleScope.launch(Dispatchers.Main) {\n                    cameraSource?.initCamera()\n                }\n            }\n            createPoseEstimator()\n        }\n    }\n\n    private fun convertPoseLabels(pair: Pair<String, Float>?): String {\n        if (pair == null) return \"empty\"\n        return \"${pair.first} (${String.format(\"%.2f\", pair.second)})\"\n    }\n\n    private fun isPoseClassifier() {\n        cameraSource?.setClassifier(if (isClassifyPose) PoseClassifier.create(this) else null)\n    }\n\n    // Initialize spinners to let user select model/accelerator/tracker.\n    private fun initSpinner() {\n        ArrayAdapter.createFromResource(\n            this,\n            R.array.tfe_pe_models_array,\n            android.R.layout.simple_spinner_item\n        ).also { adapter ->\n            // Specify the layout to use when the list of choices appears\n            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)\n            // Apply the adapter to the spinner\n            spnModel.adapter = adapter\n            spnModel.onItemSelectedListener = changeModelListener\n        }\n\n        ArrayAdapter.createFromResource(\n            this,\n            R.array.tfe_pe_device_name, android.R.layout.simple_spinner_item\n        ).also { adaper ->\n            adaper.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)\n\n            spnDevice.adapter = adaper\n            spnDevice.onItemSelectedListener = changeDeviceListener\n        }\n\n        ArrayAdapter.createFromResource(\n            this,\n            R.array.tfe_pe_tracker_array, android.R.layout.simple_spinner_item\n        ).also { adaper ->\n            adaper.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)\n\n            spnTracker.adapter = adaper\n            spnTracker.onItemSelectedListener = changeTrackerListener\n        }\n    }\n\n    // Change model when app is running\n    private fun changeModel(position: Int) {\n        if (modelPos == position) return\n        modelPos = position\n        createPoseEstimator()\n    }\n\n    // Change device (accelerator) type when app is running\n    private fun changeDevice(position: Int) {\n        val targetDevice = when (position) {\n            0 -> Device.CPU\n            1 -> Device.GPU\n            else -> Device.NNAPI\n        }\n        if (device == targetDevice) return\n        device = targetDevice\n        createPoseEstimator()\n    }\n\n    // Change tracker for Movenet MultiPose model\n    private fun changeTracker(position: Int) {\n        cameraSource?.setTracker(\n            when (position) {\n                1 -> TrackerType.BOUNDING_BOX\n                2 -> TrackerType.KEYPOINTS\n                else -> TrackerType.OFF\n            }\n        )\n    }\n\n    private fun createPoseEstimator() {\n        // For MoveNet MultiPose, hide score and disable pose classifier as the model returns\n        // multiple Person instances.\n        val poseDetector = when (modelPos) {\n            0 -> {\n                // MoveNet Lightning (SinglePose)\n                showPoseClassifier(true)\n                showDetectionScore(true)\n                showTracker(false)\n                MoveNet.create(this, device, ModelType.Lightning)\n            }\n            1 -> {\n                // MoveNet Thunder (SinglePose)\n                showPoseClassifier(true)\n                showDetectionScore(true)\n                showTracker(false)\n                MoveNet.create(this, device, ModelType.Thunder)\n            }\n            2 -> {\n                // MoveNet (Lightning) MultiPose\n                showPoseClassifier(false)\n                showDetectionScore(false)\n                // Movenet MultiPose Dynamic does not support GPUDelegate\n                if (device == Device.GPU) {\n                    showToast(getString(R.string.tfe_pe_gpu_error))\n                }\n                showTracker(true)\n                MoveNetMultiPose.create(\n                    this,\n                    device,\n                    Type.Dynamic\n                )\n            }\n            3 -> {\n                // PoseNet (SinglePose)\n                showPoseClassifier(true)\n                showDetectionScore(true)\n                showTracker(false)\n                PoseNet.create(this, device)\n            }\n            else -> {\n                null\n            }\n        }\n        poseDetector?.let { detector ->\n            cameraSource?.setDetector(detector)\n        }\n    }\n\n    // Show/hide the pose classification option.\n    private fun showPoseClassifier(isVisible: Boolean) {\n        vClassificationOption.visibility = if (isVisible) View.VISIBLE else View.GONE\n        if (!isVisible) {\n            swClassification.isChecked = false\n        }\n    }\n\n    // Show/hide the detection score.\n    private fun showDetectionScore(isVisible: Boolean) {\n        tvScore.visibility = if (isVisible) View.VISIBLE else View.GONE\n    }\n\n    // Show/hide classification result.\n    private fun showClassificationResult(isVisible: Boolean) {\n        val visibility = if (isVisible) View.VISIBLE else View.GONE\n        tvClassificationValue1.visibility = visibility\n        tvClassificationValue2.visibility = visibility\n        tvClassificationValue3.visibility = visibility\n    }\n\n    // Show/hide the tracking options.\n    private fun showTracker(isVisible: Boolean) {\n        if (isVisible) {\n            // Show tracker options and enable Bounding Box tracker.\n            vTrackerOption.visibility = View.VISIBLE\n            spnTracker.setSelection(1)\n        } else {\n            // Set tracker type to off and hide tracker option.\n            vTrackerOption.visibility = View.GONE\n            spnTracker.setSelection(0)\n        }\n    }\n\n    private fun requestPermission() {\n        when (PackageManager.PERMISSION_GRANTED) {\n            ContextCompat.checkSelfPermission(\n                this,\n                Manifest.permission.CAMERA\n            ) -> {\n                // You can use the API that requires the permission.\n                openCamera()\n            }\n            else -> {\n                // You can directly ask for the permission.\n                // The registered ActivityResultCallback gets the result of this request.\n                requestPermissionLauncher.launch(\n                    Manifest.permission.CAMERA\n                )\n            }\n        }\n    }\n\n    private fun showToast(message: String) {\n        Toast.makeText(this, message, Toast.LENGTH_LONG).show()\n    }\n\n    /**\n     * Shows an error message dialog.\n     */\n    class ErrorDialog : DialogFragment() {\n\n        override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =\n            AlertDialog.Builder(activity)\n                .setMessage(requireArguments().getString(ARG_MESSAGE))\n                .setPositiveButton(android.R.string.ok) { _, _ ->\n                    // do nothing\n                }\n                .create()\n\n        companion object {\n\n            @JvmStatic\n            private val ARG_MESSAGE = \"message\"\n\n            @JvmStatic\n            fun newInstance(message: String): ErrorDialog = ErrorDialog().apply {\n                arguments = Bundle().apply { putString(ARG_MESSAGE, message) }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/VisualizationUtils.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation\n\nimport android.graphics.Bitmap\nimport android.graphics.Canvas\nimport android.graphics.Color\nimport android.graphics.Paint\nimport org.tensorflow.lite.examples.poseestimation.data.BodyPart\nimport org.tensorflow.lite.examples.poseestimation.data.Person\nimport kotlin.math.max\n\nobject VisualizationUtils {\n    /** Radius of circle used to draw keypoints.  */\n    private const val CIRCLE_RADIUS = 6f\n\n    /** Width of line used to connected two keypoints.  */\n    private const val LINE_WIDTH = 4f\n\n    /** The text size of the person id that will be displayed when the tracker is available.  */\n    private const val PERSON_ID_TEXT_SIZE = 30f\n\n    /** Distance from person id to the nose keypoint.  */\n    private const val PERSON_ID_MARGIN = 6f\n\n    /** Pair of keypoints to draw lines between.  */\n    private val bodyJoints = listOf(\n        Pair(BodyPart.NOSE, BodyPart.LEFT_EYE),\n        Pair(BodyPart.NOSE, BodyPart.RIGHT_EYE),\n        Pair(BodyPart.LEFT_EYE, BodyPart.LEFT_EAR),\n        Pair(BodyPart.RIGHT_EYE, BodyPart.RIGHT_EAR),\n        Pair(BodyPart.NOSE, BodyPart.LEFT_SHOULDER),\n        Pair(BodyPart.NOSE, BodyPart.RIGHT_SHOULDER),\n        Pair(BodyPart.LEFT_SHOULDER, BodyPart.LEFT_ELBOW),\n        Pair(BodyPart.LEFT_ELBOW, BodyPart.LEFT_WRIST),\n        Pair(BodyPart.RIGHT_SHOULDER, BodyPart.RIGHT_ELBOW),\n        Pair(BodyPart.RIGHT_ELBOW, BodyPart.RIGHT_WRIST),\n        Pair(BodyPart.LEFT_SHOULDER, BodyPart.RIGHT_SHOULDER),\n        Pair(BodyPart.LEFT_SHOULDER, BodyPart.LEFT_HIP),\n        Pair(BodyPart.RIGHT_SHOULDER, BodyPart.RIGHT_HIP),\n        Pair(BodyPart.LEFT_HIP, BodyPart.RIGHT_HIP),\n        Pair(BodyPart.LEFT_HIP, BodyPart.LEFT_KNEE),\n        Pair(BodyPart.LEFT_KNEE, BodyPart.LEFT_ANKLE),\n        Pair(BodyPart.RIGHT_HIP, BodyPart.RIGHT_KNEE),\n        Pair(BodyPart.RIGHT_KNEE, BodyPart.RIGHT_ANKLE)\n    )\n\n    // Draw line and point indicate body pose\n    fun drawBodyKeypoints(\n        input: Bitmap,\n        persons: List<Person>,\n        isTrackerEnabled: Boolean = false\n    ): Bitmap {\n        val paintCircle = Paint().apply {\n            strokeWidth = CIRCLE_RADIUS\n            color = Color.RED\n            style = Paint.Style.FILL\n        }\n        val paintLine = Paint().apply {\n            strokeWidth = LINE_WIDTH\n            color = Color.RED\n            style = Paint.Style.STROKE\n        }\n\n        val paintText = Paint().apply {\n            textSize = PERSON_ID_TEXT_SIZE\n            color = Color.BLUE\n            textAlign = Paint.Align.LEFT\n        }\n\n        val output = input.copy(Bitmap.Config.ARGB_8888, true)\n        val originalSizeCanvas = Canvas(output)\n        persons.forEach { person ->\n            // draw person id if tracker is enable\n            if (isTrackerEnabled) {\n                person.boundingBox?.let {\n                    val personIdX = max(0f, it.left)\n                    val personIdY = max(0f, it.top)\n\n                    originalSizeCanvas.drawText(\n                        person.id.toString(),\n                        personIdX,\n                        personIdY - PERSON_ID_MARGIN,\n                        paintText\n                    )\n                    originalSizeCanvas.drawRect(it, paintLine)\n                }\n            }\n            bodyJoints.forEach {\n                val pointA = person.keyPoints[it.first.position].coordinate\n                val pointB = person.keyPoints[it.second.position].coordinate\n                originalSizeCanvas.drawLine(pointA.x, pointA.y, pointB.x, pointB.y, paintLine)\n            }\n\n            person.keyPoints.forEach { point ->\n                originalSizeCanvas.drawCircle(\n                    point.coordinate.x,\n                    point.coordinate.y,\n                    CIRCLE_RADIUS,\n                    paintCircle\n                )\n            }\n        }\n        return output\n    }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/YuvToRgbConverter.kt",
    "content": "package org.tensorflow.lite.examples.poseestimation\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.graphics.ImageFormat\nimport android.graphics.Rect\nimport android.media.Image\nimport android.renderscript.Allocation\nimport android.renderscript.Element\nimport android.renderscript.RenderScript\nimport android.renderscript.ScriptIntrinsicYuvToRGB\nimport java.nio.ByteBuffer\n\nclass YuvToRgbConverter(context: Context) {\n    private val rs = RenderScript.create(context)\n    private val scriptYuvToRgb = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs))\n\n    private var pixelCount: Int = -1\n    private lateinit var yuvBuffer: ByteBuffer\n    private lateinit var inputAllocation: Allocation\n    private lateinit var outputAllocation: Allocation\n\n    @Synchronized\n    fun yuvToRgb(image: Image, output: Bitmap) {\n\n        // Ensure that the intermediate output byte buffer is allocated\n        if (!::yuvBuffer.isInitialized) {\n            pixelCount = image.cropRect.width() * image.cropRect.height()\n            yuvBuffer = ByteBuffer.allocateDirect(\n                pixelCount * ImageFormat.getBitsPerPixel(ImageFormat.YUV_420_888) / 8)\n        }\n\n        // Get the YUV data in byte array form\n        imageToByteBuffer(image, yuvBuffer)\n\n        // Ensure that the RenderScript inputs and outputs are allocated\n        if (!::inputAllocation.isInitialized) {\n            inputAllocation = Allocation.createSized(rs, Element.U8(rs), yuvBuffer.array().size)\n        }\n        if (!::outputAllocation.isInitialized) {\n            outputAllocation = Allocation.createFromBitmap(rs, output)\n        }\n\n        // Convert YUV to RGB\n        inputAllocation.copyFrom(yuvBuffer.array())\n        scriptYuvToRgb.setInput(inputAllocation)\n        scriptYuvToRgb.forEach(outputAllocation)\n        outputAllocation.copyTo(output)\n    }\n\n    private fun imageToByteBuffer(image: Image, outputBuffer: ByteBuffer) {\n        assert(image.format == ImageFormat.YUV_420_888)\n\n        val imageCrop = image.cropRect\n        val imagePlanes = image.planes\n        val rowData = ByteArray(imagePlanes.first().rowStride)\n\n        imagePlanes.forEachIndexed { planeIndex, plane ->\n\n            // How many values are read in input for each output value written\n            // Only the Y plane has a value for every pixel, U and V have half the resolution i.e.\n            //\n            // Y Plane            U Plane    V Plane\n            // ===============    =======    =======\n            // Y Y Y Y Y Y Y Y    U U U U    V V V V\n            // Y Y Y Y Y Y Y Y    U U U U    V V V V\n            // Y Y Y Y Y Y Y Y    U U U U    V V V V\n            // Y Y Y Y Y Y Y Y    U U U U    V V V V\n            // Y Y Y Y Y Y Y Y\n            // Y Y Y Y Y Y Y Y\n            // Y Y Y Y Y Y Y Y\n            val outputStride: Int\n\n            // The index in the output buffer the next value will be written at\n            // For Y it's zero, for U and V we start at the end of Y and interleave them i.e.\n            //\n            // First chunk        Second chunk\n            // ===============    ===============\n            // Y Y Y Y Y Y Y Y    U V U V U V U V\n            // Y Y Y Y Y Y Y Y    U V U V U V U V\n            // Y Y Y Y Y Y Y Y    U V U V U V U V\n            // Y Y Y Y Y Y Y Y    U V U V U V U V\n            // Y Y Y Y Y Y Y Y\n            // Y Y Y Y Y Y Y Y\n            // Y Y Y Y Y Y Y Y\n            var outputOffset: Int\n\n            when (planeIndex) {\n                0 -> {\n                    outputStride = 1\n                    outputOffset = 0\n                }\n                1 -> {\n                    outputStride = 2\n                    outputOffset = pixelCount + 1\n                }\n                2 -> {\n                    outputStride = 2\n                    outputOffset = pixelCount\n                }\n                else -> {\n                    // Image contains more than 3 planes, something strange is going on\n                    return@forEachIndexed\n                }\n            }\n\n            val buffer = plane.buffer\n            val rowStride = plane.rowStride\n            val pixelStride = plane.pixelStride\n\n            // We have to divide the width and height by two if it's not the Y plane\n            val planeCrop = if (planeIndex == 0) {\n                imageCrop\n            } else {\n                Rect(\n                    imageCrop.left / 2,\n                    imageCrop.top / 2,\n                    imageCrop.right / 2,\n                    imageCrop.bottom / 2\n                )\n            }\n\n            val planeWidth = planeCrop.width()\n            val planeHeight = planeCrop.height()\n\n            buffer.position(rowStride * planeCrop.top + pixelStride * planeCrop.left)\n            for (row in 0 until planeHeight) {\n                val length: Int\n                if (pixelStride == 1 && outputStride == 1) {\n                    // When there is a single stride value for pixel and output, we can just copy\n                    // the entire row in a single step\n                    length = planeWidth\n                    buffer.get(outputBuffer.array(), outputOffset, length)\n                    outputOffset += length\n                } else {\n                    // When either pixel or output have a stride > 1 we must copy pixel by pixel\n                    length = (planeWidth - 1) * pixelStride + 1\n                    buffer.get(rowData, 0, length)\n                    for (col in 0 until planeWidth) {\n                        outputBuffer.array()[outputOffset] = rowData[col * pixelStride]\n                        outputOffset += outputStride\n                    }\n                }\n\n                if (row < planeHeight - 1) {\n                    buffer.position(buffer.position() + rowStride - length)\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/camera/CameraSource.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.camera\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.graphics.ImageFormat\nimport android.graphics.Matrix\nimport android.graphics.Rect\nimport android.hardware.camera2.CameraCaptureSession\nimport android.hardware.camera2.CameraCharacteristics\nimport android.hardware.camera2.CameraDevice\nimport android.hardware.camera2.CameraManager\nimport android.media.ImageReader\nimport android.os.Handler\nimport android.os.HandlerThread\nimport android.util.Log\nimport android.view.Surface\nimport android.view.SurfaceView\nimport kotlinx.coroutines.suspendCancellableCoroutine\nimport org.tensorflow.lite.examples.poseestimation.VisualizationUtils\nimport org.tensorflow.lite.examples.poseestimation.YuvToRgbConverter\nimport org.tensorflow.lite.examples.poseestimation.data.Person\nimport org.tensorflow.lite.examples.poseestimation.ml.MoveNetMultiPose\nimport org.tensorflow.lite.examples.poseestimation.ml.PoseClassifier\nimport org.tensorflow.lite.examples.poseestimation.ml.PoseDetector\nimport org.tensorflow.lite.examples.poseestimation.ml.TrackerType\nimport java.util.*\nimport kotlin.coroutines.resume\nimport kotlin.coroutines.resumeWithException\n\nclass CameraSource(\n    private val surfaceView: SurfaceView,\n    private val listener: CameraSourceListener? = null\n) {\n\n    companion object {\n        private const val PREVIEW_WIDTH = 640\n        private const val PREVIEW_HEIGHT = 480\n\n        /** Threshold for confidence score. */\n        private const val MIN_CONFIDENCE = .2f\n        private const val TAG = \"Camera Source\"\n    }\n\n    private val lock = Any()\n    private var detector: PoseDetector? = null\n    private var classifier: PoseClassifier? = null\n    private var isTrackerEnabled = false\n    private var yuvConverter: YuvToRgbConverter = YuvToRgbConverter(surfaceView.context)\n    private lateinit var imageBitmap: Bitmap\n\n    /** Frame count that have been processed so far in an one second interval to calculate FPS. */\n    private var fpsTimer: Timer? = null\n    private var frameProcessedInOneSecondInterval = 0\n    private var framesPerSecond = 0\n\n    /** Detects, characterizes, and connects to a CameraDevice (used for all camera operations) */\n    private val cameraManager: CameraManager by lazy {\n        val context = surfaceView.context\n        context.getSystemService(Context.CAMERA_SERVICE) as CameraManager\n    }\n\n    /** Readers used as buffers for camera still shots */\n    private var imageReader: ImageReader? = null\n\n    /** The [CameraDevice] that will be opened in this fragment */\n    private var camera: CameraDevice? = null\n\n    /** Internal reference to the ongoing [CameraCaptureSession] configured with our parameters */\n    private var session: CameraCaptureSession? = null\n\n    /** [HandlerThread] where all buffer reading operations run */\n    private var imageReaderThread: HandlerThread? = null\n\n    /** [Handler] corresponding to [imageReaderThread] */\n    private var imageReaderHandler: Handler? = null\n    private var cameraId: String = \"\"\n\n    suspend fun initCamera() {\n        camera = openCamera(cameraManager, cameraId)\n        imageReader =\n            ImageReader.newInstance(PREVIEW_WIDTH, PREVIEW_HEIGHT, ImageFormat.YUV_420_888, 3)\n        imageReader?.setOnImageAvailableListener({ reader ->\n            val image = reader.acquireLatestImage()\n            if (image != null) {\n                if (!::imageBitmap.isInitialized) {\n                    imageBitmap =\n                        Bitmap.createBitmap(\n                            PREVIEW_WIDTH,\n                            PREVIEW_HEIGHT,\n                            Bitmap.Config.ARGB_8888\n                        )\n                }\n                yuvConverter.yuvToRgb(image, imageBitmap)\n                // Create rotated version for portrait display\n                val rotateMatrix = Matrix()\n                rotateMatrix.postRotate(90.0f)\n\n                val rotatedBitmap = Bitmap.createBitmap(\n                    imageBitmap, 0, 0, PREVIEW_WIDTH, PREVIEW_HEIGHT,\n                    rotateMatrix, false\n                )\n                processImage(rotatedBitmap)\n                image.close()\n            }\n        }, imageReaderHandler)\n\n        imageReader?.surface?.let { surface ->\n            session = createSession(listOf(surface))\n            val cameraRequest = camera?.createCaptureRequest(\n                CameraDevice.TEMPLATE_PREVIEW\n            )?.apply {\n                addTarget(surface)\n            }\n            cameraRequest?.build()?.let {\n                session?.setRepeatingRequest(it, null, null)\n            }\n        }\n    }\n\n    private suspend fun createSession(targets: List<Surface>): CameraCaptureSession =\n        suspendCancellableCoroutine { cont ->\n            camera?.createCaptureSession(targets, object : CameraCaptureSession.StateCallback() {\n                override fun onConfigured(captureSession: CameraCaptureSession) =\n                    cont.resume(captureSession)\n\n                override fun onConfigureFailed(session: CameraCaptureSession) {\n                    cont.resumeWithException(Exception(\"Session error\"))\n                }\n            }, null)\n        }\n\n    @SuppressLint(\"MissingPermission\")\n    private suspend fun openCamera(manager: CameraManager, cameraId: String): CameraDevice =\n        suspendCancellableCoroutine { cont ->\n            manager.openCamera(cameraId, object : CameraDevice.StateCallback() {\n                override fun onOpened(camera: CameraDevice) = cont.resume(camera)\n\n                override fun onDisconnected(camera: CameraDevice) {\n                    camera.close()\n                }\n\n                override fun onError(camera: CameraDevice, error: Int) {\n                    if (cont.isActive) cont.resumeWithException(Exception(\"Camera error\"))\n                }\n            }, imageReaderHandler)\n        }\n\n    fun prepareCamera() {\n        for (cameraId in cameraManager.cameraIdList) {\n            val characteristics = cameraManager.getCameraCharacteristics(cameraId)\n\n            // We don't use a front facing camera in this sample.\n            val cameraDirection = characteristics.get(CameraCharacteristics.LENS_FACING)\n            if (cameraDirection != null &&\n                cameraDirection == CameraCharacteristics.LENS_FACING_FRONT\n            ) {\n                continue\n            }\n            this.cameraId = cameraId\n        }\n    }\n\n    fun setDetector(detector: PoseDetector) {\n        synchronized(lock) {\n            if (this.detector != null) {\n                this.detector?.close()\n                this.detector = null\n            }\n            this.detector = detector\n        }\n    }\n\n    fun setClassifier(classifier: PoseClassifier?) {\n        synchronized(lock) {\n            if (this.classifier != null) {\n                this.classifier?.close()\n                this.classifier = null\n            }\n            this.classifier = classifier\n        }\n    }\n\n    /**\n     * Set Tracker for Movenet MuiltiPose model.\n     */\n    fun setTracker(trackerType: TrackerType) {\n        isTrackerEnabled = trackerType != TrackerType.OFF\n        (this.detector as? MoveNetMultiPose)?.setTracker(trackerType)\n    }\n\n    fun resume() {\n        imageReaderThread = HandlerThread(\"imageReaderThread\").apply { start() }\n        imageReaderHandler = Handler(imageReaderThread!!.looper)\n        fpsTimer = Timer()\n        fpsTimer?.scheduleAtFixedRate(\n            object : TimerTask() {\n                override fun run() {\n                    framesPerSecond = frameProcessedInOneSecondInterval\n                    frameProcessedInOneSecondInterval = 0\n                }\n            },\n            0,\n            1000\n        )\n    }\n\n    fun close() {\n        session?.close()\n        session = null\n        camera?.close()\n        camera = null\n        imageReader?.close()\n        imageReader = null\n        stopImageReaderThread()\n        detector?.close()\n        detector = null\n        classifier?.close()\n        classifier = null\n        fpsTimer?.cancel()\n        fpsTimer = null\n        frameProcessedInOneSecondInterval = 0\n        framesPerSecond = 0\n    }\n\n    // process image\n    private fun processImage(bitmap: Bitmap) {\n        val persons = mutableListOf<Person>()\n        var classificationResult: List<Pair<String, Float>>? = null\n\n        synchronized(lock) {\n            detector?.estimatePoses(bitmap)?.let {\n                persons.addAll(it)\n\n                // if the model only returns one item, allow running the Pose classifier.\n                if (persons.isNotEmpty()) {\n                    classifier?.run {\n                        classificationResult = classify(persons[0])\n                    }\n                }\n            }\n        }\n        frameProcessedInOneSecondInterval++\n        if (frameProcessedInOneSecondInterval == 1) {\n            // send fps to view\n            listener?.onFPSListener(framesPerSecond)\n        }\n\n        // if the model returns only one item, show that item's score.\n        if (persons.isNotEmpty()) {\n            listener?.onDetectedInfo(persons[0].score, classificationResult)\n        }\n        visualize(persons, bitmap)\n    }\n\n    private fun visualize(persons: List<Person>, bitmap: Bitmap) {\n\n        val outputBitmap = VisualizationUtils.drawBodyKeypoints(\n            bitmap,\n            persons.filter { it.score > MIN_CONFIDENCE }, isTrackerEnabled\n        )\n\n        val holder = surfaceView.holder\n        val surfaceCanvas = holder.lockCanvas()\n        surfaceCanvas?.let { canvas ->\n            val screenWidth: Int\n            val screenHeight: Int\n            val left: Int\n            val top: Int\n\n            if (canvas.height > canvas.width) {\n                val ratio = outputBitmap.height.toFloat() / outputBitmap.width\n                screenWidth = canvas.width\n                left = 0\n                screenHeight = (canvas.width * ratio).toInt()\n                top = (canvas.height - screenHeight) / 2\n            } else {\n                val ratio = outputBitmap.width.toFloat() / outputBitmap.height\n                screenHeight = canvas.height\n                top = 0\n                screenWidth = (canvas.height * ratio).toInt()\n                left = (canvas.width - screenWidth) / 2\n            }\n            val right: Int = left + screenWidth\n            val bottom: Int = top + screenHeight\n\n            canvas.drawBitmap(\n                outputBitmap, Rect(0, 0, outputBitmap.width, outputBitmap.height),\n                Rect(left, top, right, bottom), null\n            )\n            surfaceView.holder.unlockCanvasAndPost(canvas)\n        }\n    }\n\n    private fun stopImageReaderThread() {\n        imageReaderThread?.quitSafely()\n        try {\n            imageReaderThread?.join()\n            imageReaderThread = null\n            imageReaderHandler = null\n        } catch (e: InterruptedException) {\n            Log.d(TAG, e.message.toString())\n        }\n    }\n\n    interface CameraSourceListener {\n        fun onFPSListener(fps: Int)\n\n        fun onDetectedInfo(personScore: Float?, poseLabels: List<Pair<String, Float>>?)\n    }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/data/BodyPart.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.data\n\nenum class BodyPart(val position: Int) {\n    NOSE(0),\n    LEFT_EYE(1),\n    RIGHT_EYE(2),\n    LEFT_EAR(3),\n    RIGHT_EAR(4),\n    LEFT_SHOULDER(5),\n    RIGHT_SHOULDER(6),\n    LEFT_ELBOW(7),\n    RIGHT_ELBOW(8),\n    LEFT_WRIST(9),\n    RIGHT_WRIST(10),\n    LEFT_HIP(11),\n    RIGHT_HIP(12),\n    LEFT_KNEE(13),\n    RIGHT_KNEE(14),\n    LEFT_ANKLE(15),\n    RIGHT_ANKLE(16);\n    companion object{\n        private val map = values().associateBy(BodyPart::position)\n        fun fromInt(position: Int): BodyPart = map.getValue(position)\n    }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/data/Device.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.data\n\nenum class Device {\n    CPU,\n    NNAPI,\n    GPU\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/data/KeyPoint.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.data\n\nimport android.graphics.PointF\n\ndata class KeyPoint(val bodyPart: BodyPart, var coordinate: PointF, val score: Float)\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/data/Person.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.data\n\nimport android.graphics.RectF\n\ndata class Person(\n    var id: Int = -1, // default id is -1\n    val keyPoints: List<KeyPoint>,\n    val boundingBox: RectF? = null, // Only MoveNet MultiPose return bounding box.\n    val score: Float\n)\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/data/TorsoAndBodyDistance.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.data\n\ndata class TorsoAndBodyDistance(\n    val maxTorsoYDistance: Float,\n    val maxTorsoXDistance: Float,\n    val maxBodyYDistance: Float,\n    val maxBodyXDistance: Float\n)\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/ml/MoveNet.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.ml\n\nimport android.content.Context\nimport android.graphics.*\nimport android.os.SystemClock\nimport org.tensorflow.lite.DataType\nimport org.tensorflow.lite.Interpreter\nimport org.tensorflow.lite.examples.poseestimation.data.*\nimport org.tensorflow.lite.gpu.GpuDelegate\nimport org.tensorflow.lite.support.common.FileUtil\nimport org.tensorflow.lite.support.image.ImageProcessor\nimport org.tensorflow.lite.support.image.TensorImage\nimport org.tensorflow.lite.support.image.ops.ResizeOp\nimport org.tensorflow.lite.support.image.ops.ResizeWithCropOrPadOp\nimport org.tensorflow.lite.support.tensorbuffer.TensorBuffer\nimport kotlin.math.abs\nimport kotlin.math.max\nimport kotlin.math.min\n\nenum class ModelType {\n    Lightning,\n    Thunder\n}\n\nclass MoveNet(private val interpreter: Interpreter, private var gpuDelegate: GpuDelegate?) :\n    PoseDetector {\n\n    companion object {\n        private const val MIN_CROP_KEYPOINT_SCORE = .2f\n        private const val CPU_NUM_THREADS = 4\n\n        // Parameters that control how large crop region should be expanded from previous frames'\n        // body keypoints.\n        private const val TORSO_EXPANSION_RATIO = 1.9f\n        private const val BODY_EXPANSION_RATIO = 1.2f\n\n        // TFLite file names.\n        private const val LIGHTNING_FILENAME = \"movenet_lightning.tflite\"\n        private const val THUNDER_FILENAME = \"movenet_thunder.tflite\"\n\n        // allow specifying model type.\n        fun create(context: Context, device: Device, modelType: ModelType): MoveNet {\n            val options = Interpreter.Options()\n            var gpuDelegate: GpuDelegate? = null\n            options.setNumThreads(CPU_NUM_THREADS)\n            when (device) {\n                Device.CPU -> {\n                }\n                Device.GPU -> {\n                    gpuDelegate = GpuDelegate()\n                    options.addDelegate(gpuDelegate)\n                }\n                Device.NNAPI -> options.setUseNNAPI(true)\n            }\n            return MoveNet(\n                Interpreter(\n                    FileUtil.loadMappedFile(\n                        context,\n                        if (modelType == ModelType.Lightning) LIGHTNING_FILENAME\n                        else THUNDER_FILENAME\n                    ), options\n                ),\n                gpuDelegate\n            )\n        }\n\n        // default to lightning.\n        fun create(context: Context, device: Device): MoveNet =\n            create(context, device, ModelType.Lightning)\n    }\n\n    private var cropRegion: RectF? = null\n    private var lastInferenceTimeNanos: Long = -1\n    private val inputWidth = interpreter.getInputTensor(0).shape()[1]\n    private val inputHeight = interpreter.getInputTensor(0).shape()[2]\n    private var outputShape: IntArray = interpreter.getOutputTensor(0).shape()\n\n    override fun estimatePoses(bitmap: Bitmap): List<Person> {\n        val inferenceStartTimeNanos = SystemClock.elapsedRealtimeNanos()\n        if (cropRegion == null) {\n            cropRegion = initRectF(bitmap.width, bitmap.height)\n        }\n        var totalScore = 0f\n\n        val numKeyPoints = outputShape[2]\n        val keyPoints = mutableListOf<KeyPoint>()\n\n        cropRegion?.run {\n            val rect = RectF(\n                (left * bitmap.width),\n                (top * bitmap.height),\n                (right * bitmap.width),\n                (bottom * bitmap.height)\n            )\n            val detectBitmap = Bitmap.createBitmap(\n                rect.width().toInt(),\n                rect.height().toInt(),\n                Bitmap.Config.ARGB_8888\n            )\n            Canvas(detectBitmap).drawBitmap(\n                bitmap,\n                -rect.left,\n                -rect.top,\n                null\n            )\n            val inputTensor = processInputImage(detectBitmap, inputWidth, inputHeight)\n            val outputTensor = TensorBuffer.createFixedSize(outputShape, DataType.FLOAT32)\n            val widthRatio = detectBitmap.width.toFloat() / inputWidth\n            val heightRatio = detectBitmap.height.toFloat() / inputHeight\n\n            val positions = mutableListOf<Float>()\n\n            inputTensor?.let { input ->\n                interpreter.run(input.buffer, outputTensor.buffer.rewind())\n                val output = outputTensor.floatArray\n                for (idx in 0 until numKeyPoints) {\n                    val x = output[idx * 3 + 1] * inputWidth * widthRatio\n                    val y = output[idx * 3 + 0] * inputHeight * heightRatio\n\n                    positions.add(x)\n                    positions.add(y)\n                    val score = output[idx * 3 + 2]\n                    keyPoints.add(\n                        KeyPoint(\n                            BodyPart.fromInt(idx),\n                            PointF(\n                                x,\n                                y\n                            ),\n                            score\n                        )\n                    )\n                    totalScore += score\n                }\n            }\n            val matrix = Matrix()\n            val points = positions.toFloatArray()\n\n            matrix.postTranslate(rect.left, rect.top)\n            matrix.mapPoints(points)\n            keyPoints.forEachIndexed { index, keyPoint ->\n                keyPoint.coordinate =\n                    PointF(\n                        points[index * 2],\n                        points[index * 2 + 1]\n                    )\n            }\n            // new crop region\n            cropRegion = determineRectF(keyPoints, bitmap.width, bitmap.height)\n        }\n        lastInferenceTimeNanos =\n            SystemClock.elapsedRealtimeNanos() - inferenceStartTimeNanos\n        return listOf(Person(keyPoints = keyPoints, score = totalScore / numKeyPoints))\n    }\n\n    override fun lastInferenceTimeNanos(): Long = lastInferenceTimeNanos\n\n    override fun close() {\n        gpuDelegate?.close()\n        interpreter.close()\n        cropRegion = null\n    }\n\n    /**\n     * Prepare input image for detection\n     */\n    private fun processInputImage(bitmap: Bitmap, inputWidth: Int, inputHeight: Int): TensorImage? {\n        val width: Int = bitmap.width\n        val height: Int = bitmap.height\n\n        val size = if (height > width) width else height\n        val imageProcessor = ImageProcessor.Builder().apply {\n            add(ResizeWithCropOrPadOp(size, size))\n            add(ResizeOp(inputWidth, inputHeight, ResizeOp.ResizeMethod.BILINEAR))\n        }.build()\n        val tensorImage = TensorImage(DataType.UINT8)\n        tensorImage.load(bitmap)\n        return imageProcessor.process(tensorImage)\n    }\n\n    /**\n     * Defines the default crop region.\n     * The function provides the initial crop region (pads the full image from both\n     * sides to make it a square image) when the algorithm cannot reliably determine\n     * the crop region from the previous frame.\n     */\n    private fun initRectF(imageWidth: Int, imageHeight: Int): RectF {\n        val xMin: Float\n        val yMin: Float\n        val width: Float\n        val height: Float\n        if (imageWidth > imageHeight) {\n            width = 1f\n            height = imageWidth.toFloat() / imageHeight\n            xMin = 0f\n            yMin = (imageHeight / 2f - imageWidth / 2f) / imageHeight\n        } else {\n            height = 1f\n            width = imageHeight.toFloat() / imageWidth\n            yMin = 0f\n            xMin = (imageWidth / 2f - imageHeight / 2) / imageWidth\n        }\n        return RectF(\n            xMin,\n            yMin,\n            xMin + width,\n            yMin + height\n        )\n    }\n\n    /**\n     * Checks whether there are enough torso keypoints.\n     * This function checks whether the model is confident at predicting one of the\n     * shoulders/hips which is required to determine a good crop region.\n     */\n    private fun torsoVisible(keyPoints: List<KeyPoint>): Boolean {\n        return ((keyPoints[BodyPart.LEFT_HIP.position].score > MIN_CROP_KEYPOINT_SCORE).or(\n            keyPoints[BodyPart.RIGHT_HIP.position].score > MIN_CROP_KEYPOINT_SCORE\n        )).and(\n            (keyPoints[BodyPart.LEFT_SHOULDER.position].score > MIN_CROP_KEYPOINT_SCORE).or(\n                keyPoints[BodyPart.RIGHT_SHOULDER.position].score > MIN_CROP_KEYPOINT_SCORE\n            )\n        )\n    }\n\n    /**\n     * Determines the region to crop the image for the model to run inference on.\n     * The algorithm uses the detected joints from the previous frame to estimate\n     * the square region that encloses the full body of the target person and\n     * centers at the midpoint of two hip joints. The crop size is determined by\n     * the distances between each joints and the center point.\n     * When the model is not confident with the four torso joint predictions, the\n     * function returns a default crop which is the full image padded to square.\n     */\n    private fun determineRectF(\n        keyPoints: List<KeyPoint>,\n        imageWidth: Int,\n        imageHeight: Int\n    ): RectF {\n        val targetKeyPoints = mutableListOf<KeyPoint>()\n        keyPoints.forEach {\n            targetKeyPoints.add(\n                KeyPoint(\n                    it.bodyPart,\n                    PointF(\n                        it.coordinate.x,\n                        it.coordinate.y\n                    ),\n                    it.score\n                )\n            )\n        }\n        if (torsoVisible(keyPoints)) {\n            val centerX =\n                (targetKeyPoints[BodyPart.LEFT_HIP.position].coordinate.x +\n                        targetKeyPoints[BodyPart.RIGHT_HIP.position].coordinate.x) / 2f\n            val centerY =\n                (targetKeyPoints[BodyPart.LEFT_HIP.position].coordinate.y +\n                        targetKeyPoints[BodyPart.RIGHT_HIP.position].coordinate.y) / 2f\n\n            val torsoAndBodyDistances =\n                determineTorsoAndBodyDistances(keyPoints, targetKeyPoints, centerX, centerY)\n\n            val list = listOf(\n                torsoAndBodyDistances.maxTorsoXDistance * TORSO_EXPANSION_RATIO,\n                torsoAndBodyDistances.maxTorsoYDistance * TORSO_EXPANSION_RATIO,\n                torsoAndBodyDistances.maxBodyXDistance * BODY_EXPANSION_RATIO,\n                torsoAndBodyDistances.maxBodyYDistance * BODY_EXPANSION_RATIO\n            )\n\n            var cropLengthHalf = list.maxOrNull() ?: 0f\n            val tmp = listOf(centerX, imageWidth - centerX, centerY, imageHeight - centerY)\n            cropLengthHalf = min(cropLengthHalf, tmp.maxOrNull() ?: 0f)\n            val cropCorner = Pair(centerY - cropLengthHalf, centerX - cropLengthHalf)\n\n            return if (cropLengthHalf > max(imageWidth, imageHeight) / 2f) {\n                initRectF(imageWidth, imageHeight)\n            } else {\n                val cropLength = cropLengthHalf * 2\n                RectF(\n                    cropCorner.second / imageWidth,\n                    cropCorner.first / imageHeight,\n                    (cropCorner.second + cropLength) / imageWidth,\n                    (cropCorner.first + cropLength) / imageHeight,\n                )\n            }\n        } else {\n            return initRectF(imageWidth, imageHeight)\n        }\n    }\n\n    /**\n     * Calculates the maximum distance from each keypoints to the center location.\n     * The function returns the maximum distances from the two sets of keypoints:\n     * full 17 keypoints and 4 torso keypoints. The returned information will be\n     * used to determine the crop size. See determineRectF for more detail.\n     */\n    private fun determineTorsoAndBodyDistances(\n        keyPoints: List<KeyPoint>,\n        targetKeyPoints: List<KeyPoint>,\n        centerX: Float,\n        centerY: Float\n    ): TorsoAndBodyDistance {\n        val torsoJoints = listOf(\n            BodyPart.LEFT_SHOULDER.position,\n            BodyPart.RIGHT_SHOULDER.position,\n            BodyPart.LEFT_HIP.position,\n            BodyPart.RIGHT_HIP.position\n        )\n\n        var maxTorsoYRange = 0f\n        var maxTorsoXRange = 0f\n        torsoJoints.forEach { joint ->\n            val distY = abs(centerY - targetKeyPoints[joint].coordinate.y)\n            val distX = abs(centerX - targetKeyPoints[joint].coordinate.x)\n            if (distY > maxTorsoYRange) maxTorsoYRange = distY\n            if (distX > maxTorsoXRange) maxTorsoXRange = distX\n        }\n\n        var maxBodyYRange = 0f\n        var maxBodyXRange = 0f\n        for (joint in keyPoints.indices) {\n            if (keyPoints[joint].score < MIN_CROP_KEYPOINT_SCORE) continue\n            val distY = abs(centerY - keyPoints[joint].coordinate.y)\n            val distX = abs(centerX - keyPoints[joint].coordinate.x)\n\n            if (distY > maxBodyYRange) maxBodyYRange = distY\n            if (distX > maxBodyXRange) maxBodyXRange = distX\n        }\n        return TorsoAndBodyDistance(\n            maxTorsoYRange,\n            maxTorsoXRange,\n            maxBodyYRange,\n            maxBodyXRange\n        )\n    }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/ml/MoveNetMultiPose.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.ml\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.graphics.PointF\nimport android.graphics.RectF\nimport android.os.SystemClock\nimport org.tensorflow.lite.DataType\nimport org.tensorflow.lite.Interpreter\nimport org.tensorflow.lite.examples.poseestimation.data.BodyPart\nimport org.tensorflow.lite.examples.poseestimation.data.Device\nimport org.tensorflow.lite.examples.poseestimation.data.KeyPoint\nimport org.tensorflow.lite.examples.poseestimation.data.Person\nimport org.tensorflow.lite.examples.poseestimation.tracker.*\nimport org.tensorflow.lite.gpu.GpuDelegate\nimport org.tensorflow.lite.support.common.FileUtil\nimport org.tensorflow.lite.support.image.ImageOperator\nimport org.tensorflow.lite.support.image.ImageProcessor\nimport org.tensorflow.lite.support.image.TensorImage\nimport org.tensorflow.lite.support.image.ops.ResizeOp\nimport org.tensorflow.lite.support.image.ops.ResizeWithCropOrPadOp\nimport org.tensorflow.lite.support.tensorbuffer.TensorBuffer\nimport kotlin.math.ceil\n\nclass MoveNetMultiPose(\n    private val interpreter: Interpreter,\n    private val type: Type,\n    private val gpuDelegate: GpuDelegate?,\n) : PoseDetector {\n    private val outputShape = interpreter.getOutputTensor(0).shape()\n    private val inputShape = interpreter.getInputTensor(0).shape()\n    private var imageWidth: Int = 0\n    private var imageHeight: Int = 0\n    private var targetWidth: Int = 0\n    private var targetHeight: Int = 0\n    private var scaleHeight: Int = 0\n    private var scaleWidth: Int = 0\n    private var lastInferenceTimeNanos: Long = -1\n    private var tracker: AbstractTracker? = null\n\n    companion object {\n        private const val DYNAMIC_MODEL_TARGET_INPUT_SIZE = 256\n        private const val SHAPE_MULTIPLE = 32.0\n        private const val DETECTION_THRESHOLD = 0.11\n        private const val DETECTION_SCORE_INDEX = 55\n        private const val BOUNDING_BOX_Y_MIN_INDEX = 51\n        private const val BOUNDING_BOX_X_MIN_INDEX = 52\n        private const val BOUNDING_BOX_Y_MAX_INDEX = 53\n        private const val BOUNDING_BOX_X_MAX_INDEX = 54\n        private const val KEYPOINT_COUNT = 17\n        private const val OUTPUTS_COUNT_PER_KEYPOINT = 3\n        private const val CPU_NUM_THREADS = 4\n\n        // allow specifying model type.\n        fun create(\n            context: Context,\n            device: Device,\n            type: Type,\n        ): MoveNetMultiPose {\n            val options = Interpreter.Options()\n            var gpuDelegate: GpuDelegate? = null\n            when (device) {\n                Device.CPU -> {\n                    options.setNumThreads(CPU_NUM_THREADS)\n                }\n                Device.GPU -> {\n                    // only fixed model support Gpu delegate option.\n                    if (type == Type.Fixed) {\n                        gpuDelegate = GpuDelegate()\n                        options.addDelegate(gpuDelegate)\n                    }\n                }\n                else -> {\n                    // nothing to do\n                }\n            }\n            return MoveNetMultiPose(\n                Interpreter(\n                    FileUtil.loadMappedFile(\n                        context,\n                        if (type == Type.Dynamic)\n                            \"movenet_multipose_fp16.tflite\" else \"\"\n                        //@TODO: (khanhlvg) Add support for fixed shape model if it's released.\n                    ), options\n                ), type, gpuDelegate\n            )\n        }\n    }\n\n    /**\n     * Convert x and y coordinates ([0-1]) returns from the TFlite model\n     * to the coordinates corresponding to the input image.\n     */\n    private fun resizeKeypoint(x: Float, y: Float): PointF {\n        return PointF(resizeX(x), resizeY(y))\n    }\n\n    private fun resizeX(x: Float): Float {\n        return if (imageWidth > imageHeight) {\n            val ratioWidth = imageWidth.toFloat() / targetWidth\n            x * targetWidth * ratioWidth\n        } else {\n            val detectedWidth =\n                if (type == Type.Dynamic) targetWidth else inputShape[2]\n            val paddingWidth = detectedWidth - scaleWidth\n            val ratioWidth = imageWidth.toFloat() / scaleWidth\n            (x * detectedWidth - paddingWidth / 2f) * ratioWidth\n        }\n    }\n\n    private fun resizeY(y: Float): Float {\n        return if (imageWidth > imageHeight) {\n            val detectedHeight =\n                if (type == Type.Dynamic) targetHeight else inputShape[1]\n            val paddingHeight = detectedHeight - scaleHeight\n            val ratioHeight = imageHeight.toFloat() / scaleHeight\n            (y * detectedHeight - paddingHeight / 2f) * ratioHeight\n        } else {\n            val ratioHeight = imageHeight.toFloat() / targetHeight\n            y * targetHeight * ratioHeight\n        }\n    }\n\n    /**\n     * Prepare input image for detection\n     */\n    private fun processInputTensor(bitmap: Bitmap): TensorImage {\n        imageWidth = bitmap.width\n        imageHeight = bitmap.height\n\n        // if model type is fixed. get input size from input shape.\n        val inputSizeHeight =\n            if (type == Type.Dynamic) DYNAMIC_MODEL_TARGET_INPUT_SIZE else inputShape[1]\n        val inputSizeWidth =\n            if (type == Type.Dynamic) DYNAMIC_MODEL_TARGET_INPUT_SIZE else inputShape[2]\n\n        val resizeOp: ImageOperator\n        if (imageWidth > imageHeight) {\n            val scale = inputSizeWidth / imageWidth.toFloat()\n            targetWidth = inputSizeWidth\n            scaleHeight = ceil(imageHeight * scale).toInt()\n            targetHeight = (ceil((scaleHeight / SHAPE_MULTIPLE)) * SHAPE_MULTIPLE).toInt()\n            resizeOp = ResizeOp(scaleHeight, targetWidth, ResizeOp.ResizeMethod.BILINEAR)\n        } else {\n            val scale = inputSizeHeight / imageHeight.toFloat()\n            targetHeight = inputSizeHeight\n            scaleWidth = ceil(imageWidth * scale).toInt()\n            targetWidth = (ceil((scaleWidth / SHAPE_MULTIPLE)) * SHAPE_MULTIPLE).toInt()\n            resizeOp = ResizeOp(targetHeight, scaleWidth, ResizeOp.ResizeMethod.BILINEAR)\n        }\n\n        val resizeWithCropOrPad = if (type == Type.Dynamic) ResizeWithCropOrPadOp(\n            targetHeight,\n            targetWidth\n        ) else ResizeWithCropOrPadOp(\n            inputSizeHeight,\n            inputSizeWidth\n        )\n        val imageProcessor = ImageProcessor.Builder().apply {\n            add(resizeOp)\n            add(resizeWithCropOrPad)\n        }.build()\n        val tensorImage = TensorImage(DataType.UINT8)\n        tensorImage.load(bitmap)\n        return imageProcessor.process(tensorImage)\n    }\n\n    /**\n     * Run tracker (if available) and process the output.\n     */\n    private fun postProcess(modelOutput: FloatArray): List<Person> {\n        val persons = mutableListOf<Person>()\n        for (idx in modelOutput.indices step outputShape[2]) {\n            val personScore = modelOutput[idx + DETECTION_SCORE_INDEX]\n            if (personScore < DETECTION_THRESHOLD) continue\n            val positions = modelOutput.copyOfRange(idx, idx + 51)\n            val keyPoints = mutableListOf<KeyPoint>()\n            for (i in 0 until KEYPOINT_COUNT) {\n                val y = positions[i * OUTPUTS_COUNT_PER_KEYPOINT]\n                val x = positions[i * OUTPUTS_COUNT_PER_KEYPOINT + 1]\n                val score = positions[i * OUTPUTS_COUNT_PER_KEYPOINT + 2]\n                keyPoints.add(KeyPoint(BodyPart.fromInt(i), PointF(x, y), score))\n            }\n            val yMin = modelOutput[idx + BOUNDING_BOX_Y_MIN_INDEX]\n            val xMin = modelOutput[idx + BOUNDING_BOX_X_MIN_INDEX]\n            val yMax = modelOutput[idx + BOUNDING_BOX_Y_MAX_INDEX]\n            val xMax = modelOutput[idx + BOUNDING_BOX_X_MAX_INDEX]\n            val boundingBox = RectF(xMin, yMin, xMax, yMax)\n            persons.add(\n                Person(\n                    keyPoints = keyPoints,\n                    boundingBox = boundingBox,\n                    score = personScore\n                )\n            )\n        }\n\n        if (persons.isEmpty()) return emptyList()\n\n        if (tracker == null) {\n            persons.forEach {\n                it.keyPoints.forEach { key ->\n                    key.coordinate = resizeKeypoint(key.coordinate.x, key.coordinate.y)\n                }\n            }\n            return persons\n        } else {\n            val trackPersons = mutableListOf<Person>()\n            tracker?.apply(persons, System.currentTimeMillis() * 1000)?.forEach {\n                val resizeKeyPoint = mutableListOf<KeyPoint>()\n                it.keyPoints.forEach { key ->\n                    resizeKeyPoint.add(\n                        KeyPoint(\n                            key.bodyPart,\n                            resizeKeypoint(key.coordinate.x, key.coordinate.y),\n                            key.score\n                        )\n                    )\n                }\n\n                var resizeBoundingBox: RectF? = null\n                it.boundingBox?.let { boundingBox ->\n                    resizeBoundingBox = RectF(\n                        resizeX(boundingBox.left),\n                        resizeY(boundingBox.top),\n                        resizeX(boundingBox.right),\n                        resizeY(boundingBox.bottom)\n                    )\n                }\n                trackPersons.add(Person(it.id, resizeKeyPoint, resizeBoundingBox, it.score))\n            }\n            return trackPersons\n        }\n    }\n\n    /**\n     * Create and set tracker.\n     */\n    fun setTracker(trackerType: TrackerType) {\n        tracker = when (trackerType) {\n            TrackerType.BOUNDING_BOX -> {\n                BoundingBoxTracker()\n            }\n            TrackerType.KEYPOINTS -> {\n                KeyPointsTracker()\n            }\n            TrackerType.OFF -> {\n                null\n            }\n        }\n    }\n\n    /**\n     * Run TFlite model and Returns a list of \"Person\" corresponding to the input image.\n     */\n    override fun estimatePoses(bitmap: Bitmap): List<Person> {\n        val inferenceStartTimeNanos = SystemClock.elapsedRealtimeNanos()\n        val inputTensor = processInputTensor(bitmap)\n        val outputTensor = TensorBuffer.createFixedSize(outputShape, DataType.FLOAT32)\n\n        // if model is dynamic, resize input before run interpreter\n        if (type == Type.Dynamic) {\n            val inputShape = intArrayOf(1).plus(inputTensor.tensorBuffer.shape)\n            interpreter.resizeInput(0, inputShape, true)\n            interpreter.allocateTensors()\n        }\n        interpreter.run(inputTensor.buffer, outputTensor.buffer.rewind())\n\n        val processedPerson = postProcess(outputTensor.floatArray)\n        lastInferenceTimeNanos =\n            SystemClock.elapsedRealtimeNanos() - inferenceStartTimeNanos\n        return processedPerson\n    }\n\n    override fun lastInferenceTimeNanos(): Long = lastInferenceTimeNanos\n\n    /**\n     * Close all resources when not in use.\n     */\n    override fun close() {\n        gpuDelegate?.close()\n        interpreter.close()\n        tracker = null\n    }\n}\n\nenum class Type {\n    Dynamic, Fixed\n}\n\nenum class TrackerType {\n    OFF, BOUNDING_BOX, KEYPOINTS\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/ml/PoseClassifier.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.ml\n\nimport android.content.Context\nimport org.tensorflow.lite.Interpreter\nimport org.tensorflow.lite.examples.poseestimation.data.Person\nimport org.tensorflow.lite.support.common.FileUtil\n\nclass PoseClassifier(\n    private val interpreter: Interpreter,\n    private val labels: List<String>\n) {\n    private val input = interpreter.getInputTensor(0).shape()\n    private val output = interpreter.getOutputTensor(0).shape()\n\n    companion object {\n        private const val MODEL_FILENAME = \"classifier.tflite\"\n        private const val LABELS_FILENAME = \"labels.txt\"\n        private const val CPU_NUM_THREADS = 4\n\n        fun create(context: Context): PoseClassifier {\n            val options = Interpreter.Options().apply {\n                setNumThreads(CPU_NUM_THREADS)\n            }\n            return PoseClassifier(\n                Interpreter(\n                    FileUtil.loadMappedFile(\n                        context, MODEL_FILENAME\n                    ), options\n                ),\n                FileUtil.loadLabels(context, LABELS_FILENAME)\n            )\n        }\n    }\n\n    fun classify(person: Person?): List<Pair<String, Float>> {\n        // Preprocess the pose estimation result to a flat array\n        val inputVector = FloatArray(input[1])\n        person?.keyPoints?.forEachIndexed { index, keyPoint ->\n            inputVector[index * 3] = keyPoint.coordinate.y\n            inputVector[index * 3 + 1] = keyPoint.coordinate.x\n            inputVector[index * 3 + 2] = keyPoint.score\n        }\n\n        // Postprocess the model output to human readable class names\n        val outputTensor = FloatArray(output[1])\n        interpreter.run(arrayOf(inputVector), arrayOf(outputTensor))\n        val output = mutableListOf<Pair<String, Float>>()\n        outputTensor.forEachIndexed { index, score ->\n            output.add(Pair(labels[index], score))\n        }\n        return output\n    }\n\n    fun close() {\n        interpreter.close()\n    }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/ml/PoseDetector.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.ml\n\nimport android.graphics.Bitmap\nimport org.tensorflow.lite.examples.poseestimation.data.Person\n\ninterface PoseDetector : AutoCloseable {\n\n    fun estimatePoses(bitmap: Bitmap): List<Person>\n\n    fun lastInferenceTimeNanos(): Long\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/ml/PoseNet.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.ml\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.graphics.PointF\nimport android.os.SystemClock\nimport android.util.Log\nimport org.tensorflow.lite.DataType\nimport org.tensorflow.lite.Interpreter\nimport org.tensorflow.lite.examples.poseestimation.data.BodyPart\nimport org.tensorflow.lite.examples.poseestimation.data.Device\nimport org.tensorflow.lite.examples.poseestimation.data.KeyPoint\nimport org.tensorflow.lite.examples.poseestimation.data.Person\nimport org.tensorflow.lite.gpu.GpuDelegate\nimport org.tensorflow.lite.support.common.FileUtil\nimport org.tensorflow.lite.support.common.ops.NormalizeOp\nimport org.tensorflow.lite.support.image.ImageProcessor\nimport org.tensorflow.lite.support.image.TensorImage\nimport org.tensorflow.lite.support.image.ops.ResizeOp\nimport org.tensorflow.lite.support.image.ops.ResizeWithCropOrPadOp\nimport kotlin.math.exp\n\nclass PoseNet(private val interpreter: Interpreter, private var gpuDelegate: GpuDelegate?) :\n    PoseDetector {\n\n    companion object {\n        private const val CPU_NUM_THREADS = 4\n        private const val MEAN = 127.5f\n        private const val STD = 127.5f\n        private const val TAG = \"Posenet\"\n        private const val MODEL_FILENAME = \"posenet.tflite\"\n\n        fun create(context: Context, device: Device): PoseNet {\n            val options = Interpreter.Options()\n            var gpuDelegate: GpuDelegate? = null\n            options.setNumThreads(CPU_NUM_THREADS)\n            when (device) {\n                Device.CPU -> {\n                }\n                Device.GPU -> {\n                    gpuDelegate = GpuDelegate()\n                    options.addDelegate(gpuDelegate)\n                }\n                Device.NNAPI -> options.setUseNNAPI(true)\n            }\n            return PoseNet(\n                Interpreter(\n                    FileUtil.loadMappedFile(\n                        context,\n                        MODEL_FILENAME\n                    ), options\n                ),\n                gpuDelegate\n            )\n        }\n    }\n\n    private var lastInferenceTimeNanos: Long = -1\n    private val inputWidth = interpreter.getInputTensor(0).shape()[1]\n    private val inputHeight = interpreter.getInputTensor(0).shape()[2]\n    private var cropHeight = 0f\n    private var cropWidth = 0f\n    private var cropSize = 0\n\n    @Suppress(\"UNCHECKED_CAST\")\n    override fun estimatePoses(bitmap: Bitmap): List<Person> {\n        val estimationStartTimeNanos = SystemClock.elapsedRealtimeNanos()\n        val inputArray = arrayOf(processInputImage(bitmap).tensorBuffer.buffer)\n        Log.i(\n            TAG,\n            String.format(\n                \"Scaling to [-1,1] took %.2f ms\",\n                (SystemClock.elapsedRealtimeNanos() - estimationStartTimeNanos) / 1_000_000f\n            )\n        )\n\n        val outputMap = initOutputMap(interpreter)\n\n        val inferenceStartTimeNanos = SystemClock.elapsedRealtimeNanos()\n        interpreter.runForMultipleInputsOutputs(inputArray, outputMap)\n        lastInferenceTimeNanos = SystemClock.elapsedRealtimeNanos() - inferenceStartTimeNanos\n        Log.i(\n            TAG,\n            String.format(\"Interpreter took %.2f ms\", 1.0f * lastInferenceTimeNanos / 1_000_000)\n        )\n\n        val heatmaps = outputMap[0] as Array<Array<Array<FloatArray>>>\n        val offsets = outputMap[1] as Array<Array<Array<FloatArray>>>\n\n        val postProcessingStartTimeNanos = SystemClock.elapsedRealtimeNanos()\n        val person = postProcessModelOuputs(heatmaps, offsets)\n        Log.i(\n            TAG,\n            String.format(\n                \"Postprocessing took %.2f ms\",\n                (SystemClock.elapsedRealtimeNanos() - postProcessingStartTimeNanos) / 1_000_000f\n            )\n        )\n\n        return listOf(person)\n    }\n\n    /**\n     * Convert heatmaps and offsets output of Posenet into a list of keypoints\n     */\n    private fun postProcessModelOuputs(\n        heatmaps: Array<Array<Array<FloatArray>>>,\n        offsets: Array<Array<Array<FloatArray>>>\n    ): Person {\n        val height = heatmaps[0].size\n        val width = heatmaps[0][0].size\n        val numKeypoints = heatmaps[0][0][0].size\n\n        // Finds the (row, col) locations of where the keypoints are most likely to be.\n        val keypointPositions = Array(numKeypoints) { Pair(0, 0) }\n        for (keypoint in 0 until numKeypoints) {\n            var maxVal = heatmaps[0][0][0][keypoint]\n            var maxRow = 0\n            var maxCol = 0\n            for (row in 0 until height) {\n                for (col in 0 until width) {\n                    if (heatmaps[0][row][col][keypoint] > maxVal) {\n                        maxVal = heatmaps[0][row][col][keypoint]\n                        maxRow = row\n                        maxCol = col\n                    }\n                }\n            }\n            keypointPositions[keypoint] = Pair(maxRow, maxCol)\n        }\n\n        // Calculating the x and y coordinates of the keypoints with offset adjustment.\n        val xCoords = IntArray(numKeypoints)\n        val yCoords = IntArray(numKeypoints)\n        val confidenceScores = FloatArray(numKeypoints)\n        keypointPositions.forEachIndexed { idx, position ->\n            val positionY = keypointPositions[idx].first\n            val positionX = keypointPositions[idx].second\n\n            val inputImageCoordinateY =\n                position.first / (height - 1).toFloat() * inputHeight + offsets[0][positionY][positionX][idx]\n            val ratioHeight = cropSize.toFloat() / inputHeight\n            val paddingHeight = cropHeight / 2\n            yCoords[idx] = (inputImageCoordinateY * ratioHeight - paddingHeight).toInt()\n\n            val inputImageCoordinateX =\n                position.second / (width - 1).toFloat() * inputWidth + offsets[0][positionY][positionX][idx + numKeypoints]\n            val ratioWidth = cropSize.toFloat() / inputWidth\n            val paddingWidth = cropWidth / 2\n            xCoords[idx] = (inputImageCoordinateX * ratioWidth - paddingWidth).toInt()\n\n            confidenceScores[idx] = sigmoid(heatmaps[0][positionY][positionX][idx])\n        }\n\n        val keypointList = mutableListOf<KeyPoint>()\n        var totalScore = 0.0f\n        enumValues<BodyPart>().forEachIndexed { idx, it ->\n            keypointList.add(\n                KeyPoint(\n                    it,\n                    PointF(xCoords[idx].toFloat(), yCoords[idx].toFloat()),\n                    confidenceScores[idx]\n                )\n            )\n            totalScore += confidenceScores[idx]\n        }\n        return Person(keyPoints = keypointList.toList(), score = totalScore / numKeypoints)\n    }\n\n    override fun lastInferenceTimeNanos(): Long = lastInferenceTimeNanos\n\n    override fun close() {\n        gpuDelegate?.close()\n        interpreter.close()\n    }\n\n    /**\n     * Scale and crop the input image to a TensorImage.\n     */\n    private fun processInputImage(bitmap: Bitmap): TensorImage {\n        // reset crop width and height\n        cropWidth = 0f\n        cropHeight = 0f\n        cropSize = if (bitmap.width > bitmap.height) {\n            cropHeight = (bitmap.width - bitmap.height).toFloat()\n            bitmap.width\n        } else {\n            cropWidth = (bitmap.height - bitmap.width).toFloat()\n            bitmap.height\n        }\n\n        val imageProcessor = ImageProcessor.Builder().apply {\n            add(ResizeWithCropOrPadOp(cropSize, cropSize))\n            add(ResizeOp(inputWidth, inputHeight, ResizeOp.ResizeMethod.BILINEAR))\n            add(NormalizeOp(MEAN, STD))\n        }.build()\n        val tensorImage = TensorImage(DataType.FLOAT32)\n        tensorImage.load(bitmap)\n        return imageProcessor.process(tensorImage)\n    }\n\n    /**\n     * Initializes an outputMap of 1 * x * y * z FloatArrays for the model processing to populate.\n     */\n    private fun initOutputMap(interpreter: Interpreter): HashMap<Int, Any> {\n        val outputMap = HashMap<Int, Any>()\n\n        // 1 * 9 * 9 * 17 contains heatmaps\n        val heatmapsShape = interpreter.getOutputTensor(0).shape()\n        outputMap[0] = Array(heatmapsShape[0]) {\n            Array(heatmapsShape[1]) {\n                Array(heatmapsShape[2]) { FloatArray(heatmapsShape[3]) }\n            }\n        }\n\n        // 1 * 9 * 9 * 34 contains offsets\n        val offsetsShape = interpreter.getOutputTensor(1).shape()\n        outputMap[1] = Array(offsetsShape[0]) {\n            Array(offsetsShape[1]) { Array(offsetsShape[2]) { FloatArray(offsetsShape[3]) } }\n        }\n\n        // 1 * 9 * 9 * 32 contains forward displacements\n        val displacementsFwdShape = interpreter.getOutputTensor(2).shape()\n        outputMap[2] = Array(offsetsShape[0]) {\n            Array(displacementsFwdShape[1]) {\n                Array(displacementsFwdShape[2]) { FloatArray(displacementsFwdShape[3]) }\n            }\n        }\n\n        // 1 * 9 * 9 * 32 contains backward displacements\n        val displacementsBwdShape = interpreter.getOutputTensor(3).shape()\n        outputMap[3] = Array(displacementsBwdShape[0]) {\n            Array(displacementsBwdShape[1]) {\n                Array(displacementsBwdShape[2]) { FloatArray(displacementsBwdShape[3]) }\n            }\n        }\n\n        return outputMap\n    }\n\n    /** Returns value within [0,1].   */\n    private fun sigmoid(x: Float): Float {\n        return (1.0f / (1.0f + exp(-x)))\n    }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/tracker/AbstractTracker.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.tracker\n\nimport org.tensorflow.lite.examples.poseestimation.data.Person\n\nabstract class AbstractTracker(val config: TrackerConfig) {\n\n    private val maxAge = config.maxAge * 1000 // convert milliseconds to microseconds\n    private var nextTrackId = 0\n    var tracks = mutableListOf<Track>()\n        private set\n\n    /**\n     * Computes pairwise similarity scores between detections and tracks, based\n     * on detected features.\n     * @param persons A list of detected person.\n     * @returns A list of shape [num_det, num_tracks] with pairwise\n     * similarity scores between detections and tracks.\n     */\n    abstract fun computeSimilarity(persons: List<Person>): List<List<Float>>\n\n    /**\n     * Tracks person instances across frames based on detections.\n     * @param persons A list of person\n     * @param timestamp The current timestamp in microseconds\n     * @return An updated list of persons with tracking id.\n     */\n    fun apply(persons: List<Person>, timestamp: Long): List<Person> {\n        tracks = filterOldTrack(timestamp).toMutableList()\n        val simMatrix = computeSimilarity(persons)\n        assignTrack(persons, simMatrix, timestamp)\n        tracks = updateTrack().toMutableList()\n        return persons\n    }\n\n    /**\n     * Clear all track in list of tracks\n     */\n    fun reset() {\n        tracks.clear()\n    }\n\n    /**\n     * Return the next track id\n     */\n    private fun nextTrackID() = ++nextTrackId\n\n    /**\n     * Performs a greedy optimization to link detections with tracks. The person\n     * list is updated in place by providing an `id` property. If incoming\n     * detections are not linked with existing tracks, new tracks will be created.\n     * @param persons A list of detected person. It's assumed that persons are\n     * sorted from most confident to least confident.\n     * @param simMatrix A list of shape [num_det, num_tracks] with pairwise\n     * similarity scores between detections and tracks.\n     * @param timestamp The current timestamp in microseconds.\n     */\n    private fun assignTrack(persons: List<Person>, simMatrix: List<List<Float>>, timestamp: Long) {\n        if ((simMatrix.size != persons.size) != (simMatrix[0].size != tracks.size)) {\n            throw IllegalArgumentException(\n                \"Size of person array and similarity matrix does not match.\")\n        }\n\n        val unmatchedTrackIndices = MutableList(tracks.size) { it }\n        val unmatchedDetectionIndices = mutableListOf<Int>()\n\n        for (detectionIndex in persons.indices) {\n            // If the track list is empty, add the person's index\n            // to unmatched detections to create a new track later.\n            if (unmatchedTrackIndices.isEmpty()) {\n                unmatchedDetectionIndices.add(detectionIndex)\n                continue\n            }\n\n            // Assign the detection to the track which produces the highest pairwise\n            // similarity score, assuming the score exceeds the minimum similarity\n            // threshold.\n            var maxTrackIndex = -1\n            var maxSimilarity = -1f\n            unmatchedTrackIndices.forEach { trackIndex ->\n                val similarity = simMatrix[detectionIndex][trackIndex]\n                if (similarity >= config.minSimilarity && similarity > maxSimilarity) {\n                    maxTrackIndex = trackIndex\n                    maxSimilarity = similarity\n                }\n            }\n            if (maxTrackIndex >= 0) {\n                val linkedTrack = tracks[maxTrackIndex]\n                tracks[maxTrackIndex] =\n                    createTrack(persons[detectionIndex], linkedTrack.person.id, timestamp)\n                persons[detectionIndex].id = linkedTrack.person.id\n                val index = unmatchedTrackIndices.indexOf(maxTrackIndex)\n                unmatchedTrackIndices.removeAt(index)\n            } else {\n                unmatchedDetectionIndices.add(detectionIndex)\n            }\n        }\n\n        // Spawn new tracks for all unmatched detections.\n        unmatchedDetectionIndices.forEach { detectionIndex ->\n            val newTrack = createTrack(persons[detectionIndex], timestamp = timestamp)\n            tracks.add(newTrack)\n            persons[detectionIndex].id = newTrack.person.id\n        }\n    }\n\n    /**\n     * Filters tracks based on their age.\n     * @param timestamp The timestamp in microseconds\n     */\n    private fun filterOldTrack(timestamp: Long): List<Track> {\n        return tracks.filter {\n            timestamp - it.lastTimestamp <= maxAge\n        }\n    }\n\n    /**\n     *  Sort the track list by timestamp (newer first)\n     *  and return the track list with size equal to config.maxTracks\n     */\n    private fun updateTrack(): List<Track> {\n        tracks.sortByDescending { it.lastTimestamp }\n        return tracks.take(config.maxTracks)\n    }\n\n    /**\n     * Create a new track from person's information.\n     * @param person A person\n     * @param id The Id assign to the new track. If it is null, assign the next track id.\n     * @param timestamp The timestamp in microseconds\n     */\n    private fun createTrack(person: Person, id: Int? = null, timestamp: Long): Track {\n        return Track(\n            person = Person(\n                id = id ?: nextTrackID(),\n                keyPoints = person.keyPoints,\n                boundingBox = person.boundingBox,\n                score = person.score\n            ),\n            lastTimestamp = timestamp\n        )\n    }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/tracker/BoundingBoxTracker.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.tracker\n\nimport androidx.annotation.VisibleForTesting\nimport org.tensorflow.lite.examples.poseestimation.data.Person\nimport kotlin.math.max\nimport kotlin.math.min\n\n/**\n * BoundingBoxTracker, which tracks objects based on bounding box similarity,\n * currently defined as intersection-over-union (IoU).\n */\nclass BoundingBoxTracker(config: TrackerConfig = TrackerConfig()) : AbstractTracker(config) {\n\n    /**\n     * Computes similarity based on intersection-over-union (IoU). See `AbstractTracker`\n     * for more details.\n     */\n    override fun computeSimilarity(persons: List<Person>): List<List<Float>> {\n        if (persons.isEmpty() && tracks.isEmpty()) {\n            return emptyList()\n        }\n        return persons.map { person -> tracks.map { track -> iou(person, track.person) } }\n    }\n\n    /**\n     * Computes the intersection-over-union (IoU) between a person and a track person.\n     * @param person1 A person\n     * @param person2 A track person\n     * @return The IoU  between the person and the track person. This number is\n     * between 0 and 1, and larger values indicate more box similarity.\n     */\n    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)\n    fun iou(person1: Person, person2: Person): Float {\n        if (person1.boundingBox != null && person2.boundingBox != null) {\n            val xMin = max(person1.boundingBox.left, person2.boundingBox.left)\n            val yMin = max(person1.boundingBox.top, person2.boundingBox.top)\n            val xMax = min(person1.boundingBox.right, person2.boundingBox.right)\n            val yMax = min(person1.boundingBox.bottom, person2.boundingBox.bottom)\n            if (xMin >= xMax || yMin >= yMax) return 0f\n            val intersection = (xMax - xMin) * (yMax - yMin)\n            val areaPerson = person1.boundingBox.width() * person1.boundingBox.height()\n            val areaTrack = person2.boundingBox.width() * person2.boundingBox.height()\n            return intersection / (areaPerson + areaTrack - intersection)\n        }\n        return 0f\n    }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/tracker/KeyPointsTracker.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.tracker\n\nimport androidx.annotation.VisibleForTesting\nimport org.tensorflow.lite.examples.poseestimation.data.KeyPoint\nimport org.tensorflow.lite.examples.poseestimation.data.Person\nimport kotlin.math.exp\nimport kotlin.math.max\nimport kotlin.math.min\nimport kotlin.math.pow\n\n/**\n * KeypointTracker, which tracks poses based on keypoint similarity. This\n * tracker assumes that keypoints are provided in normalized image\n * coordinates.\n */\nclass KeyPointsTracker(\n    trackerConfig: TrackerConfig = TrackerConfig(\n        keyPointsTrackerParams = KeyPointsTrackerParams()\n    )\n) : AbstractTracker(trackerConfig) {\n    /**\n     * Computes similarity based on Object Keypoint Similarity (OKS). It's\n     * assumed that the keypoints within each person are in normalized image\n     * coordinates. See `AbstractTracker` for more details.\n     */\n    override fun computeSimilarity(persons: List<Person>): List<List<Float>> {\n        if (persons.isEmpty() && tracks.isEmpty()) {\n            return emptyList()\n        }\n        val simMatrix = mutableListOf<MutableList<Float>>()\n        persons.forEach { person ->\n            val row = mutableListOf<Float>()\n            tracks.forEach { track ->\n                val oksValue = oks(person, track.person)\n                row.add(oksValue)\n            }\n            simMatrix.add(row)\n        }\n        return simMatrix\n    }\n\n    /**\n     * Computes the Object Keypoint Similarity (OKS) between a person and track person.\n     * This is similar in spirit to the calculation used by COCO keypoint eval:\n     * https://cocodataset.org/#keypoints-eval\n     * In this case, OKS is calculated as:\n     * (1/sum_i d(c_i, c_ti)) * \\sum_i exp(-d_i^2/(2*a_ti*x_i^2))*d(c_i, c_ti)\n     * where\n     *   d(x, y) is an indicator function which only produces 1 if x and y\n     *     exceed a given threshold (i.e. keypointThreshold), otherwise 0.\n     *   c_i is the confidence of keypoint i from the new person\n     *   c_ti is the confidence of keypoint i from the track person\n     *   d_i is the Euclidean distance between the person and track person keypoint\n     *   a_ti is the area of the track object (the box covering the keypoints)\n     *   x_i is a constant that controls falloff in a Gaussian distribution,\n     *    computed as 2*keypointFalloff[i].\n     * @param person1 A person.\n     * @param person2 A track person.\n     * @returns The OKS score between the person and the track person. This number is\n     * between 0 and 1, and larger values indicate more keypoint similarity.\n     */\n    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)\n    fun oks(person1: Person, person2: Person): Float {\n        if (config.keyPointsTrackerParams == null) return 0f\n        person2.keyPoints.let { keyPoints ->\n            val boxArea = area(keyPoints) + 1e-6\n            var oksTotal = 0f\n            var numValidKeyPoints = 0\n\n            person1.keyPoints.forEachIndexed { index, _ ->\n                val poseKpt = person1.keyPoints[index]\n                val trackKpt = person2.keyPoints[index]\n                val threshold = config.keyPointsTrackerParams.keypointThreshold\n                if (poseKpt.score < threshold || trackKpt.score < threshold) {\n                    return@forEachIndexed\n                }\n                numValidKeyPoints += 1\n                val dSquared: Float =\n                    (poseKpt.coordinate.x - trackKpt.coordinate.x).pow(2) + (poseKpt.coordinate.y - trackKpt.coordinate.y).pow(\n                        2\n                    )\n                val x = 2f * config.keyPointsTrackerParams.keypointFalloff[index]\n                oksTotal += exp(-1f * dSquared / (2f * boxArea * x.pow(2))).toFloat()\n            }\n            if (numValidKeyPoints < config.keyPointsTrackerParams.minNumKeyPoints) {\n                return 0f\n            }\n            return oksTotal / numValidKeyPoints\n        }\n    }\n\n    /**\n     * Computes the area of a bounding box that tightly covers keypoints.\n     * @param keyPoints A list of KeyPoint.\n     * @returns The area of the object.\n     */\n    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)\n    fun area(keyPoints: List<KeyPoint>): Float {\n        val validKeypoint = keyPoints.filter {\n            it.score > config.keyPointsTrackerParams?.keypointThreshold ?: 0f\n        }\n        if (validKeypoint.isEmpty()) return 0f\n        val minX = min(1f, validKeypoint.minOf { it.coordinate.x })\n        val maxX = max(0f, validKeypoint.maxOf { it.coordinate.x })\n        val minY = min(1f, validKeypoint.minOf { it.coordinate.y })\n        val maxY = max(0f, validKeypoint.maxOf { it.coordinate.y })\n        return (maxX - minX) * (maxY - minY)\n    }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/tracker/Track.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.tracker\n\nimport org.tensorflow.lite.examples.poseestimation.data.Person\n\ndata class Track(\n    val person: Person,\n    val lastTimestamp: Long\n)\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/java/org/tensorflow/lite/examples/poseestimation/tracker/TrackerConfig.kt",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================\n*/\n\npackage org.tensorflow.lite.examples.poseestimation.tracker\n\ndata class TrackerConfig(\n    val maxTracks: Int = MAX_TRACKS,\n    val maxAge: Int = MAX_AGE,\n    val minSimilarity: Float = MIN_SIMILARITY,\n    val keyPointsTrackerParams: KeyPointsTrackerParams? = null\n) {\n    companion object {\n        private const val MAX_TRACKS = 18\n        private const val MAX_AGE = 1000 // millisecond\n        private const val MIN_SIMILARITY = 0.15f\n    }\n}\n\ndata class KeyPointsTrackerParams(\n    val keypointThreshold: Float = KEYPOINT_THRESHOLD,\n    // List of per-keypoint standard deviation `σ`, keypoints on a person's body (shoulders, knees, hips, etc.)\n    // tend to have a `σ` much larger than on a person's head (eyes, nose, ears).\n    // Read more at: https://cocodataset.org/#keypoints-eval\n    val keypointFalloff: List<Float> = KEYPOINT_FALLOFF,\n    val minNumKeyPoints: Int = MIN_NUM_KEYPOINT\n) {\n    companion object {\n        // From COCO:\n        // https://cocodataset.org/#keypoints-eval\n        private val KEYPOINT_FALLOFF: List<Float> = listOf(\n            0.026f, 0.025f, 0.025f, 0.035f, 0.035f, 0.079f, 0.079f, 0.072f, 0.072f, 0.062f,\n            0.062f, 0.107f, 0.107f, 0.087f, 0.087f, 0.089f, 0.089f\n        )\n        private const val KEYPOINT_THRESHOLD = 0.3f\n        private const val MIN_NUM_KEYPOINT = 4\n    }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/res/drawable/rounded_edge.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <solid\n        android:color=\"#e4e4e4\">\n    </solid>\n    <stroke\n        android:width=\"0dp\"\n        android:color=\"#424242\">\n    </stroke>\n    <corners\n        android:topLeftRadius=\"30dp\"\n        android:topRightRadius=\"30dp\"\n        android:bottomLeftRadius=\"0dp\"\n        android:bottomRightRadius=\"0dp\">\n    </corners>\n\n</shape>\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".MainActivity\">\n\n    <SurfaceView\n        android:id=\"@+id/surfaceView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n    <androidx.appcompat.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?attr/actionBarSize\"\n        android:background=\"#66000000\">\n\n        <ImageView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:contentDescription=\"@null\"\n            android:src=\"@drawable/tfl2_logo\" />\n    </androidx.appcompat.widget.Toolbar>\n\n    <include layout=\"@layout/bottom_sheet_layout\"/>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/res/layout/bottom_sheet_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.appcompat.widget.LinearLayoutCompat xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/bottom_sheet\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_margin=\"8dp\"\n    android:background=\"@drawable/rounded_edge\"\n    android:orientation=\"vertical\"\n    android:paddingStart=\"8dp\"\n    android:paddingTop=\"10dp\"\n    android:paddingEnd=\"8dp\"\n    android:paddingBottom=\"16dp\"\n    app:behavior_hideable=\"false\"\n    app:behavior_peekHeight=\"36dp\"\n    app:layout_behavior=\"com.google.android.material.bottomsheet.BottomSheetBehavior\"\n    tools:showIn=\"@layout/activity_main\">\n\n    <ImageView\n        android:id=\"@+id/bottom_sheet_arrow\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:contentDescription=\"@null\"\n        android:src=\"@drawable/icn_chevron_up\" />\n\n    <TextView\n        android:id=\"@+id/tvFps\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\" />\n\n    <TextView\n        android:id=\"@+id/tvScore\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/tfe_pe_tv_device\" />\n\n        <Spinner\n            android:id=\"@+id/spnDevice\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/tfe_pe_tv_model\" />\n\n        <Spinner\n            android:id=\"@+id/spnModel\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:id=\"@+id/vTrackerOption\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/tfe_pe_tv_tracking\" />\n\n        <Spinner\n            android:id=\"@+id/spnTracker\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\" />\n    </LinearLayout>\n\n    <RelativeLayout\n        android:id=\"@+id/vClassificationOption\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toStartOf=\"@id/swPoseClassification\"\n            android:text=\"@string/tfe_pe_tv_pose_classification\" />\n\n        <androidx.appcompat.widget.SwitchCompat\n            android:id=\"@+id/swPoseClassification\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentEnd=\"true\" />\n    </RelativeLayout>\n\n    <TextView\n        android:id=\"@+id/tvClassificationValue1\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:visibility=\"gone\" />\n\n    <TextView\n        android:id=\"@+id/tvClassificationValue2\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:visibility=\"gone\" />\n\n    <TextView\n        android:id=\"@+id/tvClassificationValue3\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:visibility=\"gone\" />\n</androidx.appcompat.widget.LinearLayoutCompat>\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"purple_500\">#FF6200EE</color>\n    <color name=\"purple_700\">#FF3700B3</color>\n    <color name=\"teal_200\">#FF03DAC5</color>\n    <color name=\"teal_700\">#FF018786</color>\n    <color name=\"black\">#FF000000</color>\n    <color name=\"white\">#FFFFFFFF</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"tfe_pe_app_name\">TFL Pose Estimation</string>\n    <string name=\"tfe_pe_request_permission\">This app needs camera permission.</string>\n    <string name=\"tfe_pe_tv_score\">Score: %.2f</string>\n    <string name=\"tfe_pe_tv_model\">Model: </string>\n    <string name=\"tfe_pe_tv_fps\">Fps: %d</string>\n    <string name=\"tfe_pe_tv_device\">Device: </string>\n    <string name=\"tfe_pe_tv_classification_value\">- %s</string>\n    <string name=\"tfe_pe_tv_pose_classification\">Pose Classification</string>\n    <string name=\"tfe_pe_tv_tracking\">Tracker: </string>\n    <string name=\"tfe_pe_gpu_error\">Movenet MultiPose does not support GPU. Fallback to CPU.</string>\n    <string-array name=\"tfe_pe_models_array\">\n        <item>Movenet Lightning</item>\n        <item>Movenet Thunder</item>\n        <item>Movenet MultiPose</item>\n        <item>Posenet</item>\n    </string-array>\n\n    <string-array name=\"tfe_pe_device_name\">\n        <item>CPU</item>\n        <item>GPU</item>\n        <item>NNAPI</item>\n    </string-array>\n\n    <string-array name=\"tfe_pe_tracker_array\">\n        <item>Off</item>\n        <item>BoundingBox</item>\n        <item>Keypoint</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/app/src/main/res/values/themes.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <!-- Base application theme. -->\n    <style name=\"Theme.PoseEstimation\" parent=\"Theme.MaterialComponents.Light.NoActionBar\">\n        <!-- Primary brand color. -->\n        <item name=\"colorPrimary\">@color/purple_500</item>\n        <item name=\"colorPrimaryVariant\">@color/purple_700</item>\n        <item name=\"colorOnPrimary\">@color/white</item>\n        <!-- Secondary brand color. -->\n        <item name=\"colorSecondary\">@color/teal_200</item>\n        <item name=\"colorSecondaryVariant\">@color/teal_700</item>\n        <item name=\"colorOnSecondary\">@color/black</item>\n        <!-- Status bar color. -->\n        <item name=\"android:statusBarColor\" tools:targetApi=\"l\">?attr/colorPrimaryVariant</item>\n        <!-- Customize your theme here. -->\n        <item name=\"android:textColor\">@color/black</item>\n        <item name=\"android:textSize\">16sp</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n    ext.kotlin_version = \"1.9.21\"\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath \"com.android.tools.build:gradle:8.0.2\"\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n        maven {\n            name 'ossrh-snapshot'\n            url 'https://oss.sonatype.org/content/repositories/snapshots'\n        }\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app\"s APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Automatically convert third-party libraries to use AndroidX\nandroid.enableJetifier=true\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/pose_estimation/android/settings.gradle",
    "content": "include ':app'\nrootProject.name = \"TFLite Pose Estimation\"\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/Podfile",
    "content": "# Uncomment the next line to define a global platform for your project\nplatform :ios, '12.0'\n\n# ignore all warnings from all pods\ninhibit_all_warnings!\n\ntarget 'PoseEstimation' do\n  # Comment the next line if you don't want to use dynamic frameworks\n  use_frameworks!\n\n  # Pods for PoseEstimation\n  pod 'LiteRTSwift', '~> 0.0.1-nightly', :subspecs => ['CoreML', 'Metal']\n\nend\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/PoseEstimation/AppDelegate.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n\nimport UIKit\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n  var window: UIWindow?\n\n  func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n  ) -> Bool {\n    return true\n  }\n\n  func applicationWillResignActive(_ application: UIApplication) {\n  }\n\n  func applicationDidEnterBackground(_ application: UIApplication) {\n  }\n\n  func applicationWillEnterForeground(_ application: UIApplication) {\n  }\n\n  func applicationDidBecomeActive(_ application: UIApplication) {\n  }\n\n  func applicationWillTerminate(_ application: UIApplication) {\n  }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/PoseEstimation/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\"images\":[{\"size\":\"60x60\",\"expected-size\":\"180\",\"filename\":\"180.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"40x40\",\"expected-size\":\"80\",\"filename\":\"80.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"40x40\",\"expected-size\":\"120\",\"filename\":\"120.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"60x60\",\"expected-size\":\"120\",\"filename\":\"120.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"57x57\",\"expected-size\":\"57\",\"filename\":\"57.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"58\",\"filename\":\"58.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"29x29\",\"expected-size\":\"29\",\"filename\":\"29.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"87\",\"filename\":\"87.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"57x57\",\"expected-size\":\"114\",\"filename\":\"114.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"60\",\"filename\":\"60.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"1024x1024\",\"filename\":\"1024.png\",\"expected-size\":\"1024\",\"idiom\":\"ios-marketing\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"scale\":\"1x\"},{\"size\":\"40x40\",\"expected-size\":\"80\",\"filename\":\"80.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"72x72\",\"expected-size\":\"72\",\"filename\":\"72.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"76x76\",\"expected-size\":\"152\",\"filename\":\"152.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"50x50\",\"expected-size\":\"100\",\"filename\":\"100.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"29x29\",\"expected-size\":\"58\",\"filename\":\"58.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"76x76\",\"expected-size\":\"76\",\"filename\":\"76.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"29\",\"filename\":\"29.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"50x50\",\"expected-size\":\"50\",\"filename\":\"50.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"72x72\",\"expected-size\":\"144\",\"filename\":\"144.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"40x40\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"83.5x83.5\",\"expected-size\":\"167\",\"filename\":\"167.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"20\",\"filename\":\"20.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"20x20\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"}]}"
  },
  {
    "path": "lite/examples/pose_estimation/ios/PoseEstimation/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/PoseEstimation/Assets.xcassets/tfl_logo.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"tfl_logo.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"filename\" : \"tfl_logo@2x.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"filename\" : \"tfl_logo@3x.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/PoseEstimation/Camera/CameraFeedManager.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n\nimport AVFoundation\nimport Accelerate.vImage\nimport UIKit\n\n/// Delegate to receive the frames captured from the device's camera.\nprotocol CameraFeedManagerDelegate: AnyObject {\n\n  /// Callback method that receives frames from the camera.\n  /// - Parameters:\n  ///     - cameraFeedManager: The CameraFeedManager instance which calls the delegate.\n  ///     - pixelBuffer: The frame received from the camera.\n  func cameraFeedManager(\n    _ cameraFeedManager: CameraFeedManager, didOutput pixelBuffer: CVPixelBuffer)\n}\n\n/// Manage the camera pipeline.\nfinal class CameraFeedManager: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate {\n\n  /// Delegate to receive the frames captured by the device's camera.\n  var delegate: CameraFeedManagerDelegate?\n\n  override init() {\n    super.init()\n    configureSession()\n  }\n\n  /// Start capturing frames from the camera.\n  func startRunning() {\n    captureSession.startRunning()\n  }\n\n  /// Stop capturing frames from the camera.\n  func stopRunning() {\n    captureSession.stopRunning()\n  }\n\n  let captureSession = AVCaptureSession()\n\n  /// Initialize the capture session.\n  private func configureSession() {\n    captureSession.sessionPreset = AVCaptureSession.Preset.photo\n\n    guard\n      let backCamera = AVCaptureDevice.default(\n        .builtInWideAngleCamera, for: .video, position: .back)\n    else {\n      return\n    }\n    do {\n      let input = try AVCaptureDeviceInput(device: backCamera)\n      captureSession.addInput(input)\n    } catch {\n      return\n    }\n\n    let videoOutput = AVCaptureVideoDataOutput()\n    videoOutput.videoSettings = [\n      (kCVPixelBufferPixelFormatTypeKey as String): NSNumber(value: kCVPixelFormatType_32BGRA)\n    ]\n    videoOutput.alwaysDiscardsLateVideoFrames = true\n    let dataOutputQueue = DispatchQueue(\n      label: \"video data queue\",\n      qos: .userInitiated,\n      attributes: [],\n      autoreleaseFrequency: .workItem)\n    if captureSession.canAddOutput(videoOutput) {\n      captureSession.addOutput(videoOutput)\n      videoOutput.connection(with: .video)?.videoOrientation = .portrait\n      captureSession.startRunning()\n    }\n    videoOutput.setSampleBufferDelegate(self, queue: dataOutputQueue)\n  }\n\n  // MARK: Methods of the AVCaptureVideoDataOutputSampleBufferDelegate\n  func captureOutput(\n    _ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer,\n    from connection: AVCaptureConnection\n  ) {\n    guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {\n      return\n    }\n    CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags.readOnly)\n    delegate?.cameraFeedManager(self, didOutput: pixelBuffer)\n    CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags.readOnly)\n  }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/PoseEstimation/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>NSCameraUsageDescription</key>\n\t<string>This app will use camera to continuously estimate the pose the user shows.</string>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UIRequiresFullScreen</key>\n\t<true/>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/PoseEstimation/ML/Extensions/CGSize+TFLite.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n\nimport Accelerate\nimport Foundation\n\nextension CGSize {\n  /// Returns `CGAffineTransform` to resize `self` to fit in destination size, keeping aspect ratio\n  /// of `self`. `self` image is resized to be inscribe to destination size and located in center of\n  /// destination.\n  ///\n  /// - Parameter toFitIn: destination size to be filled.\n  /// - Returns: `CGAffineTransform` to transform `self` image to `dest` image.\n  func transformKeepAspect(toFitIn dest: CGSize) -> CGAffineTransform {\n    let sourceRatio = self.height / self.width\n    let destRatio = dest.height / dest.width\n\n    // Calculates ratio `self` to `dest`.\n    var ratio: CGFloat\n    var x: CGFloat = 0\n    var y: CGFloat = 0\n    if sourceRatio > destRatio {\n      // Source size is taller than destination. Resized to fit in destination height, and find\n      // horizontal starting point to be centered.\n      ratio = dest.height / self.height\n      x = (dest.width - self.width * ratio) / 2\n    } else {\n      ratio = dest.width / self.width\n      y = (dest.height - self.height * ratio) / 2\n    }\n    return CGAffineTransform(a: ratio, b: 0, c: 0, d: ratio, tx: x, ty: y)\n  }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/PoseEstimation/ML/Extensions/CVPixelBuffer+TFLite.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n\nimport Accelerate\nimport Foundation\n\nextension CVPixelBuffer {\n\n  /// Size of the buffer.\n  var size: CGSize {\n    return CGSize(width: CVPixelBufferGetWidth(self), height: CVPixelBufferGetHeight(self))\n  }\n\n  /// Returns thumbnail by cropping pixel buffer to biggest square and scaling the cropped image\n  /// to model dimensions. This method only supports 32BGRA or 32ARGB format. It returns nil for\n  /// other format.\n  func resized(to size: CGSize) -> CVPixelBuffer? {\n    let imageWidth = CVPixelBufferGetWidth(self)\n    let imageHeight = CVPixelBufferGetHeight(self)\n    let pixelBufferType = CVPixelBufferGetPixelFormatType(self)\n    guard\n      pixelBufferType == kCVPixelFormatType_32BGRA || pixelBufferType == kCVPixelFormatType_32ARGB\n    else {\n      return nil\n    }\n\n    let inputImageRowBytes = CVPixelBufferGetBytesPerRow(self)\n    let imageChannels = 4\n    CVPixelBufferLockBaseAddress(self, CVPixelBufferLockFlags(rawValue: 0))\n    // Finds the biggest square in the pixel buffer and advances rows based on it.\n    guard let inputBaseAddress = CVPixelBufferGetBaseAddress(self) else {\n      return nil\n    }\n\n    // Gets vImage Buffer from input image\n    var inputVImageBuffer = vImage_Buffer(\n      data: inputBaseAddress, height: UInt(imageHeight), width: UInt(imageWidth),\n      rowBytes: inputImageRowBytes)\n\n    let scaledImageRowBytes = Int(size.width) * imageChannels\n    guard let scaledImageBytes = malloc(Int(size.height) * scaledImageRowBytes) else {\n      return nil\n    }\n\n    // Allocates a vImage buffer for scaled image.\n    var scaledVImageBuffer = vImage_Buffer(\n      data: scaledImageBytes, height: UInt(size.height), width: UInt(size.width),\n      rowBytes: scaledImageRowBytes)\n\n    // Performs the scale operation on input image buffer and stores it in scaled image buffer.\n    let scaleError = vImageScale_ARGB8888(\n      &inputVImageBuffer, &scaledVImageBuffer, nil, vImage_Flags(0))\n\n    CVPixelBufferUnlockBaseAddress(self, CVPixelBufferLockFlags(rawValue: 0))\n\n    guard scaleError == kvImageNoError else {\n      return nil\n    }\n\n    let releaseCallBack: CVPixelBufferReleaseBytesCallback = { mutablePointer, pointer in\n      if let pointer = pointer {\n        free(UnsafeMutableRawPointer(mutating: pointer))\n      }\n    }\n    var scaledPixelBuffer: CVPixelBuffer?\n\n    // Converts the scaled vImage buffer to CVPixelBuffer\n    let conversionStatus = CVPixelBufferCreateWithBytes(\n      nil, Int(size.width), Int(size.height), pixelBufferType, scaledImageBytes,\n      scaledImageRowBytes, releaseCallBack, nil, nil, &scaledPixelBuffer)\n\n    guard conversionStatus == kCVReturnSuccess else {\n      free(scaledImageBytes)\n      return nil\n    }\n    return scaledPixelBuffer\n  }\n\n  /// Returns a new `CVPixelBuffer` created by taking the self area and resizing it to the\n  /// specified target size. Aspect ratios of source image and destination image are expected to be\n  /// same.\n  ///\n  /// - Parameters:\n  ///   - from: Source area of image to be cropped and resized.\n  ///   - to: Size to scale the image to(i.e. image size used while training the model).\n  /// - Returns: The cropped and resized image of itself.\n  func cropAndResize(fromRect source: CGRect, toSize size: CGSize) -> CVPixelBuffer? {\n    let inputImageRowBytes = CVPixelBufferGetBytesPerRow(self)\n    let imageChannels = 4\n    CVPixelBufferLockBaseAddress(self, CVPixelBufferLockFlags(rawValue: 0))\n    defer { CVPixelBufferUnlockBaseAddress(self, CVPixelBufferLockFlags(rawValue: 0)) }\n\n    // Finds the address of the upper leftmost pixel of the source area.\n    guard\n      let inputBaseAddress = CVPixelBufferGetBaseAddress(self)?.advanced(\n        by: Int(source.minY) * inputImageRowBytes + Int(source.minX) * imageChannels)\n    else {\n      return nil\n    }\n\n    // Crops given area as vImage Buffer.\n    var croppedImage = vImage_Buffer(\n      data: inputBaseAddress, height: UInt(source.height), width: UInt(source.width),\n      rowBytes: inputImageRowBytes)\n\n    let resultRowBytes = Int(size.width) * imageChannels\n    guard let resultAddress = malloc(Int(size.height) * resultRowBytes) else {\n      return nil\n    }\n\n    // Allocates a vacant vImage buffer for resized image.\n    var resizedImage = vImage_Buffer(\n      data: resultAddress,\n      height: UInt(size.height), width: UInt(size.width),\n      rowBytes: resultRowBytes\n    )\n\n    let error = vImageScale_ARGB8888(&croppedImage, &resizedImage, nil, vImage_Flags(0))\n    CVPixelBufferUnlockBaseAddress(self, CVPixelBufferLockFlags(rawValue: 0))\n    if error != kvImageNoError {\n      os_log(\"Error scaling the image.\", type: .error)\n      free(resultAddress)\n      return nil\n    }\n\n    let releaseCallBack: CVPixelBufferReleaseBytesCallback = { mutablePointer, pointer in\n      if let pointer = pointer {\n        free(UnsafeMutableRawPointer(mutating: pointer))\n      }\n    }\n\n    var result: CVPixelBuffer?\n\n    // Converts the thumbnail vImage buffer to CVPixelBuffer\n    let conversionStatus = CVPixelBufferCreateWithBytes(\n      nil,\n      Int(size.width), Int(size.height),\n      CVPixelBufferGetPixelFormatType(self),\n      resultAddress,\n      resultRowBytes,\n      releaseCallBack,\n      nil,\n      nil,\n      &result\n    )\n\n    guard conversionStatus == kCVReturnSuccess else {\n      free(resultAddress)\n      return nil\n    }\n    return result\n  }\n\n  /// Returns the RGB data representation of the given image buffer with the specified `byteCount`.\n  ///\n  /// - Parameters\n  ///   - buffer: The BGRA pixel buffer to convert to RGB data.\n  ///   - isModelQuantized: Whether the model is quantized (i.e. fixed point values rather than\n  ///       floating point values).\n  /// - Returns: The RGB data representation of the image buffer or `nil` if the buffer could not be\n  ///     converted.\n  func rgbData(isModelQuantized: Bool, imageMean: Float, imageStd: Float) -> Data? {\n    CVPixelBufferLockBaseAddress(self, .readOnly)\n    defer {\n      CVPixelBufferUnlockBaseAddress(self, .readOnly)\n    }\n    guard let sourceData = CVPixelBufferGetBaseAddress(self) else {\n      return nil\n    }\n\n    let width = CVPixelBufferGetWidth(self)\n    let height = CVPixelBufferGetHeight(self)\n    let sourceBytesPerRow = CVPixelBufferGetBytesPerRow(self)\n    let destinationChannelCount = 3\n    let destinationBytesPerRow = destinationChannelCount * width\n\n    var sourceBuffer = vImage_Buffer(\n      data: sourceData,\n      height: vImagePixelCount(height),\n      width: vImagePixelCount(width),\n      rowBytes: sourceBytesPerRow)\n\n    guard let destinationData = malloc(height * destinationBytesPerRow) else {\n      os_log(\"Error: out of memory.\", type: .error)\n      return nil\n    }\n\n    defer {\n      free(destinationData)\n    }\n\n    var destinationBuffer = vImage_Buffer(\n      data: destinationData,\n      height: vImagePixelCount(height),\n      width: vImagePixelCount(width),\n      rowBytes: destinationBytesPerRow)\n\n    if CVPixelBufferGetPixelFormatType(self) == kCVPixelFormatType_32BGRA {\n      vImageConvert_BGRA8888toRGB888(&sourceBuffer, &destinationBuffer, UInt32(kvImageNoFlags))\n    } else if CVPixelBufferGetPixelFormatType(self) == kCVPixelFormatType_32ARGB {\n      vImageConvert_ARGB8888toRGB888(&sourceBuffer, &destinationBuffer, UInt32(kvImageNoFlags))\n    }\n\n    let byteData = Data(bytes: destinationBuffer.data, count: destinationBuffer.rowBytes * height)\n    if isModelQuantized {\n      return byteData\n    }\n\n    // Not quantized, convert to floats\n    let bytes = [UInt8](byteData)\n    let floats = bytes.map { (Float($0) - imageMean) / imageStd }\n    return Data(copyingBufferOf: floats)\n  }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/PoseEstimation/ML/Extensions/Data+TFLite.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n\nimport Foundation\nimport TensorFlowLite\n\n// MARK: - Data\nextension Data {\n  /// Creates a new buffer by copying the buffer pointer of the given array.\n  ///\n  /// - Warning: The given array's element type `T` must be trivial in that it can be copied bit\n  ///     for bit with no indirection or reference-counting operations; otherwise, reinterpreting\n  ///     data from the resulting buffer has undefined behavior.\n  /// - Parameter array: An array with elements of type `T`.\n  init<T>(copyingBufferOf array: [T]) {\n    self = array.withUnsafeBufferPointer(Data.init)\n  }\n\n  /// Convert a Data instance to Array representation.\n  func toArray<T>(type: T.Type) -> [T] where T: AdditiveArithmetic {\n    var array = [T](repeating: T.zero, count: self.count / MemoryLayout<T>.stride)\n    _ = array.withUnsafeMutableBytes { self.copyBytes(to: $0) }\n    return array\n  }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/PoseEstimation/ML/Models/MoveNet.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n//\n\nimport Accelerate\nimport Foundation\nimport TensorFlowLite\n\n/// A wrapper to run pose estimation using MoveNet models\nfinal class MoveNet: PoseEstimator {\n\n  // MARK: - Private Properties\n  // TensorFlow Lite `Interpreter` object for performing inference on a given model.\n  private var interpreter: Interpreter\n\n  // TensorFlow Lite `Tensor` of model input and output.\n  private var inputTensor: Tensor\n  private var outputTensor: Tensor\n\n  // Model config\n  private var torsoExpansionRatio = 1.9\n  private var bodyExpandsionRatio = 1.2\n  private let imageMean: Float = 0\n  private let imageStd: Float = 1\n  private let minCropKeyPointScore: Float32 = 0.2\n  private var cropRegion: RectF?\n  private var isProcessing = false\n\n  // Model files\n  private let movenetLightningFile = FileInfo(name: \"movenet_lightning\", ext: \"tflite\")\n  private let movenetThunderFile = FileInfo(name: \"movenet_thunder\", ext: \"tflite\")\n\n  // MARK: - Initialization\n\n  /// A failable initializer for `MoveNet`. A new instance is created if the model is\n  /// successfully loaded from the app's main bundle. Default `threadCount` is 4.\n  init(threadCount: Int, delegate: Delegates, modelType: ModelType) throws {\n    // Construct the path to the model file.\n    let fileInfo: FileInfo!\n    switch modelType {\n    case .movenetThunder:\n      fileInfo = movenetThunderFile\n    case .movenetLighting:\n      fileInfo = movenetLightningFile\n    case .posenet: fatalError(\"Failed to use MoveNet\")\n    }\n    guard\n      let modelPath = Bundle.main.path(\n        forResource: fileInfo.name,\n        ofType: fileInfo.ext)\n    else {\n      fatalError(\"Failed to load the model file with name: \\(fileInfo.name).\")\n    }\n\n    // Specify the options for the `Interpreter`.\n    var options = Interpreter.Options()\n    options.threadCount = threadCount\n\n    // Specify the delegates for the `Interpreter`.\n    var delegates: [Delegate]?\n    switch delegate {\n    case .gpu:\n      delegates = [MetalDelegate()]\n    case .npu:\n      os_log(\"WARNING: MoveNet currently doesn't support NPU yet. Fall back to CPU\", type: .info)\n      delegates = nil\n    case .cpu:\n      delegates = nil\n    }\n\n    // Create the `Interpreter`.\n    interpreter = try Interpreter(modelPath: modelPath, options: options, delegates: delegates)\n\n    // Initialize input and output `Tensor`s.\n    // Allocate memory for the model's input `Tensor`s.\n    try interpreter.allocateTensors()\n\n    // Get allocated input and output `Tensor`s.\n    inputTensor = try interpreter.input(at: 0)\n    outputTensor = try interpreter.output(at: 0)\n  }\n\n  /// Runs PoseEstimation model with given image with given source area to destination area.\n  /// This pose detector can process only one frame at each moment.\n  ///\n  /// - Parameters:\n  ///   - on: Input image to run the model.\n  /// - Returns: Result of the inference and the times consumed in every steps.\n  func estimateSinglePose(on pixelBuffer: CVPixelBuffer) throws -> (Person, Times) {\n    // Check if this MoveNet instance is already processing a video frame.\n    // Return an empty detection result if it's currently busy.\n    guard !isProcessing else {\n      throw PoseEstimationError.modelBusy\n    }\n    isProcessing = true\n    defer {\n      isProcessing = false\n    }\n\n    // Start times of each process.\n    let preprocessingStartTime: Date\n    let inferenceStartTime: Date\n    let postprocessingStartTime: Date\n\n    // Processing times in seconds.\n    let preprocessingTime: TimeInterval\n    let inferenceTime: TimeInterval\n    let postprocessingTime: TimeInterval\n\n    preprocessingStartTime = Date()\n    guard let data = preprocess(pixelBuffer) else {\n      os_log(\"Preprocessing failed.\", type: .error)\n      throw PoseEstimationError.preprocessingFailed\n    }\n    preprocessingTime = Date().timeIntervalSince(preprocessingStartTime)\n\n    // Run inference with the TFLite model\n    inferenceStartTime = Date()\n    do {\n      // Copy the initialized `Data` to the input `Tensor`.\n      try interpreter.copy(data, toInputAt: 0)\n\n      // Run inference by invoking the `Interpreter`.\n      try interpreter.invoke()\n      // Get the output `Tensor` to process the inference results.\n      outputTensor = try interpreter.output(at: 0)\n    } catch let error {\n      os_log(\n        \"Failed to invoke the interpreter with error: %s\", type: .error,\n        error.localizedDescription)\n      throw PoseEstimationError.inferenceFailed\n    }\n    inferenceTime = Date().timeIntervalSince(inferenceStartTime)\n\n    postprocessingStartTime = Date()\n    guard let result = postprocess(imageSize: pixelBuffer.size, modelOutput: outputTensor) else {\n      os_log(\"Postprocessing failed.\", type: .error)\n      throw PoseEstimationError.postProcessingFailed\n    }\n    postprocessingTime = Date().timeIntervalSince(postprocessingStartTime)\n\n    let times = Times(\n      preprocessing: preprocessingTime,\n      inference: inferenceTime,\n      postprocessing: postprocessingTime)\n    return (result, times)\n  }\n\n  // MARK: - Private functions to run the model\n  /// Preprocesses given rectangle image to be `Data` of desired size by cropping and resizing it.\n  ///\n  /// - Parameters:\n  ///   - of: Input image to crop and resize.\n  /// - Returns: The cropped and resized image. `nil` if it can not be processed.\n  private func preprocess(_ pixelBuffer: CVPixelBuffer) -> Data? {\n    let sourcePixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer)\n    assert(\n      sourcePixelFormat == kCVPixelFormatType_32BGRA\n        || sourcePixelFormat == kCVPixelFormatType_32ARGB)\n    // Resize `targetSquare` of input image to `modelSize`.\n    let dimensions = inputTensor.shape.dimensions\n    let inputWidth = CGFloat(dimensions[1])\n    let inputHeight = CGFloat(dimensions[2])\n    let imageWidth = pixelBuffer.size.width\n    let imageHeight = pixelBuffer.size.height\n\n    let cropRegion = self.cropRegion ??\n      initialCropRegion(imageWidth: imageWidth, imageHeight: imageHeight)\n    self.cropRegion = cropRegion\n\n    let rectF = RectF(\n      left: cropRegion.left * imageWidth,\n      top: cropRegion.top * imageHeight,\n      right: cropRegion.right * imageWidth,\n      bottom: cropRegion.bottom * imageHeight)\n\n    // Detect region\n    let modelSize = CGSize(width: inputWidth, height: inputHeight)\n    guard let thumbnail = pixelBuffer.cropAndResize(fromRect: rectF.rect, toSize: modelSize) else {\n      return nil\n    }\n\n    // Remove the alpha component from the image buffer to get the initialized `Data`.\n    guard\n      let inputData = thumbnail.rgbData(\n        isModelQuantized: inputTensor.dataType == .uInt8, imageMean: imageMean, imageStd: imageStd)\n    else {\n      os_log(\"Failed to convert the image buffer to RGB data.\", type: .error)\n      return nil\n    }\n\n    return inputData\n  }\n\n  /// Postprocesses the output `Tensor` of TFLite model to the `Person` type\n  ///\n  /// - Parameters:\n  ///   - imageSize: Size of the input image.\n  ///   - modelOutput: Output tensor from the TFLite model.\n  /// - Returns: Postprocessed `Person`. `nil` if it can not be processed.\n  private func postprocess(imageSize: CGSize, modelOutput: Tensor) -> Person? {\n    let imageWidth = imageSize.width\n    let imageHeight = imageSize.height\n\n    let cropRegion = self.cropRegion ??\n      initialCropRegion(imageWidth: imageWidth, imageHeight: imageHeight)\n\n    let minX: CGFloat = cropRegion.left * imageWidth\n    let minY: CGFloat = cropRegion.top * imageHeight\n\n    let output = modelOutput.data.toArray(type: Float32.self)\n    let dimensions = modelOutput.shape.dimensions\n    let numKeyPoints = dimensions[2]\n    let inputWidth = CGFloat(inputTensor.shape.dimensions[1])\n    let inputHeight = CGFloat(inputTensor.shape.dimensions[2])\n\n    let widthRatio = (cropRegion.width * imageWidth / inputWidth)\n    let heightRatio = (cropRegion.height * imageHeight / inputHeight)\n\n    // Translate the coordinates from the model output's [0..1] back to that of\n    // the input image\n    var positions: [CGFloat] = []\n    var totalScoreSum: Float32 = 0\n    var keyPoints: [KeyPoint] = []\n    for idx in 0..<numKeyPoints {\n      let x = ((CGFloat(output[idx * 3 + 1]) * inputWidth) * widthRatio) + minX\n      let y = ((CGFloat(output[idx * 3 + 0]) * inputHeight) * heightRatio) + minY\n      positions.append(x)\n      positions.append(y)\n      let score = output[idx * 3 + 2]\n      totalScoreSum += score\n      let keyPoint = KeyPoint(\n        bodyPart: BodyPart.allCases[idx], coordinate: CGPoint(x: x, y: y), score: score)\n      keyPoints.append(keyPoint)\n    }\n\n    // Calculate the crop region for the subsequent frame.\n    self.cropRegion = nextFrameCropRegion(\n      keyPoints: keyPoints, imageWidth: imageWidth, imageHeight: imageHeight)\n\n    // Calculates total confidence score of each key position.\n    let totalScore = totalScoreSum / Float32(numKeyPoints)\n\n    // Make `Person` from `keypoints'. Each point is adjusted to the coordinate of the input image.\n    return Person(keyPoints: keyPoints, score: totalScore)\n  }\n\n  // MARK: MoveNet's smart cropping logic\n  /// Determines the region to crop the image for the model to run inference on.\n  /// The algorithm uses the detected joints from the previous frame to estimate\n  /// the square region that encloses the full body of the target person and\n  /// centers at the midpoint of two hip joints. The crop size is determined by\n  /// the distances between each joints and the center point.\n  /// When the model is not confident with the four torso joint predictions, the\n  /// function returns a default crop which is the full image padded to square.\n  private func nextFrameCropRegion(keyPoints: [KeyPoint], imageWidth: CGFloat, imageHeight: CGFloat)\n    -> RectF\n  {\n    let targetKeyPoints = keyPoints.map { keyPoint in\n        KeyPoint.init(bodyPart: keyPoint.bodyPart,\n                      coordinate: CGPoint(x: keyPoint.coordinate.x, y: keyPoint.coordinate.y),\n                      score: keyPoint.score)\n    }\n    if torsoVisible(keyPoints) {\n      let centerX =\n        (targetKeyPoints[BodyPart.leftHip.position].coordinate.x\n          + targetKeyPoints[BodyPart.rightHip.position].coordinate.x) / 2.0\n      let centerY =\n        (targetKeyPoints[BodyPart.leftHip.position].coordinate.y\n          + targetKeyPoints[BodyPart.rightHip.position].coordinate.y) / 2.0\n\n      let torsoAndBodyDistances =\n        determineTorsoAndBodyDistances(\n          keyPoints: keyPoints, targetKeyPoints: targetKeyPoints, centerX: centerX, centerY: centerY\n        )\n\n      let list = [\n        torsoAndBodyDistances.maxTorsoXDistance * CGFloat(torsoExpansionRatio),\n        torsoAndBodyDistances.maxTorsoYDistance * CGFloat(torsoExpansionRatio),\n        torsoAndBodyDistances.maxBodyXDistance * CGFloat(bodyExpandsionRatio),\n        torsoAndBodyDistances.maxBodyYDistance * CGFloat(bodyExpandsionRatio),\n      ]\n\n      var cropLengthHalf = list.max() ?? 0.0\n      let tmp: [CGFloat] = [\n        centerX, CGFloat(imageWidth) - centerX, centerY, CGFloat(imageHeight) - centerY,\n      ]\n      cropLengthHalf = min(cropLengthHalf, tmp.max() ?? 0.0)\n      let cropCornerY = centerY - cropLengthHalf\n      let cropCornerX = centerX - cropLengthHalf\n      if cropLengthHalf > (CGFloat(max(imageWidth, imageHeight)) / 2.0) {\n        return initialCropRegion(imageWidth: imageWidth, imageHeight: imageHeight)\n      } else {\n        let cropLength = cropLengthHalf * 2\n        return RectF(\n          left: max(cropCornerX, 0) / imageWidth,\n          top: max(cropCornerY, 0) / imageHeight,\n          right: min((cropCornerX + cropLength) / imageWidth, 1),\n          bottom: min((cropCornerY + cropLength) / imageHeight, 1))\n      }\n    } else {\n      return initialCropRegion(imageWidth: imageWidth, imageHeight: imageHeight)\n    }\n  }\n\n  /// Defines the default crop region.\n  /// The function provides the initial crop region (pads the full image from both\n  /// sides to make it a square image) when the algorithm cannot reliably determine\n  /// the crop region from the previous frame.\n  private func initialCropRegion(imageWidth: CGFloat, imageHeight: CGFloat) -> RectF {\n    var xMin: CGFloat\n    var yMin: CGFloat\n    var width: CGFloat\n    var height: CGFloat\n    if imageWidth > imageHeight {\n      height = 1\n      width = imageHeight / imageWidth\n      yMin = 0\n      xMin = ((imageWidth - imageHeight) / 2.0) / imageWidth\n    } else {\n      width = 1\n      height = imageWidth / imageHeight\n      xMin = 0\n      yMin = ((imageHeight - imageWidth) / 2.0) / imageHeight\n    }\n    return RectF(left: xMin, top: yMin, right: xMin + width, bottom: yMin + height)\n  }\n\n  /// Checks whether there are enough torso keypoints.\n  /// This function checks whether the model is confident at predicting one of the\n  /// shoulders/hips which is required to determine a good crop region.\n  private func torsoVisible(_ keyPoints: [KeyPoint]) -> Bool {\n    return\n      ((keyPoints[BodyPart.leftHip.position].score > minCropKeyPointScore\n      || keyPoints[BodyPart.rightHip.position].score > minCropKeyPointScore))\n      && ((keyPoints[BodyPart.leftShoulder.position].score > minCropKeyPointScore\n        || keyPoints[BodyPart.rightShoulder.position].score > minCropKeyPointScore))\n  }\n\n  /// Calculates the maximum distance from each keypoints to the center location.\n  /// The function returns the maximum distances from the two sets of keypoints:\n  /// full 17 keypoints and 4 torso keypoints. The returned information will be\n  /// used to determine the crop size. See determineRectF for more detail.\n  private func determineTorsoAndBodyDistances(\n    keyPoints: [KeyPoint], targetKeyPoints: [KeyPoint], centerX: CGFloat, centerY: CGFloat\n  ) -> TorsoAndBodyDistance {\n    let torsoJoints = [\n      BodyPart.leftShoulder.position,\n      BodyPart.rightShoulder.position,\n      BodyPart.leftHip.position,\n      BodyPart.rightHip.position,\n    ]\n\n    let maxTorsoYRange = torsoJoints.lazy.map { abs(centerY - targetKeyPoints[$0].coordinate.y) }\n      .max() ?? 0.0\n    let maxTorsoXRange = torsoJoints.lazy.map { abs(centerX - targetKeyPoints[$0].coordinate.x) }\n      .max() ?? 0.0\n\n    let confidentKeypoints = keyPoints.lazy.filter( {$0.score < self.minCropKeyPointScore} )\n    let maxBodyYRange = confidentKeypoints.map({ abs(centerY - $0.coordinate.y) }).max() ?? 0.0\n    let maxBodyXRange = confidentKeypoints.map({ abs(centerX - $0.coordinate.x) }).max() ?? 0.0\n\n    return TorsoAndBodyDistance(\n      maxTorsoYDistance: maxTorsoYRange,\n      maxTorsoXDistance: maxTorsoXRange,\n      maxBodyYDistance: maxBodyYRange,\n      maxBodyXDistance: maxBodyXRange)\n  }\n}\n\n/// Size of a detected person.\nstruct TorsoAndBodyDistance {\n  var maxTorsoYDistance: CGFloat\n  var maxTorsoXDistance: CGFloat\n  var maxBodyYDistance: CGFloat\n  var maxBodyXDistance: CGFloat\n}\n\n/// A rectangle with calculated properties for convenient access.\nstruct RectF {\n  var left: CGFloat\n  var top: CGFloat\n  var right: CGFloat\n  var bottom: CGFloat\n  var width: CGFloat { right - left }\n  var height: CGFloat { bottom - top }\n\n  var rect: CGRect { .init(x: left, y: top, width: width, height: height) }\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/PoseEstimation/ML/Models/PoseConfig.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n//\n\nimport Foundation\n\n// MARK: Run configurations\n\n/// TFLite Delegate used to run the model.\nenum Delegates: String, CaseIterable {\n  case cpu = \"CPU\"\n  case gpu = \"GPU\"\n  case npu = \"NPU\"\n}\n\n/// Information about a TFLite model file.\nstruct FileInfo {\n  var name: String\n  var ext: String\n}\n\n/// Type of the pose estimation model to be used.\nenum ModelType: String, CaseIterable {\n  case posenet = \"Posenet\"\n  case movenetLighting = \"Lightning\"  // Movenet lightning\n  case movenetThunder = \"Thunder\"  // Movenet thunder\n}\n\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/PoseEstimation/ML/Models/PoseData.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n//\n\nimport UIKit\n\n// MARK: Detection result\n/// Time required to run pose estimation on one frame.\nstruct Times {\n  var preprocessing: TimeInterval\n  var inference: TimeInterval\n  var postprocessing: TimeInterval\n  var total: TimeInterval { preprocessing + inference + postprocessing }\n}\n\n/// An enum describing a body part (e.g. nose, left eye etc.).\nenum BodyPart: String, CaseIterable {\n  case nose = \"nose\"\n  case leftEye = \"left eye\"\n  case rightEye = \"right eye\"\n  case leftEar = \"left ear\"\n  case rightEar = \"right ear\"\n  case leftShoulder = \"left shoulder\"\n  case rightShoulder = \"right shoulder\"\n  case leftElbow = \"left elbow\"\n  case rightElbow = \"right elbow\"\n  case leftWrist = \"left wrist\"\n  case rightWrist = \"right wrist\"\n  case leftHip = \"left hip\"\n  case rightHip = \"right hip\"\n  case leftKnee = \"left knee\"\n  case rightKnee = \"right knee\"\n  case leftAnkle = \"left ankle\"\n  case rightAnkle = \"right ankle\"\n\n  /// Get the index of the body part in the array returned by pose estimation models.\n  var position: Int {\n    return BodyPart.allCases.firstIndex(of: self) ?? 0\n  }\n}\n\n/// A body keypoint (e.g. nose) 's detection result.\nstruct KeyPoint {\n  var bodyPart: BodyPart = .nose\n  var coordinate: CGPoint = .zero\n  var score: Float32 = 0.0\n}\n\n/// A person detected by a pose estimation model.\nstruct Person {\n  var keyPoints: [KeyPoint]\n  var score: Float32\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/PoseEstimation/ML/Models/PoseEstimator.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n\nimport UIKit\n\n/// Protocol to  run a pose estimator.\nprotocol PoseEstimator {\n  func estimateSinglePose(on pixelbuffer: CVPixelBuffer) throws -> (Person, Times)\n}\n\n// MARK: - Custom Errors\nenum PoseEstimationError: Error {\n  case modelBusy\n  case preprocessingFailed\n  case inferenceFailed\n  case postProcessingFailed\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/PoseEstimation/ML/Models/PoseNet.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n\nimport Accelerate\nimport Foundation\nimport TensorFlowLite\n\n/// A wrapper to run pose estimation using the PoseNet model.\nfinal class PoseNet: PoseEstimator {\n\n  // MARK: - Private Properties\n\n  /// TensorFlow Lite `Interpreter` object for performing inference on a given model.\n  private var interpreter: Interpreter\n\n  /// TensorFlow lite `Tensor` of model input and output.\n  private var inputTensor: Tensor\n  private var heatsTensor: Tensor\n  private var offsetsTensor: Tensor\n  private let imageMean: Float = 127.5\n  private let imageStd: Float = 127.5\n\n  /// Model files\n  private let posenetFile = FileInfo(name: \"posenet\", ext: \"tflite\")\n\n  // MARK: - Initialization\n\n  /// A failable initializer for `ModelDataHandler`. A new instance is created if the model is\n  /// successfully loaded from the app's main bundle. Default `threadCount` is 2.\n  init(threadCount: Int, delegate: Delegates) throws {\n    // Construct the path to the model file.\n    guard\n      let modelPath = Bundle.main.path(forResource: posenetFile.name, ofType: posenetFile.ext)\n    else {\n      fatalError(\"Failed to load the model file.\")\n    }\n\n    // Specify the options for the `Interpreter`.\n    var options = Interpreter.Options()\n    options.threadCount = threadCount\n\n    // Specify the delegates for the `Interpreter`.\n    var delegates: [Delegate]?\n    switch delegate {\n    case .gpu:\n      delegates = [MetalDelegate()]\n    case .npu:\n      if let coreMLDelegate = CoreMLDelegate() {\n        delegates = [coreMLDelegate]\n      } else {\n        delegates = nil\n      }\n    case .cpu:\n      delegates = nil\n    }\n    // Create the `Interpreter`.\n    interpreter = try Interpreter(modelPath: modelPath, options: options, delegates: delegates)\n\n    // Initialize input and output `Tensor`s.\n    // Allocate memory for the model's input `Tensor`s.\n    try interpreter.allocateTensors()\n\n    // Get allocated input and output `Tensor`s.\n    inputTensor = try interpreter.input(at: 0)\n    heatsTensor = try interpreter.output(at: 0)\n    offsetsTensor = try interpreter.output(at: 1)\n  }\n\n  /// Runs PoseEstimation model with given image with given source area to destination area.\n  ///\n  /// - Parameters:\n  ///   - on: Input image to run the model.\n  ///   - from: Range of input image to run the model.\n  ///   - to: Size of view to render the result.\n  /// - Returns: Result of the inference and the times consumed in every steps.\n  func estimateSinglePose(on pixelBuffer: CVPixelBuffer) throws -> (Person, Times) {\n    // Start times of each process.\n    let preprocessingStartTime: Date\n    let inferenceStartTime: Date\n    let postprocessingStartTime: Date\n\n    // Processing times in seconds.\n    let preprocessingTime: TimeInterval\n    let inferenceTime: TimeInterval\n    let postprocessingTime: TimeInterval\n\n    preprocessingStartTime = Date()\n    guard let data = preprocess(pixelBuffer) else {\n      os_log(\"Preprocessing failed.\", type: .error)\n      throw PoseEstimationError.preprocessingFailed\n    }\n    preprocessingTime = Date().timeIntervalSince(preprocessingStartTime)\n\n    // Run inference with the TFLite model.\n    inferenceStartTime = Date()\n    do {\n      try interpreter.copy(data, toInputAt: 0)\n\n      // Run inference by invoking the `Interpreter`.\n      try interpreter.invoke()\n\n      // Get the output `Tensor` to process the inference results.\n      heatsTensor = try interpreter.output(at: 0)\n      offsetsTensor = try interpreter.output(at: 1)\n\n    } catch let error {\n      os_log(\n        \"Failed to invoke the interpreter with error: %s\", type: .error,\n        error.localizedDescription)\n      throw PoseEstimationError.inferenceFailed\n    }\n    inferenceTime = Date().timeIntervalSince(inferenceStartTime)\n\n    postprocessingStartTime = Date()\n    guard let result = postprocess(to: pixelBuffer.size) else {\n      os_log(\"Postprocessing failed.\", type: .error)\n      throw PoseEstimationError.postProcessingFailed\n    }\n    postprocessingTime = Date().timeIntervalSince(postprocessingStartTime)\n\n    let times = Times(\n      preprocessing: preprocessingTime,\n      inference: inferenceTime,\n      postprocessing: postprocessingTime)\n    return (result, times)\n  }\n\n  // MARK: - Private functions to run model\n  /// Preprocesses given rectangle image to be `Data` of desired size by cropping and resizing it.\n  ///\n  /// - Parameters:\n  ///   - of: Input image to crop and resize.\n  ///   - from: Target area to be cropped and resized.\n  /// - Returns: The cropped and resized image. `nil` if it can not be processed.\n  private func preprocess(_ pixelBuffer: CVPixelBuffer) -> Data? {\n    let sourcePixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer)\n    assert(\n      sourcePixelFormat == kCVPixelFormatType_32BGRA\n        || sourcePixelFormat == kCVPixelFormatType_32ARGB)\n\n    // Resize `targetSquare` of input image to `modelSize`.\n    let dimensions = inputTensor.shape.dimensions\n    let inputWidth = dimensions[1]\n    let inputHeight = dimensions[2]\n    let modelSize = CGSize(width: inputWidth, height: inputHeight)\n    guard let thumbnail = pixelBuffer.resized(to: modelSize) else {\n      return nil\n    }\n    // Remove the alpha component from the image buffer to get the initialized `Data`.\n    return thumbnail.rgbData(isModelQuantized: false, imageMean: imageMean, imageStd: imageStd)\n  }\n\n  /// Postprocesses output `Tensor`s to `Result` with size of view to render the result.\n  ///\n  /// - Parameters:\n  ///   - to: Size of view to be displayed.\n  /// - Returns: Postprocessed `Result`. `nil` if it can not be processed.\n  private func postprocess(to viewSize: CGSize) -> Person? {\n    // MARK: Formats output tensors\n    // Convert `Tensor` to `FlatArray`. As PoseEstimation is not quantized, convert them to Float\n    // type `FlatArray`.\n    let heats = FlatArray<Float32>(tensor: heatsTensor)\n    let offsets = FlatArray<Float32>(tensor: offsetsTensor)\n    let outputHeight = heats.dimensions[1]\n    let outputWidth = heats.dimensions[2]\n    let keyPointSize = heats.dimensions[3]\n    // MARK: Find position of each key point\n    // Finds the (row, col) locations of where the keypoints are most likely to be. The highest\n    // `heats[0, row, col, keypoint]` value, the more likely `keypoint` being located in (`row`,\n    // `col`).\n    let keypointPositions = (0..<keyPointSize).map { keypoint -> (Int, Int) in\n      var maxValue = heats[0, 0, 0, keypoint]\n      var maxRow = 0\n      var maxCol = 0\n      for row in 0..<outputHeight {\n        for col in 0..<outputWidth {\n          if heats[0, row, col, keypoint] > maxValue {\n            maxValue = heats[0, row, col, keypoint]\n            maxRow = row\n            maxCol = col\n          }\n        }\n      }\n      return (maxRow, maxCol)\n    }\n\n    // MARK: Calculates total confidence score\n    // Calculates total confidence score of each key position.\n    let totalScoreSum = keypointPositions.enumerated().reduce(0.0) { accumulator, elem -> Float32 in\n      accumulator + sigmoid(heats[0, elem.element.0, elem.element.1, elem.offset])\n    }\n    let totalScore = totalScoreSum / Float32(keyPointSize)\n\n    // MARK: Calculate key point position on model input\n    // Calculates `KeyPoint` coordination model input image with `offsets` adjustment.\n    let dimensions = inputTensor.shape.dimensions\n    let inputHeight = dimensions[1]\n    let inputWidth = dimensions[2]\n    let coords = keypointPositions.enumerated().map { index, elem -> (y: Float32, x: Float32) in\n      let (y, x) = elem\n      let yCoord =\n        Float32(y) / Float32(outputHeight - 1) * Float32(inputHeight)\n        + offsets[0, y, x, index]\n      let xCoord =\n        Float32(x) / Float32(outputWidth - 1) * Float32(inputWidth)\n        + offsets[0, y, x, index + keyPointSize]\n      return (y: yCoord, x: xCoord)\n    }\n\n    // MARK: Transform key point position and make lines\n    // Make `Result` from `keypointPosition'. Each point is adjusted to `ViewSize` to be drawn.\n    var result = Person(keyPoints: [], score: totalScore)\n\n    for (index, part) in BodyPart.allCases.enumerated() {\n      let x = CGFloat(coords[index].x) * viewSize.width / CGFloat(inputWidth)\n      let y = CGFloat(coords[index].y) * viewSize.height / CGFloat(inputHeight)\n      let keyPoint = KeyPoint(bodyPart: part, coordinate: CGPoint(x: x, y: y))\n      result.keyPoints.append(keyPoint)\n\n    }\n    return result\n  }\n\n  /// Run inference with given `Data`\n  ///\n  /// Parameter `from`: `Data` of input image to run model.\n  private func inference(from data: Data) {\n    // Copy the initialized `Data` to the input `Tensor`.\n    do {\n      try interpreter.copy(data, toInputAt: 0)\n\n      // Run inference by invoking the `Interpreter`.\n      try interpreter.invoke()\n\n      // Get the output `Tensor` to process the inference results.\n      heatsTensor = try interpreter.output(at: 0)\n      offsetsTensor = try interpreter.output(at: 1)\n\n    } catch let error {\n      os_log(\n        \"Failed to invoke the interpreter with error: %s\", type: .error,\n        error.localizedDescription)\n      return\n    }\n  }\n\n  /// Returns value within [0,1].\n  private func sigmoid(_ x: Float32) -> Float32 {\n    return (1.0 / (1.0 + exp(-x)))\n  }\n}\n\n// MARK: - Wrappers\n/// Struct for handling multidimension `Data` in flat `Array`.\nfileprivate struct FlatArray<Element: AdditiveArithmetic> {\n  private var array: [Element]\n  var dimensions: [Int]\n\n  init(tensor: Tensor) {\n    dimensions = tensor.shape.dimensions\n    array = tensor.data.toArray(type: Element.self)\n  }\n\n  private func flatIndex(_ indices: [Int]) -> Int {\n    guard indices.count == dimensions.count else {\n      fatalError(\"Invalid index: got \\(indices.count) index(es) for \\(dimensions.count) index(es).\")\n    }\n\n    var result = 0\n    for (dimension, index) in zip(dimensions, indices) {\n      guard dimension > index else {\n        fatalError(\"Invalid index: \\(index) is bigger than \\(dimension)\")\n      }\n      result = dimension * result + index\n    }\n    return result\n  }\n\n  subscript(_ index: Int...) -> Element {\n    get {\n      return array[flatIndex(index)]\n    }\n    set(newValue) {\n      array[flatIndex(index)] = newValue\n    }\n  }\n}\n\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/PoseEstimation/UI/Storyboards/Base.lproj/Launch Screen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"18122\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <device id=\"retina6_1\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"18093\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"896\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <label opaque=\"NO\" clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Copyright © 2021 tensorflow. All rights reserved.\" textAlignment=\"center\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" minimumFontSize=\"9\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"obG-Y5-kRd\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"855.5\" width=\"414\" height=\"20.5\"/>\n                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                <color key=\"textColor\" red=\"0.0\" green=\"0.0\" blue=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                <nil key=\"highlightedColor\"/>\n                            </label>\n                            <label opaque=\"NO\" clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"PoseEstimation\" textAlignment=\"center\" lineBreakMode=\"middleTruncation\" baselineAdjustment=\"alignBaselines\" minimumFontSize=\"18\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"GJd-Yh-RWb\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"278\" width=\"414\" height=\"43\"/>\n                                <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"36\"/>\n                                <color key=\"textColor\" red=\"0.0\" green=\"0.0\" blue=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                <nil key=\"highlightedColor\"/>\n                            </label>\n                        </subviews>\n                        <viewLayoutGuide key=\"safeArea\" id=\"Bcu-3y-fUS\"/>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <constraints>\n                            <constraint firstItem=\"Bcu-3y-fUS\" firstAttribute=\"centerX\" secondItem=\"obG-Y5-kRd\" secondAttribute=\"centerX\" id=\"5cz-MP-9tL\"/>\n                            <constraint firstItem=\"Bcu-3y-fUS\" firstAttribute=\"centerX\" secondItem=\"GJd-Yh-RWb\" secondAttribute=\"centerX\" id=\"Q3B-4B-g5h\"/>\n                            <constraint firstItem=\"obG-Y5-kRd\" firstAttribute=\"leading\" secondItem=\"Bcu-3y-fUS\" secondAttribute=\"leading\" symbolic=\"YES\" id=\"SfN-ll-jLj\"/>\n                            <constraint firstAttribute=\"bottom\" secondItem=\"obG-Y5-kRd\" secondAttribute=\"bottom\" constant=\"20\" id=\"Y44-ml-fuU\"/>\n                            <constraint firstItem=\"GJd-Yh-RWb\" firstAttribute=\"centerY\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"bottom\" multiplier=\"1/3\" constant=\"1\" id=\"moa-c2-u7t\"/>\n                            <constraint firstItem=\"GJd-Yh-RWb\" firstAttribute=\"leading\" secondItem=\"Bcu-3y-fUS\" secondAttribute=\"leading\" constant=\"20\" symbolic=\"YES\" id=\"x7j-FC-K8j\"/>\n                        </constraints>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/PoseEstimation/UI/Storyboards/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"18122\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <device id=\"retina6_1\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"18093\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"ViewController\" customModule=\"PoseEstimation\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"896\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"aEU-Ov-crs\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"896\"/>\n                                <subviews>\n                                    <imageView clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleAspectFill\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"lSZ-59-BsY\" customClass=\"OverlayView\" customModule=\"PoseEstimation\" customModuleProvider=\"target\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"119\" width=\"414\" height=\"577\"/>\n                                    </imageView>\n                                    <view contentMode=\"scaleToFill\" insetsLayoutMarginsFromSafeArea=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"tWv-pt-h17\" userLabel=\"TopView\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"119\"/>\n                                        <subviews>\n                                            <imageView clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleAspectFit\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" image=\"tfl_logo\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ijS-RU-qlo\">\n                                                <rect key=\"frame\" x=\"74.5\" y=\"42.5\" width=\"265.5\" height=\"68\"/>\n                                            </imageView>\n                                        </subviews>\n                                        <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.46765713853900942\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                        <constraints>\n                                            <constraint firstItem=\"ijS-RU-qlo\" firstAttribute=\"bottom\" secondItem=\"tWv-pt-h17\" secondAttribute=\"bottom\" multiplier=\"6.5:7\" id=\"06z-Bq-BW1\"/>\n                                            <constraint firstItem=\"ijS-RU-qlo\" firstAttribute=\"centerX\" secondItem=\"tWv-pt-h17\" secondAttribute=\"centerX\" id=\"DjM-bj-7Aa\"/>\n                                            <constraint firstItem=\"ijS-RU-qlo\" firstAttribute=\"height\" secondItem=\"tWv-pt-h17\" secondAttribute=\"height\" multiplier=\"4:7\" id=\"fU4-ZD-b2i\"/>\n                                        </constraints>\n                                    </view>\n                                    <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"nFg-A5-Dew\" userLabel=\"BottomView\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"696\" width=\"414\" height=\"200\"/>\n                                        <subviews>\n                                            <stackView opaque=\"NO\" contentMode=\"scaleToFill\" axis=\"vertical\" spacing=\"10\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"fDz-Y6-sxX\">\n                                                <rect key=\"frame\" x=\"60\" y=\"10\" width=\"294\" height=\"60\"/>\n                                                <subviews>\n                                                    <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"TNN-lu-RpW\">\n                                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"294\" height=\"20.5\"/>\n                                                        <subviews>\n                                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Score\" lineBreakMode=\"clip\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"NHJ-8Y-uZQ\" userLabel=\"Field Name Label\">\n                                                                <rect key=\"frame\" x=\"0.0\" y=\"0.5\" width=\"43.5\" height=\"19.5\"/>\n                                                                <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                                <color key=\"tintColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                                <fontDescription key=\"fontDescription\" type=\"system\" weight=\"medium\" pointSize=\"16\"/>\n                                                                <color key=\"textColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                                <color key=\"highlightedColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                            </label>\n                                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"100\" verticalHuggingPriority=\"251\" text=\"Info Label\" textAlignment=\"right\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ZTc-Zz-fNr\" userLabel=\"Info Label\">\n                                                                <rect key=\"frame\" x=\"223\" y=\"0.5\" width=\"71\" height=\"19.5\"/>\n                                                                <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                                <color key=\"tintColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"16\"/>\n                                                                <color key=\"textColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                                <color key=\"highlightedColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                            </label>\n                                                        </subviews>\n                                                        <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                        <constraints>\n                                                            <constraint firstItem=\"NHJ-8Y-uZQ\" firstAttribute=\"leading\" secondItem=\"TNN-lu-RpW\" secondAttribute=\"leading\" id=\"FGV-qx-WcF\"/>\n                                                            <constraint firstItem=\"ZTc-Zz-fNr\" firstAttribute=\"centerY\" secondItem=\"TNN-lu-RpW\" secondAttribute=\"centerY\" id=\"Lst-jv-4Do\"/>\n                                                            <constraint firstItem=\"ZTc-Zz-fNr\" firstAttribute=\"leading\" relation=\"greaterThanOrEqual\" secondItem=\"NHJ-8Y-uZQ\" secondAttribute=\"trailing\" constant=\"8\" symbolic=\"YES\" id=\"p60-gK-g9a\"/>\n                                                            <constraint firstItem=\"NHJ-8Y-uZQ\" firstAttribute=\"centerY\" secondItem=\"TNN-lu-RpW\" secondAttribute=\"centerY\" id=\"qLd-cA-e6i\"/>\n                                                            <constraint firstAttribute=\"trailing\" secondItem=\"ZTc-Zz-fNr\" secondAttribute=\"trailing\" id=\"uRI-Kf-dTc\"/>\n                                                        </constraints>\n                                                    </view>\n                                                    <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ZNt-Qo-bzy\">\n                                                        <rect key=\"frame\" x=\"0.0\" y=\"30.5\" width=\"294\" height=\"29.5\"/>\n                                                        <subviews>\n                                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Time\" lineBreakMode=\"clip\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"9xE-93-8uD\" userLabel=\"Field Name Label\">\n                                                                <rect key=\"frame\" x=\"0.0\" y=\"5\" width=\"37\" height=\"19.5\"/>\n                                                                <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                                <color key=\"tintColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                                <fontDescription key=\"fontDescription\" type=\"system\" weight=\"medium\" pointSize=\"16\"/>\n                                                                <color key=\"textColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                                <color key=\"highlightedColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                            </label>\n                                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"100\" verticalHuggingPriority=\"251\" text=\"Info Label\" textAlignment=\"right\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Z8V-vn-27i\" userLabel=\"Info Label\">\n                                                                <rect key=\"frame\" x=\"223\" y=\"5\" width=\"71\" height=\"19.5\"/>\n                                                                <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                                <color key=\"tintColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"16\"/>\n                                                                <color key=\"textColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                                <color key=\"highlightedColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                            </label>\n                                                        </subviews>\n                                                        <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                        <constraints>\n                                                            <constraint firstItem=\"9xE-93-8uD\" firstAttribute=\"leading\" secondItem=\"ZNt-Qo-bzy\" secondAttribute=\"leading\" id=\"Mdv-Qi-ks3\"/>\n                                                            <constraint firstItem=\"9xE-93-8uD\" firstAttribute=\"centerY\" secondItem=\"ZNt-Qo-bzy\" secondAttribute=\"centerY\" id=\"Rcx-En-QqG\"/>\n                                                            <constraint firstItem=\"9xE-93-8uD\" firstAttribute=\"top\" secondItem=\"ZNt-Qo-bzy\" secondAttribute=\"top\" constant=\"5\" id=\"Vky-c9-kjW\"/>\n                                                            <constraint firstItem=\"Z8V-vn-27i\" firstAttribute=\"leading\" relation=\"greaterThanOrEqual\" secondItem=\"9xE-93-8uD\" secondAttribute=\"trailing\" constant=\"8\" symbolic=\"YES\" id=\"YmX-G5-qtM\"/>\n                                                            <constraint firstAttribute=\"trailing\" secondItem=\"Z8V-vn-27i\" secondAttribute=\"trailing\" id=\"aYF-wX-MSi\"/>\n                                                            <constraint firstItem=\"Z8V-vn-27i\" firstAttribute=\"top\" secondItem=\"ZNt-Qo-bzy\" secondAttribute=\"top\" constant=\"5\" id=\"ezT-pz-OLx\"/>\n                                                            <constraint firstItem=\"Z8V-vn-27i\" firstAttribute=\"centerY\" secondItem=\"ZNt-Qo-bzy\" secondAttribute=\"centerY\" id=\"mCA-g1-lWG\"/>\n                                                        </constraints>\n                                                    </view>\n                                                </subviews>\n                                                <constraints>\n                                                    <constraint firstAttribute=\"height\" constant=\"60\" id=\"7Ru-bn-JIL\"/>\n                                                </constraints>\n                                            </stackView>\n                                            <stackView opaque=\"NO\" contentMode=\"scaleToFill\" spacing=\"15\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"cOj-2t-SbP\" userLabel=\"Thread Stack View\">\n                                                <rect key=\"frame\" x=\"35\" y=\"70\" width=\"344\" height=\"30\"/>\n                                                <subviews>\n                                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" verticalHuggingPriority=\"251\" text=\"Threads\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"eX7-9U-WcH\">\n                                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"209\" height=\"30\"/>\n                                                        <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"16\"/>\n                                                        <color key=\"textColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                        <nil key=\"highlightedColor\"/>\n                                                    </label>\n                                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"4\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"bIb-UK-3tz\">\n                                                        <rect key=\"frame\" x=\"224\" y=\"0.0\" width=\"11\" height=\"30\"/>\n                                                        <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"16\"/>\n                                                        <color key=\"textColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                        <color key=\"highlightedColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                    </label>\n                                                    <stepper opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" continuous=\"NO\" wraps=\"YES\" value=\"4\" minimumValue=\"1\" maximumValue=\"8\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"P6P-6m-RWk\">\n                                                        <rect key=\"frame\" x=\"250\" y=\"0.0\" width=\"94\" height=\"30\"/>\n                                                        <color key=\"tintColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                        <connections>\n                                                            <action selector=\"threadStepperValueChanged:\" destination=\"BYZ-38-t0r\" eventType=\"valueChanged\" id=\"qTz-md-WV1\"/>\n                                                        </connections>\n                                                    </stepper>\n                                                </subviews>\n                                                <constraints>\n                                                    <constraint firstAttribute=\"height\" constant=\"30\" id=\"PmW-q1-hKU\"/>\n                                                </constraints>\n                                            </stackView>\n                                            <stackView opaque=\"NO\" contentMode=\"scaleToFill\" spacing=\"10\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"RXH-7X-MUd\" userLabel=\"Delegates Stack View\">\n                                                <rect key=\"frame\" x=\"35\" y=\"110\" width=\"344\" height=\"30\"/>\n                                                <subviews>\n                                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Delegates\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"gmc-69-mzQ\" userLabel=\"Delegates label\">\n                                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"78.5\" height=\"30\"/>\n                                                        <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"16\"/>\n                                                        <color key=\"textColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                        <nil key=\"highlightedColor\"/>\n                                                    </label>\n                                                    <segmentedControl opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"left\" contentVerticalAlignment=\"top\" segmentControlStyle=\"plain\" selectedSegmentIndex=\"0\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"kS9-M8-aNu\">\n                                                        <rect key=\"frame\" x=\"88.5\" y=\"0.0\" width=\"255.5\" height=\"31\"/>\n                                                        <segments>\n                                                            <segment title=\"First\"/>\n                                                            <segment title=\"Second\"/>\n                                                        </segments>\n                                                        <connections>\n                                                            <action selector=\"delegatesValueChanged:\" destination=\"BYZ-38-t0r\" eventType=\"valueChanged\" id=\"n3b-ee-BxE\"/>\n                                                        </connections>\n                                                    </segmentedControl>\n                                                </subviews>\n                                                <constraints>\n                                                    <constraint firstAttribute=\"height\" constant=\"30\" id=\"m5c-hD-g2s\"/>\n                                                </constraints>\n                                            </stackView>\n                                            <stackView opaque=\"NO\" contentMode=\"scaleToFill\" spacing=\"10\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"O6l-Da-TKb\" userLabel=\"Delegates Stack View\">\n                                                <rect key=\"frame\" x=\"35\" y=\"150\" width=\"344\" height=\"30\"/>\n                                                <subviews>\n                                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Model\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"kv2-Xh-Mva\" userLabel=\"Delegates label\">\n                                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"48\" height=\"30\"/>\n                                                        <constraints>\n                                                            <constraint firstAttribute=\"width\" relation=\"greaterThanOrEqual\" constant=\"48\" id=\"sII-wz-Rq9\"/>\n                                                        </constraints>\n                                                        <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"16\"/>\n                                                        <color key=\"textColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                        <nil key=\"highlightedColor\"/>\n                                                    </label>\n                                                    <segmentedControl opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"left\" contentVerticalAlignment=\"top\" segmentControlStyle=\"plain\" selectedSegmentIndex=\"0\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"NhD-WD-YrI\">\n                                                        <rect key=\"frame\" x=\"58\" y=\"0.0\" width=\"286\" height=\"31\"/>\n                                                        <segments>\n                                                            <segment title=\"First\"/>\n                                                            <segment title=\"Second\"/>\n                                                        </segments>\n                                                        <connections>\n                                                            <action selector=\"modelTypeValueChanged:\" destination=\"BYZ-38-t0r\" eventType=\"valueChanged\" id=\"VKh-Kl-lyb\"/>\n                                                        </connections>\n                                                    </segmentedControl>\n                                                </subviews>\n                                            </stackView>\n                                        </subviews>\n                                        <color key=\"backgroundColor\" red=\"0.0\" green=\"0.0\" blue=\"0.0\" alpha=\"0.80000000000000004\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                        <constraints>\n                                            <constraint firstAttribute=\"bottom\" secondItem=\"O6l-Da-TKb\" secondAttribute=\"bottom\" constant=\"20\" id=\"1v0-BG-xN5\"/>\n                                            <constraint firstItem=\"RXH-7X-MUd\" firstAttribute=\"leading\" secondItem=\"cOj-2t-SbP\" secondAttribute=\"leading\" id=\"3wP-lN-o3t\"/>\n                                            <constraint firstItem=\"fDz-Y6-sxX\" firstAttribute=\"top\" secondItem=\"nFg-A5-Dew\" secondAttribute=\"top\" constant=\"10\" id=\"7qw-yn-4XQ\"/>\n                                            <constraint firstItem=\"fDz-Y6-sxX\" firstAttribute=\"leading\" secondItem=\"nFg-A5-Dew\" secondAttribute=\"leading\" constant=\"60\" id=\"8qo-5B-Uaq\"/>\n                                            <constraint firstItem=\"cOj-2t-SbP\" firstAttribute=\"top\" secondItem=\"fDz-Y6-sxX\" secondAttribute=\"bottom\" id=\"Cfn-QH-yYe\"/>\n                                            <constraint firstItem=\"RXH-7X-MUd\" firstAttribute=\"trailing\" secondItem=\"cOj-2t-SbP\" secondAttribute=\"trailing\" id=\"Ej9-gR-bWk\"/>\n                                            <constraint firstItem=\"O6l-Da-TKb\" firstAttribute=\"top\" secondItem=\"RXH-7X-MUd\" secondAttribute=\"bottom\" constant=\"10\" id=\"Hjo-KN-apm\"/>\n                                            <constraint firstItem=\"cOj-2t-SbP\" firstAttribute=\"width\" secondItem=\"nFg-A5-Dew\" secondAttribute=\"width\" constant=\"-70\" id=\"VyB-yT-Tey\"/>\n                                            <constraint firstItem=\"O6l-Da-TKb\" firstAttribute=\"height\" secondItem=\"RXH-7X-MUd\" secondAttribute=\"height\" id=\"YEw-aC-08w\"/>\n                                            <constraint firstAttribute=\"bottom\" relation=\"greaterThanOrEqual\" secondItem=\"cOj-2t-SbP\" secondAttribute=\"bottom\" priority=\"250\" constant=\"30\" id=\"YjT-kc-3Yi\"/>\n                                            <constraint firstItem=\"O6l-Da-TKb\" firstAttribute=\"leading\" secondItem=\"RXH-7X-MUd\" secondAttribute=\"leading\" id=\"dUk-Ia-eej\"/>\n                                            <constraint firstAttribute=\"trailing\" secondItem=\"fDz-Y6-sxX\" secondAttribute=\"trailing\" constant=\"60\" id=\"hrd-nD-5ft\"/>\n                                            <constraint firstItem=\"cOj-2t-SbP\" firstAttribute=\"bottom\" secondItem=\"RXH-7X-MUd\" secondAttribute=\"top\" constant=\"-10\" id=\"npw-gf-o7m\"/>\n                                            <constraint firstItem=\"O6l-Da-TKb\" firstAttribute=\"trailing\" secondItem=\"RXH-7X-MUd\" secondAttribute=\"trailing\" id=\"qOk-zn-Btc\"/>\n                                            <constraint firstItem=\"cOj-2t-SbP\" firstAttribute=\"centerX\" secondItem=\"nFg-A5-Dew\" secondAttribute=\"centerX\" id=\"xwh-fU-JP1\"/>\n                                        </constraints>\n                                    </view>\n                                </subviews>\n                                <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                <constraints>\n                                    <constraint firstItem=\"tWv-pt-h17\" firstAttribute=\"leading\" secondItem=\"aEU-Ov-crs\" secondAttribute=\"leading\" id=\"1Jc-ch-65t\"/>\n                                    <constraint firstItem=\"nFg-A5-Dew\" firstAttribute=\"leading\" secondItem=\"aEU-Ov-crs\" secondAttribute=\"leading\" id=\"2aq-VQ-8jy\"/>\n                                    <constraint firstItem=\"lSZ-59-BsY\" firstAttribute=\"leading\" secondItem=\"aEU-Ov-crs\" secondAttribute=\"leading\" id=\"5UZ-IH-kFP\"/>\n                                    <constraint firstItem=\"tWv-pt-h17\" firstAttribute=\"top\" secondItem=\"aEU-Ov-crs\" secondAttribute=\"top\" id=\"7aI-Bb-gUy\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"lSZ-59-BsY\" secondAttribute=\"trailing\" id=\"CcL-YT-FV0\"/>\n                                    <constraint firstItem=\"nFg-A5-Dew\" firstAttribute=\"top\" secondItem=\"lSZ-59-BsY\" secondAttribute=\"bottom\" id=\"Y80-50-k9U\"/>\n                                    <constraint firstItem=\"tWv-pt-h17\" firstAttribute=\"width\" secondItem=\"aEU-Ov-crs\" secondAttribute=\"width\" id=\"Yni-oR-3Bk\"/>\n                                    <constraint firstItem=\"lSZ-59-BsY\" firstAttribute=\"top\" secondItem=\"tWv-pt-h17\" secondAttribute=\"bottom\" id=\"dHx-Mh-6zV\"/>\n                                    <constraint firstAttribute=\"bottom\" secondItem=\"nFg-A5-Dew\" secondAttribute=\"bottom\" id=\"dxP-KD-5bd\"/>\n                                    <constraint firstItem=\"nFg-A5-Dew\" firstAttribute=\"width\" secondItem=\"aEU-Ov-crs\" secondAttribute=\"width\" id=\"peY-3I-8cV\"/>\n                                </constraints>\n                            </view>\n                        </subviews>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <constraints>\n                            <constraint firstItem=\"aEU-Ov-crs\" firstAttribute=\"bottom\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"bottom\" id=\"3OD-6W-uRh\"/>\n                            <constraint firstItem=\"RXH-7X-MUd\" firstAttribute=\"centerX\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"centerX\" id=\"Hiz-DI-CwX\"/>\n                            <constraint firstItem=\"aEU-Ov-crs\" firstAttribute=\"leading\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"leading\" id=\"e9c-LL-cJf\"/>\n                            <constraint firstItem=\"aEU-Ov-crs\" firstAttribute=\"top\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"top\" id=\"eyF-LV-5Jb\"/>\n                            <constraint firstItem=\"aEU-Ov-crs\" firstAttribute=\"trailing\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"trailing\" id=\"g7I-ct-vQc\"/>\n                            <constraint firstItem=\"tWv-pt-h17\" firstAttribute=\"bottom\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"top\" constant=\"75\" id=\"oFt-dT-p0b\"/>\n                        </constraints>\n                    </view>\n                    <connections>\n                        <outlet property=\"delegatesSegmentedControl\" destination=\"kS9-M8-aNu\" id=\"F98-u6-anR\"/>\n                        <outlet property=\"modelSegmentedControl\" destination=\"NhD-WD-YrI\" id=\"LPJ-fl-SWY\"/>\n                        <outlet property=\"overlayView\" destination=\"lSZ-59-BsY\" id=\"HUU-CT-hZQ\"/>\n                        <outlet property=\"scoreLabel\" destination=\"ZTc-Zz-fNr\" id=\"c3N-ji-KDy\"/>\n                        <outlet property=\"threadStepper\" destination=\"P6P-6m-RWk\" id=\"Gbm-hz-MHL\"/>\n                        <outlet property=\"threadStepperLabel\" destination=\"bIb-UK-3tz\" id=\"SE3-JZ-cyl\"/>\n                        <outlet property=\"totalTimeLabel\" destination=\"Z8V-vn-27i\" id=\"pgw-z5-8Ky\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"137.68115942028987\" y=\"137.94642857142856\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"tfl_logo\" width=\"265.5\" height=\"42.5\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/PoseEstimation/UI/ViewControllers/ViewController.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n\nimport AVFoundation\nimport UIKit\nimport os\n\nfinal class ViewController: UIViewController {\n\n  // MARK: Storyboards Connections\n  @IBOutlet private weak var overlayView: OverlayView!\n  @IBOutlet private weak var threadStepperLabel: UILabel!\n  @IBOutlet private weak var threadStepper: UIStepper!\n  @IBOutlet private weak var totalTimeLabel: UILabel!\n  @IBOutlet private weak var scoreLabel: UILabel!\n  @IBOutlet private weak var delegatesSegmentedControl: UISegmentedControl!\n  @IBOutlet private weak var modelSegmentedControl: UISegmentedControl!\n\n  // MARK: Pose estimation model configs\n  private var modelType: ModelType = Constants.defaultModelType\n  private var threadCount: Int = Constants.defaultThreadCount\n  private var delegate: Delegates = Constants.defaultDelegate\n  private let minimumScore = Constants.minimumScore\n\n  // MARK: Visualization\n  // Relative location of `overlayView` to `previewView`.\n  private var imageViewFrame: CGRect?\n  // Input image overlaid with the detected keypoints.\n  var overlayImage: OverlayView?\n\n  // MARK: Controllers that manage functionality\n  // Handles all data preprocessing and makes calls to run inference.\n  private var poseEstimator: PoseEstimator?\n  private var cameraFeedManager: CameraFeedManager!\n\n  // Serial queue to control all tasks related to the TFLite model.\n  let queue = DispatchQueue(label: \"serial_queue\")\n\n  // Flag to make sure there's only one frame processed at each moment.\n  var isRunning = false\n\n  // MARK: View Handling Methods\n  override func viewDidLoad() {\n    super.viewDidLoad()\n    configSegmentedControl()\n    configStepper()\n    updateModel()\n    configCameraCapture()\n  }\n\n  override func viewWillAppear(_ animated: Bool) {\n    super.viewWillAppear(animated)\n    cameraFeedManager?.startRunning()\n  }\n\n  override func viewWillDisappear(_ animated: Bool) {\n    super.viewWillDisappear(animated)\n    cameraFeedManager?.stopRunning()\n  }\n\n  override func viewDidLayoutSubviews() {\n    super.viewDidLayoutSubviews()\n    imageViewFrame = overlayView.frame\n  }\n\n  private func configCameraCapture() {\n    cameraFeedManager = CameraFeedManager()\n    cameraFeedManager.startRunning()\n    cameraFeedManager.delegate = self\n  }\n\n  private func configStepper() {\n    threadStepper.value = Double(threadCount)\n    threadStepper.setDecrementImage(threadStepper.decrementImage(for: .normal), for: .normal)\n    threadStepper.setIncrementImage(threadStepper.incrementImage(for: .normal), for: .normal)\n  }\n\n  private func configSegmentedControl() {\n    // Set title for device control\n    delegatesSegmentedControl.setTitleTextAttributes(\n      [NSAttributedString.Key.foregroundColor: UIColor.lightGray],\n      for: .normal)\n    delegatesSegmentedControl.setTitleTextAttributes(\n      [NSAttributedString.Key.foregroundColor: UIColor.black],\n      for: .selected)\n    // Remove existing segments to initialize it with `Delegates` entries.\n    delegatesSegmentedControl.removeAllSegments()\n    var defaultDelegateIndex = 0\n    Delegates.allCases.enumerated().forEach { (index, eachDelegate) in\n      if eachDelegate == delegate {\n        defaultDelegateIndex = index\n      }\n      delegatesSegmentedControl.insertSegment(\n        withTitle: eachDelegate.rawValue,\n        at: index,\n        animated: false)\n    }\n    delegatesSegmentedControl.selectedSegmentIndex = defaultDelegateIndex\n\n    // Config model segment attributed\n    modelSegmentedControl.setTitleTextAttributes(\n      [NSAttributedString.Key.foregroundColor: UIColor.lightGray],\n      for: .normal)\n    modelSegmentedControl.setTitleTextAttributes(\n      [NSAttributedString.Key.foregroundColor: UIColor.black],\n      for: .selected)\n    // Remove existing segments to initialize it with `Delegates` entries.\n    modelSegmentedControl.removeAllSegments()\n    var defaultModelTypeIndex = 0\n    ModelType.allCases.enumerated().forEach { (index, eachModelType) in\n      if eachModelType == modelType {\n        defaultModelTypeIndex = index\n      }\n      modelSegmentedControl.insertSegment(\n        withTitle: eachModelType.rawValue,\n        at: index,\n        animated: false)\n    }\n    modelSegmentedControl.selectedSegmentIndex = defaultModelTypeIndex\n  }\n\n  /// Call this method when there's change in pose estimation model config, including changing model\n  /// or updating runtime config.\n  private func updateModel() {\n    // Update the model in the same serial queue with the inference logic to avoid race condition\n    queue.async {\n      do {\n        switch self.modelType {\n        case .posenet:\n          self.poseEstimator = try PoseNet(\n            threadCount: self.threadCount,\n            delegate: self.delegate)\n        case .movenetLighting, .movenetThunder:\n          self.poseEstimator = try MoveNet(\n            threadCount: self.threadCount,\n            delegate: self.delegate,\n            modelType: self.modelType)\n        }\n      } catch let error {\n        os_log(\"Error: %@\", log: .default, type: .error, String(describing: error))\n      }\n    }\n  }\n\n  @IBAction private func threadStepperValueChanged(_ sender: UIStepper) {\n    threadCount = Int(sender.value)\n    threadStepperLabel.text = \"\\(threadCount)\"\n    updateModel()\n  }\n  @IBAction private func delegatesValueChanged(_ sender: UISegmentedControl) {\n    delegate = Delegates.allCases[sender.selectedSegmentIndex]\n    updateModel()\n  }\n\n  @IBAction private func modelTypeValueChanged(_ sender: UISegmentedControl) {\n    modelType = ModelType.allCases[sender.selectedSegmentIndex]\n    updateModel()\n  }\n}\n\n// MARK: - CameraFeedManagerDelegate Methods\nextension ViewController: CameraFeedManagerDelegate {\n  func cameraFeedManager(\n    _ cameraFeedManager: CameraFeedManager, didOutput pixelBuffer: CVPixelBuffer\n  ) {\n    self.runModel(pixelBuffer)\n  }\n\n  /// Run pose estimation on the input frame from the camera.\n  private func runModel(_ pixelBuffer: CVPixelBuffer) {\n    // Guard to make sure that there's only 1 frame process at each moment.\n    guard !isRunning else { return }\n\n    // Guard to make sure that the pose estimator is already initialized.\n    guard let estimator = poseEstimator else { return }\n\n    // Run inference on a serial queue to avoid race condition.\n    queue.async {\n      self.isRunning = true\n      defer { self.isRunning = false }\n\n      // Run pose estimation\n      do {\n        let (result, times) = try estimator.estimateSinglePose(\n            on: pixelBuffer)\n\n        // Return to main thread to show detection results on the app UI.\n        DispatchQueue.main.async {\n          self.totalTimeLabel.text = String(format: \"%.2fms\",\n                                            times.total * 1000)\n          self.scoreLabel.text = String(format: \"%.3f\", result.score)\n\n          // Allowed to set image and overlay\n          let image = UIImage(ciImage: CIImage(cvPixelBuffer: pixelBuffer))\n\n          // If score is too low, clear result remaining in the overlayView.\n          if result.score < self.minimumScore {\n            self.overlayView.image = image\n            return\n          }\n\n          // Visualize the pose estimation result.\n          self.overlayView.draw(at: image, person: result)\n        }\n      } catch {\n        os_log(\"Error running pose estimation.\", type: .error)\n        return\n      }\n    }\n  }\n}\n\nenum Constants {\n  // Configs for the TFLite interpreter.\n  static let defaultThreadCount = 4\n  static let defaultDelegate: Delegates = .gpu\n  static let defaultModelType: ModelType = .movenetThunder\n\n  // Minimum score to render the result.\n  static let minimumScore: Float32 = 0.2\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/PoseEstimation/UI/Views/OverlayView.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n\nimport UIKit\nimport os\n\n/// Custom view to visualize the pose estimation result on top of the input image.\nclass OverlayView: UIImageView {\n\n  /// Visualization configs\n  private enum Config {\n    static let dot = (radius: CGFloat(10), color: UIColor.orange)\n    static let line = (width: CGFloat(5.0), color: UIColor.orange)\n  }\n\n  /// List of lines connecting each part to be visualized.\n  private static let lines = [\n    (from: BodyPart.leftWrist, to: BodyPart.leftElbow),\n    (from: BodyPart.leftElbow, to: BodyPart.leftShoulder),\n    (from: BodyPart.leftShoulder, to: BodyPart.rightShoulder),\n    (from: BodyPart.rightShoulder, to: BodyPart.rightElbow),\n    (from: BodyPart.rightElbow, to: BodyPart.rightWrist),\n    (from: BodyPart.leftShoulder, to: BodyPart.leftHip),\n    (from: BodyPart.leftHip, to: BodyPart.rightHip),\n    (from: BodyPart.rightHip, to: BodyPart.rightShoulder),\n    (from: BodyPart.leftHip, to: BodyPart.leftKnee),\n    (from: BodyPart.leftKnee, to: BodyPart.leftAnkle),\n    (from: BodyPart.rightHip, to: BodyPart.rightKnee),\n    (from: BodyPart.rightKnee, to: BodyPart.rightAnkle),\n  ]\n\n  /// CGContext to draw the detection result.\n  var context: CGContext!\n\n  /// Draw the detected keypoints on top of the input image.\n  ///\n  /// - Parameters:\n  ///     - image: The input image.\n  ///     - person: Keypoints of the person detected (i.e. output of a pose estimation model)\n  func draw(at image: UIImage, person: Person) {\n    if context == nil {\n      UIGraphicsBeginImageContext(image.size)\n      guard let context = UIGraphicsGetCurrentContext() else {\n        fatalError(\"set current context faild\")\n      }\n      self.context = context\n    }\n    guard let strokes = strokes(from: person) else { return }\n    image.draw(at: .zero)\n    context.setLineWidth(Config.dot.radius)\n    drawDots(at: context, dots: strokes.dots)\n    drawLines(at: context, lines: strokes.lines)\n    context.setStrokeColor(UIColor.blue.cgColor)\n    context.strokePath()\n    guard let newImage = UIGraphicsGetImageFromCurrentImageContext() else { fatalError() }\n    self.image = newImage\n  }\n\n  /// Draw the dots (i.e. keypoints).\n  ///\n  /// - Parameters:\n  ///     - context: The context to be drawn on.\n  ///     - dots: The list of dots to be drawn.\n  private func drawDots(at context: CGContext, dots: [CGPoint]) {\n    for dot in dots {\n      let dotRect = CGRect(\n        x: dot.x - Config.dot.radius / 2, y: dot.y - Config.dot.radius / 2,\n        width: Config.dot.radius, height: Config.dot.radius)\n      let path = CGPath(\n        roundedRect: dotRect, cornerWidth: Config.dot.radius, cornerHeight: Config.dot.radius,\n        transform: nil)\n      context.addPath(path)\n    }\n  }\n\n  /// Draw the lines (i.e. conneting the keypoints).\n  ///\n  /// - Parameters:\n  ///     - context: The context to be drawn on.\n  ///     - lines: The list of lines to be drawn.\n  private func drawLines(at context: CGContext, lines: [Line]) {\n    for line in lines {\n      context.move(to: CGPoint(x: line.from.x, y: line.from.y))\n      context.addLine(to: CGPoint(x: line.to.x, y: line.to.y))\n    }\n  }\n\n  /// Generate a list of strokes to draw in order to visualize the pose estimation result.\n  ///\n  /// - Parameters:\n  ///     - person: The detected person (i.e. output of a pose estimation model).\n  private func strokes(from person: Person) -> Strokes? {\n    var strokes = Strokes(dots: [], lines: [])\n    // MARK: Visualization of detection result\n    var bodyPartToDotMap: [BodyPart: CGPoint] = [:]\n    for (index, part) in BodyPart.allCases.enumerated() {\n      let position = CGPoint(\n        x: person.keyPoints[index].coordinate.x,\n        y: person.keyPoints[index].coordinate.y)\n      bodyPartToDotMap[part] = position\n      strokes.dots.append(position)\n    }\n\n    do {\n      try strokes.lines = OverlayView.lines.map { map throws -> Line in\n        guard let from = bodyPartToDotMap[map.from] else {\n          throw VisualizationError.missingBodyPart(of: map.from)\n        }\n        guard let to = bodyPartToDotMap[map.to] else {\n          throw VisualizationError.missingBodyPart(of: map.to)\n        }\n        return Line(from: from, to: to)\n      }\n    } catch VisualizationError.missingBodyPart(let missingPart) {\n      os_log(\"Visualization error: %s is missing.\", type: .error, missingPart.rawValue)\n      return nil\n    } catch {\n      os_log(\"Visualization error: %s\", type: .error, error.localizedDescription)\n      return nil\n    }\n    return strokes\n  }\n}\n\n/// The strokes to be drawn in order to visualize a pose estimation result.\nfileprivate struct Strokes {\n  var dots: [CGPoint]\n  var lines: [Line]\n}\n\n/// A straight line.\nfileprivate struct Line {\n  let from: CGPoint\n  let to: CGPoint\n}\n\nfileprivate enum VisualizationError: Error {\n  case missingBodyPart(of: BodyPart)\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/PoseEstimation.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t0F1854E826A95F48004780E1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0F1854CC26A95F48004780E1 /* Main.storyboard */; };\n\t\t0F1854E926A95F48004780E1 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0F1854CE26A95F48004780E1 /* Launch Screen.storyboard */; };\n\t\t0F1854EB26A95F48004780E1 /* CameraFeedManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F1854D226A95F48004780E1 /* CameraFeedManager.swift */; };\n\t\t0F1854EC26A95F48004780E1 /* MoveNet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F1854D426A95F48004780E1 /* MoveNet.swift */; };\n\t\t0F1854ED26A95F48004780E1 /* PoseNet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F1854D526A95F48004780E1 /* PoseNet.swift */; };\n\t\t0F1854EE26A95F48004780E1 /* PoseEstimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F1854D626A95F48004780E1 /* PoseEstimator.swift */; };\n\t\t0F1854EF26A95F48004780E1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0F1854D726A95F48004780E1 /* Assets.xcassets */; };\n\t\t0F1854F026A95F48004780E1 /* CGSize+TFLite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F1854D926A95F48004780E1 /* CGSize+TFLite.swift */; };\n\t\t0F1854F226A95F48004780E1 /* CVPixelBuffer+TFLite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F1854DB26A95F48004780E1 /* CVPixelBuffer+TFLite.swift */; };\n\t\t0F1854F326A95F48004780E1 /* Data+TFLite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F1854DC26A95F48004780E1 /* Data+TFLite.swift */; };\n\t\t0F1854F426A95F48004780E1 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F1854DE26A95F48004780E1 /* ViewController.swift */; };\n\t\t0F1854F826A95F48004780E1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F1854E326A95F48004780E1 /* AppDelegate.swift */; };\n\t\t0F1854F926A95F48004780E1 /* OverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F1854E526A95F48004780E1 /* OverlayView.swift */; };\n\t\t33CECB32BE623AB8D720B3EA /* Pods_PoseEstimation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A08F93117A75BFCA9BB9C0F /* Pods_PoseEstimation.framework */; };\n\t\t7FB1192B26EA499500B86E0A /* PoseData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FB1192926EA499500B86E0A /* PoseData.swift */; };\n\t\t7FB1192C26EA499500B86E0A /* PoseConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FB1192A26EA499500B86E0A /* PoseConfig.swift */; };\n\t\tF172070B26D38C8900BB91D2 /* movenet_lightning.tflite in Resources */ = {isa = PBXBuildFile; fileRef = F172070926D38C3200BB91D2 /* movenet_lightning.tflite */; };\n\t\tF172070C26D38C8900BB91D2 /* movenet_thunder.tflite in Resources */ = {isa = PBXBuildFile; fileRef = F172070A26D38C3200BB91D2 /* movenet_thunder.tflite */; };\n\t\tF172070D26D38C8900BB91D2 /* posenet.tflite in Resources */ = {isa = PBXBuildFile; fileRef = F172070826D38C3200BB91D2 /* posenet.tflite */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\t0F1854CD26A95F48004780E1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t0F1854CF26A95F48004780E1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = \"Base.lproj/Launch Screen.storyboard\"; sourceTree = \"<group>\"; };\n\t\t0F1854D226A95F48004780E1 /* CameraFeedManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraFeedManager.swift; sourceTree = \"<group>\"; };\n\t\t0F1854D426A95F48004780E1 /* MoveNet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoveNet.swift; sourceTree = \"<group>\"; };\n\t\t0F1854D526A95F48004780E1 /* PoseNet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PoseNet.swift; sourceTree = \"<group>\"; };\n\t\t0F1854D626A95F48004780E1 /* PoseEstimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PoseEstimator.swift; sourceTree = \"<group>\"; };\n\t\t0F1854D726A95F48004780E1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t0F1854D926A95F48004780E1 /* CGSize+TFLite.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"CGSize+TFLite.swift\"; sourceTree = \"<group>\"; };\n\t\t0F1854DB26A95F48004780E1 /* CVPixelBuffer+TFLite.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"CVPixelBuffer+TFLite.swift\"; sourceTree = \"<group>\"; };\n\t\t0F1854DC26A95F48004780E1 /* Data+TFLite.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"Data+TFLite.swift\"; sourceTree = \"<group>\"; };\n\t\t0F1854DE26A95F48004780E1 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = \"<group>\"; };\n\t\t0F1854E326A95F48004780E1 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t0F1854E526A95F48004780E1 /* OverlayView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OverlayView.swift; sourceTree = \"<group>\"; };\n\t\t0F1854E626A95F48004780E1 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t515FF91A588EB715C5E320A7 /* Pods-PoseEstimation.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-PoseEstimation.release.xcconfig\"; path = \"Target Support Files/Pods-PoseEstimation/Pods-PoseEstimation.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t737F4A896F2232015436950C /* Pods-PoseEstimation.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-PoseEstimation.debug.xcconfig\"; path = \"Target Support Files/Pods-PoseEstimation/Pods-PoseEstimation.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t7FB1192926EA499500B86E0A /* PoseData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PoseData.swift; sourceTree = \"<group>\"; };\n\t\t7FB1192A26EA499500B86E0A /* PoseConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PoseConfig.swift; sourceTree = \"<group>\"; };\n\t\t84B67CEB2326338300A11A08 /* PoseEstimation.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PoseEstimation.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t9A08F93117A75BFCA9BB9C0F /* Pods_PoseEstimation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PoseEstimation.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tF172070826D38C3200BB91D2 /* posenet.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = posenet.tflite; sourceTree = \"<group>\"; };\n\t\tF172070926D38C3200BB91D2 /* movenet_lightning.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = movenet_lightning.tflite; sourceTree = \"<group>\"; };\n\t\tF172070A26D38C3200BB91D2 /* movenet_thunder.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = movenet_thunder.tflite; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t84B67CE82326338300A11A08 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t33CECB32BE623AB8D720B3EA /* Pods_PoseEstimation.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t0F1854C926A95F48004780E1 /* PoseEstimation */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t0F1854E326A95F48004780E1 /* AppDelegate.swift */,\n\t\t\t\t0F1854D726A95F48004780E1 /* Assets.xcassets */,\n\t\t\t\t0F1854D026A95F48004780E1 /* Camera */,\n\t\t\t\t0F1854E626A95F48004780E1 /* Info.plist */,\n\t\t\t\t7F0CE7FD26DF6A1600CC7BC7 /* ML */,\n\t\t\t\t7F0CE7FC26DF69E500CC7BC7 /* UI */,\n\t\t\t);\n\t\t\tpath = PoseEstimation;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t0F1854CB26A95F48004780E1 /* Storyboards */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t0F1854CC26A95F48004780E1 /* Main.storyboard */,\n\t\t\t\t0F1854CE26A95F48004780E1 /* Launch Screen.storyboard */,\n\t\t\t);\n\t\t\tpath = Storyboards;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t0F1854D026A95F48004780E1 /* Camera */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t0F1854D226A95F48004780E1 /* CameraFeedManager.swift */,\n\t\t\t);\n\t\t\tpath = Camera;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t0F1854D826A95F48004780E1 /* Extensions */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t0F1854D926A95F48004780E1 /* CGSize+TFLite.swift */,\n\t\t\t\t0F1854DB26A95F48004780E1 /* CVPixelBuffer+TFLite.swift */,\n\t\t\t\t0F1854DC26A95F48004780E1 /* Data+TFLite.swift */,\n\t\t\t);\n\t\t\tpath = Extensions;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t0F1854DD26A95F48004780E1 /* ViewControllers */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t0F1854DE26A95F48004780E1 /* ViewController.swift */,\n\t\t\t);\n\t\t\tpath = ViewControllers;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t0F1854DF26A95F48004780E1 /* Models */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF172070926D38C3200BB91D2 /* movenet_lightning.tflite */,\n\t\t\t\tF172070A26D38C3200BB91D2 /* movenet_thunder.tflite */,\n\t\t\t\tF172070826D38C3200BB91D2 /* posenet.tflite */,\n\t\t\t\t7FB1192A26EA499500B86E0A /* PoseConfig.swift */,\n\t\t\t\t7FB1192926EA499500B86E0A /* PoseData.swift */,\n\t\t\t\t0F1854D426A95F48004780E1 /* MoveNet.swift */,\n\t\t\t\t0F1854D526A95F48004780E1 /* PoseNet.swift */,\n\t\t\t\t0F1854D626A95F48004780E1 /* PoseEstimator.swift */,\n\t\t\t);\n\t\t\tpath = Models;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t0F1854E426A95F48004780E1 /* Views */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t0F1854E526A95F48004780E1 /* OverlayView.swift */,\n\t\t\t);\n\t\t\tpath = Views;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7F0CE7FC26DF69E500CC7BC7 /* UI */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t0F1854CB26A95F48004780E1 /* Storyboards */,\n\t\t\t\t0F1854DD26A95F48004780E1 /* ViewControllers */,\n\t\t\t\t0F1854E426A95F48004780E1 /* Views */,\n\t\t\t);\n\t\t\tpath = UI;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7F0CE7FD26DF6A1600CC7BC7 /* ML */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t0F1854D826A95F48004780E1 /* Extensions */,\n\t\t\t\t0F1854DF26A95F48004780E1 /* Models */,\n\t\t\t);\n\t\t\tpath = ML;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t84B67CE22326338300A11A08 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t0F1854C926A95F48004780E1 /* PoseEstimation */,\n\t\t\t\t84B67CEC2326338300A11A08 /* Products */,\n\t\t\t\tB4DFDCC28443B641BC36251D /* Pods */,\n\t\t\t\tA3DA804B8D3F6891E3A02852 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t84B67CEC2326338300A11A08 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t84B67CEB2326338300A11A08 /* PoseEstimation.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tA3DA804B8D3F6891E3A02852 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9A08F93117A75BFCA9BB9C0F /* Pods_PoseEstimation.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tB4DFDCC28443B641BC36251D /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t737F4A896F2232015436950C /* Pods-PoseEstimation.debug.xcconfig */,\n\t\t\t\t515FF91A588EB715C5E320A7 /* Pods-PoseEstimation.release.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t84B67CEA2326338300A11A08 /* PoseEstimation */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 84B67CFD2326338400A11A08 /* Build configuration list for PBXNativeTarget \"PoseEstimation\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t14067F3CF309C9DB723C9F6F /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t7FB1192826E5F56700B86E0A /* Download TFLite Models */,\n\t\t\t\t84B67CE72326338300A11A08 /* Sources */,\n\t\t\t\t84B67CE82326338300A11A08 /* Frameworks */,\n\t\t\t\t84B67CE92326338300A11A08 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = PoseEstimation;\n\t\t\tproductName = PoseNet;\n\t\t\tproductReference = 84B67CEB2326338300A11A08 /* PoseEstimation.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t84B67CE32326338300A11A08 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 1030;\n\t\t\t\tLastUpgradeCheck = 1250;\n\t\t\t\tORGANIZATIONNAME = tensorflow;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t84B67CEA2326338300A11A08 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 10.3;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 84B67CE62326338300A11A08 /* Build configuration list for PBXProject \"PoseEstimation\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 84B67CE22326338300A11A08;\n\t\t\tproductRefGroup = 84B67CEC2326338300A11A08 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t84B67CEA2326338300A11A08 /* PoseEstimation */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t84B67CE92326338300A11A08 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF172070B26D38C8900BB91D2 /* movenet_lightning.tflite in Resources */,\n\t\t\t\tF172070C26D38C8900BB91D2 /* movenet_thunder.tflite in Resources */,\n\t\t\t\tF172070D26D38C8900BB91D2 /* posenet.tflite in Resources */,\n\t\t\t\t0F1854E926A95F48004780E1 /* Launch Screen.storyboard in Resources */,\n\t\t\t\t0F1854EF26A95F48004780E1 /* Assets.xcassets in Resources */,\n\t\t\t\t0F1854E826A95F48004780E1 /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t14067F3CF309C9DB723C9F6F /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-PoseEstimation-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t7FB1192826E5F56700B86E0A /* Download TFLite Models */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Download TFLite Models\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"/bin/sh \\\"$SRCROOT/RunScripts/download_models.sh\\\"\\n\";\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t84B67CE72326338300A11A08 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t7FB1192B26EA499500B86E0A /* PoseData.swift in Sources */,\n\t\t\t\t0F1854F826A95F48004780E1 /* AppDelegate.swift in Sources */,\n\t\t\t\t0F1854EE26A95F48004780E1 /* PoseEstimator.swift in Sources */,\n\t\t\t\t0F1854F226A95F48004780E1 /* CVPixelBuffer+TFLite.swift in Sources */,\n\t\t\t\t0F1854F026A95F48004780E1 /* CGSize+TFLite.swift in Sources */,\n\t\t\t\t0F1854F426A95F48004780E1 /* ViewController.swift in Sources */,\n\t\t\t\t0F1854EB26A95F48004780E1 /* CameraFeedManager.swift in Sources */,\n\t\t\t\t0F1854ED26A95F48004780E1 /* PoseNet.swift in Sources */,\n\t\t\t\t7FB1192C26EA499500B86E0A /* PoseConfig.swift in Sources */,\n\t\t\t\t0F1854F926A95F48004780E1 /* OverlayView.swift in Sources */,\n\t\t\t\t0F1854F326A95F48004780E1 /* Data+TFLite.swift in Sources */,\n\t\t\t\t0F1854EC26A95F48004780E1 /* MoveNet.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\t0F1854CC26A95F48004780E1 /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t0F1854CD26A95F48004780E1 /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t0F1854CE26A95F48004780E1 /* Launch Screen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t0F1854CF26A95F48004780E1 /* Base */,\n\t\t\t);\n\t\t\tname = \"Launch Screen.storyboard\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t84B67CFB2326338400A11A08 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.4;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t84B67CFC2326338400A11A08 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.4;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t84B67CFE2326338400A11A08 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 737F4A896F2232015436950C /* Pods-PoseEstimation.debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_IDENTITY = \"Apple Development\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tINFOPLIST_FILE = PoseEstimation/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.tensorflow.PoseEstimation;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t84B67CFF2326338400A11A08 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 515FF91A588EB715C5E320A7 /* Pods-PoseEstimation.release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_IDENTITY = \"Apple Development\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tINFOPLIST_FILE = PoseEstimation/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.tensorflow.PoseEstimation;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t84B67CE62326338300A11A08 /* Build configuration list for PBXProject \"PoseEstimation\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t84B67CFB2326338400A11A08 /* Debug */,\n\t\t\t\t84B67CFC2326338400A11A08 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t84B67CFD2326338400A11A08 /* Build configuration list for PBXNativeTarget \"PoseEstimation\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t84B67CFE2326338400A11A08 /* Debug */,\n\t\t\t\t84B67CFF2326338400A11A08 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 84B67CE32326338300A11A08 /* Project object */;\n}\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/PoseEstimation.xcodeproj/xcshareddata/xcschemes/PoseEstimation.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1250\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"84B67CEA2326338300A11A08\"\n               BuildableName = \"PoseEstimation.app\"\n               BlueprintName = \"PoseEstimation\"\n               ReferencedContainer = \"container:PoseEstimation.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"84B67CEA2326338300A11A08\"\n            BuildableName = \"PoseEstimation.app\"\n            BlueprintName = \"PoseEstimation\"\n            ReferencedContainer = \"container:PoseEstimation.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n      <EnvironmentVariables>\n         <EnvironmentVariable\n            key = \"CG_NUMERICS_SHOW_BACKTRACE\"\n            value = \"YES\"\n            isEnabled = \"YES\">\n         </EnvironmentVariable>\n      </EnvironmentVariables>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"84B67CEA2326338300A11A08\"\n            BuildableName = \"PoseEstimation.app\"\n            BlueprintName = \"PoseEstimation\"\n            ReferencedContainer = \"container:PoseEstimation.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/README.md",
    "content": "# TensorFlow Lite Pose Estimation iOS Demo\n\n### Overview\n\nThis is an app that continuously detects the human body parts in the frames seen\nby your device's camera. These instructions walk you through building and\nrunning the demo on an iOS device. Camera captures are discarded immediately\nafter use, nothing is stored or saved.\n\nThe app demonstrates how to use 3 models:\n* Posenet\n* Movenet Lightning\n* Movenet Thunder\n\nSee this\n[blog post](https://blog.tensorflow.org/2021/08/pose-estimation-and-classification-on-edge-devices-with-MoveNet-and-TensorFlow-Lite.html)\nfor a comparison between these models.\n\n![Demo image](https://storage.googleapis.com/download.tensorflow.org/models/tflite/screenshots/posenet_ios_demo.gif)\n\n## Build the demo using Xcode\n\n### Prerequisites\n\n*   [Xcode](https://developer.apple.com/xcode/) 12.5 or later\n*   A valid Apple Developer ID\n*   Real iOS device with camera\n*   iOS version 12.4 or above\n*   Xcode command line tools (to install, run `xcode-select --install`)\n*   CocoaPods (to install, run `sudo gem install cocoapods`)\n\n### Build and run the app\n\n1.  Clone the TensorFlow examples GitHub repository to your computer to get the\n    demo application.\n\n    ```\n    git clone https://github.com/tensorflow/examples\n    ```\n\n1.  Install the pod to generate the workspace file:\n\n    ```\n    cd examples/lite/examples/pose_estimation/ios && pod install\n    ```\n\n    Note: If you have installed this pod before and that command doesn't work,\n    try `pod update`. At the end of this step you should have a directory called\n    `PoseEstimation.xcworkspace`.\n\n1.  Open the project in Xcode with the following command:\n\n    ```\n    open PoseEstimation.xcworkspace\n    ```\n\n    This launches Xcode and opens the `PoseEstimation` project.\n\n1.  In Menu bar, select `Product` &rarr; `Destination` and choose your physical\n    device.\n\n1.  In Menu bar, select `Product` &rarr; `Run` to install the app on your\n    device.\n\n### Model used\n\nThe pose estimation is downloaded by `RunScripts/download_models.sh`. The script\nis run automatically during the Xcode built process.\n\nIf you explicitly want to download the models, you can download them from here:\n\n*   [Posenet](https://storage.googleapis.com/download.tensorflow.org/models/tflite/posenet_mobilenet_v1_100_257x257_multi_kpt_stripped.tflite)\n*   [Movenet Lightning](https://tfhub.dev/google/movenet/singlepose/lightning/)\n*   [Movenet Thunder](https://tfhub.dev/google/movenet/singlepose/thunder/)\n"
  },
  {
    "path": "lite/examples/pose_estimation/ios/RunScripts/download_models.sh",
    "content": "#!/bin/bash\n# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\n\nMODEL_DIR=\"PoseEstimation/ML/Models\"\n\n# Download TF Lite models\nFILE=${MODEL_DIR}/posenet.tflite\nif [ ! -f \"$FILE\" ]; then\n  curl \\\n    -L 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/posenet_mobilenet_v1_100_257x257_multi_kpt_stripped.tflite' \\\n    -o ${FILE}\nfi\n\nFILE=${MODEL_DIR}/movenet_lightning.tflite\nif [ ! -f \"$FILE\" ]; then\n  curl \\\n    -L 'https://tfhub.dev/google/lite-model/movenet/singlepose/lightning/tflite/float16/4?lite-format=tflite' \\\n    -o ${FILE}\nfi\n\nFILE=${MODEL_DIR}/movenet_thunder.tflite\nif [ ! -f \"$FILE\" ]; then\n  curl \\\n    -L 'https://tfhub.dev/google/lite-model/movenet/singlepose/thunder/tflite/float16/4?lite-format=tflite' \\\n    -o ${FILE}\nfi\n\necho -e \"Downloaded files are in ${MODEL_DIR}\"\n\n\n\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/README.md",
    "content": "# Pose estimation and classification with TensorFlow Lite\n\nSee this blog post (TBD) for a full guide on doing pose estimation and\nclassification using TensorFlow Lite.\n*   Pose estimation: Detect keypoints, such as eye, ear, arm etc., from an input\n    image.\n    *   Input: An image\n    *   Output: A list of keypoint coordinates and confidence score.\n*   Pose classificaiton: Classify a human pose into predefined classes, such as\n    different yoga poses. Pose classification internally use pose estimation to\n    detect the keypoints, and use the keypoints to classify the pose.\n    *   Input: An image\n    *   Output: A list of predefined classes and their confidence score.\n\nThis sample can run on Raspberry Pi or any computer that has a camera. It uses\nOpenCV to capture images from the camera and TensorFlow Lite to run inference on\nthe input image.\n\n## Install the dependencies\n\n*   Run this script to install the Python dependencies, and download the TFLite\n    models. `sh setup.sh`\n\n## Run the pose estimation sample\n\n*   Use this command to run the pose estimation sample using the default\n    `movenet_lightning` model.\n\n```\npython3 pose_estimation.py\n```\n\n*   You can optionally specify the `model_name` parameter to try other pose\n    estimation models:\n    *   Use values:\n        * Single-pose: `posenet`, `movenet_lightning`, `movenet_thunder`\n        * Multi-poses: `movenet_multipose`\n    *   The default value is `movenet_lightning`.\n\n```\npython3 pose_estimation.py --model_name movenet_thunder\n```\n\n## Run the pose classification sample\n\n*   Use this command to run the pose estimation sample using the default\n    `movenet_lightning` pose estimation model and the `classifier.tflite` yoga\n    pose classification model.\n\n```\npython3 pose_estimation.py \\\n    --classifier classifier\n    --label_file labels.txt\n```\n\n*   If you want to train a custom pose classification model, check out\n    [this tutorial](https://www.tensorflow.org/lite/tutorials/pose_classification).\n\n## Customization options\n\n*  Here is the full list of parameters supported by the sample:\n```python3 pose_classification.py```\n  *   `model`: Name of the TFLite pose estimation model to be used.\n    *   One of these values: `posenet`, `movenet_lightning`, `movenet_thunder`, `movenet_multipose`\n    *   Default value is `movenet_lightning`.\n  *   `tracker`: Type of tracker to track poses across frames.\n    *   One of these values: `bounding_box`, `keypoint`\n    *   Only supported in multi-poses models.\n    *   Default value is `bounding_box`.\n  *   `classifier`: Name of the TFLite pose classification model to be used.\n    *   Default value is empty.\n    *   If no classification model specified, the sample will only run the pose\n        estimation step.\n  *   `camera_id`: Specify the camera for OpenCV to capture images from.\n    *   Default value is `0`.\n  *   `frameWidth`, `frameHeight`: Resolution of the image to be captured from\n      the camera.\n    *   Default value is `(640, 480)`.\n\n## Visualize pose estimation result of test data\n\n*  Run this script to visualize the pose estimation on test data\n\n```python3 visualizer.py```\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/data.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Module contains the data types used in pose estimation.\"\"\"\n\nimport enum\nfrom typing import List, NamedTuple\n\nimport numpy as np\n\n\nclass BodyPart(enum.Enum):\n  \"\"\"Enum representing human body keypoints detected by pose estimation models.\"\"\"\n  NOSE = 0\n  LEFT_EYE = 1\n  RIGHT_EYE = 2\n  LEFT_EAR = 3\n  RIGHT_EAR = 4\n  LEFT_SHOULDER = 5\n  RIGHT_SHOULDER = 6\n  LEFT_ELBOW = 7\n  RIGHT_ELBOW = 8\n  LEFT_WRIST = 9\n  RIGHT_WRIST = 10\n  LEFT_HIP = 11\n  RIGHT_HIP = 12\n  LEFT_KNEE = 13\n  RIGHT_KNEE = 14\n  LEFT_ANKLE = 15\n  RIGHT_ANKLE = 16\n\n\nclass Point(NamedTuple):\n  \"\"\"A point in 2D space.\"\"\"\n  x: float\n  y: float\n\n\nclass Rectangle(NamedTuple):\n  \"\"\"A rectangle in 2D space.\"\"\"\n  start_point: Point\n  end_point: Point\n\n\nclass KeyPoint(NamedTuple):\n  \"\"\"A detected human keypoint.\"\"\"\n  body_part: BodyPart\n  coordinate: Point\n  score: float\n\n\nclass Person(NamedTuple):\n  \"\"\"A pose detected by a pose estimation model.\"\"\"\n  keypoints: List[KeyPoint]\n  bounding_box: Rectangle\n  score: float\n  id: int = None\n\n\ndef person_from_keypoints_with_scores(\n    keypoints_with_scores: np.ndarray,\n    image_height: float,\n    image_width: float,\n    keypoint_score_threshold: float = 0.1) -> Person:\n  \"\"\"Creates a Person instance from single pose estimation model output.\n\n  Args:\n    keypoints_with_scores: Output of the TFLite pose estimation model. A numpy\n      array with shape [17, 3]. Each row represents a keypoint: [y, x, score].\n    image_height: height of the image in pixels.\n    image_width: width of the image in pixels.\n    keypoint_score_threshold: Only use keypoints with above this threshold to\n      calculate the person average score.\n\n  Returns:\n    A Person instance.\n  \"\"\"\n\n  kpts_x = keypoints_with_scores[:, 1]\n  kpts_y = keypoints_with_scores[:, 0]\n  scores = keypoints_with_scores[:, 2]\n\n  # Convert keypoints to the input image coordinate system.\n  keypoints = []\n  for i in range(scores.shape[0]):\n    keypoints.append(\n        KeyPoint(\n            BodyPart(i),\n            Point(int(kpts_x[i] * image_width), int(kpts_y[i] * image_height)),\n            scores[i]))\n\n  # Calculate bounding box as SinglePose models don't return bounding box.\n  start_point = Point(\n      int(np.amin(kpts_x) * image_width), int(np.amin(kpts_y) * image_height))\n  end_point = Point(\n      int(np.amax(kpts_x) * image_width), int(np.amax(kpts_y) * image_height))\n  bounding_box = Rectangle(start_point, end_point)\n\n  # Calculate person score by averaging keypoint scores.\n  scores_above_threshold = list(\n      filter(lambda x: x > keypoint_score_threshold, scores))\n  person_score = np.average(scores_above_threshold)\n\n  return Person(keypoints, bounding_box, person_score)\n\n\nclass Category(NamedTuple):\n  \"\"\"A classification category.\"\"\"\n  label: str\n  score: float\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/labels.txt",
    "content": "chair\ncobra\ndog\ntree\nwarrior"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/ml/__init__.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Module contains pose estimation and pose classification implementations.\"\"\"\n\nfrom ml.classifier import Classifier\nfrom ml.movenet import Movenet\nfrom ml.movenet_multipose import MoveNetMultiPose\nfrom ml.posenet import Posenet\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/ml/classifier.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Code to run a TFLite pose classification model.\"\"\"\nimport os\nfrom typing import List\n\nfrom data import Category\nfrom data import Person\nimport numpy as np\n\n# pylint: disable=g-import-not-at-top\ntry:\n  # Import TFLite interpreter from tflite_runtime package if it's available.\n  from ai_edge_litert.interpreter import Interpreter\nexcept ImportError:\n  # If not, fallback to use the TFLite interpreter from the full TF package.\n  import tensorflow as tf\n  Interpreter = tf.lite.Interpreter\n# pylint: enable=g-import-not-at-top\n\n\nclass Classifier(object):\n  \"\"\"A wrapper class for a TFLite pose classification model.\"\"\"\n\n  def __init__(self, model_name: str, label_file: str) -> None:\n    \"\"\"Initialize a pose classification model.\n\n    Args:\n      model_name: Name of the TFLite pose classification model.\n      label_file: Path of the label list file.\n    \"\"\"\n\n    # Append TFLITE extension to model_name if there's no extension\n    _, ext = os.path.splitext(model_name)\n    if not ext:\n      model_name += '.tflite'\n\n    # Initialize the TFLite model.\n    interpreter = Interpreter(model_path=model_name, num_threads=4)\n    interpreter.allocate_tensors()\n\n    self._input_index = interpreter.get_input_details()[0]['index']\n    self._output_index = interpreter.get_output_details()[0]['index']\n    self._interpreter = interpreter\n\n    self.pose_class_names = self._load_labels(label_file)\n\n  def _load_labels(self, label_path: str) -> List[str]:\n    \"\"\"Load label list from file.\n\n    Args:\n      label_path: Full path of label file.\n\n    Returns:\n      An array contains the list of labels.\n    \"\"\"\n    with open(label_path, 'r') as f:\n      return [line.strip() for _, line in enumerate(f.readlines())]\n\n  def classify_pose(self, person: Person) -> List[Category]:\n    \"\"\"Run classification on an input.\n\n    Args:\n      person: A data.Person instance.\n\n    Returns:\n      A list of classification result (data.Category).\n      Sorted by probability descending.\n    \"\"\"\n\n    # Flatten the input and add an extra dimension to match with the requirement\n    # of the TFLite model.\n    input_tensor = [[\n        keypoint.coordinate.y, keypoint.coordinate.x, keypoint.score\n    ] for keypoint in person.keypoints]\n    input_tensor = np.array(input_tensor).flatten().astype(np.float32)\n    input_tensor = np.expand_dims(input_tensor, axis=0)\n\n    # Set the input and run inference.\n    self._interpreter.set_tensor(self._input_index, input_tensor)\n    self._interpreter.invoke()\n\n    # Extract the output and squeeze the batch dimension\n    output = self._interpreter.get_tensor(self._output_index)\n    output = np.squeeze(output, axis=0)\n\n    # Sort output by probability descending.\n    prob_descending = sorted(\n        range(len(output)), key=lambda k: output[k], reverse=True)\n    prob_list = [\n        Category(label=self.pose_class_names[idx], score=output[idx])\n        for idx in prob_descending\n    ]\n\n    return prob_list\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/ml/classifier_test.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Unit test of pose classification.\"\"\"\n\nimport unittest\n\nimport cv2\nfrom ml.classifier import Classifier\nfrom ml.movenet import Movenet\n\n_ESTIMATION_MODEL = 'movenet_lightning'\n_CLASSIFIER_MODEL = 'classifier'\n_TEST_IMAGE = 'test_data/image3.jpeg'\n_LABELS = 'labels.txt'\n\n\nclass ClassifierTest(unittest.TestCase):\n\n  def test_pose_classification(self):\n    \"\"\"Test if yoga pose classifier returns correct result on test image.\"\"\"\n    # Detect the pose from the input image\n    pose_detector = Movenet(_ESTIMATION_MODEL)\n    image = cv2.imread(_TEST_IMAGE)\n    person = pose_detector.detect(image)\n\n    # Initialize a pose classifier\n    classifier = Classifier(_CLASSIFIER_MODEL, _LABELS)\n    categories = classifier.classify_pose(person)\n    class_name = categories[0].label\n    self.assertEqual(class_name, 'tree',\n                     'Predicted pose is different from ground truth.')\n\n\nif __name__ == '__main__':\n  unittest.main()\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/ml/movenet.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Code to run a pose estimation with a TFLite MoveNet model.\"\"\"\n\nimport os\nfrom typing import Dict, List\n\nimport cv2\nfrom data import BodyPart\nfrom data import Person\nfrom data import person_from_keypoints_with_scores\nimport numpy as np\n\n# pylint: disable=g-import-not-at-top\ntry:\n  # Import TFLite interpreter from tflite_runtime package if it's available.\n  from ai_edge_litert.interpreter import Interpreter\nexcept ImportError:\n  # If not, fallback to use the TFLite interpreter from the full TF package.\n  import tensorflow as tf\n  Interpreter = tf.lite.Interpreter\n# pylint: enable=g-import-not-at-top\n\n\nclass Movenet(object):\n  \"\"\"A wrapper class for a Movenet TFLite pose estimation model.\"\"\"\n\n  # Configure how confidence the model should be on the detected keypoints to\n  # proceed with using smart cropping logic.\n  _MIN_CROP_KEYPOINT_SCORE = 0.2\n  _TORSO_EXPANSION_RATIO = 1.9\n  _BODY_EXPANSION_RATIO = 1.2\n\n  def __init__(self, model_name: str) -> None:\n    \"\"\"Initialize a MoveNet pose estimation model.\n\n    Args:\n      model_name: Name of the TFLite MoveNet model.\n    \"\"\"\n\n    # Append TFLITE extension to model_name if there's no extension\n    _, ext = os.path.splitext(model_name)\n    if not ext:\n      model_name += '.tflite'\n\n    # Initialize model\n    interpreter = Interpreter(model_path=model_name, num_threads=4)\n    interpreter.allocate_tensors()\n\n    self._input_index = interpreter.get_input_details()[0]['index']\n    self._output_index = interpreter.get_output_details()[0]['index']\n\n    self._input_height = interpreter.get_input_details()[0]['shape'][1]\n    self._input_width = interpreter.get_input_details()[0]['shape'][2]\n\n    self._interpreter = interpreter\n    self._crop_region = None\n\n  def init_crop_region(self, image_height: int,\n                       image_width: int) -> Dict[(str, float)]:\n    \"\"\"Defines the default crop region.\n\n    The function provides the initial crop region (pads the full image from\n    both sides to make it a square image) when the algorithm cannot reliably\n    determine the crop region from the previous frame.\n\n    Args:\n      image_height (int): The input image width\n      image_width (int): The input image height\n\n    Returns:\n      crop_region (dict): The default crop region.\n    \"\"\"\n    if image_width > image_height:\n      x_min = 0.0\n      box_width = 1.0\n      # Pad the vertical dimension to become a square image.\n      y_min = (image_height / 2 - image_width / 2) / image_height\n      box_height = image_width / image_height\n    else:\n      y_min = 0.0\n      box_height = 1.0\n      # Pad the horizontal dimension to become a square image.\n      x_min = (image_width / 2 - image_height / 2) / image_width\n      box_width = image_height / image_width\n\n    return {\n        'y_min': y_min,\n        'x_min': x_min,\n        'y_max': y_min + box_height,\n        'x_max': x_min + box_width,\n        'height': box_height,\n        'width': box_width\n    }\n\n  def _torso_visible(self, keypoints: np.ndarray) -> bool:\n    \"\"\"Checks whether there are enough torso keypoints.\n\n    This function checks whether the model is confident at predicting one of\n    the shoulders/hips which is required to determine a good crop region.\n\n    Args:\n      keypoints: Detection result of Movenet model.\n\n    Returns:\n      True/False\n    \"\"\"\n    left_hip_score = keypoints[BodyPart.LEFT_HIP.value, 2]\n    right_hip_score = keypoints[BodyPart.RIGHT_HIP.value, 2]\n    left_shoulder_score = keypoints[BodyPart.LEFT_SHOULDER.value, 2]\n    right_shoulder_score = keypoints[BodyPart.RIGHT_SHOULDER.value, 2]\n\n    left_hip_visible = left_hip_score > Movenet._MIN_CROP_KEYPOINT_SCORE\n    right_hip_visible = right_hip_score > Movenet._MIN_CROP_KEYPOINT_SCORE\n    left_shoulder_visible = left_shoulder_score > Movenet._MIN_CROP_KEYPOINT_SCORE\n    right_shoulder_visible = right_shoulder_score > Movenet._MIN_CROP_KEYPOINT_SCORE\n\n    return ((left_hip_visible or right_hip_visible) and\n            (left_shoulder_visible or right_shoulder_visible))\n\n  def _determine_torso_and_body_range(self, keypoints: np.ndarray,\n                                      target_keypoints: Dict[(str, float)],\n                                      center_y: float,\n                                      center_x: float) -> List[float]:\n    \"\"\"Calculates the maximum distance from each keypoints to the center.\n\n    The function returns the maximum distances from the two sets of keypoints:\n    full 17 keypoints and 4 torso keypoints. The returned information will\n    be used to determine the crop size. See determine_crop_region for more\n    details.\n\n    Args:\n      keypoints: Detection result of Movenet model.\n      target_keypoints: The 4 torso keypoints.\n      center_y (float): Vertical coordinate of the body center.\n      center_x (float): Horizontal coordinate of the body center.\n\n    Returns:\n      The maximum distance from each keypoints to the center location.\n    \"\"\"\n    torso_joints = [\n        BodyPart.LEFT_SHOULDER, BodyPart.RIGHT_SHOULDER, BodyPart.LEFT_HIP,\n        BodyPart.RIGHT_HIP\n    ]\n    max_torso_yrange = 0.0\n    max_torso_xrange = 0.0\n    for joint in torso_joints:\n      dist_y = abs(center_y - target_keypoints[joint][0])\n      dist_x = abs(center_x - target_keypoints[joint][1])\n      if dist_y > max_torso_yrange:\n        max_torso_yrange = dist_y\n      if dist_x > max_torso_xrange:\n        max_torso_xrange = dist_x\n\n    max_body_yrange = 0.0\n    max_body_xrange = 0.0\n    for idx in range(len(BodyPart)):\n      if keypoints[BodyPart(idx).value, 2] < Movenet._MIN_CROP_KEYPOINT_SCORE:\n        continue\n      dist_y = abs(center_y - target_keypoints[joint][0])\n      dist_x = abs(center_x - target_keypoints[joint][1])\n      if dist_y > max_body_yrange:\n        max_body_yrange = dist_y\n\n      if dist_x > max_body_xrange:\n        max_body_xrange = dist_x\n\n    return [\n        max_torso_yrange, max_torso_xrange, max_body_yrange, max_body_xrange\n    ]\n\n  def _determine_crop_region(self, keypoints: np.ndarray, image_height: int,\n                             image_width: int) -> Dict[(str, float)]:\n    \"\"\"Determines the region to crop the image for the model to run inference on.\n\n    The algorithm uses the detected joints from the previous frame to\n    estimate the square region that encloses the full body of the target\n    person and centers at the midpoint of two hip joints. The crop size is\n    determined by the distances between each joints and the center point.\n    When the model is not confident with the four torso joint predictions,\n    the function returns a default crop which is the full image padded to\n    square.\n\n    Args:\n      keypoints: Detection result of Movenet model.\n      image_height (int): The input image width\n      image_width (int): The input image height\n\n    Returns:\n      crop_region (dict): The crop region to run inference on.\n    \"\"\"\n    # Convert keypoint index to human-readable names.\n    target_keypoints = {}\n    for idx in range(len(BodyPart)):\n      target_keypoints[BodyPart(idx)] = [\n          keypoints[idx, 0] * image_height, keypoints[idx, 1] * image_width\n      ]\n\n    # Calculate crop region if the torso is visible.\n    if self._torso_visible(keypoints):\n      center_y = (target_keypoints[BodyPart.LEFT_HIP][0] +\n                  target_keypoints[BodyPart.RIGHT_HIP][0]) / 2\n      center_x = (target_keypoints[BodyPart.LEFT_HIP][1] +\n                  target_keypoints[BodyPart.RIGHT_HIP][1]) / 2\n\n      (max_torso_yrange, max_torso_xrange, max_body_yrange,\n       max_body_xrange) = self._determine_torso_and_body_range(\n           keypoints, target_keypoints, center_y, center_x)\n\n      crop_length_half = np.amax([\n          max_torso_xrange * Movenet._TORSO_EXPANSION_RATIO,\n          max_torso_yrange * Movenet._TORSO_EXPANSION_RATIO,\n          max_body_yrange * Movenet._BODY_EXPANSION_RATIO,\n          max_body_xrange * Movenet._BODY_EXPANSION_RATIO\n      ])\n\n      # Adjust crop length so that it is still within the image border\n      distances_to_border = np.array(\n          [center_x, image_width - center_x, center_y, image_height - center_y])\n      crop_length_half = np.amin(\n          [crop_length_half, np.amax(distances_to_border)])\n\n      # If the body is large enough, there's no need to apply cropping logic.\n      if crop_length_half > max(image_width, image_height) / 2:\n        return self.init_crop_region(image_height, image_width)\n      # Calculate the crop region that nicely covers the full body.\n      else:\n        crop_length = crop_length_half * 2\n      crop_corner = [center_y - crop_length_half, center_x - crop_length_half]\n      return {\n          'y_min':\n              crop_corner[0] / image_height,\n          'x_min':\n              crop_corner[1] / image_width,\n          'y_max': (crop_corner[0] + crop_length) / image_height,\n          'x_max': (crop_corner[1] + crop_length) / image_width,\n          'height': (crop_corner[0] + crop_length) / image_height -\n                    crop_corner[0] / image_height,\n          'width': (crop_corner[1] + crop_length) / image_width -\n                   crop_corner[1] / image_width\n      }\n    # Return the initial crop regsion if the torso isn't visible.\n    else:\n      return self.init_crop_region(image_height, image_width)\n\n  def _crop_and_resize(\n      self, image: np.ndarray, crop_region: Dict[(str, float)],\n      crop_size: (int, int)) -> np.ndarray:\n    \"\"\"Crops and resize the image to prepare for the model input.\"\"\"\n    y_min, x_min, y_max, x_max = [\n        crop_region['y_min'], crop_region['x_min'], crop_region['y_max'],\n        crop_region['x_max']\n    ]\n\n    crop_top = int(0 if y_min < 0 else y_min * image.shape[0])\n    crop_bottom = int(image.shape[0] if y_max >= 1 else y_max * image.shape[0])\n    crop_left = int(0 if x_min < 0 else x_min * image.shape[1])\n    crop_right = int(image.shape[1] if x_max >= 1 else x_max * image.shape[1])\n\n    padding_top = int(0 - y_min * image.shape[0] if y_min < 0 else 0)\n    padding_bottom = int((y_max - 1) * image.shape[0] if y_max >= 1 else 0)\n    padding_left = int(0 - x_min * image.shape[1] if x_min < 0 else 0)\n    padding_right = int((x_max - 1) * image.shape[1] if x_max >= 1 else 0)\n\n    # Crop and resize image\n    output_image = image[crop_top:crop_bottom, crop_left:crop_right]\n    output_image = cv2.copyMakeBorder(output_image, padding_top, padding_bottom,\n                                      padding_left, padding_right,\n                                      cv2.BORDER_CONSTANT)\n    output_image = cv2.resize(output_image, (crop_size[0], crop_size[1]))\n\n    return output_image\n\n  def _run_detector(\n      self, image: np.ndarray, crop_region: Dict[(str, float)],\n      crop_size: (int, int)) -> np.ndarray:\n    \"\"\"Runs model inference on the cropped region.\n\n    The function runs the model inference on the cropped region and updates\n    the model output to the original image coordinate system.\n\n    Args:\n      image: The input image.\n      crop_region: The region of interest to run inference on.\n      crop_size: The size of the crop region.\n\n    Returns:\n      An array of shape [17, 3] representing the keypoint absolute coordinates\n      and scores.\n    \"\"\"\n\n    input_image = self._crop_and_resize(image, crop_region, crop_size=crop_size)\n    input_image = input_image.astype(dtype=np.uint8)\n\n    self._interpreter.set_tensor(self._input_index,\n                                 np.expand_dims(input_image, axis=0))\n    self._interpreter.invoke()\n\n    keypoints_with_scores = self._interpreter.get_tensor(self._output_index)\n    keypoints_with_scores = np.squeeze(keypoints_with_scores)\n\n    # Update the coordinates.\n    for idx in range(len(BodyPart)):\n      keypoints_with_scores[idx, 0] = crop_region[\n          'y_min'] + crop_region['height'] * keypoints_with_scores[idx, 0]\n      keypoints_with_scores[idx, 1] = crop_region[\n          'x_min'] + crop_region['width'] * keypoints_with_scores[idx, 1]\n\n    return keypoints_with_scores\n\n  def detect(self,\n             input_image: np.ndarray,\n             reset_crop_region: bool = False) -> Person:\n    \"\"\"Run detection on an input image.\n\n    Args:\n      input_image: A [height, width, 3] RGB image. Note that height and width\n        can be anything since the image will be immediately resized according to\n        the needs of the model within this function.\n      reset_crop_region: Whether to use the crop region inferred from the\n        previous detection result to improve accuracy. Set to True if this is a\n        frame from a video. Set to False if this is a static image. Default\n        value is True.\n\n    Returns:\n      An array of shape [17, 3] representing the keypoint coordinates and\n      scores.\n    \"\"\"\n    image_height, image_width, _ = input_image.shape\n    if (self._crop_region is None) or reset_crop_region:\n      # Set crop region for the first frame.\n      self._crop_region = self.init_crop_region(image_height, image_width)\n\n    # Detect pose using the crop region inferred from the detection result in\n    # the previous frame\n    keypoint_with_scores = self._run_detector(\n        input_image,\n        self._crop_region,\n        crop_size=(self._input_height, self._input_width))\n    # Calculate the crop region for the next frame\n    self._crop_region = self._determine_crop_region(keypoint_with_scores,\n                                                    image_height, image_width)\n\n    # Convert the keypoints with scores to a Person data type\n\n    return person_from_keypoints_with_scores(keypoint_with_scores, image_height,\n                                             image_width)\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/ml/movenet_multipose.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Code to run a pose estimation with a TFLite Movenet_multipose model.\"\"\"\n\nimport os\nimport time\nfrom typing import List\n\nimport cv2\nfrom data import BodyPart\nfrom data import KeyPoint\nfrom data import Person\nfrom data import Point\nfrom data import Rectangle\nimport numpy as np\nfrom tracker import BoundingBoxTracker\nfrom tracker import KeypointTracker\nfrom tracker import TrackerConfig\nimport utils\n\n# pylint: disable=g-import-not-at-top\ntry:\n  # Import TFLite interpreter from tflite_runtime package if it's available.\n  from ai_edge_litert.interpreter import Interpreter\nexcept ImportError:\n  # If not, fallback to use the TFLite interpreter from the full TF package.\n  import tensorflow as tf\n  Interpreter = tf.lite.Interpreter\n\n\nclass MoveNetMultiPose(object):\n  \"\"\"A wrapper class for a MultiPose TFLite pose estimation model.\"\"\"\n\n  def __init__(self,\n               model_name: str,\n               tracker_type: str = 'bounding_box',\n               input_size: int = 256) -> None:\n    \"\"\"Initialize a MultiPose pose estimation model.\n\n    Args:\n      model_name: Name of the TFLite multipose model.\n      tracker_type: Type of Tracker('keypoint' or 'bounding_box')\n      input_size: Size of the longer dimension of the input image.\n    \"\"\"\n    # Append .tflite extension to model_name if there's no extension.\n    _, ext = os.path.splitext(model_name)\n    if not ext:\n      model_name += '.tflite'\n\n    # Store the input size parameter.\n    self._input_size = input_size\n\n    # Initialize the TFLite model.\n    interpreter = Interpreter(model_path=model_name, num_threads=4)\n\n    self._input_details = interpreter.get_input_details()\n    self._output_details = interpreter.get_output_details()\n    self._input_type = self._input_details[0]['dtype']\n\n    self._input_height = interpreter.get_input_details()[0]['shape'][1]\n    self._input_width = interpreter.get_input_details()[0]['shape'][2]\n\n    self._interpreter = interpreter\n\n    # Initialize a tracker.\n    config = TrackerConfig()\n    if tracker_type == 'keypoint':\n      self._tracker = KeypointTracker(config)\n    elif tracker_type == 'bounding_box':\n      self._tracker = BoundingBoxTracker(config)\n    else:\n      print('ERROR: Tracker type {0} not supported. No tracker will be used.'\n            .format(tracker_type))\n      self._tracker = None\n\n  def detect(self,\n             input_image: np.ndarray,\n             detection_threshold: float = 0.11) -> List[Person]:\n    \"\"\"Run detection on an input image.\n\n    Args:\n      input_image: A [height, width, 3] RGB image. Note that height and width\n        can be anything since the image will be immediately resized according to\n        the needs of the model within this function.\n      detection_threshold: minimum confidence score for an detected pose to be\n        considered.\n\n    Returns:\n      A list of Person instances detected from the input image.\n    \"\"\"\n\n    is_dynamic_shape_model = self._input_details[0]['shape_signature'][2] == -1\n    # Resize and pad the image to keep the aspect ratio and fit the expected\n    # size.\n    if is_dynamic_shape_model:\n      resized_image, _ = utils.keep_aspect_ratio_resizer(\n          input_image, self._input_size)\n      input_tensor = np.expand_dims(resized_image, axis=0)\n      self._interpreter.resize_tensor_input(\n          self._input_details[0]['index'], input_tensor.shape, strict=True)\n    else:\n      resized_image = cv2.resize(input_image,\n                                 (self._input_width, self._input_height))\n      input_tensor = np.expand_dims(resized_image, axis=0)\n    self._interpreter.allocate_tensors()\n\n    # Run inference with the MoveNet MultiPose model.\n    self._interpreter.set_tensor(self._input_details[0]['index'],\n                                 input_tensor.astype(self._input_type))\n    self._interpreter.invoke()\n\n    # Get the model output\n    model_output = self._interpreter.get_tensor(\n        self._output_details[0]['index'])\n\n    image_height, image_width, _ = input_image.shape\n    return self._postprocess(model_output, image_height, image_width,\n                             detection_threshold)\n\n  def _postprocess(self, keypoints_with_scores: np.ndarray, image_height: int,\n                   image_width: int,\n                   detection_threshold: float) -> List[Person]:\n    \"\"\"Returns a list \"Person\" corresponding to the input image.\n\n    Note that coordinates are expressed in (x, y) format for drawing\n    utilities.\n\n    Args:\n      keypoints_with_scores: Output of the MultiPose TFLite model.\n      image_height: height of the image in pixels.\n      image_width: width of the image in pixels.\n      detection_threshold: minimum confidence score for an entity to be\n        considered.\n\n    Returns:\n      A list of Person(keypoints, bounding_box, scores), each containing:\n        * the coordinates of all keypoints of the detected entity;\n        * the bounding boxes of the entity.\n        * the confidence core of the entity.\n    \"\"\"\n\n    _, num_instances, _ = keypoints_with_scores.shape\n    list_persons = []\n    for idx in range(num_instances):\n      # Skip a detected pose if its confidence score is below the threshold\n      person_score = keypoints_with_scores[0, idx, 55]\n      if person_score < detection_threshold:\n        continue\n\n      # Extract the keypoint coordinates and scores\n      kpts_y = keypoints_with_scores[0, idx, range(0, 51, 3)]\n      kpts_x = keypoints_with_scores[0, idx, range(1, 51, 3)]\n      scores = keypoints_with_scores[0, idx, range(2, 51, 3)]\n\n      # Create the list of keypoints\n      keypoints = []\n      for i in range(scores.shape[0]):\n        keypoints.append(\n            KeyPoint(\n                BodyPart(i),\n                Point(\n                    int(kpts_x[i] * image_width),\n                    int(kpts_y[i] * image_height)), scores[i]))\n\n      # Calculate the bounding box\n      rect = [\n          keypoints_with_scores[0, idx, 51], keypoints_with_scores[0, idx, 52],\n          keypoints_with_scores[0, idx, 53], keypoints_with_scores[0, idx, 54]\n      ]\n      bounding_box = Rectangle(\n          Point(int(rect[1] * image_width), int(rect[0] * image_height)),\n          Point(int(rect[3] * image_width), int(rect[2] * image_height)))\n\n      # Create a Person instance corresponding to the detected entity.\n      list_persons.append(Person(keypoints, bounding_box, person_score))\n    if self._tracker:\n      list_persons = self._tracker.apply(list_persons, time.time() * 1000)\n\n    return list_persons\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/ml/movenet_multipose_test.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Unit test of pose estimation using MoveNet Multipose.\"\"\"\n\nimport logging\nfrom typing import List\nimport unittest\n\nimport cv2\nfrom data import BodyPart\nfrom data import KeyPoint\nfrom .movenet_multipose import MoveNetMultiPose\nimport numpy as np\nimport pandas as pd\n\n_MODEL_MOVENET_MULTILPOSE = 'movenet_multipose'\n_IMAGE_TEST1 = 'test_data/image1.png'\n_IMAGE_TEST2 = 'test_data/image2.jpeg'\n_GROUND_TRUTH_CSV = 'test_data/pose_landmark_truth.csv'\n_ALLOWED_DISTANCE = 41\n\n\nclass MovenetMultiPoseTest(unittest.TestCase):\n\n  def setUp(self):\n    super().setUp()\n    image_1 = cv2.imread(_IMAGE_TEST1)\n    image_2 = cv2.imread(_IMAGE_TEST2)\n\n    # Merge image_1 and image_2 into a single image for testing MultiPose model.\n    image = cv2.hconcat([image_1, image_2])\n\n    # Initialize the MultiPose model.\n    detector = MoveNetMultiPose(_MODEL_MOVENET_MULTILPOSE)\n\n    # Run detection on the merged image\n    self.list_persons = detector.detect(image)\n\n    # Sort the results so that the person on the right side come first.\n    self.list_persons.sort(key=lambda person: person.bounding_box.start_point.x)\n\n    # Load the pose landmarks ground truth.\n    pose_landmarks_truth = pd.read_csv(_GROUND_TRUTH_CSV)\n    keypoints_truth_1 = pose_landmarks_truth.iloc[0].to_numpy().reshape((17, 2))\n    keypoints_truth_2 = pose_landmarks_truth.iloc[1].to_numpy().reshape((17, 2))\n\n    # Shift keypoints_truth_2 to the right to account for the space occupied by\n    # image1.\n    for idx in range(keypoints_truth_2.shape[0]):\n      keypoints_truth_2[idx][0] += image_1.shape[1]\n\n    self.keypoints_truth = [keypoints_truth_1, keypoints_truth_2]\n\n  def _assert(self, keypoints: List[KeyPoint],\n              keypoints_truth: np.ndarray) -> None:\n    \"\"\"Assert if the detection result is close to ground truth.\n\n    Args:\n      keypoints: List Keypoint detected by from the Movenet Multipose model.\n      keypoints_truth: Ground truth keypoints.\n    \"\"\"\n    for idx in range(len(BodyPart)):\n      kpt_estimate = np.array(\n          [keypoints[idx].coordinate.x, keypoints[idx].coordinate.y])\n      kpt_truth = keypoints_truth[idx]\n      distance = np.linalg.norm(kpt_estimate - kpt_truth, np.inf)\n\n      self.assertGreaterEqual(\n          _ALLOWED_DISTANCE, distance,\n          '{0} is too far away ({1}) from ground truth data.'.format(\n              BodyPart(idx).name, int(distance)))\n      logging.debug('Detected %s close to expected result (%d)',\n                    BodyPart(idx).name, int(distance))\n\n  def test_pose_estimation_image1_multipose(self):\n    \"\"\"Test if MoveNet Multipose's detection is close to ground truth of image1.\"\"\"\n    keypoints = self.list_persons[0].keypoints\n    self._assert(keypoints, self.keypoints_truth[0])\n\n  def test_pose_estimation_image2_multipose(self):\n    \"\"\"Test if MoveNet Multipose's detection is close to ground truth of image2.\"\"\"\n    keypoints = self.list_persons[1].keypoints\n    self._assert(keypoints, self.keypoints_truth[1])\n\n\nif __name__ == '__main__':\n  unittest.main()\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/ml/movenet_test.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Unit test of pose estimation using MoveNet.\"\"\"\n\nimport logging\nimport unittest\n\nimport cv2\nfrom data import BodyPart\nfrom ml.movenet import Movenet\nimport numpy as np\nimport pandas as pd\n\n_MODEL_LIGHTNING = 'movenet_lightning'\n_MODEL_THUNDER = 'movenet_thunder'\n_IMAGE_TEST1 = 'test_data/image1.png'\n_IMAGE_TEST2 = 'test_data/image2.jpeg'\n_GROUND_TRUTH_CSV = 'test_data/pose_landmark_truth.csv'\n_ALLOWED_DISTANCE = 21\n\n\nclass MovenetTest(unittest.TestCase):\n\n  def setUp(self):\n    super().setUp()\n    self.image_1 = cv2.imread(_IMAGE_TEST1)\n    self.image_2 = cv2.imread(_IMAGE_TEST2)\n\n    # Initialize model\n    self.movenet_lightning = Movenet(_MODEL_LIGHTNING)\n    self.movenet_thunder = Movenet(_MODEL_THUNDER)\n    # Get pose landmarks truth\n    pose_landmarks_truth = pd.read_csv(_GROUND_TRUTH_CSV)\n    self.keypoints_truth_1 = pose_landmarks_truth.iloc[0].to_numpy().reshape(\n        (17, 2))\n    self.keypoints_truth_2 = pose_landmarks_truth.iloc[1].to_numpy().reshape(\n        (17, 2))\n\n  def _detect_and_assert(self, detector: Movenet, image: np.ndarray,\n                         keypoints_truth: np.ndarray) -> None:\n    \"\"\"Run pose estimation and assert if the result is close to ground truth.\n\n    Args:\n      detector: A Movenet pose estimator.\n      image: A [height, width, 3] RGB image.\n      keypoints_truth: Ground truth keypoint coordinates to be compared to.\n    \"\"\"\n    person = detector.detect(image, reset_crop_region=True)\n    keypoints = person.keypoints\n\n    for idx in range(len(BodyPart)):\n      kpt_estimate = np.array(\n          [keypoints[idx].coordinate.x, keypoints[idx].coordinate.y])\n      distance = np.linalg.norm(kpt_estimate - keypoints_truth[idx], np.inf)\n\n      self.assertGreaterEqual(\n          _ALLOWED_DISTANCE, distance,\n          '{0} is too far away ({1}) from ground truth data.'.format(\n              BodyPart(idx).name, int(distance)))\n      logging.debug('Detected %s close to expected result (%d)',\n                    BodyPart(idx).name, int(distance))\n\n  def test_pose_estimation_image1_lightning(self):\n    \"\"\"Test if MoveNet Lightning detection's close to ground truth of image1.\"\"\"\n    self._detect_and_assert(self.movenet_lightning, self.image_1,\n                            self.keypoints_truth_1)\n\n  def test_pose_estimation_image1_thunder(self):\n    \"\"\"Test if MoveNet Thunder detection's close to ground truth of image1.\"\"\"\n    self._detect_and_assert(self.movenet_thunder, self.image_1,\n                            self.keypoints_truth_1)\n\n  def test_pose_estimation_image2_lightning(self):\n    \"\"\"Test if MoveNet Lightning detection's close to ground truth of image2.\"\"\"\n    self._detect_and_assert(self.movenet_lightning, self.image_2,\n                            self.keypoints_truth_2)\n\n  def test_pose_estimation_image2_thunder(self):\n    \"\"\"Test if MoveNet Thunder detection's close to ground truth of image2.\"\"\"\n    self._detect_and_assert(self.movenet_thunder, self.image_2,\n                            self.keypoints_truth_2)\n\n\nif __name__ == '__main__':\n  unittest.main()\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/ml/posenet.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Code to run a pose estimation with a TFLite PoseNet model.\"\"\"\n\nimport os\n\nimport cv2\nfrom data import Person\nfrom data import person_from_keypoints_with_scores\nimport numpy as np\n\n# pylint: disable=g-import-not-at-top\ntry:\n  # Import TFLite interpreter from tflite_runtime package if it's available.\n  from ai_edge_litert.interpreter import Interpreter\nexcept ImportError:\n  # If not, fallback to use the TFLite interpreter from the full TF package.\n  import tensorflow as tf\n  Interpreter = tf.lite.Interpreter\n# pylint: enable=g-import-not-at-top\n\n\nclass Posenet(object):\n  \"\"\"A wrapper class for a Posenet TFLite pose estimation model.\"\"\"\n\n  def __init__(self, model_name: str) -> None:\n    \"\"\"Initialize a PoseNet pose estimation model.\n\n    Args:\n        model_name: Name of the TFLite PoseNet model.\n    \"\"\"\n    # Append TFLITE extension to model_name if there's no extension\n    _, ext = os.path.splitext(model_name)\n    if not ext:\n      model_name += '.tflite'\n\n    # Initialize model\n    interpreter = Interpreter(model_path=model_name, num_threads=4)\n    interpreter.allocate_tensors()\n\n    self._input_index = interpreter.get_input_details()[0]['index']\n    self._output_heatmap_index = interpreter.get_output_details()[0]['index']\n    self._output_offset_index = interpreter.get_output_details()[1]['index']\n\n    self._input_height = interpreter.get_input_details()[0]['shape'][1]\n    self._input_width = interpreter.get_input_details()[0]['shape'][2]\n\n    self._interpreter = interpreter\n\n  def detect(self, input_image: np.ndarray) -> Person:\n    \"\"\"Run detection on an input image.\n\n    Args:\n        input_image: A [height, width, 3] RGB image. Note that height and width\n          can be anything since the image will be immediately resized according\n          to the needs of the model within this function.\n\n    Returns:\n        A Person instance.\n    \"\"\"\n\n    image_height, image_width, _ = input_image.shape\n    input_image = cv2.resize(input_image,\n                             (self._input_width, self._input_height))\n    input_tensor = np.expand_dims(input_image, axis=0)\n\n    # check the type of the input tensor\n    is_float_model = self._interpreter.get_input_details(\n    )[0]['dtype'] == np.float32\n\n    if is_float_model:\n      input_tensor = (np.float32(input_tensor) - 127.5) / 127.5\n\n    # Process template image\n    # Sets the value of the input tensor\n    self._interpreter.set_tensor(self._input_index, input_tensor)\n    # Runs the computation\n    self._interpreter.invoke()\n\n    # Extract output data from the interpreter\n    raw_heatmap = self._interpreter.get_tensor(self._output_heatmap_index)\n    raw_offset = self._interpreter.get_tensor(self._output_offset_index)\n\n    # Getting rid of the extra dimension\n    raw_heatmap = np.squeeze(raw_heatmap)\n    raw_offset = np.squeeze(raw_offset)\n\n    keypoints_with_scores = self._process_output(raw_heatmap, raw_offset)\n\n    return person_from_keypoints_with_scores(keypoints_with_scores,\n                                             image_height, image_width)\n\n  def _sigmoid(self, x: np.ndarray) -> float:\n    return 1 / (1 + np.exp(-x))\n\n  def _process_output(self, heatmap_data: np.ndarray,\n                      offset_data: np.ndarray) -> np.ndarray:\n    \"\"\"Post-process the output of Posenet TFLite model.\n\n    Args:\n      heatmap_data: heatmaps output from Posenet. [height_resolution,\n        width_resolution, 17]\n      offset_data: offset vectors (XY) output from Posenet. [height_resolution,\n        width_resolution, 34]\n\n    Returns:\n      An array of shape [17, 3] representing the keypoint absolute coordinates\n      and scores.\n    \"\"\"\n    joint_num = heatmap_data.shape[-1]\n    keypoints_with_scores = np.zeros((joint_num, 3), np.float32)\n    scores = self._sigmoid(heatmap_data)\n\n    for idx in range(joint_num):\n      joint_heatmap = heatmap_data[..., idx]\n      x, y = np.unravel_index(\n          np.argmax(scores[:, :, idx]), scores[:, :, idx].shape)\n      max_val_pos = np.squeeze(\n          np.argwhere(joint_heatmap == np.max(joint_heatmap)))\n      remap_pos = np.array(max_val_pos / 8 * 257, dtype=np.int32)\n\n      keypoints_with_scores[idx, 0] = (\n          remap_pos[0] + offset_data[max_val_pos[0], max_val_pos[1], idx]) / 257\n      keypoints_with_scores[idx, 1] = (\n          remap_pos[1] +\n          offset_data[max_val_pos[0], max_val_pos[1], idx + joint_num]) / 257\n      keypoints_with_scores[idx, 2] = scores[x, y, idx]\n\n    return keypoints_with_scores\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/ml/posenet_test.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Unit test of pose estimation using PoseNet.\"\"\"\n\nimport logging\nimport unittest\n\nimport cv2\nfrom data import BodyPart\nfrom ml.posenet import Posenet\nimport numpy as np\nimport pandas as pd\n\n_MODEL = 'posenet'\n_IMAGE_TEST1 = 'test_data/image1.png'\n_IMAGE_TEST2 = 'test_data/image2.jpeg'\n_GROUND_TRUTH_CSV = 'test_data/pose_landmark_truth.csv'\n_ALLOWED_DISTANCE = 35\n\n\nclass PosenetTest(unittest.TestCase):\n\n  def setUp(self):\n    super().setUp()\n    self.image_1 = cv2.imread(_IMAGE_TEST1)\n    self.image_2 = cv2.imread(_IMAGE_TEST2)\n\n    # Initialize model\n    self.posenet = Posenet(_MODEL)\n    # Get pose landmarks truth\n    pose_landmarks_truth = pd.read_csv(_GROUND_TRUTH_CSV)\n\n    self.keypoints_truth_1 = pose_landmarks_truth.iloc[0].to_numpy().reshape(\n        (17, 2))\n    self.keypoints_truth_2 = pose_landmarks_truth.iloc[1].to_numpy().reshape(\n        (17, 2))\n\n  def _detect_and_assert(self, detector: Posenet, image: np.ndarray,\n                         keypoints_truth: np.ndarray) -> None:\n    \"\"\"Run pose estimation and assert if the result is close to ground truth.\n\n    Args:\n      detector: Posenet detector.\n      image: A [height, width, 3] RGB image.\n      keypoints_truth: Ground truth keypoint coordinates to be compared to.\n    \"\"\"\n    person = detector.detect(image)\n    keypoints = person.keypoints\n    for idx in range(len(BodyPart)):\n      kpt_estimate = np.array(\n          [keypoints[idx].coordinate.x, keypoints[idx].coordinate.y])\n      distance = np.linalg.norm(kpt_estimate - keypoints_truth[idx], np.inf)\n\n      self.assertGreaterEqual(\n          _ALLOWED_DISTANCE, distance,\n          '{0} is too far away ({1}) from ground truth data.'.format(\n              BodyPart(idx).name, int(distance)))\n      logging.debug('Detected %s close to expected result (%d)',\n                    BodyPart(idx).name, int(distance))\n\n  def test_pose_estimation_image1(self):\n    \"\"\"Test if Posenet detection's close to ground truth of image1.\"\"\"\n    self._detect_and_assert(self.posenet, self.image_1, self.keypoints_truth_1)\n\n  def test_pose_estimation_image2(self):\n    \"\"\"Test if Posenet detection's close to ground truth of image2.\"\"\"\n    self._detect_and_assert(self.posenet, self.image_2, self.keypoints_truth_2)\n\n\nif __name__ == '__main__':\n  unittest.main()\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/pose_estimation.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Main script to run pose classification and pose estimation.\"\"\"\nimport argparse\nimport logging\nimport sys\nimport time\n\nimport cv2\nfrom ml import Classifier\nfrom ml import Movenet\nfrom ml import MoveNetMultiPose\nfrom ml import Posenet\nimport utils\n\n\ndef run(estimation_model: str, tracker_type: str, classification_model: str,\n        label_file: str, camera_id: int, width: int, height: int) -> None:\n  \"\"\"Continuously run inference on images acquired from the camera.\n\n  Args:\n    estimation_model: Name of the TFLite pose estimation model.\n    tracker_type: Type of Tracker('keypoint' or 'bounding_box').\n    classification_model: Name of the TFLite pose classification model.\n      (Optional)\n    label_file: Path to the label file for the pose classification model. Class\n      names are listed one name per line, in the same order as in the\n      classification model output. See an example in the yoga_labels.txt file.\n    camera_id: The camera id to be passed to OpenCV.\n    width: The width of the frame captured from the camera.\n    height: The height of the frame captured from the camera.\n  \"\"\"\n\n  # Notify users that tracker is only enabled for MoveNet MultiPose model.\n  if tracker_type and (estimation_model != 'movenet_multipose'):\n    logging.warning(\n        'No tracker will be used as tracker can only be enabled for '\n        'MoveNet MultiPose model.')\n\n  # Initialize the pose estimator selected.\n  if estimation_model in ['movenet_lightning', 'movenet_thunder']:\n    pose_detector = Movenet(estimation_model)\n  elif estimation_model == 'posenet':\n    pose_detector = Posenet(estimation_model)\n  elif estimation_model == 'movenet_multipose':\n    pose_detector = MoveNetMultiPose(estimation_model, tracker_type)\n  else:\n    sys.exit('ERROR: Model is not supported.')\n\n  # Variables to calculate FPS\n  counter, fps = 0, 0\n  start_time = time.time()\n\n  # Start capturing video input from the camera\n  cap = cv2.VideoCapture(camera_id)\n  cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)\n  cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)\n\n  # Visualization parameters\n  row_size = 20  # pixels\n  left_margin = 24  # pixels\n  text_color = (0, 0, 255)  # red\n  font_size = 1\n  font_thickness = 1\n  classification_results_to_show = 3\n  fps_avg_frame_count = 10\n  keypoint_detection_threshold_for_classifier = 0.1\n  classifier = None\n\n  # Initialize the classification model\n  if classification_model:\n    classifier = Classifier(classification_model, label_file)\n    classification_results_to_show = min(classification_results_to_show,\n                                         len(classifier.pose_class_names))\n\n  # Continuously capture images from the camera and run inference\n  while cap.isOpened():\n    success, image = cap.read()\n    if not success:\n      sys.exit(\n          'ERROR: Unable to read from webcam. Please verify your webcam settings.'\n      )\n\n    counter += 1\n    image = cv2.flip(image, 1)\n\n    if estimation_model == 'movenet_multipose':\n      # Run pose estimation using a MultiPose model.\n      list_persons = pose_detector.detect(image)\n    else:\n      # Run pose estimation using a SinglePose model, and wrap the result in an\n      # array.\n      list_persons = [pose_detector.detect(image)]\n\n    # Draw keypoints and edges on input image\n    image = utils.visualize(image, list_persons)\n\n    if classifier:\n      # Check if all keypoints are detected before running the classifier.\n      # If there's a keypoint below the threshold, show an error.\n      person = list_persons[0]\n      min_score = min([keypoint.score for keypoint in person.keypoints])\n      if min_score < keypoint_detection_threshold_for_classifier:\n        error_text = 'Some keypoints are not detected.'\n        text_location = (left_margin, 2 * row_size)\n        cv2.putText(image, error_text, text_location, cv2.FONT_HERSHEY_PLAIN,\n                    font_size, text_color, font_thickness)\n        error_text = 'Make sure the person is fully visible in the camera.'\n        text_location = (left_margin, 3 * row_size)\n        cv2.putText(image, error_text, text_location, cv2.FONT_HERSHEY_PLAIN,\n                    font_size, text_color, font_thickness)\n      else:\n        # Run pose classification\n        prob_list = classifier.classify_pose(person)\n\n        # Show classification results on the image\n        for i in range(classification_results_to_show):\n          class_name = prob_list[i].label\n          probability = round(prob_list[i].score, 2)\n          result_text = class_name + ' (' + str(probability) + ')'\n          text_location = (left_margin, (i + 2) * row_size)\n          cv2.putText(image, result_text, text_location, cv2.FONT_HERSHEY_PLAIN,\n                      font_size, text_color, font_thickness)\n\n    # Calculate the FPS\n    if counter % fps_avg_frame_count == 0:\n      end_time = time.time()\n      fps = fps_avg_frame_count / (end_time - start_time)\n      start_time = time.time()\n\n    # Show the FPS\n    fps_text = 'FPS = ' + str(int(fps))\n    text_location = (left_margin, row_size)\n    cv2.putText(image, fps_text, text_location, cv2.FONT_HERSHEY_PLAIN,\n                font_size, text_color, font_thickness)\n\n    # Stop the program if the ESC key is pressed.\n    if cv2.waitKey(1) == 27:\n      break\n    cv2.imshow(estimation_model, image)\n\n  cap.release()\n  cv2.destroyAllWindows()\n\n\ndef main():\n  parser = argparse.ArgumentParser(\n      formatter_class=argparse.ArgumentDefaultsHelpFormatter)\n  parser.add_argument(\n      '--model',\n      help='Name of estimation model.',\n      required=False,\n      default='movenet_lightning')\n  parser.add_argument(\n      '--tracker',\n      help='Type of tracker to track poses across frames.',\n      required=False,\n      default='bounding_box')\n  parser.add_argument(\n      '--classifier', help='Name of classification model.', required=False)\n  parser.add_argument(\n      '--label_file',\n      help='Label file for classification.',\n      required=False,\n      default='labels.txt')\n  parser.add_argument(\n      '--cameraId', help='Id of camera.', required=False, default=0)\n  parser.add_argument(\n      '--frameWidth',\n      help='Width of frame to capture from camera.',\n      required=False,\n      default=640)\n  parser.add_argument(\n      '--frameHeight',\n      help='Height of frame to capture from camera.',\n      required=False,\n      default=480)\n  args = parser.parse_args()\n\n  run(args.model, args.tracker, args.classifier, args.label_file,\n      int(args.cameraId), args.frameWidth, args.frameHeight)\n\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/requirements.txt",
    "content": "argparse\nnumpy>=1.20.0 # To ensure compatibility with OpenCV on Raspberry Pi.\nopencv-python~=4.5.3.56\npandas>=1.3.1\ntflite-runtime>=2.7.0\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/setup.sh",
    "content": "#!/bin/bash\n\nif [ $# -eq 0 ]; then\n  DATA_DIR=\"./\"\nelse\n  DATA_DIR=\"$1\"\nfi\n\n# Install Python dependencies\npython3 -m pip install pip --upgrade\npython3 -m pip install -r requirements.txt\n\n# Download TF Lite models\nFILE=${DATA_DIR}/posenet.tflite\nif [ ! -f \"$FILE\" ]; then\n  curl \\\n    -L 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/posenet_mobilenet_v1_100_257x257_multi_kpt_stripped.tflite' \\\n    -o ${FILE}\nfi\n\nFILE=${DATA_DIR}/movenet_lightning.tflite\nif [ ! -f \"$FILE\" ]; then\n  curl \\\n    -L 'https://tfhub.dev/google/lite-model/movenet/singlepose/lightning/tflite/float16/4?lite-format=tflite' \\\n    -o ${FILE}\nfi\n\nFILE=${DATA_DIR}/movenet_thunder.tflite\nif [ ! -f \"$FILE\" ]; then\n  curl \\\n    -L 'https://tfhub.dev/google/lite-model/movenet/singlepose/thunder/tflite/float16/4?lite-format=tflite' \\\n    -o ${FILE}\nfi\n\nFILE=${DATA_DIR}/movenet_multipose.tflite\nif [ ! -f \"$FILE\" ]; then\n  curl \\\n    -L 'https://tfhub.dev/google/lite-model/movenet/multipose/lightning/tflite/float16/1?lite-format=tflite' \\\n    -o ${FILE}\nfi\n\nFILE=${DATA_DIR}/classifier.tflite\nif [ ! -f \"$FILE\" ]; then\n  curl \\\n    -L 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/pose_classifier/yoga_classifier.tflite' \\\n    -o ${FILE}\nfi\n\necho -e \"Downloaded files are in ${DATA_DIR}\"\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/test_data/image_credits.txt",
    "content": "image1.png: https://pixabay.com/illustrations/woman-stand-wait-person-shoes-1427073/\nimage2.jpeg: https://pixabay.com/photos/businessman-suit-germany-black-1146791/\nImage3.jpeg: https://pixabay.com/photos/tree-pose-yoga-yogini-lifestyle-4823155/"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/test_data/pose_landmark_truth.csv",
    "content": "nose_x,nose_y,left_eye_x,left_eye_y,right_eye_x,right_eye_y,left_ear_x,left_ear_y,right_ear_x,right_ear_y,left_shoulder_x,left_shoulder_y,right_shoulder_x,right_shoulder_y,left_elbow_x,left_elbow_y,right_elbow_x,right_elbow_y,left_wrist_x,left_wrist_y,right_wrist_x,right_wrist_y,left_hip_x,left_hip_y,right_hip_x,right_hip_y,left_knee_x,left_knee_y,right_knee_x,right_knee_y,left_ankle_x,left_ankle_y,right_ankle_x,right_ankle_y\n186,89,200,77,177,78,224,86,167,85,244,158,154,154,258,248,143,239,265,327,136,313,234,311,170,311,247,446,134,445,262,561,92,571\n182,84,191,73,171,74,202,75,157,77,220,119,139,136,260,192,185,230,268,209,246,217,221,288,176,294,205,421,174,421,186,538,155,564\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/tracker/__init__.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Module contains pose tracker implementations.\"\"\"\n\nfrom tracker.bounding_box_tracker import BoundingBoxTracker\nfrom tracker.config import KeypointTrackerConfig\nfrom tracker.config import TrackerConfig\nfrom tracker.keypoint_tracker import KeypointTracker\nfrom tracker.tracker import Track\nfrom tracker.tracker import Tracker\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/tracker/bounding_box_tracker.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Bounding box tracker implementation.\"\"\"\n\nfrom typing import List\n\nfrom data import Person\nfrom tracker.tracker import Track\nfrom tracker.tracker import Tracker\n\n\nclass BoundingBoxTracker(Tracker):\n  \"\"\"Tracks objects based on bounding box similarity.\n\n  Similarity is currently defined as intersection-over-union (IoU).\n  \"\"\"\n\n  def _compute_similarity(self, persons: List[Person]) -> List[List[float]]:\n    \"\"\"Computes similarity based on intersection-over-union (IoU).\n\n    Args:\n      persons: An array of detected `Person`s.\n\n    Returns:\n      A 2D array of shape [num_det, num_tracks] with pairwise similarity scores\n      between detections and tracks.\n    \"\"\"\n    if (not persons) or (not self._tracks):\n      return [[]]\n\n    sim_matrix = []\n    for person in persons:\n      row = []\n      for track in self._tracks:\n        row.append(self._iou(person, track))\n      sim_matrix.append(row)\n    return sim_matrix\n\n  def _iou(self, person: Person, track: Track) -> float:\n    \"\"\"Computes the intersection-over-union (IoU) between a pose and a track.\n\n    Args:\n      person: A `Person`.\n      track: A `Track`.\n\n    Returns:\n      The IoU  between the person and the track. This number is between 0 and 1,\n      and larger values indicate more box similarity.\n    \"\"\"\n    x_min = max(person.bounding_box.start_point.x,\n                track.person.bounding_box.start_point.x)\n    y_min = max(person.bounding_box.start_point.y,\n                track.person.bounding_box.start_point.y)\n    x_max = min(person.bounding_box.end_point.x,\n                track.person.bounding_box.end_point.x)\n    y_max = min(person.bounding_box.end_point.y,\n                track.person.bounding_box.end_point.y)\n\n    person_width = person.bounding_box.end_point.x - person.bounding_box.start_point.x\n    person_height = person.bounding_box.end_point.y - person.bounding_box.start_point.y\n    track_width = track.person.bounding_box.end_point.x - track.person.bounding_box.start_point.x\n    track_height = track.person.bounding_box.end_point.y - track.person.bounding_box.start_point.y\n\n    if (x_min >= x_max or y_min >= y_max):\n      return 0.0\n\n    intersection = (x_max - x_min) * (y_max - y_min)\n    area_person = person_width * person_height\n    area_track = track_width * track_height\n    return float(intersection) / (area_person + area_track - intersection)\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/tracker/bounding_box_tracker_test.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Unit test of Bounding box tracker.\"\"\"\n\nimport unittest\n\nfrom data import Person\nfrom data import Point\nfrom data import Rectangle\nfrom tracker.bounding_box_tracker import BoundingBoxTracker\nfrom tracker.config import KeypointTrackerConfig\nfrom tracker.config import TrackerConfig\nfrom tracker.tracker import Track\n\n_MAX_TRACKS = 4\n_MAX_AGE = 1000 * 1000\n_MIN_SIMILARITY = 0.5\n\n\nclass BoundingBoxTrackerTest(unittest.TestCase):\n\n  def setUp(self):\n    super().setUp()\n    self.tracker_config = TrackerConfig(KeypointTrackerConfig(), _MAX_TRACKS,\n                                        _MAX_AGE, _MIN_SIMILARITY)\n    self.bbox_tracker = BoundingBoxTracker(self.tracker_config)\n\n  def test_iou(self):\n    \"\"\"Test IoU.\"\"\"\n    person = Person([], Rectangle(Point(0, 0), Point(1, 2 / 3)), 1)\n    track = Track(\n        Person([], Rectangle(Point(0, 1 / 3), Point(1, 1)), 1), 1000000)\n    computed_iou = self.bbox_tracker._iou(person, track)\n    self.assertAlmostEqual(computed_iou, 1 / 3, 6)\n\n  def test_iou_full_overlap(self):\n    \"\"\"Test IoU full overlap.\"\"\"\n    person = Person([], Rectangle(Point(0, 0), Point(1, 1)), 1)\n    track = Track(Person([], Rectangle(Point(0, 0), Point(1, 1)), 1), 1000000)\n    computed_iou = self.bbox_tracker._iou(person, track)\n    self.assertAlmostEqual(computed_iou, 1.0, 6)\n\n  def test_iou_no_intersection(self):\n    \"\"\"Test IoU with no intersection.\"\"\"\n    person = Person([], Rectangle(Point(0, 0), Point(0.5, 0.5)), 1)\n    track = Track(\n        Person([], Rectangle(Point(0.5, 0.5), Point(1, 1)), 1), 1000000)\n    computed_iou = self.bbox_tracker._iou(person, track)\n    self.assertAlmostEqual(computed_iou, 0.0, 6)\n\n  def test_bounding_box_tracker(self):\n    \"\"\"Test BoundingBoxTracker.\"\"\"\n\n    # Timestamp: 0. Person becomes the first two tracks\n    persons = [\n        Person([], Rectangle(Point(0, 0), Point(0.5, 0.5)), 1),\n        Person(\n            [],\n            Rectangle(Point(0, 0), Point(1, 1)),\n            1,\n        )\n    ]\n    persons = self.bbox_tracker.apply(persons, 0)\n    tracks = self.bbox_tracker._tracks\n    self.assertEqual(len(persons), 2)\n    self.assertEqual(persons[0].id, 1)\n    self.assertEqual(persons[1].id, 2)\n    self.assertEqual(len(tracks), 2)\n    self.assertEqual(tracks[0].person.id, 1)\n    self.assertEqual(tracks[0].last_timestamp, 0)\n    self.assertEqual(tracks[1].person.id, 2)\n    self.assertEqual(tracks[1].last_timestamp, 0)\n\n    # Timestamp: 100000. First person is linked with track 1. Second person\n    # spawns a new track (id = 2).\n    persons = [\n        Person([], Rectangle(Point(0.1, 0.1), Point(0.5, 0.5)), 1),\n        Person([], Rectangle(Point(0.3, 0.2), Point(0.9, 0.9)), 1)\n    ]\n    persons = self.bbox_tracker.apply(persons, 100000)\n    tracks = self.bbox_tracker._tracks\n    self.assertEqual(len(persons), 2)\n    self.assertEqual(persons[0].id, 1)\n    self.assertEqual(persons[1].id, 3)\n    self.assertEqual(len(tracks), 3)\n    self.assertEqual(tracks[0].person.id, 1)\n    self.assertEqual(tracks[0].last_timestamp, 100000)\n    self.assertEqual(tracks[1].person.id, 3)\n    self.assertEqual(tracks[1].last_timestamp, 100000)\n    self.assertEqual(tracks[2].person.id, 2)\n    self.assertEqual(tracks[2].last_timestamp, 0)\n\n    # Timestamp: 1050000. First person is linked with track 1. Second person is\n    # identical to track 2, but is not linked because track 2 is deleted due to\n    # age. Instead it spawns track 4.\n    persons = [\n        Person([], Rectangle(Point(0.1, 0.1), Point(0.5, 0.55)), 1),\n        Person([], Rectangle(Point(0, 0), Point(1, 1)), 1)\n    ]\n    persons = self.bbox_tracker.apply(persons, 1050000)\n    tracks = self.bbox_tracker._tracks\n    self.assertEqual(len(persons), 2)\n    self.assertEqual(persons[0].id, 1)\n    self.assertEqual(persons[1].id, 4)\n    self.assertEqual(len(tracks), 3)\n    self.assertEqual(tracks[0].person.id, 1)\n    self.assertEqual(tracks[0].last_timestamp, 1050000)\n    self.assertEqual(tracks[1].person.id, 4)\n    self.assertEqual(tracks[1].last_timestamp, 1050000)\n    self.assertEqual(tracks[2].person.id, 3)\n    self.assertEqual(tracks[2].last_timestamp, 100000)\n\n\nif __name__ == '__main__':\n  unittest.main()\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/tracker/config.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Configuration of pose trackers.\"\"\"\nfrom typing import NamedTuple, List\n\n\nclass KeypointTrackerConfig(NamedTuple):\n  \"\"\"Keypoint tracker specific configuration.\"\"\"\n\n  keypoint_confidence_threshold: float = 0.3\n  \"\"\"The minimum keypoint confidence threshold.\n\n    A keypoint is only compared in the OKS calculation if both the new detected\n    keypoint and the\n    corresponding track keypoint have confidences above this threshold\n  \"\"\"\n\n  keypoint_falloff: List[float] = [\n      0.026, 0.025, 0.025, 0.035, 0.035, 0.079, 0.079, 0.072, 0.072, 0.062,\n      0.062, 0.107, 0.107, 0.087, 0.087, 0.089, 0.089\n  ]\n  \"\"\"Per-keypoint falloff in OKS calculation.\"\"\"\n\n  min_number_of_keypoints: int = 4\n  \"\"\"The minimum number of keypoints that are necessary for computing OKS.\n\n    If the number of confident keypoints (between a pose and track) are under\n    this value, an OKS of 0.0\n    will be given.\n  \"\"\"\n\n\nclass TrackerConfig(NamedTuple):\n  \"\"\"Shared config for pose trackers.\"\"\"\n\n  keypoint_tracker_params: KeypointTrackerConfig = KeypointTrackerConfig()\n  \"\"\"Keypoint tracker params.\"\"\"\n\n  max_tracks: int = 12\n  \"\"\"The maximum number of tracks that an internal tracker will maintain.\"\"\"\n\n  max_age: int = 1000 * 1000\n  \"\"\" Maximum track lifetime.\n\n    The maximum duration of time (in milliseconds) that a track can exist\n    without being\n    linked with a new detection before it is removed. Set this value large if\n    you would\n    like to recover people that are not detected for long stretches of time (at\n    the cost\n    of potential false re-identifications).\n  \"\"\"\n\n  min_similarity: float = 0.4\n  \"\"\"New poses will only be linked with tracks if the similarity score exceeds this threshold.\"\"\"\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/tracker/keypoint_tracker.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Keypoint tracker implementation.\"\"\"\n\nimport math\nfrom typing import List\n\nfrom data import Person\nfrom tracker.tracker import Track\nfrom tracker.tracker import Tracker\n\n\nclass KeypointTracker(Tracker):\n  \"\"\"KeypointTracker, which tracks poses based on keypoint similarity.\n\n  This tracker assumes that keypoints are provided in normalized image\n  coordinates.\n  \"\"\"\n\n  def _compute_similarity(self, persons: List[Person]) -> List[List[float]]:\n    \"\"\"Computes similarity based on Object Keypoint Similarity (OKS).\n\n    Args:\n        persons: An array of detected `Person`s.\n\n    Returns:\n      A 2D array of shape [num_det, num_tracks] with pairwise similarity scores\n      between detections and tracks.\n    \"\"\"\n    if (not persons) or (not self._tracks):\n      return [[]]\n\n    sim_matrix = []\n    for person in persons:\n      row = []\n      for track in self._tracks:\n        row.append(self._object_keypoint_similarity(person, track))\n      sim_matrix.append(row)\n    return sim_matrix\n\n  def _object_keypoint_similarity(self, person: Person, track: Track) -> float:\n    \"\"\"Computes the Object Keypoint Similarity (OKS) between a person and track.\n\n    This is similar in spirit to the calculation used by COCO keypoint eval:\n    https://cocodataset.org/#keypoints-eval\n    In this case, OKS is calculated as:\n    (1/sum_i d(c_i, c_ti)) * sum_i exp(-d_i^2/(2*a_ti*x_i^2))*d(c_i, c_ti)\n    where:\n        d(x, y) is an indicator function which only produces 1 if x and y\n    exceed a given threshold (i.e. keypointThreshold), otherwise 0.\n        c_i is the confidence of keypoint i from the new person\n        c_ti is the confidence of keypoint i from the track\n        d_i is the Euclidean distance between the person and track keypoint\n        a_ti is the area of the track object (the box covering the\n        keypoints)\n        x_i is a constant that controls falloff in a Gaussian distribution,\n    computed as 2*keypointFalloff[i].\n\n    Args:\n      person: A `Person`.\n      track: A `Track`.\n\n    Returns:\n      The OKS score between the person and the track. This number is between 0\n      and 1, and larger values indicate more keypoint similarity.\n    \"\"\"\n    box_area = self._area(track) + 1e-6\n    oks_total = 0\n    num_valid_keypoints = 0\n    for i in range(len(person.keypoints)):\n      person_kpt = person.keypoints[i]\n      track_kpt = track.person.keypoints[i]\n      if (person_kpt.score <\n          self._config.keypoint_tracker_params.keypoint_confidence_threshold or\n          track_kpt.score <\n          self._config.keypoint_tracker_params.keypoint_confidence_threshold):\n        continue\n\n      num_valid_keypoints += 1\n      d_squared = ((person_kpt.coordinate.x - track_kpt.coordinate.x)**2 +\n                   (person_kpt.coordinate.y - track_kpt.coordinate.y)**2)\n      x = 2 * self._config.keypoint_tracker_params.keypoint_falloff[i]\n      oks_total += math.exp(-1 * d_squared / (2 * box_area * (x**2)))\n    if (num_valid_keypoints <\n        self._config.keypoint_tracker_params.min_number_of_keypoints):\n      return 0.0\n\n    return oks_total / num_valid_keypoints\n\n  def _area(self, track: Track) -> float:\n    \"\"\"Computes the area of a bounding box that tightly covers keypoints.\n\n    Args:\n        track: A 'Track'.\n\n    Returns:\n      The area of the object.\n    \"\"\"\n    keypoint = list(\n        filter(\n            lambda kpt: kpt.score > self._config.keypoint_tracker_params.\n            keypoint_confidence_threshold, track.person.keypoints))\n    x_min = min([1] + [kpt.coordinate.x for kpt in keypoint])\n    y_min = min([1] + [kpt.coordinate.y for kpt in keypoint])\n    x_max = max([0] + [kpt.coordinate.x for kpt in keypoint])\n    y_max = max([0] + [kpt.coordinate.y for kpt in keypoint])\n\n    return (x_max - x_min) * (y_max - y_min)\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/tracker/keypoint_tracker_test.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Unit test of Keypoint tracker.\"\"\"\n\nimport math\nimport unittest\n\nfrom data import BodyPart\nfrom data import KeyPoint\nfrom data import Person\nfrom data import Point\nfrom data import Rectangle\nfrom tracker.config import KeypointTrackerConfig\nfrom tracker.config import TrackerConfig\nfrom tracker.keypoint_tracker import KeypointTracker\nfrom tracker.tracker import Track\n\n_KEYPOINT_CONFIDENCE_THRESHOLD = 0.2\n_KEYPOINT_FALLOFF = [0.1, 0.1, 0.1, 0.1]\n_MIN_NUMBER_OF_KEYPOINTS = 2\n\n\nclass KeypointTrackerTest(unittest.TestCase):\n\n  def setUp(self):\n    super().setUp()\n    kpt_config = KeypointTrackerConfig(_KEYPOINT_CONFIDENCE_THRESHOLD,\n                                       _KEYPOINT_FALLOFF,\n                                       _MIN_NUMBER_OF_KEYPOINTS)\n    self.tracker_config = TrackerConfig(kpt_config)\n    self.kpt_tracker = KeypointTracker(self.tracker_config)\n\n  def test_oks(self):\n    \"\"\"Test OKS.\"\"\"\n    person = Person([\n        KeyPoint(BodyPart(0), Point(0.2, 0.2), 1),\n        KeyPoint(BodyPart(1), Point(0.4, 0.4), 0.8),\n        KeyPoint(BodyPart(2), Point(0.6, 0.6), 0.1),\n        KeyPoint(BodyPart(3), Point(0.8, 0.7), 0.8)\n    ], Rectangle(Point(0, 0), Point(0, 0)), 1)\n    track = Track(\n        Person([\n            KeyPoint(BodyPart(0), Point(0.2, 0.2), 1),\n            KeyPoint(BodyPart(1), Point(0.4, 0.4), 0.8),\n            KeyPoint(BodyPart(2), Point(0.6, 0.6), 0.9),\n            KeyPoint(BodyPart(3), Point(0.8, 0.8), 0.8)\n        ], Rectangle(Point(0, 0), Point(0, 0)), 1), 1000000)\n\n    oks = self.kpt_tracker._object_keypoint_similarity(person, track)\n\n    box_area = (0.8 - 0.2) * (0.8 - 0.2)\n    x = 2 * self.tracker_config.keypoint_tracker_params.keypoint_falloff[3]\n    d = 0.1\n    expected_oks = (1 + 1 + math.exp(-1 * (d**2) / (2 * box_area * (x**2)))) / 3\n\n    self.assertAlmostEqual(oks, expected_oks, 6)\n\n  def test_oks_returns_zero(self):\n    \"\"\"Compute OKS returns 0.0 with less than 2 valid keypoints.\"\"\"\n    person = Person([\n        KeyPoint(BodyPart(0), Point(0.2, 0.2), 1),\n        KeyPoint(BodyPart(1), Point(0.4, 0.4), 0.1),\n        KeyPoint(BodyPart(2), Point(0.6, 0.6), 0.9),\n        KeyPoint(BodyPart(3), Point(0.8, 0.8), 0.8)\n    ], Rectangle(Point(0, 0), Point(0, 0)), 1)\n    track = Track(\n        Person([\n            KeyPoint(BodyPart(0), Point(0.2, 0.2), 1),\n            KeyPoint(BodyPart(1), Point(0.4, 0.4), 0.8),\n            KeyPoint(BodyPart(2), Point(0.6, 0.6), 0.1),\n            KeyPoint(BodyPart(3), Point(0.8, 0.8), 0.1)\n        ], Rectangle(Point(0, 0), Point(0, 0)), 1), 1000000)\n\n    oks = self.kpt_tracker._object_keypoint_similarity(person, track)\n    self.assertAlmostEqual(oks, 0.0, 6)\n\n  def test_area(self):\n    \"\"\"Test area.\"\"\"\n    track = Track(\n        Person([\n            KeyPoint(BodyPart(0), Point(0.1, 0.2), 1),\n            KeyPoint(BodyPart(1), Point(0.3, 0.4), 0.9),\n            KeyPoint(BodyPart(2), Point(0.4, 0.6), 0.9),\n            KeyPoint(BodyPart(3), Point(0.7, 0.8), 0.1)\n        ], Rectangle(Point(0, 0), Point(0, 0)), 1), 1000000)\n\n    area = self.kpt_tracker._area(track)\n    expected_area = (0.4 - 0.1) * (0.6 - 0.2)\n    self.assertAlmostEqual(area, expected_area, 6)\n\n  def test_keypoint_tracker(self):\n    \"\"\"Test Keypoint tracker.\"\"\"\n\n    # Timestamp: 0. Person becomes the only track\n    persons = [\n        Person([\n            KeyPoint(BodyPart(0), Point(0.2, 0.2), 1),\n            KeyPoint(BodyPart(1), Point(0.4, 0.4), 0.8),\n            KeyPoint(BodyPart(2), Point(0.6, 0.6), 0.9),\n            KeyPoint(BodyPart(3), Point(0.8, 0.8), 0.0)\n        ], Rectangle(Point(0, 0), Point(0, 0)), 1)\n    ]\n\n    persons = self.kpt_tracker.apply(persons, 0)\n    tracks = self.kpt_tracker._tracks\n    self.assertEqual(len(persons), 1)\n    self.assertEqual(persons[0].id, 1)\n    self.assertEqual(len(tracks), 1)\n    self.assertEqual(tracks[0].person.id, 1)\n    self.assertEqual(tracks[0].last_timestamp, 0)\n\n    # Timestamp: 100000. First person is linked with track 1. Second person\n    # spawns a new track (id = 2).\n    persons = [\n        Person([\n            KeyPoint(BodyPart(0), Point(0.2, 0.2), 1),\n            KeyPoint(BodyPart(1), Point(0.4, 0.4), 0.8),\n            KeyPoint(BodyPart(2), Point(0.6, 0.6), 0.9),\n            KeyPoint(BodyPart(3), Point(0.8, 0.8), 0.8)\n        ], Rectangle(Point(0, 0), Point(0, 0)), 1),\n        Person(\n            [\n                KeyPoint(BodyPart(0), Point(0.8, 0.8), 0.8),\n                KeyPoint(BodyPart(1), Point(0.6, 0.6), 0.3),\n                KeyPoint(BodyPart(2), Point(0.4, 0.4), 0.1),  # Low confidence.\n                KeyPoint(BodyPart(3), Point(0.2, 0.2), 0.8)\n            ],\n            Rectangle(Point(0, 0), Point(0, 0)),\n            1)\n    ]\n\n    persons = self.kpt_tracker.apply(persons, 100000)\n    tracks = self.kpt_tracker._tracks\n    self.assertEqual(len(persons), 2)\n    self.assertEqual(persons[0].id, 1)\n    self.assertEqual(persons[1].id, 2)\n    self.assertEqual(len(tracks), 2)\n    self.assertEqual(tracks[0].person.id, 1)\n    self.assertEqual(tracks[0].last_timestamp, 100000)\n    self.assertEqual(tracks[1].person.id, 2)\n    self.assertEqual(tracks[1].last_timestamp, 100000)\n\n    # Timestamp: 900000. First person is linked with track 2. Second person\n    # spawns a new track (id = 3).\n    persons = [  # Links with id = 2.\n        Person(\n            [\n                KeyPoint(BodyPart(0), Point(0.6, 0.7), 0.7),\n                KeyPoint(BodyPart(1), Point(0.5, 0.6), 0.7),\n                KeyPoint(BodyPart(2), Point(0.0, 0.0), 0.1),  # Low confidence.\n                KeyPoint(BodyPart(3), Point(0.2, 0.1), 1.0)\n            ],\n            Rectangle(Point(0, 0), Point(0, 0)),\n            1),\n        # Becomes id = 3.\n        Person(\n            [\n                KeyPoint(BodyPart(0), Point(0.5, 0.1), 0.6),\n                KeyPoint(BodyPart(1), Point(0.9, 0.3), 0.6),\n                KeyPoint(BodyPart(2), Point(0.1, 0.1), 0.9),\n                KeyPoint(BodyPart(3), Point(0.4, 0.4), 0.1)\n            ],  # Low confidence.\n            Rectangle(Point(0, 0), Point(0, 0)),\n            1)\n    ]\n\n    persons = self.kpt_tracker.apply(persons, 900000)\n    tracks = self.kpt_tracker._tracks\n    self.assertEqual(len(persons), 2)\n    self.assertEqual(persons[0].id, 2)\n    self.assertEqual(persons[1].id, 3)\n    self.assertEqual(len(tracks), 3)\n    self.assertEqual(tracks[0].person.id, 2)\n    self.assertEqual(tracks[0].last_timestamp, 900000)\n    self.assertEqual(tracks[1].person.id, 3)\n    self.assertEqual(tracks[1].last_timestamp, 900000)\n    self.assertEqual(tracks[2].person.id, 1)\n    self.assertEqual(tracks[2].last_timestamp, 100000)\n\n    # Timestamp: 1200000. First person spawns a new track (id = 4), even though\n    # it has the same keypoints as track 1. This is because the age exceeds\n    # 1000 msec. The second person links with id 2. The third person spawns a\n    # new track (id = 5).\n    persons = [  # Becomes id = 4.\n        Person([\n            KeyPoint(BodyPart(0), Point(0.2, 0.2), 1.0),\n            KeyPoint(BodyPart(1), Point(0.4, 0.4), 0.8),\n            KeyPoint(BodyPart(2), Point(0.6, 0.6), 0.9),\n            KeyPoint(BodyPart(3), Point(0.8, 0.8), 0.8)\n        ], Rectangle(Point(0, 0), Point(0, 0)), 1),\n        # Links with id = 2.\n        Person(\n            [\n                KeyPoint(BodyPart(0), Point(0.55, 0.7), 0.7),\n                KeyPoint(BodyPart(1), Point(0.5, 0.6), 0.9),\n                KeyPoint(BodyPart(2), Point(1.0, 1.0), 0.1),  # Low confidence.\n                KeyPoint(BodyPart(3), Point(0.8, 0.1), 0.0)\n            ],  # Low confidence.\n            Rectangle(Point(0, 0), Point(0, 0)),\n            1),\n        # Becomes id = 5.\n        Person(\n            [\n                KeyPoint(BodyPart(0), Point(0.1, 0.1), 0.1),  # Low confidence.\n                KeyPoint(BodyPart(1), Point(0.2, 0.2), 0.9),\n                KeyPoint(BodyPart(2), Point(0.3, 0.3), 0.7),\n                KeyPoint(BodyPart(3), Point(0.4, 0.4), 0.8)\n            ],\n            Rectangle(Point(0, 0), Point(0, 0)),\n            1)\n    ]\n\n    persons = self.kpt_tracker.apply(persons, 1200000)\n    tracks = self.kpt_tracker._tracks\n    self.assertEqual(len(persons), 3)\n    self.assertEqual(persons[0].id, 4)\n    self.assertEqual(persons[1].id, 2)\n    self.assertEqual(len(tracks), 4)\n    self.assertEqual(tracks[0].person.id, 2)\n    self.assertEqual(tracks[0].last_timestamp, 1200000)\n    self.assertEqual(tracks[1].person.id, 4)\n    self.assertEqual(tracks[1].last_timestamp, 1200000)\n    self.assertEqual(tracks[2].person.id, 5)\n    self.assertEqual(tracks[2].last_timestamp, 1200000)\n    self.assertEqual(tracks[3].person.id, 3)\n    self.assertEqual(tracks[3].last_timestamp, 900000)\n\n    # Timestamp: 1300000. First person spawns a new track (id = 6). Since\n    # max_tracks is 4, the oldest track (id = 3) is removed.\n    persons = [  # Becomes id = 6.\n        Person([\n            KeyPoint(BodyPart(0), Point(0.1, 0.8), 1.0),\n            KeyPoint(BodyPart(1), Point(0.2, 0.9), 0.6),\n            KeyPoint(BodyPart(2), Point(0.2, 0.9), 0.5),\n            KeyPoint(BodyPart(3), Point(0.8, 0.2), 0.4)\n        ], Rectangle(Point(0, 0), Point(0, 0)), 1)\n    ]\n\n    persons = self.kpt_tracker.apply(persons, 1300000)\n    tracks = self.kpt_tracker._tracks\n    self.assertEqual(len(persons), 1)\n    self.assertEqual(persons[0].id, 6)\n    self.assertEqual(len(tracks), 4)\n    self.assertEqual(tracks[0].person.id, 6)\n    self.assertEqual(tracks[0].last_timestamp, 1300000)\n    self.assertEqual(tracks[1].person.id, 2)\n    self.assertEqual(tracks[1].last_timestamp, 1200000)\n    self.assertEqual(tracks[2].person.id, 4)\n    self.assertEqual(tracks[2].last_timestamp, 1200000)\n    self.assertEqual(tracks[3].person.id, 5)\n    self.assertEqual(tracks[3].last_timestamp, 1200000)\n\n\nif __name__ == '__main__':\n  unittest.main()\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/tracker/tracker.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Shared implementation of pose trackers.\"\"\"\n\nimport abc\nfrom typing import List, NamedTuple\n\nfrom data import Person\nfrom tracker.config import TrackerConfig\n\n\nclass Track(NamedTuple):\n  person: Person\n  \"\"\"A person contain keypoint, bounding_box, score and id.\"\"\"\n\n  last_timestamp: int\n  \"\"\"The last timestamp (in milliseconds) in which a track is recorded.\"\"\"\n\n\nclass Tracker(object):\n  \"\"\"A stateful tracker for associating detections between frames.\n\n  This is an abstract base class that performs generic mechanics.\n  Implementations must inherit from this class.\n  \"\"\"\n\n  def __init__(self, config: TrackerConfig) -> None:\n    \"\"\"Initializes a Tracker.\"\"\"\n    self._tracks = []\n    self._config = config\n    self._next_track_id = 0\n\n  def apply(self, persons: List[Person], timestamp: int) -> List[Person]:\n    \"\"\"Tracks person instances across frames based on detections.\n\n    Args:\n      persons: An array of detected `Person`s.\n      timestamp: The timestamp associated with the incoming Persons, in\n        microseconds.\n\n    Returns:\n      An updated array of `Person`s with tracking id properties.\n    \"\"\"\n    self._filter_old_tracks(timestamp)\n    sim_matrix = self._compute_similarity(persons)\n    self._assign_tracks(persons, sim_matrix, timestamp)\n    self._update_tracks()\n    return persons\n\n  @abc.abstractmethod\n  def _compute_similarity(self, persons: List[Person]) -> List[List[float]]:\n    \"\"\"Computes pairwise similarity scores between detections and tracks.\n\n    Args:\n        persons: An array of detected `Person`s.\n\n    Returns:\n      A 2D array of shape [num_det, num_tracks] with pairwise similarity scores\n      between detections and tracks.\n    \"\"\"\n    pass\n\n  def _filter_old_tracks(self, timestamp: int) -> List[Track]:\n    \"\"\"Filters tracks based on their age.\n\n    Args:\n      timestamp: The current timestamp in microseconds.\n\n    Returns:\n      Filtered list of tracks.\n    \"\"\"\n    self._tracks = list(\n        filter(\n            lambda track: timestamp - track.last_timestamp <= self._config.\n            max_age, self._tracks))\n    return self._tracks\n\n  def _assign_tracks(self, persons: List[Person], sim_matrix: List[List[float]],\n                     timestamp: int) -> None:\n    \"\"\"Performs a greedy optimization to link detections with tracks.\n\n    The `poses` array is updated in place by providing an `id` property. If\n    incoming detections are not linked with existing tracks, new tracks will be\n    created.\n\n    Args:\n      persons: An array of detected `Person`s. It's assumed that persons are\n        sorted from most confident to least confident.\n      sim_matrix: A 2D array of shape [num_det, num_tracks] with pairwise\n        similarity scores between detections and tracks.\n      timestamp: The current timestamp in microseconds.\n    \"\"\"\n    unmatched_track_indices = list(range(len(sim_matrix[0])))\n    detection_indices = list(range(len(persons)))\n    unmatched_detection_indices = []\n\n    for detection_index in detection_indices:\n      if not unmatched_track_indices:\n        unmatched_detection_indices.append(detection_index)\n        continue\n\n      # Assign the detection to the track which produces the highest pairwise\n      # similarity score, assuming the score exceeds the minimum similarity\n      # threshold.\n      max_track_index = -1\n      max_similarity = -1\n      for track_index in unmatched_track_indices:\n        similarity = sim_matrix[detection_index][track_index]\n        if (similarity >= self._config.min_similarity and\n            similarity > max_similarity):\n          max_track_index = track_index\n          max_similarity = similarity\n\n      if max_track_index >= 0:\n        # Link the detection with the highest scoring track.\n        linked_track = self._tracks[max_track_index]\n        self._tracks[max_track_index] = self._create_track(\n            persons[detection_index], timestamp, linked_track.person.id)\n\n        persons[detection_index] = persons[detection_index]._replace(\n            id=linked_track.person.id)\n        unmatched_track_indices.remove(max_track_index)\n      else:\n        unmatched_detection_indices.append(detection_index)\n\n    # Spawn new tracks for all unmatched detections.\n    for detection_index in unmatched_detection_indices:\n      new_track = self._create_track(persons[detection_index], timestamp)\n      self._tracks.append(new_track)\n      persons[detection_index] = persons[detection_index]._replace(\n          id=new_track.person.id)\n\n  def _update_tracks(self) -> None:\n    \"\"\"Updates the stored tracks in the tracker.\n\n    Specifically, the following operations are applied in order:\n    * 1. Tracks are sorted based on freshness (i.e. the most recently\n    linked\n    tracks are placed at the beginning of the array and the most stale\n    are at the end).\n    * 2. The tracks array is sliced to only contain `maxTracks` tracks\n    (i.e. the\n    most fresh tracks).\n\n    Returns:\n      Updated list of tracks.\n    \"\"\"\n    self._tracks = sorted(\n        self._tracks, key=lambda track: track.last_timestamp, reverse=True)\n    self._tracks = self._tracks[:self._config.max_tracks]\n\n  def _create_track(self,\n                    person: Person,\n                    timestamp: int,\n                    track_id: int = None) -> Track:\n    \"\"\"Creates a track from information in a pose.\n\n    Args:\n      person: A `Person`.\n      timestamp: The current timestamp in microseconds.\n      track_id: The id to assign to the new track. If not provided, will assign\n        the next available id.\n\n    Returns:\n      A `Track`.\n    \"\"\"\n    person = Person(\n        person.keypoints, person.bounding_box, person.score,\n        track_id if track_id else self._update_and_get_next_track_id())\n    track = Track(person, timestamp)\n    return track\n\n  def _update_and_get_next_track_id(self):\n    \"\"\"Returns the next track ID.\"\"\"\n    self._next_track_id += 1\n    return self._next_track_id\n\n  def _remove(self, ids: List[str]) -> None:\n    \"\"\"Removes specific tracks, based on their ids.\"\"\"\n    self._tracks = list(filter(lambda x: x.person.id not in ids, self._tracks))\n\n  def _reset(self) -> None:\n    \"\"\"Resets tracks.\"\"\"\n    self._tracks = []\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/utils.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Utility functions to display the pose detection results.\"\"\"\n\nimport math\nfrom typing import List, Tuple\n\nimport cv2\nfrom data import Person\nimport numpy as np\n\n# map edges to a RGB color\nKEYPOINT_EDGE_INDS_TO_COLOR = {\n    (0, 1): (147, 20, 255),\n    (0, 2): (255, 255, 0),\n    (1, 3): (147, 20, 255),\n    (2, 4): (255, 255, 0),\n    (0, 5): (147, 20, 255),\n    (0, 6): (255, 255, 0),\n    (5, 7): (147, 20, 255),\n    (7, 9): (147, 20, 255),\n    (6, 8): (255, 255, 0),\n    (8, 10): (255, 255, 0),\n    (5, 6): (0, 255, 255),\n    (5, 11): (147, 20, 255),\n    (6, 12): (255, 255, 0),\n    (11, 12): (0, 255, 255),\n    (11, 13): (147, 20, 255),\n    (13, 15): (147, 20, 255),\n    (12, 14): (255, 255, 0),\n    (14, 16): (255, 255, 0)\n}\n\n# A list of distictive colors\nCOLOR_LIST = [\n    (47, 79, 79),\n    (139, 69, 19),\n    (0, 128, 0),\n    (0, 0, 139),\n    (255, 0, 0),\n    (255, 215, 0),\n    (0, 255, 0),\n    (0, 255, 255),\n    (255, 0, 255),\n    (30, 144, 255),\n    (255, 228, 181),\n    (255, 105, 180),\n]\n\n\ndef visualize(\n    image: np.ndarray,\n    list_persons: List[Person],\n    keypoint_color: Tuple[int, ...] = None,\n    keypoint_threshold: float = 0.05,\n    instance_threshold: float = 0.1,\n) -> np.ndarray:\n  \"\"\"Draws landmarks and edges on the input image and return it.\n\n  Args:\n    image: The input RGB image.\n    list_persons: The list of all \"Person\" entities to be visualize.\n    keypoint_color: the colors in which the landmarks should be plotted.\n    keypoint_threshold: minimum confidence score for a keypoint to be drawn.\n    instance_threshold: minimum confidence score for a person to be drawn.\n\n  Returns:\n    Image with keypoints and edges.\n  \"\"\"\n  for person in list_persons:\n    if person.score < instance_threshold:\n      continue\n\n    keypoints = person.keypoints\n    bounding_box = person.bounding_box\n\n    # Assign a color to visualize keypoints.\n    if keypoint_color is None:\n      if person.id is None:\n        # If there's no person id, which means no tracker is enabled, use\n        # a default color.\n        person_color = (0, 255, 0)\n      else:\n        # If there's a person id, use different color for each person.\n        person_color = COLOR_LIST[person.id % len(COLOR_LIST)]\n    else:\n      person_color = keypoint_color\n\n    # Draw all the landmarks\n    for i in range(len(keypoints)):\n      if keypoints[i].score >= keypoint_threshold:\n        cv2.circle(image, keypoints[i].coordinate, 2, person_color, 4)\n\n    # Draw all the edges\n    for edge_pair, edge_color in KEYPOINT_EDGE_INDS_TO_COLOR.items():\n      if (keypoints[edge_pair[0]].score > keypoint_threshold and\n          keypoints[edge_pair[1]].score > keypoint_threshold):\n        cv2.line(image, keypoints[edge_pair[0]].coordinate,\n                 keypoints[edge_pair[1]].coordinate, edge_color, 2)\n\n    # Draw bounding_box with multipose\n    if bounding_box is not None:\n      start_point = bounding_box.start_point\n      end_point = bounding_box.end_point\n      cv2.rectangle(image, start_point, end_point, person_color, 2)\n      # Draw id text when tracker is enabled for MoveNet MultiPose model.\n      # (id = None when using single pose model or when tracker is None)\n      if person.id:\n        id_text = 'id = ' + str(person.id)\n        cv2.putText(image, id_text, start_point, cv2.FONT_HERSHEY_PLAIN, 1,\n                    (0, 0, 255), 1)\n\n  return image\n\n\ndef keep_aspect_ratio_resizer(\n    image: np.ndarray, target_size: int) -> Tuple[np.ndarray, Tuple[int, int]]:\n  \"\"\"Resizes the image.\n\n  The function resizes the image such that its longer side matches the required\n  target_size while keeping the image aspect ratio. Note that the resizes image\n  is padded such that both height and width are a multiple of 32, which is\n  required by the model. See\n  https://tfhub.dev/google/tfjs-model/movenet/multipose/lightning/1 for more\n  detail.\n\n  Args:\n    image: The input RGB image as a numpy array of shape [height, width, 3].\n    target_size: Desired size that the image should be resize to.\n\n  Returns:\n    image: The resized image.\n    (target_height, target_width): The actual image size after resize.\n\n  \"\"\"\n  height, width, _ = image.shape\n  if height > width:\n    scale = float(target_size / height)\n    target_height = target_size\n    scaled_width = math.ceil(width * scale)\n    image = cv2.resize(image, (scaled_width, target_height))\n    target_width = int(math.ceil(scaled_width / 32) * 32)\n  else:\n    scale = float(target_size / width)\n    target_width = target_size\n    scaled_height = math.ceil(height * scale)\n    image = cv2.resize(image, (target_width, scaled_height))\n    target_height = int(math.ceil(scaled_height / 32) * 32)\n\n  padding_top, padding_left = 0, 0\n  padding_bottom = target_height - image.shape[0]\n  padding_right = target_width - image.shape[1]\n  # add padding to image\n  image = cv2.copyMakeBorder(image, padding_top, padding_bottom, padding_left,\n                             padding_right, cv2.BORDER_CONSTANT)\n  return image, (target_height, target_width)\n"
  },
  {
    "path": "lite/examples/pose_estimation/raspberry_pi/visualizer.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Script to run visualize pose estimation on test data.\"\"\"\nimport argparse\nimport logging\n\nimport cv2\nfrom data import BodyPart\nfrom data import person_from_keypoints_with_scores\nfrom ml import Movenet\nfrom ml import Posenet\nimport numpy as np\nimport pandas as pd\nimport utils\n\n_MODEL_POSENET = 'posenet'\n_MODEL_LIGHTNING = 'movenet_lightning'\n_MODEL_THUNDER = 'movenet_thunder'\n_GROUND_TRUTH_CSV = 'test_data/pose_landmark_truth.csv'\n_TEST_IMAGE_PATHS = ['test_data/image1.png', 'test_data/image2.jpeg']\n\n# Load test images\n_TEST_IMAGES = [cv2.imread(path) for path in _TEST_IMAGE_PATHS]\n\n# Load pose estimation models\n_POSENET = Posenet(_MODEL_POSENET)\n_MOVENET_LIGHTNING = Movenet(_MODEL_LIGHTNING)\n_MOVENET_THUNDER = Movenet(_MODEL_THUNDER)\n\n# Load pose landmarks truth\n_POSE_LANDMARKS_TRUTH = pd.read_csv(_GROUND_TRUTH_CSV)\n_KEYPOINTS_TRUTH_LIST = [\n    row.to_numpy().reshape((17, 2)) for row in _POSE_LANDMARKS_TRUTH.iloc\n]\n\n\ndef _visualize_detection_result(input_image, ground_truth):\n  \"\"\"Visualize the pose estimation result and write the output image to a file.\n\n  The detected keypoints follow these color codes:\n      * PoseNet: blue\n      * MoveNet Lightning: red\n      * MoveNet Thunder: yellow\n      * Ground truth (from CSV): green\n  Note: This test is meant to be run by a human who want to visually verify\n  the pose estimation result.\n\n  Args:\n    input_image: Numpy array of shape (height, width, 3)\n    ground_truth: Numpy array with absolute coordinates of the keypoints to be\n      plotted.\n\n  Returns:\n    Input image with pose estimation results.\n  \"\"\"\n  output_image = input_image.copy()\n\n  # Draw detection result from Posenet (blue)\n  person = _POSENET.detect(input_image)\n  output_image = utils.visualize(output_image, [person], (255, 0, 0))\n\n  # Draw detection result from Movenet Lightning (red)\n  person = _MOVENET_LIGHTNING.detect(input_image, reset_crop_region=True)\n  output_image = utils.visualize(output_image, [person], (0, 0, 255))\n\n  # Draw detection result from Movenet Thunder (yellow)\n  person = _MOVENET_THUNDER.detect(input_image, reset_crop_region=True)\n  output_image = utils.visualize(output_image, [person], (0, 255, 255))\n\n  # Create a fake score column to convert ground truth to \"Person\" type\n  ground_truth[:, :2] = ground_truth[:, 1::-1]\n  score = np.ones((17, 1), dtype=float)\n  ground_truth = np.append(ground_truth, score, axis=1)\n  person = person_from_keypoints_with_scores(ground_truth, 1, 1)\n\n  # Draw ground truth detection result (green)\n  output_image = utils.visualize(output_image, [person], (0, 255, 0))\n\n  return output_image\n\n\ndef _create_ground_truth_csv(input_images, ground_truth_csv_path):\n  \"\"\"Create ground truth CSV file from the given input images.\n\n  Args:\n    input_images: An array of input RGB images (height, width, 3).\n    ground_truth_csv_path: path to the output CSV.\n  \"\"\"\n  # Create column name for CSV file\n  column_names = []\n  for body_part in BodyPart:\n    column_names.append(body_part.name + '_x')\n    column_names.append(body_part.name + '_y')\n\n  # Create ground truth data by feeding the test images through MoveNet\n  # Thunder 3 times to leverage the cropping logic and improve accuracy.\n  keypoints_data = []\n  for input_image in input_images:\n    person = _MOVENET_THUNDER.detect(input_image, reset_crop_region=True)\n    for _ in range(3):\n      person = _MOVENET_THUNDER.detect(input_image, reset_crop_region=False)\n\n    kpts = []\n    keypoints = person.keypoints\n    for idx in range(len(keypoints)):\n      kpts.extend((keypoints[idx].coordinate.x, keypoints[idx].coordinate.y))\n\n    # Store kpts into keypoints_data\n    keypoints_data.append(kpts)\n\n  # Write ground truth CSV file\n  keypoints_df = pd.DataFrame(keypoints_data, columns=column_names)\n  keypoints_df.to_csv(ground_truth_csv_path, index=False)\n\n\ndef main():\n  parser = argparse.ArgumentParser(\n      formatter_class=argparse.ArgumentDefaultsHelpFormatter)\n  parser.add_argument(\n      '--ground_truth_csv_output',\n      help='Path to generate ground truth CSV file. (Optional)',\n      required=False)\n  args = parser.parse_args()\n\n  # Create ground truth CSV if the ground_truth_csv parameter is set\n  if args.ground_truth_csv_output:\n    _create_ground_truth_csv(_TEST_IMAGES, args.ground_truth_csv_output)\n    logging.info('Created ground truth keypoint CSV: %s',\n                 args.ground_truth_csv_output)\n\n  # Visualize detection result of the test images\n  for index in range(len(_TEST_IMAGES)):\n    test_image_path = _TEST_IMAGE_PATHS[index]\n    test_image = _TEST_IMAGES[index]\n    keypoint_truth = _KEYPOINTS_TRUTH_LIST[index]\n    visualized_image = _visualize_detection_result(test_image, keypoint_truth)\n    cv2.imshow(test_image_path, visualized_image)\n\n  cv2.waitKey(0)\n  cv2.destroyAllWindows()\n\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "lite/examples/posenet/android/README.md",
    "content": "# See the new pose estimation TFLite Android sample [here](../../pose_estimation/android), which demonstrates both Posenet and Movenet models. This old Posenet sample will no longer be maintained.\n\n<br/> <br/> <br/> <br/>\n\n## TensorFlow Lite PoseNet Android Demo\n\n### Overview\nThis is an app that continuously detects the body parts in the frames seen by\n your device's camera. These instructions walk you through building and running\n the demo on an Android device. Camera captures are discarded immediately after\n use, nothing is stored or saved.\n\n![Demo Image](posenetimage.png)\n\n## Build the demo using Android Studio\n\n### Prerequisites\n\n* If you don't have it already, install **[Android Studio](\n https://developer.android.com/studio/index.html)** 4.2 or\n above, following the instructions on the website.\n\n* Android device and Android development environment with minimum API 21.\n\n### Building\n* Open Android Studio, and from the `Welcome` screen, select\n`Open an existing Android Studio project`.\n\n* From the `Open File or Project` window that appears, navigate to and select\n the `tensorflow-lite/examples/posenet/android` directory from wherever you\n cloned the TensorFlow Lite sample GitHub repo. Click `OK`.\n\n* If it asks you to do a `Gradle Sync`, click `OK`.\n\n* You may also need to install various platforms and tools, if you get errors\n like `Failed to find target with hash string 'android-21'` and similar. Click\n the `Run` button (the green arrow) or select `Run` > `Run 'android'` from the\n top menu. You may need to rebuild the project using `Build` > `Rebuild Project`.\n\n* If it asks you to use `Instant Run`, click `Proceed Without Instant Run`.\n\n* Also, you need to have an Android device plugged in with developer options\n enabled at this point. See **[here](\n https://developer.android.com/studio/run/device)** for more details\n on setting up developer devices.\n\n\n### Model used\nDownloading, extraction and placement in assets folder has been managed\n automatically by `download.gradle`.\n\nIf you explicitly want to download the model, you can download it from\n **[here](\n https://storage.googleapis.com/download.tensorflow.org/models/tflite/posenet_mobilenet_v1_100_257x257_multi_kpt_stripped.tflite)**.\n\n### Additional Note\n_Please do not delete the assets folder content_. If you explicitly deleted the\n files, then please choose `Build` > `Rebuild` from menu to re-download the\n deleted model files into assets folder.\n\n"
  },
  {
    "path": "lite/examples/posenet/android/app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\napply plugin: 'kotlin-android'\n\napply plugin: 'kotlin-android-extensions'\n\nandroid {\n  compileSdkVersion 29\n  buildToolsVersion \"29.0.0\"\n  defaultConfig {\n    applicationId \"org.tensorflow.lite.examples.posenet\"\n    minSdkVersion 21\n    targetSdkVersion 23\n    versionCode 1\n    versionName \"1.0\"\n    testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n  }\n  buildTypes {\n    release {\n      minifyEnabled false\n      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n    }\n  }\n  aaptOptions {\n    noCompress \"tflite\"\n  }\n  lintOptions {\n    checkReleaseBuilds false\n    // Or, if you prefer, you can continue to check for errors in release builds,\n    // but continue the build even when errors are found:\n    abortOnError false\n  }\n}\n\ndependencies {\n  implementation fileTree(dir: 'libs', include: ['*.jar'])\n  implementation project(\":posenet\")\n  implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version\"\n  implementation 'androidx.appcompat:appcompat:1.1.0'\n  implementation 'androidx.core:core-ktx:1.1.0'\n  implementation 'androidx.constraintlayout:constraintlayout:1.1.3'\n  testImplementation 'junit:junit:4.12'\n  androidTestImplementation 'androidx.test:runner:1.2.0'\n  androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'\n}\n"
  },
  {
    "path": "lite/examples/posenet/android/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "lite/examples/posenet/android/app/src/androidTest/java/org/tensorflow/lite/examples/posenet/ExampleInstrumentedTest.kt",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.posenet\n\nimport androidx.test.InstrumentationRegistry\nimport androidx.test.runner.AndroidJUnit4\nimport org.junit.Assert.assertEquals\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass ExampleInstrumentedTest {\n  @Test\n  fun useAppContext() {\n    // Context of the app under test.\n    val appContext = InstrumentationRegistry.getTargetContext()\n    assertEquals(\"org.tensorflow.lite.examples.posenet\", appContext.packageName)\n  }\n}\n"
  },
  {
    "path": "lite/examples/posenet/android/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.tensorflow.lite.examples.posenet\">\n\n  <uses-permission android:name=\"android.permission.CAMERA\" />\n\n  <uses-feature android:name=\"android.hardware.camera\" />\n  <uses-feature android:name=\"android.hardware.camera.autofocus\" />\n\n  <uses-sdk />\n\n  <application\n      android:allowBackup=\"true\"\n      android:icon=\"@mipmap/ic_launcher\"\n      android:label=\"@string/tfe_pn_app_name\"\n      android:roundIcon=\"@mipmap/ic_launcher_round\"\n      android:supportsRtl=\"true\"\n      android:theme=\"@style/AppTheme.Posenet\">\n\n    <activity\n        android:name=\".TestActivity\"\n        android:label=\"@string/tfe_pn_app_name\"\n        android:exported=\"true\">\n    </activity>\n\n    <activity android:name=\".CameraActivity\" android:exported=\"true\">\n      <intent-filter>\n        <action android:name=\"android.intent.action.MAIN\"/>\n        <category android:name=\"android.intent.category.LAUNCHER\"/>\n      </intent-filter>\n    </activity>\n\n  </application>\n\n</manifest>\n"
  },
  {
    "path": "lite/examples/posenet/android/app/src/main/java/org/tensorflow/lite/examples/posenet/CameraActivity.kt",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.posenet\n\nimport android.os.Bundle\nimport android.view.WindowManager\nimport androidx.appcompat.app.AppCompatActivity\n\nclass CameraActivity : AppCompatActivity() {\n\n  override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n    setContentView(R.layout.tfe_pn_activity_camera)\n    window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)\n    savedInstanceState ?: supportFragmentManager.beginTransaction()\n      .replace(R.id.container, PosenetActivity())\n      .commit()\n  }\n}\n"
  },
  {
    "path": "lite/examples/posenet/android/app/src/main/java/org/tensorflow/lite/examples/posenet/ConfirmationDialog.kt",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.posenet\n\nimport android.Manifest\nimport android.app.AlertDialog\nimport android.app.Dialog\nimport android.os.Bundle\nimport androidx.fragment.app.DialogFragment\n\n/**\n * Shows OK/Cancel confirmation dialog about camera permission.\n */\nclass ConfirmationDialog : DialogFragment() {\n\n  override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =\n    AlertDialog.Builder(activity)\n      .setMessage(R.string.tfe_pn_request_permission)\n      .setPositiveButton(android.R.string.ok) { _, _ ->\n        parentFragment!!.requestPermissions(\n          arrayOf(Manifest.permission.CAMERA),\n          REQUEST_CAMERA_PERMISSION\n        )\n      }\n      .setNegativeButton(android.R.string.cancel) { _, _ ->\n        parentFragment!!.activity?.finish()\n      }\n      .create()\n}\n"
  },
  {
    "path": "lite/examples/posenet/android/app/src/main/java/org/tensorflow/lite/examples/posenet/Constants.kt",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@file:JvmName(\"Constants\")\n\npackage org.tensorflow.lite.examples.posenet\n\n/** Request camera and external storage permission.   */\nconst val REQUEST_CAMERA_PERMISSION = 1\n\n/** Model input shape for images.   */\nconst val MODEL_WIDTH = 257\nconst val MODEL_HEIGHT = 257\n"
  },
  {
    "path": "lite/examples/posenet/android/app/src/main/java/org/tensorflow/lite/examples/posenet/ImageUtils.kt",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.examples.posenet\n\n/** Utility class for manipulating images.  */\nobject ImageUtils {\n  // This value is 2 ^ 18 - 1, and is used to hold the RGB values together before their ranges\n  // are normalized to eight bits.\n  private const val MAX_CHANNEL_VALUE = 262143\n\n  /** Helper function to convert y,u,v integer values to RGB format */\n  private fun convertYUVToRGB(y: Int, u: Int, v: Int): Int {\n    // Adjust and check YUV values\n    val yNew = if (y - 16 < 0) 0 else y - 16\n    val uNew = u - 128\n    val vNew = v - 128\n    val expandY = 1192 * yNew\n    var r = expandY + 1634 * vNew\n    var g = expandY - 833 * vNew - 400 * uNew\n    var b = expandY + 2066 * uNew\n\n    // Clipping RGB values to be inside boundaries [ 0 , MAX_CHANNEL_VALUE ]\n    val checkBoundaries = { x: Int ->\n      when {\n        x > MAX_CHANNEL_VALUE -> MAX_CHANNEL_VALUE\n        x < 0 -> 0\n        else -> x\n      }\n    }\n    r = checkBoundaries(r)\n    g = checkBoundaries(g)\n    b = checkBoundaries(b)\n    return -0x1000000 or (r shl 6 and 0xff0000) or (g shr 2 and 0xff00) or (b shr 10 and 0xff)\n  }\n\n  /** Converts YUV420 format image data (ByteArray) into ARGB8888 format with IntArray as output. */\n  fun convertYUV420ToARGB8888(\n    yData: ByteArray,\n    uData: ByteArray,\n    vData: ByteArray,\n    width: Int,\n    height: Int,\n    yRowStride: Int,\n    uvRowStride: Int,\n    uvPixelStride: Int,\n    out: IntArray\n  ) {\n    var outputIndex = 0\n    for (j in 0 until height) {\n      val positionY = yRowStride * j\n      val positionUV = uvRowStride * (j shr 1)\n\n      for (i in 0 until width) {\n        val uvOffset = positionUV + (i shr 1) * uvPixelStride\n\n        // \"0xff and\" is used to cut off bits from following value that are higher than\n        // the low 8 bits\n        out[outputIndex] = convertYUVToRGB(\n          0xff and yData[positionY + i].toInt(), 0xff and uData[uvOffset].toInt(),\n          0xff and vData[uvOffset].toInt()\n        )\n        outputIndex += 1\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/posenet/android/app/src/main/java/org/tensorflow/lite/examples/posenet/PosenetActivity.kt",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.posenet\n\nimport android.Manifest\nimport android.app.AlertDialog\nimport android.app.Dialog\nimport android.content.Context\nimport android.content.pm.PackageManager\nimport android.graphics.Bitmap\nimport android.graphics.Canvas\nimport android.graphics.Color\nimport android.graphics.ImageFormat\nimport android.graphics.Matrix\nimport android.graphics.Paint\nimport android.graphics.PorterDuff\nimport android.graphics.Rect\nimport android.hardware.camera2.CameraAccessException\nimport android.hardware.camera2.CameraCaptureSession\nimport android.hardware.camera2.CameraCharacteristics\nimport android.hardware.camera2.CameraDevice\nimport android.hardware.camera2.CameraManager\nimport android.hardware.camera2.CaptureRequest\nimport android.media.Image\nimport android.media.ImageReader\nimport android.media.ImageReader.OnImageAvailableListener\nimport android.os.Bundle\nimport android.os.Handler\nimport android.os.HandlerThread\nimport android.os.Process\nimport androidx.fragment.app.DialogFragment\nimport androidx.fragment.app.Fragment\nimport android.util.Log\nimport android.util.Size\nimport android.util.SparseIntArray\nimport android.view.LayoutInflater\nimport android.view.Surface\nimport android.view.SurfaceHolder\nimport android.view.SurfaceView\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.Toast\nimport androidx.core.app.ActivityCompat\nimport java.util.concurrent.Semaphore\nimport java.util.concurrent.TimeUnit\nimport kotlin.math.abs\nimport org.tensorflow.lite.examples.posenet.lib.BodyPart\nimport org.tensorflow.lite.examples.posenet.lib.Person\nimport org.tensorflow.lite.examples.posenet.lib.Posenet\n\nclass PosenetActivity :\n  Fragment(),\n  ActivityCompat.OnRequestPermissionsResultCallback {\n\n  /** List of body joints that should be connected.    */\n  private val bodyJoints = listOf(\n    Pair(BodyPart.LEFT_WRIST, BodyPart.LEFT_ELBOW),\n    Pair(BodyPart.LEFT_ELBOW, BodyPart.LEFT_SHOULDER),\n    Pair(BodyPart.LEFT_SHOULDER, BodyPart.RIGHT_SHOULDER),\n    Pair(BodyPart.RIGHT_SHOULDER, BodyPart.RIGHT_ELBOW),\n    Pair(BodyPart.RIGHT_ELBOW, BodyPart.RIGHT_WRIST),\n    Pair(BodyPart.LEFT_SHOULDER, BodyPart.LEFT_HIP),\n    Pair(BodyPart.LEFT_HIP, BodyPart.RIGHT_HIP),\n    Pair(BodyPart.RIGHT_HIP, BodyPart.RIGHT_SHOULDER),\n    Pair(BodyPart.LEFT_HIP, BodyPart.LEFT_KNEE),\n    Pair(BodyPart.LEFT_KNEE, BodyPart.LEFT_ANKLE),\n    Pair(BodyPart.RIGHT_HIP, BodyPart.RIGHT_KNEE),\n    Pair(BodyPart.RIGHT_KNEE, BodyPart.RIGHT_ANKLE)\n  )\n\n  /** Threshold for confidence score. */\n  private val minConfidence = 0.5\n\n  /** Radius of circle used to draw keypoints.  */\n  private val circleRadius = 8.0f\n\n  /** Paint class holds the style and color information to draw geometries,text and bitmaps. */\n  private var paint = Paint()\n\n  /** A shape for extracting frame data.   */\n  private val PREVIEW_WIDTH = 640\n  private val PREVIEW_HEIGHT = 480\n\n  /** An object for the Posenet library.    */\n  private lateinit var posenet: Posenet\n\n  /** ID of the current [CameraDevice].   */\n  private var cameraId: String? = null\n\n  /** A [SurfaceView] for camera preview.   */\n  private var surfaceView: SurfaceView? = null\n\n  /** A [CameraCaptureSession] for camera preview.   */\n  private var captureSession: CameraCaptureSession? = null\n\n  /** A reference to the opened [CameraDevice].    */\n  private var cameraDevice: CameraDevice? = null\n\n  /** The [android.util.Size] of camera preview.  */\n  private var previewSize: Size? = null\n\n  /** The [android.util.Size.getWidth] of camera preview. */\n  private var previewWidth = 0\n\n  /** The [android.util.Size.getHeight] of camera preview.  */\n  private var previewHeight = 0\n\n  /** A counter to keep count of total frames.  */\n  private var frameCounter = 0\n\n  /** An IntArray to save image data in ARGB8888 format  */\n  private lateinit var rgbBytes: IntArray\n\n  /** A ByteArray to save image data in YUV format  */\n  private var yuvBytes = arrayOfNulls<ByteArray>(3)\n\n  /** An additional thread for running tasks that shouldn't block the UI.   */\n  private var backgroundThread: HandlerThread? = null\n\n  /** A [Handler] for running tasks in the background.    */\n  private var backgroundHandler: Handler? = null\n\n  /** An [ImageReader] that handles preview frame capture.   */\n  private var imageReader: ImageReader? = null\n\n  /** [CaptureRequest.Builder] for the camera preview   */\n  private var previewRequestBuilder: CaptureRequest.Builder? = null\n\n  /** [CaptureRequest] generated by [.previewRequestBuilder   */\n  private var previewRequest: CaptureRequest? = null\n\n  /** A [Semaphore] to prevent the app from exiting before closing the camera.    */\n  private val cameraOpenCloseLock = Semaphore(1)\n\n  /** Whether the current camera device supports Flash or not.    */\n  private var flashSupported = false\n\n  /** Orientation of the camera sensor.   */\n  private var sensorOrientation: Int? = null\n\n  /** Abstract interface to someone holding a display surface.    */\n  private var surfaceHolder: SurfaceHolder? = null\n\n  /** [CameraDevice.StateCallback] is called when [CameraDevice] changes its state.   */\n  private val stateCallback = object : CameraDevice.StateCallback() {\n\n    override fun onOpened(cameraDevice: CameraDevice) {\n      cameraOpenCloseLock.release()\n      this@PosenetActivity.cameraDevice = cameraDevice\n      createCameraPreviewSession()\n    }\n\n    override fun onDisconnected(cameraDevice: CameraDevice) {\n      cameraOpenCloseLock.release()\n      cameraDevice.close()\n      this@PosenetActivity.cameraDevice = null\n    }\n\n    override fun onError(cameraDevice: CameraDevice, error: Int) {\n      onDisconnected(cameraDevice)\n      this@PosenetActivity.activity?.finish()\n    }\n  }\n\n  /**\n   * Shows a [Toast] on the UI thread.\n   *\n   * @param text The message to show\n   */\n  private fun showToast(text: String) {\n    val activity = activity\n    activity?.runOnUiThread { Toast.makeText(activity, text, Toast.LENGTH_SHORT).show() }\n  }\n\n  override fun onCreateView(\n    inflater: LayoutInflater,\n    container: ViewGroup?,\n    savedInstanceState: Bundle?\n  ): View? = inflater.inflate(R.layout.tfe_pn_activity_posenet, container, false)\n\n  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n    surfaceView = view.findViewById(R.id.surfaceView)\n    surfaceHolder = surfaceView!!.holder\n  }\n\n  override fun onResume() {\n    super.onResume()\n    startBackgroundThread()\n  }\n\n  override fun onStart() {\n    super.onStart()\n    openCamera()\n    posenet = Posenet(this.context!!)\n  }\n\n  override fun onPause() {\n    closeCamera()\n    stopBackgroundThread()\n    super.onPause()\n  }\n\n  override fun onDestroy() {\n    super.onDestroy()\n    posenet.close()\n  }\n\n  private fun requestCameraPermission() {\n    if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {\n      ConfirmationDialog().show(childFragmentManager, FRAGMENT_DIALOG)\n    } else {\n      requestPermissions(arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION)\n    }\n  }\n\n  override fun onRequestPermissionsResult(\n    requestCode: Int,\n    permissions: Array<String>,\n    grantResults: IntArray\n  ) {\n    if (requestCode == REQUEST_CAMERA_PERMISSION) {\n      if (allPermissionsGranted(grantResults)) {\n        ErrorDialog.newInstance(getString(R.string.tfe_pn_request_permission))\n          .show(childFragmentManager, FRAGMENT_DIALOG)\n      }\n    } else {\n      super.onRequestPermissionsResult(requestCode, permissions, grantResults)\n    }\n  }\n\n  private fun allPermissionsGranted(grantResults: IntArray) = grantResults.all {\n    it == PackageManager.PERMISSION_GRANTED\n  }\n\n  /**\n   * Sets up member variables related to camera.\n   */\n  private fun setUpCameraOutputs() {\n    val activity = activity\n    val manager = activity!!.getSystemService(Context.CAMERA_SERVICE) as CameraManager\n    try {\n      for (cameraId in manager.cameraIdList) {\n        val characteristics = manager.getCameraCharacteristics(cameraId)\n\n        // We don't use a front facing camera in this sample.\n        val cameraDirection = characteristics.get(CameraCharacteristics.LENS_FACING)\n        if (cameraDirection != null &&\n          cameraDirection == CameraCharacteristics.LENS_FACING_FRONT\n        ) {\n          continue\n        }\n\n        previewSize = Size(PREVIEW_WIDTH, PREVIEW_HEIGHT)\n\n        imageReader = ImageReader.newInstance(\n          PREVIEW_WIDTH, PREVIEW_HEIGHT,\n          ImageFormat.YUV_420_888, /*maxImages*/ 2\n        )\n\n        sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!\n\n        previewHeight = previewSize!!.height\n        previewWidth = previewSize!!.width\n\n        // Initialize the storage bitmaps once when the resolution is known.\n        rgbBytes = IntArray(previewWidth * previewHeight)\n\n        // Check if the flash is supported.\n        flashSupported =\n          characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) == true\n\n        this.cameraId = cameraId\n\n        // We've found a viable camera and finished setting up member variables,\n        // so we don't need to iterate through other available cameras.\n        return\n      }\n    } catch (e: CameraAccessException) {\n      Log.e(TAG, e.toString())\n    } catch (e: NullPointerException) {\n      // Currently an NPE is thrown when the Camera2API is used but not supported on the\n      // device this code runs.\n      ErrorDialog.newInstance(getString(R.string.tfe_pn_camera_error))\n        .show(childFragmentManager, FRAGMENT_DIALOG)\n    }\n  }\n\n  /**\n   * Opens the camera specified by [PosenetActivity.cameraId].\n   */\n  private fun openCamera() {\n    val permissionCamera = getContext()!!.checkPermission(\n      Manifest.permission.CAMERA, Process.myPid(), Process.myUid()\n    )\n    if (permissionCamera != PackageManager.PERMISSION_GRANTED) {\n      requestCameraPermission()\n    }\n    setUpCameraOutputs()\n    val manager = activity!!.getSystemService(Context.CAMERA_SERVICE) as CameraManager\n    try {\n      // Wait for camera to open - 2.5 seconds is sufficient\n      if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {\n        throw RuntimeException(\"Time out waiting to lock camera opening.\")\n      }\n      manager.openCamera(cameraId!!, stateCallback, backgroundHandler)\n    } catch (e: CameraAccessException) {\n      Log.e(TAG, e.toString())\n    } catch (e: InterruptedException) {\n      throw RuntimeException(\"Interrupted while trying to lock camera opening.\", e)\n    }\n  }\n\n  /**\n   * Closes the current [CameraDevice].\n   */\n  private fun closeCamera() {\n    if (captureSession == null) {\n      return\n    }\n\n    try {\n      cameraOpenCloseLock.acquire()\n      captureSession!!.close()\n      captureSession = null\n      cameraDevice!!.close()\n      cameraDevice = null\n      imageReader!!.close()\n      imageReader = null\n    } catch (e: InterruptedException) {\n      throw RuntimeException(\"Interrupted while trying to lock camera closing.\", e)\n    } finally {\n      cameraOpenCloseLock.release()\n    }\n  }\n\n  /**\n   * Starts a background thread and its [Handler].\n   */\n  private fun startBackgroundThread() {\n    backgroundThread = HandlerThread(\"imageAvailableListener\").also { it.start() }\n    backgroundHandler = Handler(backgroundThread!!.looper)\n  }\n\n  /**\n   * Stops the background thread and its [Handler].\n   */\n  private fun stopBackgroundThread() {\n    backgroundThread?.quitSafely()\n    try {\n      backgroundThread?.join()\n      backgroundThread = null\n      backgroundHandler = null\n    } catch (e: InterruptedException) {\n      Log.e(TAG, e.toString())\n    }\n  }\n\n  /** Fill the yuvBytes with data from image planes.   */\n  private fun fillBytes(planes: Array<Image.Plane>, yuvBytes: Array<ByteArray?>) {\n    // Row stride is the total number of bytes occupied in memory by a row of an image.\n    // Because of the variable row stride it's not possible to know in\n    // advance the actual necessary dimensions of the yuv planes.\n    for (i in planes.indices) {\n      val buffer = planes[i].buffer\n      if (yuvBytes[i] == null) {\n        yuvBytes[i] = ByteArray(buffer.capacity())\n      }\n      buffer.get(yuvBytes[i]!!)\n    }\n  }\n\n  /** A [OnImageAvailableListener] to receive frames as they are available.  */\n  private var imageAvailableListener = object : OnImageAvailableListener {\n    override fun onImageAvailable(imageReader: ImageReader) {\n      // We need wait until we have some size from onPreviewSizeChosen\n      if (previewWidth == 0 || previewHeight == 0) {\n        return\n      }\n\n      val image = imageReader.acquireLatestImage() ?: return\n      fillBytes(image.planes, yuvBytes)\n\n      ImageUtils.convertYUV420ToARGB8888(\n        yuvBytes[0]!!,\n        yuvBytes[1]!!,\n        yuvBytes[2]!!,\n        previewWidth,\n        previewHeight,\n        /*yRowStride=*/ image.planes[0].rowStride,\n        /*uvRowStride=*/ image.planes[1].rowStride,\n        /*uvPixelStride=*/ image.planes[1].pixelStride,\n        rgbBytes\n      )\n\n      // Create bitmap from int array\n      val imageBitmap = Bitmap.createBitmap(\n        rgbBytes, previewWidth, previewHeight,\n        Bitmap.Config.ARGB_8888\n      )\n\n      // Create rotated version for portrait display\n      val rotateMatrix = Matrix()\n      rotateMatrix.postRotate(90.0f)\n\n      val rotatedBitmap = Bitmap.createBitmap(\n        imageBitmap, 0, 0, previewWidth, previewHeight,\n        rotateMatrix, true\n      )\n      image.close()\n\n      processImage(rotatedBitmap)\n    }\n  }\n\n  /** Crop Bitmap to maintain aspect ratio of model input.   */\n  private fun cropBitmap(bitmap: Bitmap): Bitmap {\n    val bitmapRatio = bitmap.height.toFloat() / bitmap.width\n    val modelInputRatio = MODEL_HEIGHT.toFloat() / MODEL_WIDTH\n    var croppedBitmap = bitmap\n\n    // Acceptable difference between the modelInputRatio and bitmapRatio to skip cropping.\n    val maxDifference = 1e-5\n\n    // Checks if the bitmap has similar aspect ratio as the required model input.\n    when {\n      abs(modelInputRatio - bitmapRatio) < maxDifference -> return croppedBitmap\n      modelInputRatio < bitmapRatio -> {\n        // New image is taller so we are height constrained.\n        val cropHeight = bitmap.height - (bitmap.width.toFloat() / modelInputRatio)\n        croppedBitmap = Bitmap.createBitmap(\n          bitmap,\n          0,\n          (cropHeight / 2).toInt(),\n          bitmap.width,\n          (bitmap.height - cropHeight).toInt()\n        )\n      }\n      else -> {\n        val cropWidth = bitmap.width - (bitmap.height.toFloat() * modelInputRatio)\n        croppedBitmap = Bitmap.createBitmap(\n          bitmap,\n          (cropWidth / 2).toInt(),\n          0,\n          (bitmap.width - cropWidth).toInt(),\n          bitmap.height\n        )\n      }\n    }\n    return croppedBitmap\n  }\n\n  /** Set the paint color and size.    */\n  private fun setPaint() {\n    paint.color = Color.RED\n    paint.textSize = 80.0f\n    paint.strokeWidth = 8.0f\n  }\n\n  /** Draw bitmap on Canvas.   */\n  private fun draw(canvas: Canvas, person: Person, bitmap: Bitmap) {\n    canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)\n    // Draw `bitmap` and `person` in square canvas.\n    val screenWidth: Int\n    val screenHeight: Int\n    val left: Int\n    val right: Int\n    val top: Int\n    val bottom: Int\n    if (canvas.height > canvas.width) {\n      screenWidth = canvas.width\n      screenHeight = canvas.width\n      left = 0\n      top = (canvas.height - canvas.width) / 2\n    } else {\n      screenWidth = canvas.height\n      screenHeight = canvas.height\n      left = (canvas.width - canvas.height) / 2\n      top = 0\n    }\n    right = left + screenWidth\n    bottom = top + screenHeight\n\n    setPaint()\n    canvas.drawBitmap(\n      bitmap,\n      Rect(0, 0, bitmap.width, bitmap.height),\n      Rect(left, top, right, bottom),\n      paint\n    )\n\n    val widthRatio = screenWidth.toFloat() / MODEL_WIDTH\n    val heightRatio = screenHeight.toFloat() / MODEL_HEIGHT\n\n    // Draw key points over the image.\n    for (keyPoint in person.keyPoints) {\n      if (keyPoint.score > minConfidence) {\n        val position = keyPoint.position\n        val adjustedX: Float = position.x.toFloat() * widthRatio + left\n        val adjustedY: Float = position.y.toFloat() * heightRatio + top\n        canvas.drawCircle(adjustedX, adjustedY, circleRadius, paint)\n      }\n    }\n\n    for (line in bodyJoints) {\n      if (\n        (person.keyPoints[line.first.ordinal].score > minConfidence) and\n        (person.keyPoints[line.second.ordinal].score > minConfidence)\n      ) {\n        canvas.drawLine(\n          person.keyPoints[line.first.ordinal].position.x.toFloat() * widthRatio + left,\n          person.keyPoints[line.first.ordinal].position.y.toFloat() * heightRatio + top,\n          person.keyPoints[line.second.ordinal].position.x.toFloat() * widthRatio + left,\n          person.keyPoints[line.second.ordinal].position.y.toFloat() * heightRatio + top,\n          paint\n        )\n      }\n    }\n\n    canvas.drawText(\n      \"Score: %.2f\".format(person.score),\n      (15.0f * widthRatio),\n      (30.0f * heightRatio + bottom),\n      paint\n    )\n    canvas.drawText(\n      \"Device: %s\".format(posenet.device),\n      (15.0f * widthRatio),\n      (50.0f * heightRatio + bottom),\n      paint\n    )\n    canvas.drawText(\n      \"Time: %.2f ms\".format(posenet.lastInferenceTimeNanos * 1.0f / 1_000_000),\n      (15.0f * widthRatio),\n      (70.0f * heightRatio + bottom),\n      paint\n    )\n\n    // Draw!\n    surfaceHolder!!.unlockCanvasAndPost(canvas)\n  }\n\n  /** Process image using Posenet library.   */\n  private fun processImage(bitmap: Bitmap) {\n    // Crop bitmap.\n    val croppedBitmap = cropBitmap(bitmap)\n\n    // Created scaled version of bitmap for model input.\n    val scaledBitmap = Bitmap.createScaledBitmap(croppedBitmap, MODEL_WIDTH, MODEL_HEIGHT, true)\n\n    // Perform inference.\n    val person = posenet.estimateSinglePose(scaledBitmap)\n    val canvas: Canvas = surfaceHolder!!.lockCanvas()\n    draw(canvas, person, scaledBitmap)\n  }\n\n  /**\n   * Creates a new [CameraCaptureSession] for camera preview.\n   */\n  private fun createCameraPreviewSession() {\n    try {\n      // We capture images from preview in YUV format.\n      imageReader = ImageReader.newInstance(\n        previewSize!!.width, previewSize!!.height, ImageFormat.YUV_420_888, 2\n      )\n      imageReader!!.setOnImageAvailableListener(imageAvailableListener, backgroundHandler)\n\n      // This is the surface we need to record images for processing.\n      val recordingSurface = imageReader!!.surface\n\n      // We set up a CaptureRequest.Builder with the output Surface.\n      previewRequestBuilder = cameraDevice!!.createCaptureRequest(\n        CameraDevice.TEMPLATE_PREVIEW\n      )\n      previewRequestBuilder!!.addTarget(recordingSurface)\n\n      // Here, we create a CameraCaptureSession for camera preview.\n      cameraDevice!!.createCaptureSession(\n        listOf(recordingSurface),\n        object : CameraCaptureSession.StateCallback() {\n          override fun onConfigured(cameraCaptureSession: CameraCaptureSession) {\n            // The camera is already closed\n            if (cameraDevice == null) return\n\n            // When the session is ready, we start displaying the preview.\n            captureSession = cameraCaptureSession\n            try {\n              // Auto focus should be continuous for camera preview.\n              previewRequestBuilder!!.set(\n                CaptureRequest.CONTROL_AF_MODE,\n                CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE\n              )\n              // Flash is automatically enabled when necessary.\n              setAutoFlash(previewRequestBuilder!!)\n\n              // Finally, we start displaying the camera preview.\n              previewRequest = previewRequestBuilder!!.build()\n              captureSession!!.setRepeatingRequest(\n                previewRequest!!,\n                null, null\n              )\n            } catch (e: CameraAccessException) {\n              Log.e(TAG, e.toString())\n            }\n          }\n\n          override fun onConfigureFailed(cameraCaptureSession: CameraCaptureSession) {\n            showToast(\"Failed\")\n          }\n        },\n        null\n      )\n    } catch (e: CameraAccessException) {\n      Log.e(TAG, e.toString())\n    }\n  }\n\n  private fun setAutoFlash(requestBuilder: CaptureRequest.Builder) {\n    if (flashSupported) {\n      requestBuilder.set(\n        CaptureRequest.CONTROL_AE_MODE,\n        CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH\n      )\n    }\n  }\n\n  /**\n   * Shows an error message dialog.\n   */\n  class ErrorDialog : DialogFragment() {\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =\n      AlertDialog.Builder(activity)\n        .setMessage(arguments!!.getString(ARG_MESSAGE))\n        .setPositiveButton(android.R.string.ok) { _, _ -> activity!!.finish() }\n        .create()\n\n    companion object {\n\n      @JvmStatic\n      private val ARG_MESSAGE = \"message\"\n\n      @JvmStatic\n      fun newInstance(message: String): ErrorDialog = ErrorDialog().apply {\n        arguments = Bundle().apply { putString(ARG_MESSAGE, message) }\n      }\n    }\n  }\n\n  companion object {\n    /**\n     * Conversion from screen rotation to JPEG orientation.\n     */\n    private val ORIENTATIONS = SparseIntArray()\n    private val FRAGMENT_DIALOG = \"dialog\"\n\n    init {\n      ORIENTATIONS.append(Surface.ROTATION_0, 90)\n      ORIENTATIONS.append(Surface.ROTATION_90, 0)\n      ORIENTATIONS.append(Surface.ROTATION_180, 270)\n      ORIENTATIONS.append(Surface.ROTATION_270, 180)\n    }\n\n    /**\n     * Tag for the [Log].\n     */\n    private const val TAG = \"PosenetActivity\"\n  }\n}\n"
  },
  {
    "path": "lite/examples/posenet/android/app/src/main/java/org/tensorflow/lite/examples/posenet/TestActivity.kt",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.posenet\n\nimport android.graphics.Bitmap\nimport android.graphics.Canvas\nimport android.graphics.Color\nimport android.graphics.Paint\nimport android.graphics.drawable.Drawable\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport android.widget.ImageView\nimport androidx.core.content.res.ResourcesCompat\nimport org.tensorflow.lite.examples.posenet.lib.Posenet as Posenet\n\nclass TestActivity : AppCompatActivity() {\n  /** Returns a resized bitmap of the drawable image. */\n  private fun drawableToBitmap(drawable: Drawable): Bitmap {\n    val bitmap = Bitmap.createBitmap(257, 257, Bitmap.Config.ARGB_8888)\n    val canvas = Canvas(bitmap)\n\n    drawable.setBounds(0, 0, canvas.width, canvas.height)\n\n    drawable.draw(canvas)\n    return bitmap\n  }\n\n  /** Calls the Posenet library functions. */\n  override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n    setContentView(R.layout.tfe_pn_activity_test)\n\n    val sampleImageView = findViewById<ImageView>(R.id.image)\n    val drawedImage = ResourcesCompat.getDrawable(resources, R.drawable.image, null)\n    val imageBitmap = drawableToBitmap(drawedImage!!)\n    sampleImageView.setImageBitmap(imageBitmap)\n    val posenet = Posenet(this.applicationContext)\n    val person = posenet.estimateSinglePose(imageBitmap)\n\n    // Draw the keypoints over the image.\n    val paint = Paint()\n    paint.color = Color.RED\n    val size = 2.0f\n\n    val mutableBitmap = imageBitmap.copy(Bitmap.Config.ARGB_8888, true)\n    val canvas = Canvas(mutableBitmap)\n    for (keypoint in person.keyPoints) {\n      canvas.drawCircle(keypoint.position.x.toFloat(), keypoint.position.y.toFloat(), size, paint)\n    }\n    sampleImageView.adjustViewBounds = true\n    sampleImageView.setImageBitmap(mutableBitmap)\n  }\n}\n"
  },
  {
    "path": "lite/examples/posenet/android/app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillColor=\"#26A69A\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n</vector>\n"
  },
  {
    "path": "lite/examples/posenet/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n  <path\n      android:fillType=\"evenOdd\"\n      android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\">\n    <aapt:attr name=\"android:fillColor\">\n      <gradient\n          android:endX=\"78.5885\"\n          android:endY=\"90.9159\"\n          android:startX=\"48.7653\"\n          android:startY=\"61.0927\"\n          android:type=\"linear\">\n        <item\n            android:color=\"#44000000\"\n            android:offset=\"0.0\"/>\n        <item\n            android:color=\"#00000000\"\n            android:offset=\"1.0\"/>\n      </gradient>\n    </aapt:attr>\n  </path>\n  <path\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"nonZero\"\n      android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/posenet/android/app/src/main/res/layout/tfe_pn_activity_camera.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/black\"\n    tools:context=\"org.tensorflow.lite.examples.posenet.CameraActivity\" />"
  },
  {
    "path": "lite/examples/posenet/android/app/src/main/res/layout/tfe_pn_activity_posenet.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n  <SurfaceView\n      android:id=\"@+id/surfaceView\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginBottom=\"0dp\"\n      android:layout_alignParentBottom=\"true\"\n      android:layout_alignParentStart=\"true\"\n      android:layout_alignParentTop=\"true\"/>\n\n  <androidx.appcompat.widget.Toolbar\n      android:id=\"@+id/toolbar\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"?attr/actionBarSize\"\n      android:layout_alignParentTop=\"true\"\n      android:background=\"@color/tfe_semi_transparent\">\n\n    <ImageView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:src=\"@drawable/tfl2_logo\" />\n  </androidx.appcompat.widget.Toolbar>\n\n</RelativeLayout>\n"
  },
  {
    "path": "lite/examples/posenet/android/app/src/main/res/layout/tfe_pn_activity_test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n    Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n    -->\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:orientation=\"vertical\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\">\n\n  <ImageView\n      android:id=\"@+id/image\"\n      android:scaleType=\"center\"\n      android:layout_width=\"fill_parent\"\n      android:layout_height=\"fill_parent\"/>\n\n</RelativeLayout>\n"
  },
  {
    "path": "lite/examples/posenet/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/examples/posenet/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/examples/posenet/android/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2016 The TensorFlow Authors. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<resources>\n  <color name=\"tfe_color_primary\">#ffa800</color>\n  <color name=\"tfe_color_primary_dark\">#ff6f00</color>\n  <color name=\"tfe_color_accent\">#425066</color>\n\n  <color name=\"tfe_semi_transparent\">#66000000</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/posenet/android/app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2016 The TensorFlow Authors. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<resources>\n  <string name=\"tfe_pn_app_name\" translation_description=\"Posenet demo app built with Posenet library [CHAR_LIMIT=40]\">TFL Posenet</string>\n  <string name=\"tfe_pn_request_permission\" translation_description=\"Request camera permission [CHAR_LIMIT=40]\">This app needs camera permission.</string>\n  <string name=\"tfe_pn_camera_error\" translation_description=\"Error regarding camera support[CHAR_LIMIT=40]\">This device doesn\\'t support Camera2 API.</string>\n</resources>\n"
  },
  {
    "path": "lite/examples/posenet/android/app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n  <!-- Base application theme. -->\n  <style name=\"AppTheme.Posenet\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n    <item name=\"colorPrimary\">@color/tfe_color_primary</item>\n    <item name=\"colorPrimaryDark\">@color/tfe_color_primary_dark</item>\n    <item name=\"colorAccent\">@color/tfe_color_accent</item>\n    <item name=\"android:windowContentOverlay\">@null</item>\n    <item name=\"elevation\">0dp</item>\n  </style>\n\n</resources>\n"
  },
  {
    "path": "lite/examples/posenet/android/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n  ext.kotlin_version = '1.3.41'\n  repositories {\n    google()\n    mavenCentral()\n\n  }\n  dependencies {\n    classpath 'com.android.tools.build:gradle:4.2.0'\n    classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n    // NOTE: Do not place your application dependencies here; they belong\n    // in the individual module build.gradle files\n  }\n}\n\nallprojects {\n  repositories {\n    google()\n    mavenCentral()\n    maven {\n      name 'ossrh-snapshot'\n      url 'https://oss.sonatype.org/content/repositories/snapshots'\n    }\n  }\n}\n\ntask clean(type: Delete) {\n  delete rootProject.buildDir\n}\n"
  },
  {
    "path": "lite/examples/posenet/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/posenet/android/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app's APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Automatically convert third-party libraries to use AndroidX\nandroid.enableJetifier=false\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official\n"
  },
  {
    "path": "lite/examples/posenet/android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/posenet/android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/posenet/android/posenet/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\napply plugin: 'kotlin-android'\n\napply plugin: 'kotlin-android-extensions'\n\nandroid {\n    compileSdkVersion 29\n    buildToolsVersion \"29.0.0\"\n\n    defaultConfig {\n        minSdkVersion 21\n        targetSdkVersion 23\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    aaptOptions {\n    noCompress \"tflite\"\n  }\n\n    lintOptions {\n        checkReleaseBuilds false\n        // Or, if you prefer, you can continue to check for errors in release builds,\n        // but continue the build even when errors are found:\n        abortOnError false\n    }\n}\n\n// Download default models; if you wish to use your own models then\n// place them in the \"assets\" directory and comment out this line.\napply from:'download.gradle'\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation 'androidx.appcompat:appcompat:1.1.0'\n    implementation 'androidx.core:core-ktx:1.1.0'\n    testImplementation 'junit:junit:4.12'\n    androidTestImplementation 'androidx.test:runner:1.2.0'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'\n    implementation 'org.tensorflow:tensorflow-lite:2.2.0'\n    implementation 'org.tensorflow:tensorflow-lite-gpu:2.2.0'\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version\"\n}\nrepositories {\n    mavenCentral()\n}\n"
  },
  {
    "path": "lite/examples/posenet/android/posenet/download.gradle",
    "content": "def targetFile = \"src/main/assets/posenet_model.tflite\"\ndef modelFloatDownloadUrl = \"https://storage.googleapis.com/download.tensorflow.org/models/tflite/posenet_mobilenet_v1_100_257x257_multi_kpt_stripped.tflite\"\n\ntask downloadModelFloat(type: DownloadUrlTask) {\n    doFirst {\n        println \"Downloading ${modelFloatDownloadUrl}\"\n    }\n    sourceUrl = \"${modelFloatDownloadUrl}\"\n    target = file(\"${targetFile}\")\n}\n\nclass DownloadUrlTask extends DefaultTask {\n    @Input\n    String sourceUrl\n\n    @OutputFile\n    File target\n\n    @TaskAction\n    void download() {\n        ant.get(src: sourceUrl, dest: target)\n    }\n}\n\npreBuild.dependsOn downloadModelFloat\n"
  },
  {
    "path": "lite/examples/posenet/android/posenet/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "lite/examples/posenet/android/posenet/src/androidTest/java/org/tensorflow/lite/examples/posenet/ExampleInstrumentedTest.java",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.posenet;\n\nimport static org.junit.Assert.*;\n\nimport android.content.Context;\nimport androidx.test.InstrumentationRegistry;\nimport androidx.test.runner.AndroidJUnit4;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n  @Test\n  public void useAppContext() {\n    // Context of the app under test.\n    Context appContext = InstrumentationRegistry.getTargetContext();\n\n    assertEquals(\"org.tensorflow.lite.examples.posenet.test\", appContext.getPackageName());\n  }\n}\n"
  },
  {
    "path": "lite/examples/posenet/android/posenet/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  package=\"org.tensorflow.lite.examples.posenet.lib\">\n\n  <uses-sdk />\n\n</manifest>\n"
  },
  {
    "path": "lite/examples/posenet/android/posenet/src/main/java/org/tensorflow/lite/examples/posenet/lib/Posenet.kt",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.posenet.lib\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.os.SystemClock\nimport android.util.Log\nimport java.io.FileInputStream\nimport java.nio.ByteBuffer\nimport java.nio.ByteOrder\nimport java.nio.MappedByteBuffer\nimport java.nio.channels.FileChannel\nimport kotlin.math.exp\nimport org.tensorflow.lite.Interpreter\nimport org.tensorflow.lite.gpu.GpuDelegate\n\nenum class BodyPart {\n  NOSE,\n  LEFT_EYE,\n  RIGHT_EYE,\n  LEFT_EAR,\n  RIGHT_EAR,\n  LEFT_SHOULDER,\n  RIGHT_SHOULDER,\n  LEFT_ELBOW,\n  RIGHT_ELBOW,\n  LEFT_WRIST,\n  RIGHT_WRIST,\n  LEFT_HIP,\n  RIGHT_HIP,\n  LEFT_KNEE,\n  RIGHT_KNEE,\n  LEFT_ANKLE,\n  RIGHT_ANKLE\n}\n\nclass Position {\n  var x: Int = 0\n  var y: Int = 0\n}\n\nclass KeyPoint {\n  var bodyPart: BodyPart = BodyPart.NOSE\n  var position: Position = Position()\n  var score: Float = 0.0f\n}\n\nclass Person {\n  var keyPoints = listOf<KeyPoint>()\n  var score: Float = 0.0f\n}\n\nenum class Device {\n  CPU,\n  NNAPI,\n  GPU\n}\n\nclass Posenet(\n  val context: Context,\n  val filename: String = \"posenet_model.tflite\",\n  val device: Device = Device.CPU\n) : AutoCloseable {\n  var lastInferenceTimeNanos: Long = -1\n    private set\n\n  /** An Interpreter for the TFLite model.   */\n  private var interpreter: Interpreter? = null\n  private var gpuDelegate: GpuDelegate? = null\n  private val NUM_LITE_THREADS = 4\n\n  private fun getInterpreter(): Interpreter {\n    if (interpreter != null) {\n      return interpreter!!\n    }\n    val options = Interpreter.Options()\n    options.setNumThreads(NUM_LITE_THREADS)\n    when (device) {\n      Device.CPU -> { }\n      Device.GPU -> {\n        gpuDelegate = GpuDelegate()\n        options.addDelegate(gpuDelegate)\n      }\n      Device.NNAPI -> options.setUseNNAPI(true)\n    }\n    interpreter = Interpreter(loadModelFile(filename, context), options)\n    return interpreter!!\n  }\n\n  override fun close() {\n    interpreter?.close()\n    interpreter = null\n    gpuDelegate?.close()\n    gpuDelegate = null\n  }\n\n  /** Returns value within [0,1].   */\n  private fun sigmoid(x: Float): Float {\n    return (1.0f / (1.0f + exp(-x)))\n  }\n\n  /**\n   * Scale the image to a byteBuffer of [-1,1] values.\n   */\n  private fun initInputArray(bitmap: Bitmap): ByteBuffer {\n    val bytesPerChannel = 4\n    val inputChannels = 3\n    val batchSize = 1\n    val inputBuffer = ByteBuffer.allocateDirect(\n      batchSize * bytesPerChannel * bitmap.height * bitmap.width * inputChannels\n    )\n    inputBuffer.order(ByteOrder.nativeOrder())\n    inputBuffer.rewind()\n\n    val mean = 128.0f\n    val std = 128.0f\n    val intValues = IntArray(bitmap.width * bitmap.height)\n    bitmap.getPixels(intValues, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)\n    for (pixelValue in intValues) {\n      inputBuffer.putFloat(((pixelValue shr 16 and 0xFF) - mean) / std)\n      inputBuffer.putFloat(((pixelValue shr 8 and 0xFF) - mean) / std)\n      inputBuffer.putFloat(((pixelValue and 0xFF) - mean) / std)\n    }\n    return inputBuffer\n  }\n\n  /** Preload and memory map the model file, returning a MappedByteBuffer containing the model. */\n  private fun loadModelFile(path: String, context: Context): MappedByteBuffer {\n    val fileDescriptor = context.assets.openFd(path)\n    val inputStream = FileInputStream(fileDescriptor.fileDescriptor)\n    return inputStream.channel.map(\n      FileChannel.MapMode.READ_ONLY, fileDescriptor.startOffset, fileDescriptor.declaredLength\n    )\n  }\n\n  /**\n   * Initializes an outputMap of 1 * x * y * z FloatArrays for the model processing to populate.\n   */\n  private fun initOutputMap(interpreter: Interpreter): HashMap<Int, Any> {\n    val outputMap = HashMap<Int, Any>()\n\n    // 1 * 9 * 9 * 17 contains heatmaps\n    val heatmapsShape = interpreter.getOutputTensor(0).shape()\n    outputMap[0] = Array(heatmapsShape[0]) {\n      Array(heatmapsShape[1]) {\n        Array(heatmapsShape[2]) { FloatArray(heatmapsShape[3]) }\n      }\n    }\n\n    // 1 * 9 * 9 * 34 contains offsets\n    val offsetsShape = interpreter.getOutputTensor(1).shape()\n    outputMap[1] = Array(offsetsShape[0]) {\n      Array(offsetsShape[1]) { Array(offsetsShape[2]) { FloatArray(offsetsShape[3]) } }\n    }\n\n    // 1 * 9 * 9 * 32 contains forward displacements\n    val displacementsFwdShape = interpreter.getOutputTensor(2).shape()\n    outputMap[2] = Array(offsetsShape[0]) {\n      Array(displacementsFwdShape[1]) {\n        Array(displacementsFwdShape[2]) { FloatArray(displacementsFwdShape[3]) }\n      }\n    }\n\n    // 1 * 9 * 9 * 32 contains backward displacements\n    val displacementsBwdShape = interpreter.getOutputTensor(3).shape()\n    outputMap[3] = Array(displacementsBwdShape[0]) {\n      Array(displacementsBwdShape[1]) {\n        Array(displacementsBwdShape[2]) { FloatArray(displacementsBwdShape[3]) }\n      }\n    }\n\n    return outputMap\n  }\n\n  /**\n   * Estimates the pose for a single person.\n   * args:\n   *      bitmap: image bitmap of frame that should be processed\n   * returns:\n   *      person: a Person object containing data about keypoint locations and confidence scores\n   */\n  @Suppress(\"UNCHECKED_CAST\")\n  fun estimateSinglePose(bitmap: Bitmap): Person {\n    val estimationStartTimeNanos = SystemClock.elapsedRealtimeNanos()\n    val inputArray = arrayOf(initInputArray(bitmap))\n    Log.i(\n      \"posenet\",\n      String.format(\n        \"Scaling to [-1,1] took %.2f ms\",\n        1.0f * (SystemClock.elapsedRealtimeNanos() - estimationStartTimeNanos) / 1_000_000\n      )\n    )\n\n    val outputMap = initOutputMap(getInterpreter())\n\n    val inferenceStartTimeNanos = SystemClock.elapsedRealtimeNanos()\n    getInterpreter().runForMultipleInputsOutputs(inputArray, outputMap)\n    lastInferenceTimeNanos = SystemClock.elapsedRealtimeNanos() - inferenceStartTimeNanos\n    Log.i(\n      \"posenet\",\n      String.format(\"Interpreter took %.2f ms\", 1.0f * lastInferenceTimeNanos / 1_000_000)\n    )\n\n    val heatmaps = outputMap[0] as Array<Array<Array<FloatArray>>>\n    val offsets = outputMap[1] as Array<Array<Array<FloatArray>>>\n\n    val height = heatmaps[0].size\n    val width = heatmaps[0][0].size\n    val numKeypoints = heatmaps[0][0][0].size\n\n    // Finds the (row, col) locations of where the keypoints are most likely to be.\n    val keypointPositions = Array(numKeypoints) { Pair(0, 0) }\n    for (keypoint in 0 until numKeypoints) {\n      var maxVal = heatmaps[0][0][0][keypoint]\n      var maxRow = 0\n      var maxCol = 0\n      for (row in 0 until height) {\n        for (col in 0 until width) {\n          if (heatmaps[0][row][col][keypoint] > maxVal) {\n            maxVal = heatmaps[0][row][col][keypoint]\n            maxRow = row\n            maxCol = col\n          }\n        }\n      }\n      keypointPositions[keypoint] = Pair(maxRow, maxCol)\n    }\n\n    // Calculating the x and y coordinates of the keypoints with offset adjustment.\n    val xCoords = IntArray(numKeypoints)\n    val yCoords = IntArray(numKeypoints)\n    val confidenceScores = FloatArray(numKeypoints)\n    keypointPositions.forEachIndexed { idx, position ->\n      val positionY = keypointPositions[idx].first\n      val positionX = keypointPositions[idx].second\n      yCoords[idx] = (\n        position.first / (height - 1).toFloat() * bitmap.height +\n          offsets[0][positionY][positionX][idx]\n        ).toInt()\n      xCoords[idx] = (\n        position.second / (width - 1).toFloat() * bitmap.width +\n          offsets[0][positionY]\n          [positionX][idx + numKeypoints]\n        ).toInt()\n      confidenceScores[idx] = sigmoid(heatmaps[0][positionY][positionX][idx])\n    }\n\n    val person = Person()\n    val keypointList = Array(numKeypoints) { KeyPoint() }\n    var totalScore = 0.0f\n    enumValues<BodyPart>().forEachIndexed { idx, it ->\n      keypointList[idx].bodyPart = it\n      keypointList[idx].position.x = xCoords[idx]\n      keypointList[idx].position.y = yCoords[idx]\n      keypointList[idx].score = confidenceScores[idx]\n      totalScore += confidenceScores[idx]\n    }\n\n    person.keyPoints = keypointList.toList()\n    person.score = totalScore / numKeypoints\n\n    return person\n  }\n}\n"
  },
  {
    "path": "lite/examples/posenet/android/settings.gradle",
    "content": "rootProject.name = 'TFLite PoseNet Demo App'\ninclude ':app', ':posenet'\n"
  },
  {
    "path": "lite/examples/posenet/ios/.gitignore",
    "content": "# ignore model file\n*.tflite\n"
  },
  {
    "path": "lite/examples/posenet/ios/Podfile",
    "content": "# Uncomment the next line to define a global platform for your project\n platform :ios, '12.0'\n\ntarget 'PoseNet' do\n  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks\n  use_frameworks!\n\n  # Pods for PoseNet\n   pod 'TensorFlowLiteSwift', '~> 0.0.1-nightly'\nend\n"
  },
  {
    "path": "lite/examples/posenet/ios/PoseNet/AppDelegate.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n  var window: UIWindow?\n\n  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {\n    return true\n  }\n\n  func applicationWillResignActive(_ application: UIApplication) {\n  }\n\n  func applicationDidEnterBackground(_ application: UIApplication) {\n  }\n\n  func applicationWillEnterForeground(_ application: UIApplication) {\n  }\n\n  func applicationDidBecomeActive(_ application: UIApplication) {\n  }\n\n  func applicationWillTerminate(_ application: UIApplication) {\n  }\n}\n\n"
  },
  {
    "path": "lite/examples/posenet/ios/PoseNet/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\": [\n    {\n      \"size\": \"60x60\",\n      \"expected-size\": \"180\",\n      \"filename\": \"180.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"3x\"\n    },\n    {\n      \"size\": \"40x40\",\n      \"expected-size\": \"80\",\n      \"filename\": \"80.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"40x40\",\n      \"expected-size\": \"120\",\n      \"filename\": \"120.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"3x\"\n    },\n    {\n      \"size\": \"60x60\",\n      \"expected-size\": \"120\",\n      \"filename\": \"120.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"58\",\n      \"filename\": \"58.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"29\",\n      \"filename\": \"29.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"87\",\n      \"filename\": \"87.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"3x\"\n    },\n    {\n      \"size\": \"20x20\",\n      \"expected-size\": \"40\",\n      \"filename\": \"40.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"20x20\",\n      \"expected-size\": \"60\",\n      \"filename\": \"60.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"3x\"\n    },\n    {\n      \"size\": \"1024x1024\",\n      \"filename\": \"1024.png\",\n      \"expected-size\": \"1024\",\n      \"idiom\": \"ios-marketing\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"40x40\",\n      \"expected-size\": \"80\",\n      \"filename\": \"80.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"72x72\",\n      \"expected-size\": \"72\",\n      \"filename\": \"72.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"76x76\",\n      \"expected-size\": \"152\",\n      \"filename\": \"152.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"50x50\",\n      \"expected-size\": \"100\",\n      \"filename\": \"100.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"58\",\n      \"filename\": \"58.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"76x76\",\n      \"expected-size\": \"76\",\n      \"filename\": \"76.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"29\",\n      \"filename\": \"29.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"72x72\",\n      \"expected-size\": \"144\",\n      \"filename\": \"144.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"40x40\",\n      \"expected-size\": \"40\",\n      \"filename\": \"40.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"83.5x83.5\",\n      \"expected-size\": \"167\",\n      \"filename\": \"167.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"20x20\",\n      \"expected-size\": \"20\",\n      \"filename\": \"20.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"20x20\",\n      \"expected-size\": \"40\",\n      \"filename\": \"40.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    }\n  ]\n}\n"
  },
  {
    "path": "lite/examples/posenet/ios/PoseNet/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/posenet/ios/PoseNet/Camera Feed/CameraFeedManager.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport AVFoundation\nimport UIKit\nimport os\n\n// MARK: - CameraFeedManagerDelegate Declaration\n@objc protocol CameraFeedManagerDelegate: class {\n  /// This method delivers the pixel buffer of the current frame seen by the device's camera.\n  @objc optional func cameraFeedManager(\n    _ manager: CameraFeedManager, didOutput pixelBuffer: CVPixelBuffer\n  )\n\n  /// This method informs that a session runtime error occurred.\n  func cameraFeedManagerDidEncounterSessionRunTimeError(_ manager: CameraFeedManager)\n\n  /// This method informs that the session was interrupted.\n  func cameraFeedManager(\n    _ manager: CameraFeedManager, sessionWasInterrupted canResumeManually: Bool\n  )\n\n  /// This method informs that the session interruption has ended.\n  func cameraFeedManagerDidEndSessionInterruption(_ manager: CameraFeedManager)\n\n  /// This method informs that there was an error in video configuration.\n  func presentVideoConfigurationErrorAlert(_ manager: CameraFeedManager)\n\n  /// This method informs that the camera permissions have been denied.\n  func presentCameraPermissionsDeniedAlert(_ manager: CameraFeedManager)\n}\n\n/// This enum holds the state of the camera initialization.\n// MARK: - Camera Initialization State Enum\nenum CameraConfiguration {\n  case success\n  case failed\n  case permissionDenied\n}\n\n/// This class manages all camera related functionalities.\n// MARK: - Camera Related Functionality Manager\nclass CameraFeedManager: NSObject {\n  // MARK: Camera Related Instance Variables\n  private let session: AVCaptureSession = AVCaptureSession()\n\n  private let previewView: PreviewView\n  private let sessionQueue = DispatchQueue(label: \"sessionQueue\")\n  private var cameraConfiguration: CameraConfiguration = .failed\n  private lazy var videoDataOutput = AVCaptureVideoDataOutput()\n  private var isSessionRunning = false\n\n  // MARK: CameraFeedManagerDelegate\n  weak var delegate: CameraFeedManagerDelegate?\n\n  // MARK: Initializer\n  init(previewView: PreviewView) {\n    self.previewView = previewView\n    super.init()\n\n    // Initializes the session\n    session.sessionPreset = .high\n    self.previewView.session = session\n    self.previewView.previewLayer.connection?.videoOrientation = .portrait\n    self.previewView.previewLayer.videoGravity = .resizeAspectFill\n    self.attemptToConfigureSession()\n  }\n\n  // MARK: Session Start and End methods\n\n  /// This method starts an AVCaptureSession based on whether the camera configuration was successful.\n  func checkCameraConfigurationAndStartSession() {\n    sessionQueue.async {\n      switch self.cameraConfiguration {\n      case .success:\n        self.addObservers()\n        self.startSession()\n      case .failed:\n        DispatchQueue.main.async {\n          self.delegate?.presentVideoConfigurationErrorAlert(self)\n        }\n      case .permissionDenied:\n        DispatchQueue.main.async {\n          self.delegate?.presentCameraPermissionsDeniedAlert(self)\n        }\n      }\n    }\n  }\n\n  /// This method stops a running an AVCaptureSession.\n  func stopSession() {\n    self.removeObservers()\n    sessionQueue.async {\n      if self.session.isRunning {\n        self.session.stopRunning()\n        self.isSessionRunning = self.session.isRunning\n      }\n    }\n\n  }\n\n  /// This method resumes an interrupted AVCaptureSession.\n  func resumeInterruptedSession(withCompletion completion: @escaping (Bool) -> Void) {\n    sessionQueue.async {\n      self.startSession()\n\n      DispatchQueue.main.async {\n        completion(self.isSessionRunning)\n      }\n    }\n  }\n\n  /// This method starts the AVCaptureSession\n  private func startSession() {\n    self.session.startRunning()\n    self.isSessionRunning = self.session.isRunning\n  }\n\n  // MARK: Session Configuration Methods.\n  /// This method requests for camera permissions and handles the configuration of the session and stores the result of configuration.\n  private func attemptToConfigureSession() {\n    switch AVCaptureDevice.authorizationStatus(for: .video) {\n    case .authorized:\n      self.cameraConfiguration = .success\n    case .notDetermined:\n      self.sessionQueue.suspend()\n      self.requestCameraAccess(completion: { granted in\n        self.sessionQueue.resume()\n      })\n    case .denied:\n      self.cameraConfiguration = .permissionDenied\n    default:\n      break\n    }\n\n    self.sessionQueue.async {\n      self.configureSession()\n    }\n  }\n\n  /// This method requests for camera permissions.\n  private func requestCameraAccess(completion: @escaping (Bool) -> Void) {\n    AVCaptureDevice.requestAccess(for: .video) { (granted) in\n      if !granted {\n        self.cameraConfiguration = .permissionDenied\n      } else {\n        self.cameraConfiguration = .success\n      }\n      completion(granted)\n    }\n  }\n\n  /// This method handles all the steps to configure an AVCaptureSession.\n  private func configureSession() {\n    guard cameraConfiguration == .success else {\n      return\n    }\n    session.beginConfiguration()\n\n    // Tries to add an AVCaptureDeviceInput.\n    guard addVideoDeviceInput() == true else {\n      self.session.commitConfiguration()\n      self.cameraConfiguration = .failed\n      return\n    }\n\n    // Tries to add an AVCaptureVideoDataOutput.\n    guard addVideoDataOutput() else {\n      self.session.commitConfiguration()\n      self.cameraConfiguration = .failed\n      return\n    }\n\n    session.commitConfiguration()\n    self.cameraConfiguration = .success\n  }\n\n  /// This method tries to an AVCaptureDeviceInput to the current AVCaptureSession.\n  private func addVideoDeviceInput() -> Bool {\n    /// Tries to get the default back camera.\n    guard\n      let camera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)\n      else {\n        fatalError(\"Cannot find camera\")\n    }\n\n    do {\n      let videoDeviceInput = try AVCaptureDeviceInput(device: camera)\n      if session.canAddInput(videoDeviceInput) {\n        session.addInput(videoDeviceInput)\n        return true\n      } else {\n        return false\n      }\n    } catch {\n      fatalError(\"Cannot create video device input\")\n    }\n  }\n\n  /// This method tries to an AVCaptureVideoDataOutput to the current AVCaptureSession.\n  private func addVideoDataOutput() -> Bool {\n    let sampleBufferQueue = DispatchQueue(label: \"sampleBufferQueue\")\n    videoDataOutput.setSampleBufferDelegate(self, queue: sampleBufferQueue)\n    videoDataOutput.alwaysDiscardsLateVideoFrames = true\n    videoDataOutput.videoSettings = [\n      String(kCVPixelBufferPixelFormatTypeKey): kCMPixelFormat_32BGRA\n    ]\n\n    if session.canAddOutput(videoDataOutput) {\n      session.addOutput(videoDataOutput)\n      videoDataOutput.connection(with: .video)?.videoOrientation = .portrait\n      return true\n    }\n    return false\n  }\n\n  // MARK: Notification Observer Handling\n  private func addObservers() {\n    NotificationCenter.default.addObserver(\n      self, selector: #selector(CameraFeedManager.sessionRuntimeErrorOccurred(notification:)),\n      name: NSNotification.Name.AVCaptureSessionRuntimeError, object: session)\n    NotificationCenter.default.addObserver(\n      self, selector: #selector(CameraFeedManager.sessionWasInterrupted(notification:)),\n      name: NSNotification.Name.AVCaptureSessionWasInterrupted, object: session)\n    NotificationCenter.default.addObserver(\n      self, selector: #selector(CameraFeedManager.sessionInterruptionEnded),\n      name: NSNotification.Name.AVCaptureSessionInterruptionEnded, object: session)\n  }\n\n  private func removeObservers() {\n    NotificationCenter.default.removeObserver(\n      self, name: NSNotification.Name.AVCaptureSessionRuntimeError, object: session)\n    NotificationCenter.default.removeObserver(\n      self, name: NSNotification.Name.AVCaptureSessionWasInterrupted, object: session)\n    NotificationCenter.default.removeObserver(\n      self, name: NSNotification.Name.AVCaptureSessionInterruptionEnded, object: session)\n  }\n\n  // MARK: Notification Observers\n  @objc func sessionWasInterrupted(notification: Notification) {\n    if let userInfoValue = notification.userInfo?[AVCaptureSessionInterruptionReasonKey]\n      as AnyObject?,\n      let reasonIntegerValue = userInfoValue.integerValue,\n      let reason = AVCaptureSession.InterruptionReason(rawValue: reasonIntegerValue)\n    {\n      os_log(\"Capture session was interrupted with reason: %s\", type: .error, reason.rawValue)\n\n      var canResumeManually = false\n      if reason == .videoDeviceInUseByAnotherClient {\n        canResumeManually = true\n      } else if reason == .videoDeviceNotAvailableWithMultipleForegroundApps {\n        canResumeManually = false\n      }\n\n      delegate?.cameraFeedManager(self, sessionWasInterrupted: canResumeManually)\n\n    }\n  }\n\n  @objc func sessionInterruptionEnded(notification: Notification) {\n    delegate?.cameraFeedManagerDidEndSessionInterruption(self)\n  }\n\n  @objc func sessionRuntimeErrorOccurred(notification: Notification) {\n    guard let error = notification.userInfo?[AVCaptureSessionErrorKey] as? AVError else {\n      return\n    }\n\n    os_log(\"Capture session runtime error: %s\", type: .error, error.localizedDescription)\n\n    if error.code == .mediaServicesWereReset {\n      sessionQueue.async {\n        if self.isSessionRunning {\n          self.startSession()\n        } else {\n          DispatchQueue.main.async {\n            self.delegate?.cameraFeedManagerDidEncounterSessionRunTimeError(self)\n          }\n        }\n      }\n    } else {\n      delegate?.cameraFeedManagerDidEncounterSessionRunTimeError(self)\n    }\n  }\n}\n\n/// AVCaptureVideoDataOutputSampleBufferDelegate\nextension CameraFeedManager: AVCaptureVideoDataOutputSampleBufferDelegate {\n  /// This method delegates the CVPixelBuffer of the frame seen by the camera currently.\n  func captureOutput(\n    _ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer,\n    from connection: AVCaptureConnection\n  ) {\n\n    // Converts the CMSampleBuffer to a CVPixelBuffer.\n    let pixelBuffer: CVPixelBuffer? = CMSampleBufferGetImageBuffer(sampleBuffer)\n\n    guard let imagePixelBuffer = pixelBuffer else {\n      return\n    }\n\n    // Delegates the pixel buffer to the ViewController.\n    delegate?.cameraFeedManager?(self, didOutput: imagePixelBuffer)\n  }\n}\n"
  },
  {
    "path": "lite/examples/posenet/ios/PoseNet/Camera Feed/PreviewView.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\nimport AVFoundation\n\n /// The camera frame is displayed on this view.\nclass PreviewView: UIView {\n  var previewLayer: AVCaptureVideoPreviewLayer {\n    guard let layer = layer as? AVCaptureVideoPreviewLayer else {\n      fatalError(\"Layer expected is of type VideoPreviewLayer\")\n    }\n    return layer\n  }\n\n  var session: AVCaptureSession? {\n    get {\n      return previewLayer.session\n    }\n    set {\n      previewLayer.session = newValue\n    }\n  }\n\n  override class var layerClass: AnyClass {\n    return AVCaptureVideoPreviewLayer.self\n  }\n}\n"
  },
  {
    "path": "lite/examples/posenet/ios/PoseNet/Cells/InfoCell.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n/// Table cell for inference result in bottom view.\nclass InfoCell: UITableViewCell {\n  @IBOutlet weak var fieldNameLabel: UILabel!\n  @IBOutlet weak var infoLabel: UILabel!\n}\n"
  },
  {
    "path": "lite/examples/posenet/ios/PoseNet/Constants.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n\nenum Constants {\n  // MARK: - Constants related to the image processing\n  static let bgraPixel = (channels: 4, alphaComponent: 3, lastBgrComponent: 2)\n  static let rgbPixelChannels = 3\n  static let maxRGBValue: Float32 = 255.0\n\n  // MARK: - Constants related to the model interpreter\n  static let defaultThreadCount = 2\n  static let defaultDelegate: Delegates = .CPU\n}\n"
  },
  {
    "path": "lite/examples/posenet/ios/PoseNet/Extensions/CGSizeExtension.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n\nimport Accelerate\nimport Foundation\n\nextension CGSize {\n  /// Returns `CGAffineTransform` to resize `self` to fit in destination size, keeping aspect ratio\n  /// of `self`. `self` image is resized to be inscribe to destination size and located in center of\n  /// destination.\n  ///\n  /// - Parameter toFitIn: destination size to be filled.\n  /// - Returns: `CGAffineTransform` to transform `self` image to `dest` image.\n  func transformKeepAspect(toFitIn dest: CGSize) -> CGAffineTransform {\n    let sourceRatio = self.height / self.width\n    let destRatio = dest.height / dest.width\n\n    // Calculates ratio `self` to `dest`.\n    var ratio: CGFloat\n    var x: CGFloat = 0\n    var y: CGFloat = 0\n    if sourceRatio > destRatio {\n      // Source size is taller than destination. Resized to fit in destination height, and find\n      // horizontal starting point to be centered.\n      ratio = dest.height / self.height\n      x = (dest.width - self.width * ratio) / 2\n    } else {\n      ratio = dest.width / self.width\n      y = (dest.height - self.height * ratio) / 2\n    }\n    return CGAffineTransform(a: ratio, b: 0, c: 0, d: ratio, tx: x, ty: y)\n  }\n}\n"
  },
  {
    "path": "lite/examples/posenet/ios/PoseNet/Extensions/CVPixelBufferExtension.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n\nimport Accelerate\nimport Foundation\n\nextension CVPixelBuffer {\n  var size: CGSize {\n    return CGSize(width: CVPixelBufferGetWidth(self), height: CVPixelBufferGetHeight(self))\n  }\n\n  /// Returns a new `CVPixelBuffer` created by taking the self area and resizing it to the\n  /// specified target size. Aspect ratios of source image and destination image are expected to be\n  /// same.\n  ///\n  /// - Parameters:\n  ///   - from: Source area of image to be cropped and resized.\n  ///   - to: Size to scale the image to(i.e. image size used while training the model).\n  /// - Returns: The cropped and resized image of itself.\n  func resize(from source: CGRect, to size: CGSize) -> CVPixelBuffer? {\n    let rect = CGRect(origin: .zero, size: self.size)\n    guard rect.contains(source) else {\n      os_log(\"Resizing Error: source area is out of index\", type: .error)\n      return nil\n    }\n    guard abs(size.width / size.height - source.size.width / source.size.height) < 1e-5\n    else {\n      os_log(\n        \"Resizing Error: source image ratio and destination image ratio is different\",\n        type: .error)\n      return nil\n    }\n\n    let inputImageRowBytes = CVPixelBufferGetBytesPerRow(self)\n    let imageChannels = 4\n\n    CVPixelBufferLockBaseAddress(self, CVPixelBufferLockFlags(rawValue: 0))\n    defer { CVPixelBufferUnlockBaseAddress(self, CVPixelBufferLockFlags(rawValue: 0)) }\n\n    // Finds the address of the upper leftmost pixel of the source area.\n    guard\n      let inputBaseAddress = CVPixelBufferGetBaseAddress(self)?.advanced(\n        by: Int(source.minY) * inputImageRowBytes + Int(source.minX) * imageChannels)\n    else {\n      return nil\n    }\n\n    // Crops given area as vImage Buffer.\n    var croppedImage = vImage_Buffer(\n      data: inputBaseAddress, height: UInt(source.height), width: UInt(source.width),\n      rowBytes: inputImageRowBytes)\n\n    let resultRowBytes = Int(size.width) * imageChannels\n    guard let resultAddress = malloc(Int(size.height) * resultRowBytes) else {\n      return nil\n    }\n\n    // Allocates a vacant vImage buffer for resized image.\n    var resizedImage = vImage_Buffer(\n      data: resultAddress,\n      height: UInt(size.height), width: UInt(size.width),\n      rowBytes: resultRowBytes\n    )\n\n    // Performs the scale operation on cropped image and stores it in result image buffer.\n    guard vImageScale_ARGB8888(&croppedImage, &resizedImage, nil, vImage_Flags(0)) == kvImageNoError\n    else {\n      return nil\n    }\n\n    let releaseCallBack: CVPixelBufferReleaseBytesCallback = { mutablePointer, pointer in\n      if let pointer = pointer {\n        free(UnsafeMutableRawPointer(mutating: pointer))\n      }\n    }\n\n    var result: CVPixelBuffer?\n\n    // Converts the thumbnail vImage buffer to CVPixelBuffer\n    let conversionStatus = CVPixelBufferCreateWithBytes(\n      nil,\n      Int(size.width), Int(size.height),\n      CVPixelBufferGetPixelFormatType(self),\n      resultAddress,\n      resultRowBytes,\n      releaseCallBack,\n      nil,\n      nil,\n      &result\n    )\n\n    guard conversionStatus == kCVReturnSuccess else {\n      free(resultAddress)\n      return nil\n    }\n\n    return result\n  }\n\n  /// Returns the RGB `Data` representation of the given image buffer.\n  ///\n  /// - Parameters:\n  ///   - isModelQuantized: Whether the model is quantized (i.e. fixed point values rather than\n  ///       floating point values).\n  /// - Returns: The RGB data representation of the image buffer or `nil` if the buffer could not be\n  ///     converted.\n  func rgbData(\n    isModelQuantized: Bool\n  ) -> Data? {\n    CVPixelBufferLockBaseAddress(self, .readOnly)\n    defer { CVPixelBufferUnlockBaseAddress(self, .readOnly) }\n    guard let sourceData = CVPixelBufferGetBaseAddress(self) else {\n      return nil\n    }\n\n    let width = CVPixelBufferGetWidth(self)\n    let height = CVPixelBufferGetHeight(self)\n    let sourceBytesPerRow = CVPixelBufferGetBytesPerRow(self)\n    let destinationBytesPerRow = Constants.rgbPixelChannels * width\n\n    // Assign input image to `sourceBuffer` to convert it.\n    var sourceBuffer = vImage_Buffer(\n      data: sourceData,\n      height: vImagePixelCount(height),\n      width: vImagePixelCount(width),\n      rowBytes: sourceBytesPerRow)\n\n    // Make `destinationBuffer` and `destinationData` for its data to be assigned.\n    guard let destinationData = malloc(height * destinationBytesPerRow) else {\n      os_log(\"Error: out of memory\", type: .error)\n      return nil\n    }\n    defer { free(destinationData) }\n    var destinationBuffer = vImage_Buffer(\n      data: destinationData,\n      height: vImagePixelCount(height),\n      width: vImagePixelCount(width),\n      rowBytes: destinationBytesPerRow)\n\n    // Convert image type.\n    switch CVPixelBufferGetPixelFormatType(self) {\n    case kCVPixelFormatType_32BGRA:\n      vImageConvert_BGRA8888toRGB888(&sourceBuffer, &destinationBuffer, UInt32(kvImageNoFlags))\n    case kCVPixelFormatType_32ARGB:\n      vImageConvert_BGRA8888toRGB888(&sourceBuffer, &destinationBuffer, UInt32(kvImageNoFlags))\n    default:\n      os_log(\"The type of this image is not supported.\", type: .error)\n      return nil\n    }\n\n    // Make `Data` with converted image.\n    let imageByteData = Data(\n      bytes: destinationBuffer.data, count: destinationBuffer.rowBytes * height)\n\n    if isModelQuantized { return imageByteData }\n\n    let imageBytes = [UInt8](imageByteData)\n    return Data(copyingBufferOf: imageBytes.map { Float($0) / Constants.maxRGBValue })\n  }\n}\n"
  },
  {
    "path": "lite/examples/posenet/ios/PoseNet/Extensions/TFLiteExtension.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// =============================================================================\n\nimport Accelerate\nimport CoreImage\nimport Foundation\nimport TensorFlowLite\n\n// MARK: - Data\nextension Data {\n  /// Creates a new buffer by copying the buffer pointer of the given array.\n  ///\n  /// - Warning: The given array's element type `T` must be trivial in that it can be copied bit\n  ///     for bit with no indirection or reference-counting operations; otherwise, reinterpreting\n  ///     data from the resulting buffer has undefined behavior.\n  /// - Parameter array: An array with elements of type `T`.\n  init<T>(copyingBufferOf array: [T]) {\n    self = array.withUnsafeBufferPointer(Data.init)\n  }\n\n  /// Convert a Data instance to Array representation.\n  func toArray<T>(type: T.Type) -> [T] where T: AdditiveArithmetic {\n    var array = [T](repeating: T.zero, count: self.count / MemoryLayout<T>.stride)\n    _ = array.withUnsafeMutableBytes { self.copyBytes(to: $0) }\n    return array\n  }\n}\n\n// MARK: - Wrappers\n/// Struct for handling multidimension `Data` in flat `Array`.\nstruct FlatArray<Element: AdditiveArithmetic> {\n  private var array: [Element]\n  var dimensions: [Int]\n\n  init(tensor: Tensor) {\n    dimensions = tensor.shape.dimensions\n    array = tensor.data.toArray(type: Element.self)\n  }\n\n  private func flatIndex(_ index: [Int]) -> Int {\n    guard index.count == dimensions.count else {\n      fatalError(\"Invalid index: got \\(index.count) index(es) for \\(dimensions.count) index(es).\")\n    }\n\n    var result = 0\n    for i in 0..<dimensions.count {\n      guard dimensions[i] > index[i] else {\n        fatalError(\"Invalid index: \\(index[i]) is bigger than \\(dimensions[i])\")\n      }\n      result = dimensions[i] * result + index[i]\n    }\n    return result\n  }\n\n  subscript(_ index: Int...) -> Element {\n    get {\n      return array[flatIndex(index)]\n    }\n    set(newValue) {\n      array[flatIndex(index)] = newValue\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/posenet/ios/PoseNet/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>NSCameraUsageDescription</key>\n\t<string>This app will use camera to continuously estimate the pose the user shows.</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "lite/examples/posenet/ios/PoseNet/ModelDataHandler/ModelDataHandler.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Accelerate\nimport CoreImage\nimport Foundation\nimport TensorFlowLite\nimport UIKit\n\n/// This class handles all data preprocessing and makes calls to run inference on a given frame\n/// by invoking the `Interpreter`. It then formats the inferences obtained.\nclass ModelDataHandler {\n  // MARK: - Private Properties\n\n  /// TensorFlow Lite `Interpreter` object for performing inference on a given model.\n  private var interpreter: Interpreter\n\n  /// TensorFlow lite `Tensor` of model input and output.\n  private var inputTensor: Tensor\n\n  private var heatsTensor: Tensor\n  private var offsetsTensor: Tensor\n\n  // MARK: - Initialization\n\n  /// A failable initializer for `ModelDataHandler`. A new instance is created if the model is\n  /// successfully loaded from the app's main bundle. Default `threadCount` is 2.\n  init(\n    threadCount: Int = Constants.defaultThreadCount,\n    delegate: Delegates = Constants.defaultDelegate\n  ) throws {\n    // Construct the path to the model file.\n    guard\n      let modelPath = Bundle.main.path(\n        forResource: Model.file.name,\n        ofType: Model.file.extension\n      )\n    else {\n      fatalError(\"Failed to load the model file with name: \\(Model.file.name).\")\n    }\n\n    // Specify the options for the `Interpreter`.\n    var options = Interpreter.Options()\n    options.threadCount = threadCount\n\n    // Specify the delegates for the `Interpreter`.\n    var delegates: [Delegate]?\n    switch delegate {\n    case .Metal:\n      delegates = [MetalDelegate()]\n    case .CoreML:\n      if let coreMLDelegate = CoreMLDelegate() {\n        delegates = [coreMLDelegate]\n      } else {\n        delegates = nil\n      }\n    default:\n      delegates = nil\n    }\n\n    // Create the `Interpreter`.\n    interpreter = try Interpreter(modelPath: modelPath, options: options, delegates: delegates)\n\n    // Initialize input and output `Tensor`s.\n    // Allocate memory for the model's input `Tensor`s.\n    try interpreter.allocateTensors()\n\n    // Get allocated input and output `Tensor`s.\n    inputTensor = try interpreter.input(at: 0)\n    heatsTensor = try interpreter.output(at: 0)\n    offsetsTensor = try interpreter.output(at: 1)\n\n    // Check if input and output `Tensor`s are in the expected formats.\n    guard (inputTensor.dataType == .uInt8) == Model.isQuantized else {\n      fatalError(\"Unexpected Model: quantization is \\(!Model.isQuantized)\")\n    }\n\n    guard inputTensor.shape.dimensions[0] == Model.input.batchSize,\n      inputTensor.shape.dimensions[1] == Model.input.height,\n      inputTensor.shape.dimensions[2] == Model.input.width,\n      inputTensor.shape.dimensions[3] == Model.input.channelSize\n    else {\n      fatalError(\"Unexpected Model: input shape\")\n    }\n\n    guard heatsTensor.shape.dimensions[0] == Model.output.batchSize,\n      heatsTensor.shape.dimensions[1] == Model.output.height,\n      heatsTensor.shape.dimensions[2] == Model.output.width,\n      heatsTensor.shape.dimensions[3] == Model.output.keypointSize\n    else {\n      fatalError(\"Unexpected Model: heat tensor\")\n    }\n\n    guard offsetsTensor.shape.dimensions[0] == Model.output.batchSize,\n      offsetsTensor.shape.dimensions[1] == Model.output.height,\n      offsetsTensor.shape.dimensions[2] == Model.output.width,\n      offsetsTensor.shape.dimensions[3] == Model.output.offsetSize\n    else {\n      fatalError(\"Unexpected Model: offset tensor\")\n    }\n\n  }\n\n  /// Runs PoseNet model with given image with given source area to destination area.\n  ///\n  /// - Parameters:\n  ///   - on: Input image to run the model.\n  ///   - from: Range of input image to run the model.\n  ///   - to: Size of view to render the result.\n  /// - Returns: Result of the inference and the times consumed in every steps.\n  func runPoseNet(on pixelbuffer: CVPixelBuffer, from source: CGRect, to dest: CGSize)\n    -> (Result, Times)?\n  {\n    // Start times of each process.\n    let preprocessingStartTime: Date\n    let inferenceStartTime: Date\n    let postprocessingStartTime: Date\n\n    // Processing times in milliseconds.\n    let preprocessingTime: TimeInterval\n    let inferenceTime: TimeInterval\n    let postprocessingTime: TimeInterval\n\n    preprocessingStartTime = Date()\n    guard let data = preprocess(of: pixelbuffer, from: source) else {\n      os_log(\"Preprocessing failed\", type: .error)\n      return nil\n    }\n    preprocessingTime = Date().timeIntervalSince(preprocessingStartTime) * 1000\n\n    inferenceStartTime = Date()\n    inference(from: data)\n    inferenceTime = Date().timeIntervalSince(inferenceStartTime) * 1000\n\n    postprocessingStartTime = Date()\n    guard let result = postprocess(to: dest) else {\n      os_log(\"Postprocessing failed\", type: .error)\n      return nil\n    }\n    postprocessingTime = Date().timeIntervalSince(postprocessingStartTime) * 1000\n\n    let times = Times(\n      preprocessing: preprocessingTime,\n      inference: inferenceTime,\n      postprocessing: postprocessingTime)\n    return (result, times)\n  }\n\n  // MARK: - Private functions to run model\n  /// Preprocesses given rectangle image to be `Data` of desired size by cropping and resizing it.\n  ///\n  /// - Parameters:\n  ///   - of: Input image to crop and resize.\n  ///   - from: Target area to be cropped and resized.\n  /// - Returns: The cropped and resized image. `nil` if it can not be processed.\n  private func preprocess(of pixelBuffer: CVPixelBuffer, from targetSquare: CGRect) -> Data? {\n    let sourcePixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer)\n    assert(sourcePixelFormat == kCVPixelFormatType_32BGRA)\n\n    // Resize `targetSquare` of input image to `modelSize`.\n    let modelSize = CGSize(width: Model.input.width, height: Model.input.height)\n    guard let thumbnail = pixelBuffer.resize(from: targetSquare, to: modelSize)\n    else {\n      return nil\n    }\n\n    // Remove the alpha component from the image buffer to get the initialized `Data`.\n    guard let inputData = thumbnail.rgbData(isModelQuantized: Model.isQuantized)\n    else {\n      os_log(\"Failed to convert the image buffer to RGB data.\", type: .error)\n      return nil\n    }\n\n    return inputData\n  }\n\n  /// Postprocesses output `Tensor`s to `Result` with size of view to render the result.\n  ///\n  /// - Parameters:\n  ///   - to: Size of view to be displayed.\n  /// - Returns: Postprocessed `Result`. `nil` if it can not be processed.\n  private func postprocess(to viewSize: CGSize) -> Result? {\n    // MARK: Formats output tensors\n    // Convert `Tensor` to `FlatArray`. As PoseNet is not quantized, convert them to Float type\n    // `FlatArray`.\n    let heats = FlatArray<Float32>(tensor: heatsTensor)\n    let offsets = FlatArray<Float32>(tensor: offsetsTensor)\n\n    // MARK: Find position of each key point\n    // Finds the (row, col) locations of where the keypoints are most likely to be. The highest\n    // `heats[0, row, col, keypoint]` value, the more likely `keypoint` being located in (`row`,\n    // `col`).\n    let keypointPositions = (0..<Model.output.keypointSize).map { keypoint -> (Int, Int) in\n      var maxValue = heats[0, 0, 0, keypoint]\n      var maxRow = 0\n      var maxCol = 0\n      for row in 0..<Model.output.height {\n        for col in 0..<Model.output.width {\n          if heats[0, row, col, keypoint] > maxValue {\n            maxValue = heats[0, row, col, keypoint]\n            maxRow = row\n            maxCol = col\n          }\n        }\n      }\n      return (maxRow, maxCol)\n    }\n\n    // MARK: Calculates total confidence score\n    // Calculates total confidence score of each key position.\n    let totalScoreSum = keypointPositions.enumerated().reduce(0.0) { accumulator, elem -> Float32 in\n      accumulator + sigmoid(heats[0, elem.element.0, elem.element.1, elem.offset])\n    }\n    let totalScore = totalScoreSum / Float32(Model.output.keypointSize)\n\n    // MARK: Calculate key point position on model input\n    // Calculates `KeyPoint` coordination model input image with `offsets` adjustment.\n    let coords = keypointPositions.enumerated().map { index, elem -> (y: Float32, x: Float32) in\n      let (y, x) = elem\n      let yCoord =\n        Float32(y) / Float32(Model.output.height - 1) * Float32(Model.input.height)\n        + offsets[0, y, x, index]\n      let xCoord =\n        Float32(x) / Float32(Model.output.width - 1) * Float32(Model.input.width)\n        + offsets[0, y, x, index + Model.output.keypointSize]\n      return (y: yCoord, x: xCoord)\n    }\n\n    // MARK: Transform key point position and make lines\n    // Make `Result` from `keypointPosition'. Each point is adjusted to `ViewSize` to be drawn.\n    var result = Result(dots: [], lines: [], score: totalScore)\n    var bodyPartToDotMap = [BodyPart: CGPoint]()\n    for (index, part) in BodyPart.allCases.enumerated() {\n      let position = CGPoint(\n        x: CGFloat(coords[index].x) * viewSize.width / CGFloat(Model.input.width),\n        y: CGFloat(coords[index].y) * viewSize.height / CGFloat(Model.input.height)\n      )\n      bodyPartToDotMap[part] = position\n      result.dots.append(position)\n    }\n\n    do {\n      try result.lines = BodyPart.lines.map { map throws -> Line in\n        guard let from = bodyPartToDotMap[map.from] else {\n          throw PostprocessError.missingBodyPart(of: map.from)\n        }\n        guard let to = bodyPartToDotMap[map.to] else {\n          throw PostprocessError.missingBodyPart(of: map.to)\n        }\n        return Line(from: from, to: to)\n      }\n    } catch PostprocessError.missingBodyPart(let missingPart) {\n      os_log(\"Postprocessing error: %s is missing.\", type: .error, missingPart.rawValue)\n      return nil\n    } catch {\n      os_log(\"Postprocessing error: %s\", type: .error, error.localizedDescription)\n      return nil\n    }\n\n    return result\n  }\n\n  /// Run inference with given `Data`\n  ///\n  /// Parameter `from`: `Data` of input image to run model.\n  private func inference(from data: Data) {\n    // Copy the initialized `Data` to the input `Tensor`.\n    do {\n      try interpreter.copy(data, toInputAt: 0)\n\n      // Run inference by invoking the `Interpreter`.\n      try interpreter.invoke()\n\n      // Get the output `Tensor` to process the inference results.\n      heatsTensor = try interpreter.output(at: 0)\n      offsetsTensor = try interpreter.output(at: 1)\n\n    } catch let error {\n      os_log(\n        \"Failed to invoke the interpreter with error: %s\", type: .error,\n        error.localizedDescription)\n      return\n    }\n  }\n\n  /// Returns value within [0,1].\n  private func sigmoid(_ x: Float32) -> Float32 {\n    return (1.0 / (1.0 + exp(-x)))\n  }\n}\n\n// MARK: - Data types for inference result\nstruct KeyPoint {\n  var bodyPart: BodyPart = BodyPart.NOSE\n  var position: CGPoint = CGPoint()\n  var score: Float = 0.0\n}\n\nstruct Line {\n  let from: CGPoint\n  let to: CGPoint\n}\n\nstruct Times {\n  var preprocessing: Double\n  var inference: Double\n  var postprocessing: Double\n}\n\nstruct Result {\n  var dots: [CGPoint]\n  var lines: [Line]\n  var score: Float\n}\n\nenum BodyPart: String, CaseIterable {\n  case NOSE = \"nose\"\n  case LEFT_EYE = \"left eye\"\n  case RIGHT_EYE = \"right eye\"\n  case LEFT_EAR = \"left ear\"\n  case RIGHT_EAR = \"right ear\"\n  case LEFT_SHOULDER = \"left shoulder\"\n  case RIGHT_SHOULDER = \"right shoulder\"\n  case LEFT_ELBOW = \"left elbow\"\n  case RIGHT_ELBOW = \"right elbow\"\n  case LEFT_WRIST = \"left wrist\"\n  case RIGHT_WRIST = \"right wrist\"\n  case LEFT_HIP = \"left hip\"\n  case RIGHT_HIP = \"right hip\"\n  case LEFT_KNEE = \"left knee\"\n  case RIGHT_KNEE = \"right knee\"\n  case LEFT_ANKLE = \"left ankle\"\n  case RIGHT_ANKLE = \"right ankle\"\n\n  /// List of lines connecting each part.\n  static let lines = [\n    (from: BodyPart.LEFT_WRIST, to: BodyPart.LEFT_ELBOW),\n    (from: BodyPart.LEFT_ELBOW, to: BodyPart.LEFT_SHOULDER),\n    (from: BodyPart.LEFT_SHOULDER, to: BodyPart.RIGHT_SHOULDER),\n    (from: BodyPart.RIGHT_SHOULDER, to: BodyPart.RIGHT_ELBOW),\n    (from: BodyPart.RIGHT_ELBOW, to: BodyPart.RIGHT_WRIST),\n    (from: BodyPart.LEFT_SHOULDER, to: BodyPart.LEFT_HIP),\n    (from: BodyPart.LEFT_HIP, to: BodyPart.RIGHT_HIP),\n    (from: BodyPart.RIGHT_HIP, to: BodyPart.RIGHT_SHOULDER),\n    (from: BodyPart.LEFT_HIP, to: BodyPart.LEFT_KNEE),\n    (from: BodyPart.LEFT_KNEE, to: BodyPart.LEFT_ANKLE),\n    (from: BodyPart.RIGHT_HIP, to: BodyPart.RIGHT_KNEE),\n    (from: BodyPart.RIGHT_KNEE, to: BodyPart.RIGHT_ANKLE),\n  ]\n}\n\n// MARK: - Delegates Enum\nenum Delegates: Int, CaseIterable {\n  case CPU\n  case Metal\n  case CoreML\n\n  var description: String {\n    switch self {\n    case .CPU:\n      return \"CPU\"\n    case .Metal:\n      return \"GPU\"\n    case .CoreML:\n      return \"NPU\"\n    }\n  }\n}\n\n// MARK: - Custom Errors\nenum PostprocessError: Error {\n  case missingBodyPart(of: BodyPart)\n}\n\n// MARK: - Information about the model file.\ntypealias FileInfo = (name: String, extension: String)\n\nenum Model {\n  static let file: FileInfo = (\n    name: \"posenet_mobilenet_v1_100_257x257_multi_kpt_stripped\", extension: \"tflite\"\n  )\n\n  static let input = (batchSize: 1, height: 257, width: 257, channelSize: 3)\n  static let output = (batchSize: 1, height: 9, width: 9, keypointSize: 17, offsetSize: 34)\n  static let isQuantized = false\n}\n"
  },
  {
    "path": "lite/examples/posenet/ios/PoseNet/Storyboards/Base.lproj/Launch Screen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"13142\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"12042\"/>\n        <capability name=\"Constraints with non-1.0 multipliers\" minToolsVersion=\"5.1\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <label opaque=\"NO\" clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Copyright © 2019 tensorflow. All rights reserved.\" textAlignment=\"center\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" minimumFontSize=\"9\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"obG-Y5-kRd\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"626.5\" width=\"375\" height=\"20.5\"/>\n                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                <color key=\"textColor\" red=\"0.0\" green=\"0.0\" blue=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                <nil key=\"highlightedColor\"/>\n                            </label>\n                            <label opaque=\"NO\" clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"PoseNet\" textAlignment=\"center\" lineBreakMode=\"middleTruncation\" baselineAdjustment=\"alignBaselines\" minimumFontSize=\"18\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"GJd-Yh-RWb\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"202\" width=\"375\" height=\"43\"/>\n                                <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"36\"/>\n                                <color key=\"textColor\" red=\"0.0\" green=\"0.0\" blue=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                <nil key=\"highlightedColor\"/>\n                            </label>\n                        </subviews>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <constraints>\n                            <constraint firstItem=\"Bcu-3y-fUS\" firstAttribute=\"centerX\" secondItem=\"obG-Y5-kRd\" secondAttribute=\"centerX\" id=\"5cz-MP-9tL\"/>\n                            <constraint firstItem=\"Bcu-3y-fUS\" firstAttribute=\"centerX\" secondItem=\"GJd-Yh-RWb\" secondAttribute=\"centerX\" id=\"Q3B-4B-g5h\"/>\n                            <constraint firstItem=\"obG-Y5-kRd\" firstAttribute=\"leading\" secondItem=\"Bcu-3y-fUS\" secondAttribute=\"leading\" constant=\"20\" symbolic=\"YES\" id=\"SfN-ll-jLj\"/>\n                            <constraint firstAttribute=\"bottom\" secondItem=\"obG-Y5-kRd\" secondAttribute=\"bottom\" constant=\"20\" id=\"Y44-ml-fuU\"/>\n                            <constraint firstItem=\"GJd-Yh-RWb\" firstAttribute=\"centerY\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"bottom\" multiplier=\"1/3\" constant=\"1\" id=\"moa-c2-u7t\"/>\n                            <constraint firstItem=\"GJd-Yh-RWb\" firstAttribute=\"leading\" secondItem=\"Bcu-3y-fUS\" secondAttribute=\"leading\" constant=\"20\" symbolic=\"YES\" id=\"x7j-FC-K8j\"/>\n                        </constraints>\n                        <viewLayoutGuide key=\"safeArea\" id=\"Bcu-3y-fUS\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "lite/examples/posenet/ios/PoseNet/Storyboards/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"15505\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <device id=\"retina6_1\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"15510\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"ViewController\" customModule=\"PoseNet\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"896\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"aEU-Ov-crs\" customClass=\"PreviewView\" customModule=\"PoseNet\" customModuleProvider=\"target\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"896\"/>\n                                <subviews>\n                                    <button hidden=\"YES\" opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" buttonType=\"roundedRect\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"JeV-bW-Ogb\">\n                                        <rect key=\"frame\" x=\"150.5\" y=\"433\" width=\"113\" height=\"30\"/>\n                                        <state key=\"normal\" title=\"Resume Session\"/>\n                                        <connections>\n                                            <action selector=\"didTapResumeButton:\" destination=\"BYZ-38-t0r\" eventType=\"touchUpInside\" id=\"eGE-XF-oGN\"/>\n                                        </connections>\n                                    </button>\n                                    <label hidden=\"YES\" opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Camera Unavailable\" textAlignment=\"center\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"xja-Am-Oc5\">\n                                        <rect key=\"frame\" x=\"111.5\" y=\"371.5\" width=\"191\" height=\"26.5\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"22\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <view contentMode=\"scaleToFill\" insetsLayoutMarginsFromSafeArea=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"tWv-pt-h17\" userLabel=\"TopView\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"119\"/>\n                                        <subviews>\n                                            <imageView clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleAspectFit\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" image=\"tfl_logo.png\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ijS-RU-qlo\">\n                                                <rect key=\"frame\" x=\"60\" y=\"42.5\" width=\"294\" height=\"68\"/>\n                                            </imageView>\n                                        </subviews>\n                                        <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.80000000000000004\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                        <constraints>\n                                            <constraint firstItem=\"ijS-RU-qlo\" firstAttribute=\"bottom\" secondItem=\"tWv-pt-h17\" secondAttribute=\"bottom\" multiplier=\"6.5:7\" id=\"06z-Bq-BW1\"/>\n                                            <constraint firstItem=\"ijS-RU-qlo\" firstAttribute=\"centerX\" secondItem=\"tWv-pt-h17\" secondAttribute=\"centerX\" id=\"DjM-bj-7Aa\"/>\n                                            <constraint firstItem=\"ijS-RU-qlo\" firstAttribute=\"height\" secondItem=\"tWv-pt-h17\" secondAttribute=\"height\" multiplier=\"4:7\" id=\"fU4-ZD-b2i\"/>\n                                        </constraints>\n                                    </view>\n                                    <view opaque=\"NO\" contentMode=\"scaleAspectFit\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"FLM-9o-o4W\" customClass=\"OverlayView\" customModule=\"PoseNet\" customModuleProvider=\"target\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"119\" width=\"414\" height=\"414\"/>\n                                        <constraints>\n                                            <constraint firstAttribute=\"width\" secondItem=\"FLM-9o-o4W\" secondAttribute=\"height\" multiplier=\"1:1\" id=\"1T9-Bs-Jec\"/>\n                                        </constraints>\n                                        <edgeInsets key=\"layoutMargins\" top=\"8\" left=\"8\" bottom=\"8\" right=\"8\"/>\n                                    </view>\n                                    <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"nFg-A5-Dew\" userLabel=\"BottomView\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"533\" width=\"414\" height=\"363\"/>\n                                        <subviews>\n                                            <tableView clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" bounces=\"NO\" scrollEnabled=\"NO\" delaysContentTouches=\"NO\" canCancelContentTouches=\"NO\" bouncesZoom=\"NO\" dataMode=\"prototypes\" style=\"plain\" separatorStyle=\"none\" allowsMultipleSelection=\"YES\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"28\" sectionFooterHeight=\"28\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"3e4-S8-Ouh\">\n                                                <rect key=\"frame\" x=\"60\" y=\"30\" width=\"294\" height=\"163\"/>\n                                                <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                <color key=\"tintColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                <color key=\"separatorColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                <color key=\"sectionIndexColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                <color key=\"sectionIndexBackgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                <color key=\"sectionIndexTrackingBackgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                <prototypes>\n                                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" reuseIdentifier=\"InfoCell\" id=\"THr-Uf-ggb\" customClass=\"InfoCell\" customModule=\"PoseNet\" customModuleProvider=\"target\">\n                                                        <rect key=\"frame\" x=\"0.0\" y=\"28\" width=\"294\" height=\"44\"/>\n                                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"THr-Uf-ggb\" id=\"5T1-ZQ-m0o\">\n                                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"294\" height=\"44\"/>\n                                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                                            <subviews>\n                                                                <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Field Label\" lineBreakMode=\"clip\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"mTG-UQ-Lwc\" userLabel=\"Field Name Label\">\n                                                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"176.5\" height=\"19.5\"/>\n                                                                    <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                                    <color key=\"tintColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                                    <fontDescription key=\"fontDescription\" type=\"system\" weight=\"medium\" pointSize=\"16\"/>\n                                                                    <color key=\"textColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                                    <color key=\"highlightedColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                                </label>\n                                                                <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"100\" verticalHuggingPriority=\"251\" text=\"Info Label\" textAlignment=\"right\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"3kP-Ho-Tm4\" userLabel=\"Info Label\">\n                                                                    <rect key=\"frame\" x=\"176.5\" y=\"0.0\" width=\"117.5\" height=\"19.5\"/>\n                                                                    <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                                    <color key=\"tintColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"16\"/>\n                                                                    <color key=\"textColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                                    <color key=\"highlightedColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                                </label>\n                                                            </subviews>\n                                                            <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                            <color key=\"tintColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                            <constraints>\n                                                                <constraint firstItem=\"mTG-UQ-Lwc\" firstAttribute=\"top\" secondItem=\"5T1-ZQ-m0o\" secondAttribute=\"top\" id=\"4yW-Lh-MPd\"/>\n                                                                <constraint firstItem=\"3kP-Ho-Tm4\" firstAttribute=\"top\" secondItem=\"5T1-ZQ-m0o\" secondAttribute=\"top\" id=\"JOU-KJ-V73\"/>\n                                                                <constraint firstItem=\"3kP-Ho-Tm4\" firstAttribute=\"width\" secondItem=\"5T1-ZQ-m0o\" secondAttribute=\"width\" multiplier=\"0.4\" id=\"NXu-3C-w9k\"/>\n                                                                <constraint firstItem=\"mTG-UQ-Lwc\" firstAttribute=\"width\" secondItem=\"5T1-ZQ-m0o\" secondAttribute=\"width\" multiplier=\"0.6\" id=\"Vvh-cz-q4K\"/>\n                                                                <constraint firstItem=\"3kP-Ho-Tm4\" firstAttribute=\"leading\" secondItem=\"mTG-UQ-Lwc\" secondAttribute=\"trailing\" id=\"kwA-sz-1zt\"/>\n                                                                <constraint firstItem=\"mTG-UQ-Lwc\" firstAttribute=\"leading\" secondItem=\"5T1-ZQ-m0o\" secondAttribute=\"leading\" id=\"kxa-T6-w0h\"/>\n                                                            </constraints>\n                                                        </tableViewCellContentView>\n                                                        <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                        <constraints>\n                                                            <constraint firstItem=\"3kP-Ho-Tm4\" firstAttribute=\"top\" secondItem=\"THr-Uf-ggb\" secondAttribute=\"top\" id=\"XTX-Dy-sDb\"/>\n                                                            <constraint firstItem=\"3kP-Ho-Tm4\" firstAttribute=\"trailing\" secondItem=\"THr-Uf-ggb\" secondAttribute=\"trailing\" id=\"kX7-5L-aoX\"/>\n                                                        </constraints>\n                                                        <connections>\n                                                            <outlet property=\"fieldNameLabel\" destination=\"mTG-UQ-Lwc\" id=\"7HD-r4-86n\"/>\n                                                            <outlet property=\"infoLabel\" destination=\"3kP-Ho-Tm4\" id=\"nMw-We-0cp\"/>\n                                                        </connections>\n                                                    </tableViewCell>\n                                                </prototypes>\n                                                <connections>\n                                                    <outlet property=\"dataSource\" destination=\"BYZ-38-t0r\" id=\"8Ah-El-SjN\"/>\n                                                    <outlet property=\"delegate\" destination=\"BYZ-38-t0r\" id=\"bw0-yQ-3jW\"/>\n                                                </connections>\n                                            </tableView>\n                                            <stackView opaque=\"NO\" contentMode=\"scaleToFill\" spacing=\"15\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"cOj-2t-SbP\" userLabel=\"Thread Stack View\">\n                                                <rect key=\"frame\" x=\"35\" y=\"203\" width=\"344\" height=\"30\"/>\n                                                <subviews>\n                                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" verticalHuggingPriority=\"251\" text=\"Threads\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"eX7-9U-WcH\">\n                                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"209\" height=\"30\"/>\n                                                        <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"16\"/>\n                                                        <color key=\"textColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                        <nil key=\"highlightedColor\"/>\n                                                    </label>\n                                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"0\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"bIb-UK-3tz\">\n                                                        <rect key=\"frame\" x=\"224\" y=\"0.0\" width=\"11\" height=\"30\"/>\n                                                        <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"16\"/>\n                                                        <color key=\"textColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                        <color key=\"highlightedColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                    </label>\n                                                    <stepper opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" value=\"1\" minimumValue=\"1\" maximumValue=\"10\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"P6P-6m-RWk\">\n                                                        <rect key=\"frame\" x=\"250\" y=\"0.0\" width=\"94\" height=\"30\"/>\n                                                        <color key=\"tintColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                        <connections>\n                                                            <action selector=\"didChangeThreadCount:\" destination=\"BYZ-38-t0r\" eventType=\"valueChanged\" id=\"Ty8-5M-GAN\"/>\n                                                        </connections>\n                                                    </stepper>\n                                                </subviews>\n                                                <constraints>\n                                                    <constraint firstAttribute=\"height\" constant=\"30\" id=\"PmW-q1-hKU\"/>\n                                                </constraints>\n                                            </stackView>\n                                            <stackView opaque=\"NO\" contentMode=\"scaleToFill\" spacing=\"10\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"RXH-7X-MUd\" userLabel=\"Delegates Stack View\">\n                                                <rect key=\"frame\" x=\"35\" y=\"253\" width=\"344\" height=\"31\"/>\n                                                <subviews>\n                                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Delegates\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"gmc-69-mzQ\" userLabel=\"Delegates label\">\n                                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"78.5\" height=\"31\"/>\n                                                        <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"16\"/>\n                                                        <color key=\"textColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                        <nil key=\"highlightedColor\"/>\n                                                    </label>\n                                                    <segmentedControl opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"left\" contentVerticalAlignment=\"top\" segmentControlStyle=\"plain\" selectedSegmentIndex=\"0\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"kS9-M8-aNu\">\n                                                        <rect key=\"frame\" x=\"88.5\" y=\"0.0\" width=\"255.5\" height=\"32\"/>\n                                                        <segments>\n                                                            <segment title=\"First\"/>\n                                                            <segment title=\"Second\"/>\n                                                        </segments>\n                                                        <connections>\n                                                            <action selector=\"didChangeDelegate:\" destination=\"BYZ-38-t0r\" eventType=\"valueChanged\" id=\"mGg-gI-RL2\"/>\n                                                        </connections>\n                                                    </segmentedControl>\n                                                </subviews>\n                                            </stackView>\n                                        </subviews>\n                                        <color key=\"backgroundColor\" red=\"0.0\" green=\"0.0\" blue=\"0.0\" alpha=\"0.80000000000000004\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                        <constraints>\n                                            <constraint firstItem=\"RXH-7X-MUd\" firstAttribute=\"leading\" secondItem=\"cOj-2t-SbP\" secondAttribute=\"leading\" id=\"3wP-lN-o3t\"/>\n                                            <constraint firstItem=\"RXH-7X-MUd\" firstAttribute=\"trailing\" secondItem=\"cOj-2t-SbP\" secondAttribute=\"trailing\" id=\"Ej9-gR-bWk\"/>\n                                            <constraint firstItem=\"cOj-2t-SbP\" firstAttribute=\"width\" secondItem=\"nFg-A5-Dew\" secondAttribute=\"width\" constant=\"-70\" id=\"VyB-yT-Tey\"/>\n                                            <constraint firstItem=\"3e4-S8-Ouh\" firstAttribute=\"top\" secondItem=\"nFg-A5-Dew\" secondAttribute=\"top\" constant=\"30\" id=\"XBT-uR-wI0\"/>\n                                            <constraint firstAttribute=\"bottom\" relation=\"greaterThanOrEqual\" secondItem=\"cOj-2t-SbP\" secondAttribute=\"bottom\" priority=\"250\" constant=\"30\" id=\"YjT-kc-3Yi\"/>\n                                            <constraint firstItem=\"3e4-S8-Ouh\" firstAttribute=\"bottom\" secondItem=\"cOj-2t-SbP\" secondAttribute=\"top\" constant=\"-10\" id=\"lEI-3O-3zh\"/>\n                                            <constraint firstItem=\"cOj-2t-SbP\" firstAttribute=\"bottom\" secondItem=\"RXH-7X-MUd\" secondAttribute=\"top\" constant=\"-20\" id=\"npw-gf-o7m\"/>\n                                            <constraint firstItem=\"3e4-S8-Ouh\" firstAttribute=\"width\" secondItem=\"nFg-A5-Dew\" secondAttribute=\"width\" constant=\"-120\" id=\"orY-Ah-Jzt\"/>\n                                            <constraint firstItem=\"3e4-S8-Ouh\" firstAttribute=\"centerX\" secondItem=\"nFg-A5-Dew\" secondAttribute=\"centerX\" id=\"tIF-GC-QXD\"/>\n                                            <constraint firstItem=\"cOj-2t-SbP\" firstAttribute=\"centerX\" secondItem=\"nFg-A5-Dew\" secondAttribute=\"centerX\" id=\"xwh-fU-JP1\"/>\n                                        </constraints>\n                                    </view>\n                                </subviews>\n                                <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                <constraints>\n                                    <constraint firstItem=\"JeV-bW-Ogb\" firstAttribute=\"top\" secondItem=\"xja-Am-Oc5\" secondAttribute=\"bottom\" constant=\"35\" id=\"19f-cQ-jfR\"/>\n                                    <constraint firstItem=\"tWv-pt-h17\" firstAttribute=\"leading\" secondItem=\"aEU-Ov-crs\" secondAttribute=\"leading\" id=\"1Jc-ch-65t\"/>\n                                    <constraint firstItem=\"FLM-9o-o4W\" firstAttribute=\"leading\" secondItem=\"aEU-Ov-crs\" secondAttribute=\"leading\" id=\"23L-Hb-zus\"/>\n                                    <constraint firstItem=\"nFg-A5-Dew\" firstAttribute=\"leading\" secondItem=\"aEU-Ov-crs\" secondAttribute=\"leading\" id=\"2aq-VQ-8jy\"/>\n                                    <constraint firstItem=\"tWv-pt-h17\" firstAttribute=\"top\" secondItem=\"aEU-Ov-crs\" secondAttribute=\"top\" id=\"7aI-Bb-gUy\"/>\n                                    <constraint firstItem=\"FLM-9o-o4W\" firstAttribute=\"width\" secondItem=\"aEU-Ov-crs\" secondAttribute=\"width\" priority=\"750\" id=\"94P-51-fMI\"/>\n                                    <constraint firstItem=\"FLM-9o-o4W\" firstAttribute=\"height\" relation=\"lessThanOrEqual\" secondItem=\"aEU-Ov-crs\" secondAttribute=\"height\" id=\"W3V-ka-9Fq\"/>\n                                    <constraint firstItem=\"FLM-9o-o4W\" firstAttribute=\"height\" secondItem=\"aEU-Ov-crs\" secondAttribute=\"height\" priority=\"750\" id=\"Ycv-Lj-8Xu\"/>\n                                    <constraint firstItem=\"tWv-pt-h17\" firstAttribute=\"width\" secondItem=\"aEU-Ov-crs\" secondAttribute=\"width\" id=\"Yni-oR-3Bk\"/>\n                                    <constraint firstItem=\"JeV-bW-Ogb\" firstAttribute=\"centerX\" secondItem=\"aEU-Ov-crs\" secondAttribute=\"centerX\" id=\"biE-2Z-tkx\"/>\n                                    <constraint firstItem=\"JeV-bW-Ogb\" firstAttribute=\"centerY\" secondItem=\"aEU-Ov-crs\" secondAttribute=\"centerY\" id=\"c8W-L8-dcv\"/>\n                                    <constraint firstAttribute=\"bottom\" secondItem=\"nFg-A5-Dew\" secondAttribute=\"bottom\" id=\"dxP-KD-5bd\"/>\n                                    <constraint firstItem=\"xja-Am-Oc5\" firstAttribute=\"centerX\" secondItem=\"aEU-Ov-crs\" secondAttribute=\"centerX\" id=\"ehn-uM-mdg\"/>\n                                    <constraint firstItem=\"nFg-A5-Dew\" firstAttribute=\"width\" secondItem=\"aEU-Ov-crs\" secondAttribute=\"width\" id=\"peY-3I-8cV\"/>\n                                    <constraint firstItem=\"nFg-A5-Dew\" firstAttribute=\"top\" secondItem=\"FLM-9o-o4W\" secondAttribute=\"bottom\" id=\"sFi-YM-EYA\"/>\n                                    <constraint firstItem=\"FLM-9o-o4W\" firstAttribute=\"top\" secondItem=\"tWv-pt-h17\" secondAttribute=\"bottom\" id=\"trj-PE-5oc\"/>\n                                    <constraint firstItem=\"FLM-9o-o4W\" firstAttribute=\"width\" relation=\"lessThanOrEqual\" secondItem=\"aEU-Ov-crs\" secondAttribute=\"width\" id=\"zae-wS-Evw\"/>\n                                </constraints>\n                            </view>\n                        </subviews>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <constraints>\n                            <constraint firstItem=\"aEU-Ov-crs\" firstAttribute=\"bottom\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"bottom\" id=\"3OD-6W-uRh\"/>\n                            <constraint firstItem=\"RXH-7X-MUd\" firstAttribute=\"centerX\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"centerX\" id=\"Hiz-DI-CwX\"/>\n                            <constraint firstItem=\"6Tk-OE-BBY\" firstAttribute=\"bottom\" secondItem=\"RXH-7X-MUd\" secondAttribute=\"bottom\" constant=\"45\" id=\"OGm-6n-JMM\"/>\n                            <constraint firstItem=\"aEU-Ov-crs\" firstAttribute=\"leading\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"leading\" id=\"e9c-LL-cJf\"/>\n                            <constraint firstItem=\"aEU-Ov-crs\" firstAttribute=\"top\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"top\" id=\"eyF-LV-5Jb\"/>\n                            <constraint firstItem=\"aEU-Ov-crs\" firstAttribute=\"trailing\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"trailing\" id=\"g7I-ct-vQc\"/>\n                            <constraint firstItem=\"tWv-pt-h17\" firstAttribute=\"bottom\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"top\" constant=\"75\" id=\"oFt-dT-p0b\"/>\n                        </constraints>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                    <connections>\n                        <outlet property=\"cameraUnavailableLabel\" destination=\"xja-Am-Oc5\" id=\"g9Q-Mh-1ct\"/>\n                        <outlet property=\"delegatesControl\" destination=\"kS9-M8-aNu\" id=\"aq5-y6-e3w\"/>\n                        <outlet property=\"overlayView\" destination=\"FLM-9o-o4W\" id=\"06u-Ci-QDR\"/>\n                        <outlet property=\"previewView\" destination=\"aEU-Ov-crs\" id=\"NMN-in-8FS\"/>\n                        <outlet property=\"resumeButton\" destination=\"JeV-bW-Ogb\" id=\"Y7c-x9-3t3\"/>\n                        <outlet property=\"tableView\" destination=\"3e4-S8-Ouh\" id=\"bhz-PY-Fhd\"/>\n                        <outlet property=\"threadCountLabel\" destination=\"bIb-UK-3tz\" id=\"LvS-wv-2Pq\"/>\n                        <outlet property=\"threadCountStepper\" destination=\"P6P-6m-RWk\" id=\"zPW-0r-KYP\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"137.68115942028987\" y=\"137.94642857142856\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"tfl_logo.png\" width=\"294\" height=\"47\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "lite/examples/posenet/ios/PoseNet/ViewControllers/ViewController.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport AVFoundation\nimport UIKit\nimport os\n\nclass ViewController: UIViewController {\n  // MARK: Storyboards Connections\n  @IBOutlet weak var previewView: PreviewView!\n\n  @IBOutlet weak var overlayView: OverlayView!\n\n  @IBOutlet weak var resumeButton: UIButton!\n  @IBOutlet weak var cameraUnavailableLabel: UILabel!\n\n  @IBOutlet weak var tableView: UITableView!\n\n  @IBOutlet weak var threadCountLabel: UILabel!\n  @IBOutlet weak var threadCountStepper: UIStepper!\n\n  @IBOutlet weak var delegatesControl: UISegmentedControl!\n\n  // MARK: ModelDataHandler traits\n  var threadCount: Int = Constants.defaultThreadCount\n  var delegate: Delegates = Constants.defaultDelegate\n\n  // MARK: Result Variables\n  // Inferred data to render.\n  private var inferredData: InferredData?\n\n  // Minimum score to render the result.\n  private let minimumScore: Float = 0.5\n\n  // Relative location of `overlayView` to `previewView`.\n  private var overlayViewFrame: CGRect?\n\n  private var previewViewFrame: CGRect?\n\n  // MARK: Controllers that manage functionality\n  // Handles all the camera related functionality\n  private lazy var cameraCapture = CameraFeedManager(previewView: previewView)\n\n  // Handles all data preprocessing and makes calls to run inference.\n  private var modelDataHandler: ModelDataHandler?\n\n  // MARK: View Handling Methods\n  override func viewDidLoad() {\n    super.viewDidLoad()\n\n    do {\n      modelDataHandler = try ModelDataHandler()\n    } catch let error {\n      fatalError(error.localizedDescription)\n    }\n\n    cameraCapture.delegate = self\n    tableView.delegate = self\n    tableView.dataSource = self\n\n    // MARK: UI Initialization\n    // Setup thread count stepper with white color.\n    // https://forums.developer.apple.com/thread/121495\n    threadCountStepper.setDecrementImage(\n      threadCountStepper.decrementImage(for: .normal), for: .normal)\n    threadCountStepper.setIncrementImage(\n      threadCountStepper.incrementImage(for: .normal), for: .normal)\n    // Setup initial stepper value and its label.\n    threadCountStepper.value = Double(Constants.defaultThreadCount)\n    threadCountLabel.text = Constants.defaultThreadCount.description\n\n    // Setup segmented controller's color.\n    delegatesControl.setTitleTextAttributes(\n      [NSAttributedString.Key.foregroundColor: UIColor.lightGray],\n      for: .normal)\n    delegatesControl.setTitleTextAttributes(\n      [NSAttributedString.Key.foregroundColor: UIColor.black],\n      for: .selected)\n    // Remove existing segments to initialize it with `Delegates` entries.\n    delegatesControl.removeAllSegments()\n    Delegates.allCases.forEach { delegate in\n      delegatesControl.insertSegment(\n        withTitle: delegate.description,\n        at: delegate.rawValue,\n        animated: false)\n    }\n    delegatesControl.selectedSegmentIndex = 0\n  }\n\n  override func viewWillAppear(_ animated: Bool) {\n    super.viewWillAppear(animated)\n\n    cameraCapture.checkCameraConfigurationAndStartSession()\n  }\n\n  override func viewWillDisappear(_ animated: Bool) {\n    cameraCapture.stopSession()\n  }\n\n  override func viewDidLayoutSubviews() {\n    overlayViewFrame = overlayView.frame\n    previewViewFrame = previewView.frame\n  }\n\n  // MARK: Button Actions\n  @IBAction func didChangeThreadCount(_ sender: UIStepper) {\n    let changedCount = Int(sender.value)\n    if threadCountLabel.text == changedCount.description {\n      return\n    }\n\n    do {\n      modelDataHandler = try ModelDataHandler(threadCount: changedCount, delegate: delegate)\n    } catch let error {\n      fatalError(error.localizedDescription)\n    }\n    threadCount = changedCount\n    threadCountLabel.text = changedCount.description\n    os_log(\"Thread count is changed to: %d\", threadCount)\n  }\n\n  @IBAction func didChangeDelegate(_ sender: UISegmentedControl) {\n    guard let changedDelegate = Delegates(rawValue: delegatesControl.selectedSegmentIndex) else {\n      fatalError(\"Unexpected value from delegates segemented controller.\")\n    }\n    do {\n      modelDataHandler = try ModelDataHandler(threadCount: threadCount, delegate: changedDelegate)\n    } catch let error {\n      fatalError(error.localizedDescription)\n    }\n    delegate = changedDelegate\n    os_log(\"Delegate is changed to: %s\", delegate.description)\n  }\n\n  @IBAction func didTapResumeButton(_ sender: Any) {\n    cameraCapture.resumeInterruptedSession { complete in\n\n      if complete {\n        self.resumeButton.isHidden = true\n        self.cameraUnavailableLabel.isHidden = true\n      } else {\n        self.presentUnableToResumeSessionAlert()\n      }\n    }\n  }\n\n  func presentUnableToResumeSessionAlert() {\n    let alert = UIAlertController(\n      title: \"Unable to Resume Session\",\n      message: \"There was an error while attempting to resume session.\",\n      preferredStyle: .alert\n    )\n    alert.addAction(UIAlertAction(title: \"OK\", style: .default, handler: nil))\n\n    self.present(alert, animated: true)\n  }\n}\n\n// MARK: - CameraFeedManagerDelegate Methods\nextension ViewController: CameraFeedManagerDelegate {\n  func cameraFeedManager(_ manager: CameraFeedManager, didOutput pixelBuffer: CVPixelBuffer) {\n    runModel(on: pixelBuffer)\n  }\n\n  // MARK: Session Handling Alerts\n  func cameraFeedManagerDidEncounterSessionRunTimeError(_ manager: CameraFeedManager) {\n    // Handles session run time error by updating the UI and providing a button if session can be\n    // manually resumed.\n    self.resumeButton.isHidden = false\n  }\n\n  func cameraFeedManager(\n    _ manager: CameraFeedManager, sessionWasInterrupted canResumeManually: Bool\n  ) {\n    // Updates the UI when session is interupted.\n    if canResumeManually {\n      self.resumeButton.isHidden = false\n    } else {\n      self.cameraUnavailableLabel.isHidden = false\n    }\n  }\n\n  func cameraFeedManagerDidEndSessionInterruption(_ manager: CameraFeedManager) {\n    // Updates UI once session interruption has ended.\n    self.cameraUnavailableLabel.isHidden = true\n    self.resumeButton.isHidden = true\n  }\n\n  func presentVideoConfigurationErrorAlert(_ manager: CameraFeedManager) {\n    let alertController = UIAlertController(\n      title: \"Confirguration Failed\", message: \"Configuration of camera has failed.\",\n      preferredStyle: .alert)\n    let okAction = UIAlertAction(title: \"OK\", style: .cancel, handler: nil)\n    alertController.addAction(okAction)\n\n    present(alertController, animated: true, completion: nil)\n  }\n\n  func presentCameraPermissionsDeniedAlert(_ manager: CameraFeedManager) {\n    let alertController = UIAlertController(\n      title: \"Camera Permissions Denied\",\n      message:\n        \"Camera permissions have been denied for this app. You can change this by going to Settings\",\n      preferredStyle: .alert)\n\n    let cancelAction = UIAlertAction(title: \"Cancel\", style: .cancel, handler: nil)\n    let settingsAction = UIAlertAction(title: \"Settings\", style: .default) { action in\n      if let url = URL.init(string: UIApplication.openSettingsURLString) {\n        UIApplication.shared.open(url, options: [:], completionHandler: nil)\n      }\n    }\n\n    alertController.addAction(cancelAction)\n    alertController.addAction(settingsAction)\n\n    present(alertController, animated: true, completion: nil)\n  }\n\n  @objc func runModel(on pixelBuffer: CVPixelBuffer) {\n    guard let overlayViewFrame = overlayViewFrame, let previewViewFrame = previewViewFrame\n    else {\n      return\n    }\n    // To put `overlayView` area as model input, transform `overlayViewFrame` following transform\n    // from `previewView` to `pixelBuffer`. `previewView` area is transformed to fit in\n    // `pixelBuffer`, because `pixelBuffer` as a camera output is resized to fill `previewView`.\n    // https://developer.apple.com/documentation/avfoundation/avlayervideogravity/1385607-resizeaspectfill\n    let modelInputRange = overlayViewFrame.applying(\n      previewViewFrame.size.transformKeepAspect(toFitIn: pixelBuffer.size))\n\n    // Run PoseNet model.\n    guard\n      let (result, times) = self.modelDataHandler?.runPoseNet(\n        on: pixelBuffer,\n        from: modelInputRange,\n        to: overlayViewFrame.size)\n    else {\n      os_log(\"Cannot get inference result.\", type: .error)\n      return\n    }\n\n    // Update `inferredData` to render data in `tableView`.\n    inferredData = InferredData(score: result.score, times: times)\n\n    // Draw result.\n    DispatchQueue.main.async {\n      self.tableView.reloadData()\n      // If score is too low, clear result remaining in the overlayView.\n      if result.score < self.minimumScore {\n        self.clearResult()\n        return\n      }\n      self.drawResult(of: result)\n    }\n  }\n\n  func drawResult(of result: Result) {\n    self.overlayView.dots = result.dots\n    self.overlayView.lines = result.lines\n    self.overlayView.setNeedsDisplay()\n  }\n\n  func clearResult() {\n    self.overlayView.clear()\n    self.overlayView.setNeedsDisplay()\n  }\n}\n\n// MARK: - TableViewDelegate, TableViewDataSource Methods\nextension ViewController: UITableViewDelegate, UITableViewDataSource {\n  func numberOfSections(in tableView: UITableView) -> Int {\n    return InferenceSections.allCases.count\n  }\n\n  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {\n    guard let section = InferenceSections(rawValue: section) else {\n      return 0\n    }\n\n    return section.subcaseCount\n  }\n\n  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {\n    let cell = tableView.dequeueReusableCell(withIdentifier: \"InfoCell\") as! InfoCell\n    guard let section = InferenceSections(rawValue: indexPath.section) else {\n      return cell\n    }\n    guard let data = inferredData else { return cell }\n\n    var fieldName: String\n    var info: String\n\n    switch section {\n    case .Score:\n      fieldName = section.description\n      info = String(format: \"%.3f\", data.score)\n    case .Time:\n      guard let row = ProcessingTimes(rawValue: indexPath.row) else {\n        return cell\n      }\n      var time: Double\n      switch row {\n      case .InferenceTime:\n        time = data.times.inference\n      }\n      fieldName = row.description\n      info = String(format: \"%.2fms\", time)\n    }\n\n    cell.fieldNameLabel.text = fieldName\n    cell.infoLabel.text = info\n\n    return cell\n  }\n\n  func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {\n    guard let section = InferenceSections(rawValue: indexPath.section) else {\n      return 0\n    }\n\n    var height = Traits.normalCellHeight\n    if indexPath.row == section.subcaseCount - 1 {\n      height = Traits.separatorCellHeight + Traits.bottomSpacing\n    }\n    return height\n  }\n\n}\n\n// MARK: - Private enums\n/// UI constraint values\nfileprivate enum Traits {\n  static let normalCellHeight: CGFloat = 35.0\n  static let separatorCellHeight: CGFloat = 25.0\n  static let bottomSpacing: CGFloat = 30.0\n}\n\nfileprivate struct InferredData {\n  var score: Float\n  var times: Times\n}\n\n/// Type of sections in Info Cell\nfileprivate enum InferenceSections: Int, CaseIterable {\n  case Score\n  case Time\n\n  var description: String {\n    switch self {\n    case .Score:\n      return \"Score\"\n    case .Time:\n      return \"Processing Time\"\n    }\n  }\n\n  var subcaseCount: Int {\n    switch self {\n    case .Score:\n      return 1\n    case .Time:\n      return ProcessingTimes.allCases.count\n    }\n  }\n}\n\n/// Type of processing times in Time section in Info Cell\nfileprivate enum ProcessingTimes: Int, CaseIterable {\n  case InferenceTime\n\n  var description: String {\n    switch self {\n    case .InferenceTime:\n      return \"Inference Time\"\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/posenet/ios/PoseNet/Views/OverlayView.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n/// UIView for rendering inference output.\nclass OverlayView: UIView {\n\n  var dots = [CGPoint]()\n  var lines = [Line]()\n\n  override func draw(_ rect: CGRect) {\n    for dot in dots {\n      drawDot(of: dot)\n    }\n    for line in lines {\n      drawLine(of: line)\n    }\n  }\n\n  func drawDot(of dot: CGPoint) {\n    let dotRect = CGRect(\n      x: dot.x - Traits.dot.radius / 2, y: dot.y - Traits.dot.radius / 2,\n      width: Traits.dot.radius, height: Traits.dot.radius)\n    let dotPath = UIBezierPath(ovalIn: dotRect)\n\n    Traits.dot.color.setFill()\n    dotPath.fill()\n  }\n\n  func drawLine(of line: Line) {\n    let linePath = UIBezierPath()\n    linePath.move(to: CGPoint(x: line.from.x, y: line.from.y))\n    linePath.addLine(to: CGPoint(x: line.to.x, y: line.to.y))\n    linePath.close()\n\n    linePath.lineWidth = Traits.line.width\n    Traits.line.color.setStroke()\n\n    linePath.stroke()\n  }\n\n  func clear() {\n    self.dots = []\n    self.lines = []\n  }\n}\n\nprivate enum Traits {\n  static let dot = (radius: CGFloat(5), color: UIColor.orange)\n  static let line = (width: CGFloat(1.0), color: UIColor.orange)\n}\n"
  },
  {
    "path": "lite/examples/posenet/ios/PoseNet.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t0CDA8C85042ADF65D0787629 /* Pods_PoseNet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1CE41C09920CCEC31985547 /* Pods_PoseNet.framework */; };\n\t\t8402440123D9834600704ABD /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 8402440023D9834600704ABD /* README.md */; };\n\t\t840ECB20238BAA2300C7D88A /* InfoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840ECB1F238BAA2300C7D88A /* InfoCell.swift */; };\n\t\t840EDCFD2341DDD30017ED42 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 840EDCFB2341DDD30017ED42 /* Launch Screen.storyboard */; };\n\t\t840EDD022341DE380017ED42 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 840EDD002341DE380017ED42 /* Main.storyboard */; };\n\t\t842DDB6E2372A82000F6BB94 /* OverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842DDB6D2372A82000F6BB94 /* OverlayView.swift */; };\n\t\t846499C2235DAB0D009CBBC7 /* ModelDataHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846499C1235DAB0D009CBBC7 /* ModelDataHandler.swift */; };\n\t\t846BAF7623E7FE13006FC136 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846BAF7523E7FE13006FC136 /* Constants.swift */; };\n\t\t8474FEC92341D36E00377D34 /* PreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8474FEC82341D36E00377D34 /* PreviewView.swift */; };\n\t\t8474FECB2341D39800377D34 /* CameraFeedManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8474FECA2341D39800377D34 /* CameraFeedManager.swift */; };\n\t\t848842A42370183E0043FC4C /* posenet_mobilenet_v1_100_257x257_multi_kpt_stripped.tflite in Resources */ = {isa = PBXBuildFile; fileRef = 848842A32370183E0043FC4C /* posenet_mobilenet_v1_100_257x257_multi_kpt_stripped.tflite */; };\n\t\t84952CB5236186BE0052C104 /* CVPixelBufferExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84952CB4236186BE0052C104 /* CVPixelBufferExtension.swift */; };\n\t\t84952CB92361874A0052C104 /* TFLiteExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84952CB82361874A0052C104 /* TFLiteExtension.swift */; };\n\t\t84B67CEF2326338300A11A08 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B67CEE2326338300A11A08 /* AppDelegate.swift */; };\n\t\t84B67CF12326338300A11A08 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B67CF02326338300A11A08 /* ViewController.swift */; };\n\t\t84B67CF62326338400A11A08 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 84B67CF52326338400A11A08 /* Assets.xcassets */; };\n\t\t84D6576D2387BB7E0048171E /* CGSizeExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D6576C2387BB7E0048171E /* CGSizeExtension.swift */; };\n\t\t84FCF5922387BD7900663812 /* tfl_logo.png in Resources */ = {isa = PBXBuildFile; fileRef = 84FCF5912387BD7900663812 /* tfl_logo.png */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\t8402440023D9834600704ABD /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = \"<group>\"; };\n\t\t840ECB1F238BAA2300C7D88A /* InfoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoCell.swift; sourceTree = \"<group>\"; };\n\t\t840EDCFC2341DDD30017ED42 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = \"Base.lproj/Launch Screen.storyboard\"; sourceTree = \"<group>\"; };\n\t\t840EDD012341DE380017ED42 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t842DDB6D2372A82000F6BB94 /* OverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverlayView.swift; sourceTree = \"<group>\"; };\n\t\t846499C1235DAB0D009CBBC7 /* ModelDataHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModelDataHandler.swift; sourceTree = \"<group>\"; };\n\t\t846BAF7523E7FE13006FC136 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = \"<group>\"; };\n\t\t8474FEC82341D36E00377D34 /* PreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewView.swift; sourceTree = \"<group>\"; };\n\t\t8474FECA2341D39800377D34 /* CameraFeedManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraFeedManager.swift; sourceTree = \"<group>\"; };\n\t\t84884291236FF0A30043FC4C /* download_models.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = download_models.sh; sourceTree = \"<group>\"; };\n\t\t848842A32370183E0043FC4C /* posenet_mobilenet_v1_100_257x257_multi_kpt_stripped.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = posenet_mobilenet_v1_100_257x257_multi_kpt_stripped.tflite; sourceTree = \"<group>\"; };\n\t\t84952CB4236186BE0052C104 /* CVPixelBufferExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CVPixelBufferExtension.swift; sourceTree = \"<group>\"; };\n\t\t84952CB82361874A0052C104 /* TFLiteExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TFLiteExtension.swift; sourceTree = \"<group>\"; };\n\t\t84B67CEB2326338300A11A08 /* PoseNet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PoseNet.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t84B67CEE2326338300A11A08 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t84B67CF02326338300A11A08 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = \"<group>\"; };\n\t\t84B67CF52326338400A11A08 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t84B67CFA2326338400A11A08 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t84D6576C2387BB7E0048171E /* CGSizeExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGSizeExtension.swift; sourceTree = \"<group>\"; };\n\t\t84FCF5912387BD7900663812 /* tfl_logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = tfl_logo.png; path = Assets.xcassets/tfl_logo.png; sourceTree = \"<group>\"; };\n\t\tA1CE41C09920CCEC31985547 /* Pods_PoseNet.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PoseNet.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD2BFF06D0AE9137D332447F3 /* Pods-PoseNet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-PoseNet.release.xcconfig\"; path = \"Target Support Files/Pods-PoseNet/Pods-PoseNet.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tFCA88463911267B1001A596F /* Pods-PoseNet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-PoseNet.debug.xcconfig\"; path = \"Target Support Files/Pods-PoseNet/Pods-PoseNet.debug.xcconfig\"; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t84B67CE82326338300A11A08 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t0CDA8C85042ADF65D0787629 /* Pods_PoseNet.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t840ECB1E238BAA0D00C7D88A /* Cells */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t840ECB1F238BAA2300C7D88A /* InfoCell.swift */,\n\t\t\t);\n\t\t\tpath = Cells;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t842DDB6C2372A80E00F6BB94 /* Views */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t842DDB6D2372A82000F6BB94 /* OverlayView.swift */,\n\t\t\t);\n\t\t\tpath = Views;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t846499C0235DAAE7009CBBC7 /* ModelDataHandler */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t846499C1235DAB0D009CBBC7 /* ModelDataHandler.swift */,\n\t\t\t);\n\t\t\tpath = ModelDataHandler;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t8474FEC62341D2BE00377D34 /* ViewControllers */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t84B67CF02326338300A11A08 /* ViewController.swift */,\n\t\t\t);\n\t\t\tpath = ViewControllers;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t8474FEC72341D35800377D34 /* Camera Feed */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t8474FEC82341D36E00377D34 /* PreviewView.swift */,\n\t\t\t\t8474FECA2341D39800377D34 /* CameraFeedManager.swift */,\n\t\t\t);\n\t\t\tpath = \"Camera Feed\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t84884290236FF07F0043FC4C /* RunScripts */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t84884291236FF0A30043FC4C /* download_models.sh */,\n\t\t\t);\n\t\t\tpath = RunScripts;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t848842A22370180C0043FC4C /* Model */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t848842A32370183E0043FC4C /* posenet_mobilenet_v1_100_257x257_multi_kpt_stripped.tflite */,\n\t\t\t);\n\t\t\tpath = Model;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t84952CB3236186A20052C104 /* Extensions */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t84952CB4236186BE0052C104 /* CVPixelBufferExtension.swift */,\n\t\t\t\t84952CB82361874A0052C104 /* TFLiteExtension.swift */,\n\t\t\t\t84D6576C2387BB7E0048171E /* CGSizeExtension.swift */,\n\t\t\t);\n\t\t\tpath = Extensions;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t84B67CE22326338300A11A08 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t8402440023D9834600704ABD /* README.md */,\n\t\t\t\t84884290236FF07F0043FC4C /* RunScripts */,\n\t\t\t\t84B67CED2326338300A11A08 /* PoseNet */,\n\t\t\t\t84B67CEC2326338300A11A08 /* Products */,\n\t\t\t\tB4DFDCC28443B641BC36251D /* Pods */,\n\t\t\t\tA3DA804B8D3F6891E3A02852 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t84B67CEC2326338300A11A08 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t84B67CEB2326338300A11A08 /* PoseNet.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t84B67CED2326338300A11A08 /* PoseNet */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t840ECB1E238BAA0D00C7D88A /* Cells */,\n\t\t\t\t842DDB6C2372A80E00F6BB94 /* Views */,\n\t\t\t\t848842A22370180C0043FC4C /* Model */,\n\t\t\t\t84952CB3236186A20052C104 /* Extensions */,\n\t\t\t\t846499C0235DAAE7009CBBC7 /* ModelDataHandler */,\n\t\t\t\t8474FEC72341D35800377D34 /* Camera Feed */,\n\t\t\t\t8474FEC62341D2BE00377D34 /* ViewControllers */,\n\t\t\t\t84B67D002326339000A11A08 /* Storyboards */,\n\t\t\t\t84B67CEE2326338300A11A08 /* AppDelegate.swift */,\n\t\t\t\t846BAF7523E7FE13006FC136 /* Constants.swift */,\n\t\t\t\t84B67CF52326338400A11A08 /* Assets.xcassets */,\n\t\t\t\t84FCF5912387BD7900663812 /* tfl_logo.png */,\n\t\t\t\t84B67CFA2326338400A11A08 /* Info.plist */,\n\t\t\t);\n\t\t\tpath = PoseNet;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t84B67D002326339000A11A08 /* Storyboards */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t840EDCFB2341DDD30017ED42 /* Launch Screen.storyboard */,\n\t\t\t\t840EDD002341DE380017ED42 /* Main.storyboard */,\n\t\t\t);\n\t\t\tpath = Storyboards;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tA3DA804B8D3F6891E3A02852 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tA1CE41C09920CCEC31985547 /* Pods_PoseNet.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tB4DFDCC28443B641BC36251D /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tFCA88463911267B1001A596F /* Pods-PoseNet.debug.xcconfig */,\n\t\t\t\tD2BFF06D0AE9137D332447F3 /* Pods-PoseNet.release.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t84B67CEA2326338300A11A08 /* PoseNet */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 84B67CFD2326338400A11A08 /* Build configuration list for PBXNativeTarget \"PoseNet\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t14067F3CF309C9DB723C9F6F /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t84884298237010B90043FC4C /* Download TensorFlow Lite model */,\n\t\t\t\t84B67CE72326338300A11A08 /* Sources */,\n\t\t\t\t84B67CE82326338300A11A08 /* Frameworks */,\n\t\t\t\t84B67CE92326338300A11A08 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = PoseNet;\n\t\t\tproductName = PoseNet;\n\t\t\tproductReference = 84B67CEB2326338300A11A08 /* PoseNet.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t84B67CE32326338300A11A08 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 1030;\n\t\t\t\tLastUpgradeCheck = 1030;\n\t\t\t\tORGANIZATIONNAME = tensorflow;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t84B67CEA2326338300A11A08 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 10.3;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 84B67CE62326338300A11A08 /* Build configuration list for PBXProject \"PoseNet\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 84B67CE22326338300A11A08;\n\t\t\tproductRefGroup = 84B67CEC2326338300A11A08 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t84B67CEA2326338300A11A08 /* PoseNet */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t84B67CE92326338300A11A08 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t8402440123D9834600704ABD /* README.md in Resources */,\n\t\t\t\t840EDD022341DE380017ED42 /* Main.storyboard in Resources */,\n\t\t\t\t840EDCFD2341DDD30017ED42 /* Launch Screen.storyboard in Resources */,\n\t\t\t\t84FCF5922387BD7900663812 /* tfl_logo.png in Resources */,\n\t\t\t\t84B67CF62326338400A11A08 /* Assets.xcassets in Resources */,\n\t\t\t\t848842A42370183E0043FC4C /* posenet_mobilenet_v1_100_257x257_multi_kpt_stripped.tflite in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t14067F3CF309C9DB723C9F6F /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-PoseNet-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t84884298237010B90043FC4C /* Download TensorFlow Lite model */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Download TensorFlow Lite model\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/bash;\n\t\t\tshellScript = \"\\\"$SRCROOT/RunScripts/download_models.sh\\\"\\n\";\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t84B67CE72326338300A11A08 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t842DDB6E2372A82000F6BB94 /* OverlayView.swift in Sources */,\n\t\t\t\t846BAF7623E7FE13006FC136 /* Constants.swift in Sources */,\n\t\t\t\t84952CB92361874A0052C104 /* TFLiteExtension.swift in Sources */,\n\t\t\t\t84D6576D2387BB7E0048171E /* CGSizeExtension.swift in Sources */,\n\t\t\t\t84B67CF12326338300A11A08 /* ViewController.swift in Sources */,\n\t\t\t\t84B67CEF2326338300A11A08 /* AppDelegate.swift in Sources */,\n\t\t\t\t8474FECB2341D39800377D34 /* CameraFeedManager.swift in Sources */,\n\t\t\t\t846499C2235DAB0D009CBBC7 /* ModelDataHandler.swift in Sources */,\n\t\t\t\t8474FEC92341D36E00377D34 /* PreviewView.swift in Sources */,\n\t\t\t\t84952CB5236186BE0052C104 /* CVPixelBufferExtension.swift in Sources */,\n\t\t\t\t840ECB20238BAA2300C7D88A /* InfoCell.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\t840EDCFB2341DDD30017ED42 /* Launch Screen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t840EDCFC2341DDD30017ED42 /* Base */,\n\t\t\t);\n\t\t\tname = \"Launch Screen.storyboard\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t840EDD002341DE380017ED42 /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t840EDD012341DE380017ED42 /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t84B67CFB2326338400A11A08 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.4;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t84B67CFC2326338400A11A08 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.4;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t84B67CFE2326338400A11A08 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = FCA88463911267B1001A596F /* Pods-PoseNet.debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tINFOPLIST_FILE = PoseNet/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.tensorflow.PoseNet;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t84B67CFF2326338400A11A08 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = D2BFF06D0AE9137D332447F3 /* Pods-PoseNet.release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tINFOPLIST_FILE = PoseNet/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.tensorflow.PoseNet;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t84B67CE62326338300A11A08 /* Build configuration list for PBXProject \"PoseNet\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t84B67CFB2326338400A11A08 /* Debug */,\n\t\t\t\t84B67CFC2326338400A11A08 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t84B67CFD2326338400A11A08 /* Build configuration list for PBXNativeTarget \"PoseNet\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t84B67CFE2326338400A11A08 /* Debug */,\n\t\t\t\t84B67CFF2326338400A11A08 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 84B67CE32326338300A11A08 /* Project object */;\n}\n"
  },
  {
    "path": "lite/examples/posenet/ios/README.md",
    "content": "# See the new pose estimation TFLite iOS sample [here](../../pose_estimation/ios), which demonstrates both Posenet and Movenet models. This old Posenet sample will no longer be maintained.\n\n<br/> <br/> <br/> <br/>\n\n## Tensorflow Lite PoseNet iOS Example Application\n\n![Demo image](https://storage.googleapis.com/download.tensorflow.org/models/tflite/screenshots/posenet_ios_demo.gif)\n\n### Overview\nThis is an on-device camera app that continuously detects the key points of a\nperson in real-time with the camera activity using the\n[Pose Estimation model][posenet-model]. Key point of a person means a point\nneeded to build a skeleton of the person, such as shoulders, elbows, knees, etc.\n\nApplication extracts the largest image with the same aspect ratio to model input\nsize. You can find the extracted area in the display, as the removed area is\nshaded. The result is rendered when the total inference score is higher than\n`0.5`.\n\nCamera captures are discarded immediately after use, nothing is stored or saved.\n\n#### Model used\n[Pose Estimation model][posenet-model] is a vision model that can be used to\nestimate the pose of a person in an image or video by estimating where key body\njoints are.\n\n#### Structure\n\n![Structure diagram](https://storage.googleapis.com/download.tensorflow.org/models/tflite/screenshots/posenet_ios_diagram.png)\n\n##### Preprocessing\nWhile preprocessing, it converts camera input image to satisfy the model input\nspecification. It crops the original image to have the same aspect ratio as the\nmodel input size. After that it resizes the cropped image to the model input\nsize, and converts it to the required data type.\n\n##### Inference\nAssign preprocessed data to the input tensor and run [the model][posenet-model].\nOutput data is assigned to the output tensors as a result.\n\n##### Postprocessing\nIn postprocessing, it calculates the positions of the key points and the lines\nto display, along with the total confidence score. Total confidence score is the\naverage of all the key points' confidence scores. Coordinates of the key points\nare calculated from the heat and the offset tensors. These coordinates are based\non the model input size, which implies that you should transform each coordinate\nto match the display view size of key points. As the display view size of this\napplication is different from model input size, it transforms the points. With\nthe transformed dots, the skeleton of a person can be drawn by connecting the\ndots between adjacent joints.\n\n\n### Requirements\n\n* Xcode\n* Valid Apple Developer ID\n* Real iOS device with camera\n* iOS version 12.0 or above\n* Xcode command line tools (to install, run `xcode-select --install`)\n* CocoaPods (to install, run `sudo gem install cocoapods`)\n\n### Build and run\n1. Clone the TensorFlow examples GitHub repository to your computer to get the\ndemo application.\n\n    ```\n    git clone https://github.com/tensorflow/examples\n    ```\n\n1. Install the pod to generate the workspace file:\n\n    ```\n    cd examples/lite/examples/posenet/ios && pod install\n    ```\n    Note: If you have installed this pod before and that command doesn't work,\n    try `pod update`.\n    At the end of this step you should have a directory called\n    `PoseNet.xcworkspace`.\n\n1. Open the project in Xcode with the following command:\n\n    ```\n    open PoseNet.xcworkspace\n    ```\n    This launches Xcode and opens the `PoseNet` project.\n\n1. In Menu bar, select `Product` &rarr; `Destination` and choose your physical\ndevice.\n1. In Menu bar, select `Product` &rarr; `Run` to install the app on your device.\n\n[posenet-model]: https://www.tensorflow.org/lite/models/pose_estimation/overview\n"
  },
  {
    "path": "lite/examples/posenet/ios/RunScripts/download_models.sh",
    "content": "#!/bin/bash\n# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\n# Download TF Lite model from the internet if it does not exist.\n\nTFLITE_MODEL=\"posenet_mobilenet_v1_100_257x257_multi_kpt_stripped.tflite\"\nTFLITE_FILE=\"PoseNet/Model/${TFLITE_MODEL}\"\nMODEL_SRC=\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/${TFLITE_MODEL}\"\n\nif test -f \"${TFLITE_FILE}\"; then\n    echo \"INFO: TF Lite model already exists. Skip downloading and use the local model.\"\nelse\n    curl --create-dirs -o \"${TFLITE_FILE}\" \"${MODEL_SRC}\"\n    echo \"INFO: Downloaded TensorFlow Lite model to ${TFLITE_FILE}.\"\nfi\n\n"
  },
  {
    "path": "lite/examples/recommendation/android/README.md",
    "content": "# TensorFlow Lite Recommendation Android Example Application\n\n## Overview\n\nThis is an end-to-end example of recommendation application built with\nTensorFlow 2.0, and the model is trained based on the public\n[MovieLens dataset](https://grouplens.org/datasets/movielens/). The dataset and\nmodel is used for research purpose. This demo is supposed to be a reference\narchitecture to demonstrate how to build and run on-device recommendation model\nwithout in TF Lite talking to a server.\n\nThese instructions walk you through running the demo on an Android device.\n\n## Build the demo using Android Studio\n\n### Prerequisites\n\n*   If you don't have already, install\n    [Android Studio](https://developer.android.com/studio/index.html), following\n    the instructions on the website.\n\n*   Android Studio 4.2 or above.\n\n*   You need an Android device or Android emulator and Android development\n    environment with minimum API 26.\n\n### Building\n\n*   Open Android Studio, and from the Welcome screen, select `Open an existing\n    Android Studio project`.\n\n*   From the Open File or Project window that appears, navigate to and select\n    the `recommendation/android` directory from wherever you cloned the\n    TensorFlow Lite sample GitHub repo.\n\n*   You may also need to install various platforms and tools according to error\n    messages.\n\n*   If it asks you to use Instant Run, click Proceed Without Instant Run.\n\n### Running\n\n*   You need to have an Android device plugged in with developer options enabled\n    at this point. See [here](https://developer.android.com/studio/run/device)\n    for more details on setting up developer devices.\n\n*   If you already have Android emulator installed in Android Studio, select a\n    virtual device with minimum API 26.\n\n*   Click `Run` to run the demo app on your Android device.\n\n## Build the demo using gradle (command line)\n\n### Building and Installing\n\n*   Use the following command to build a demo apk:\n\n```\ncd lite/examples/recommendation/android   # Folder for Android app.\n\n./gradlew build\n```\n\n*   Use the following command to install the apk onto your connected device:\n\n```\nadb install -t app/build/outputs/apk/debug/app-debug.apk\n```\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 29\n    buildToolsVersion \"29.0.0\"\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.bertapp\"\n        minSdkVersion 26\n        targetSdkVersion 29\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n    aaptOptions {\n        noCompress \"tflite\"\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n    // If you find lint problem like:\n    // * What went wrong:\n    // A problem was found with the configuration of task ':app:lint'.\n    // > No value has been specified for property 'lintClassPath'.\n    //\n    // Probably you haven't set ANDROID_HOME. To fix:\n    // export ANDROID_HOME=$HOME/Android/Sdk   # Your Android SDK path.\n    lintOptions {\n      abortOnError false\n    }\n}\n\n// Download TF Lite model with the following script.\napply from: 'download.gradle'\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n\n    implementation 'androidx.appcompat:appcompat:1.1.0'\n    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'\n    implementation 'androidx.recyclerview:recyclerview:1.1.0'\n    implementation 'com.google.android.material:material:1.0.0'\n\n    implementation 'com.google.code.gson:gson:2.8.5'\n    implementation 'com.google.guava:guava:28.1-android'\n    implementation 'org.tensorflow:tensorflow-lite:2.2.0'\n\n    testImplementation 'junit:junit:4.12'\n    testImplementation 'androidx.test:core:1.2.0'\n    testImplementation 'com.google.truth:truth:1.0'\n    testImplementation 'org.robolectric:robolectric:4.3.1'\n    androidTestImplementation 'androidx.test:runner:1.2.0'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'\n}\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/download.gradle",
    "content": "apply plugin: 'de.undercouch.download'\n\ntask downloadLiteModel {\n    def downloadFiles = [\n        // Models support multiple features.\n        'https://storage.googleapis.com/download.tensorflow.org/models/tflite/recommendation/20210317/recommendation_cnn_i10i32o100.tflite': 'recommendation_cnn_i10i32o100.tflite',\n        'https://storage.googleapis.com/download.tensorflow.org/models/tflite/recommendation/20210317/config_cnn_i10i32o100.json': 'config.json',\n        // Vocab files.\n        'https://storage.googleapis.com/download.tensorflow.org/models/tflite/recommendation/20200823/sorted_movie_vocab.json': 'sorted_movie_vocab.json',\n        'https://storage.googleapis.com/download.tensorflow.org/models/tflite/recommendation/20210317/movie_genre_vocab.txt': 'movie_genre_vocab.txt',\n        // Models support one feature.\n        'https://storage.googleapis.com/download.tensorflow.org/models/tflite/recommendation/20200823/recommendation_rnn_i10o100.tflite': 'recommendation_rnn_i10o100.tflite',\n        'https://storage.googleapis.com/download.tensorflow.org/models/tflite/recommendation/20200823/config_rnn_i10o100.json': 'config_rnn_i10o100.json',\n        'https://storage.googleapis.com/download.tensorflow.org/models/tflite/recommendation/20200823/recommendation_cnn_i10o100.tflite': 'recommendation_cnn_i10o100.tflite',\n        'https://storage.googleapis.com/download.tensorflow.org/models/tflite/recommendation/20200823/config_cnn_i10o100.json': 'config_cnn_i10o100.json',\n    ]\n    downloadFiles.each { key, value ->\n      download {\n          src key\n          dest \"$projectDir/src/main/assets/\" + value\n          overwrite true\n      }\n    }\n}\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.tensorflow.lite.examples.recommendation\">\n\n  <application\n      android:allowBackup=\"true\"\n      android:icon=\"@mipmap/ic_launcher\"\n      android:label=\"@string/tfe_re_app_name\"\n      android:roundIcon=\"@mipmap/ic_launcher_round\"\n      android:supportsRtl=\"true\"\n      android:theme=\"@style/AppTheme.Recommendation\">\n\n    <activity\n        android:name=\".MainActivity\"\n        android:exported=\"true\"\n        android:windowSoftInputMode=\"adjustResize\">\n      <intent-filter>\n        <action android:name=\"android.intent.action.MAIN\" />\n        <category android:name=\"android.intent.category.LAUNCHER\" />\n      </intent-filter>\n    </activity>\n  </application>\n\n</manifest>\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/assets/config.json",
    "content": "{\n  \"model\": \"recommendation_cnn_i10i32o100.tflite\",\n  \"inputs\": [\n    {\"name\": \"movieFeature\", \"index\": 0, \"inputLength\": 10},\n    {\"name\": \"genreFeature\", \"index\": 1, \"inputLength\": 32}\n  ],\n  \"movieList\": \"sorted_movie_vocab.json\",\n  \"genreList\": \"movie_genre_vocab.txt\",\n  \"topK\": 10,\n  \"outputLength\": 100,\n  \"outputIdsIndex\": 1,\n  \"outputScoresIndex\": 0\n}\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/assets/config_cnn_i10o100.json",
    "content": "{\n  \"model\": \"recommendation_cnn_i10o100.tflite\",\n  \"inputs\": [\n    {\"name\": \"movieFeature\", \"index\": 0, \"inputLength\": 10}\n  ],\n  \"movieList\": \"sorted_movie_vocab.json\",\n  \"topK\": 10,\n  \"outputLength\": 100,\n  \"outputIdsIndex\": 1,\n  \"outputScoresIndex\": 0\n}\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/assets/config_rnn_i10o100.json",
    "content": "{\n  \"model\": \"recommendation_rnn_i10o100.tflite\",\n  \"inputs\": [\n    {\"name\": \"movieFeature\", \"index\": 0, \"inputLength\": 10}\n  ],\n  \"movieList\": \"sorted_movie_vocab.json\",\n  \"topK\": 10,\n  \"outputLength\": 100,\n  \"outputIdsIndex\": 0,\n  \"outputScoresIndex\": 1\n}\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/assets/movie_genre_vocab.txt",
    "content": "UNK\nDrama\nComedy\nAction\nThriller\nRomance\nHorror\nAdventure\nSci-Fi\nChildren's\nCrime\nWar\nDocumentary\nMusical\nMystery\nAnimation\nFantasy\nWestern\nFilm-Noir\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/assets/sorted_movie_vocab.json",
    "content": "[\n  {\n    \"title\": \"American Beauty (1999)\",\n    \"id\": 2858,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 3428\n  },\n  {\n    \"title\": \"Star Wars: Episode IV - A New Hope (1977)\",\n    \"id\": 260,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Fantasy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 2991\n  },\n  {\n    \"title\": \"Star Wars: Episode V - The Empire Strikes Back (1980)\",\n    \"id\": 1196,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Drama\",\n      \"Sci-Fi\",\n      \"War\"\n    ],\n    \"count\": 2990\n  },\n  {\n    \"title\": \"Star Wars: Episode VI - Return of the Jedi (1983)\",\n    \"id\": 1210,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Romance\",\n      \"Sci-Fi\",\n      \"War\"\n    ],\n    \"count\": 2883\n  },\n  {\n    \"title\": \"Jurassic Park (1993)\",\n    \"id\": 480,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 2672\n  },\n  {\n    \"title\": \"Saving Private Ryan (1998)\",\n    \"id\": 2028,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 2653\n  },\n  {\n    \"title\": \"Terminator 2: Judgment Day (1991)\",\n    \"id\": 589,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 2649\n  },\n  {\n    \"title\": \"Matrix, The (1999)\",\n    \"id\": 2571,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 2590\n  },\n  {\n    \"title\": \"Back to the Future (1985)\",\n    \"id\": 1270,\n    \"genres\": [\n      \"Comedy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 2583\n  },\n  {\n    \"title\": \"Silence of the Lambs, The (1991)\",\n    \"id\": 593,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 2578\n  },\n  {\n    \"title\": \"Men in Black (1997)\",\n    \"id\": 1580,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Comedy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 2538\n  },\n  {\n    \"title\": \"Raiders of the Lost Ark (1981)\",\n    \"id\": 1198,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 2514\n  },\n  {\n    \"title\": \"Fargo (1996)\",\n    \"id\": 608,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 2513\n  },\n  {\n    \"title\": \"Sixth Sense, The (1999)\",\n    \"id\": 2762,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 2459\n  },\n  {\n    \"title\": \"Braveheart (1995)\",\n    \"id\": 110,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 2443\n  },\n  {\n    \"title\": \"Shakespeare in Love (1998)\",\n    \"id\": 2396,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 2369\n  },\n  {\n    \"title\": \"Princess Bride, The (1987)\",\n    \"id\": 1197,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 2318\n  },\n  {\n    \"title\": \"Schindler's List (1993)\",\n    \"id\": 527,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 2304\n  },\n  {\n    \"title\": \"L.A. Confidential (1997)\",\n    \"id\": 1617,\n    \"genres\": [\n      \"Crime\",\n      \"Film-Noir\",\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 2288\n  },\n  {\n    \"title\": \"Groundhog Day (1993)\",\n    \"id\": 1265,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 2278\n  },\n  {\n    \"title\": \"E.T. the Extra-Terrestrial (1982)\",\n    \"id\": 1097,\n    \"genres\": [\n      \"Children's\",\n      \"Drama\",\n      \"Fantasy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 2269\n  },\n  {\n    \"title\": \"Star Wars: Episode I - The Phantom Menace (1999)\",\n    \"id\": 2628,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Fantasy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 2250\n  },\n  {\n    \"title\": \"Being John Malkovich (1999)\",\n    \"id\": 2997,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 2241\n  },\n  {\n    \"title\": \"Shawshank Redemption, The (1994)\",\n    \"id\": 318,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2227\n  },\n  {\n    \"title\": \"Godfather, The (1972)\",\n    \"id\": 858,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 2223\n  },\n  {\n    \"title\": \"Forrest Gump (1994)\",\n    \"id\": 356,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\",\n      \"War\"\n    ],\n    \"count\": 2194\n  },\n  {\n    \"title\": \"Ghostbusters (1984)\",\n    \"id\": 2716,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 2181\n  },\n  {\n    \"title\": \"Pulp Fiction (1994)\",\n    \"id\": 296,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 2171\n  },\n  {\n    \"title\": \"Terminator, The (1984)\",\n    \"id\": 1240,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 2098\n  },\n  {\n    \"title\": \"Toy Story (1995)\",\n    \"id\": 1,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 2077\n  },\n  {\n    \"title\": \"Alien (1979)\",\n    \"id\": 1214,\n    \"genres\": [\n      \"Action\",\n      \"Horror\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 2024\n  },\n  {\n    \"title\": \"Total Recall (1990)\",\n    \"id\": 2916,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 1996\n  },\n  {\n    \"title\": \"Fugitive, The (1993)\",\n    \"id\": 457,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 1995\n  },\n  {\n    \"title\": \"Gladiator (2000)\",\n    \"id\": 3578,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 1924\n  },\n  {\n    \"title\": \"Aliens (1986)\",\n    \"id\": 1200,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\",\n      \"Thriller\",\n      \"War\"\n    ],\n    \"count\": 1820\n  },\n  {\n    \"title\": \"Blade Runner (1982)\",\n    \"id\": 541,\n    \"genres\": [\n      \"Film-Noir\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1800\n  },\n  {\n    \"title\": \"Who Framed Roger Rabbit? (1988)\",\n    \"id\": 2987,\n    \"genres\": [\n      \"Adventure\",\n      \"Animation\",\n      \"Film-Noir\"\n    ],\n    \"count\": 1799\n  },\n  {\n    \"title\": \"Stand by Me (1986)\",\n    \"id\": 1259,\n    \"genres\": [\n      \"Adventure\",\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 1785\n  },\n  {\n    \"title\": \"Usual Suspects, The (1995)\",\n    \"id\": 50,\n    \"genres\": [\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 1783\n  },\n  {\n    \"title\": \"Babe (1995)\",\n    \"id\": 34,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 1751\n  },\n  {\n    \"title\": \"Airplane! (1980)\",\n    \"id\": 2791,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1731\n  },\n  {\n    \"title\": \"Independence Day (ID4) (1996)\",\n    \"id\": 780,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\",\n      \"War\"\n    ],\n    \"count\": 1730\n  },\n  {\n    \"title\": \"Galaxy Quest (1999)\",\n    \"id\": 3175,\n    \"genres\": [\n      \"Adventure\",\n      \"Comedy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1728\n  },\n  {\n    \"title\": \"One Flew Over the Cuckoo's Nest (1975)\",\n    \"id\": 1193,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1725\n  },\n  {\n    \"title\": \"Wizard of Oz, The (1939)\",\n    \"id\": 919,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Drama\",\n      \"Musical\"\n    ],\n    \"count\": 1718\n  },\n  {\n    \"title\": \"2001: A Space Odyssey (1968)\",\n    \"id\": 924,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 1716\n  },\n  {\n    \"title\": \"Abyss, The (1989)\",\n    \"id\": 1127,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 1715\n  },\n  {\n    \"title\": \"Bug's Life, A (1998)\",\n    \"id\": 2355,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 1703\n  },\n  {\n    \"title\": \"Jaws (1975)\",\n    \"id\": 1387,\n    \"genres\": [\n      \"Action\",\n      \"Horror\"\n    ],\n    \"count\": 1697\n  },\n  {\n    \"title\": \"Godfather: Part II, The (1974)\",\n    \"id\": 1221,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 1692\n  },\n  {\n    \"title\": \"Casablanca (1942)\",\n    \"id\": 912,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\",\n      \"War\"\n    ],\n    \"count\": 1669\n  },\n  {\n    \"title\": \"Die Hard (1988)\",\n    \"id\": 1036,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 1666\n  },\n  {\n    \"title\": \"GoodFellas (1990)\",\n    \"id\": 1213,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 1657\n  },\n  {\n    \"title\": \"Hunt for Red October, The (1990)\",\n    \"id\": 1610,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 1652\n  },\n  {\n    \"title\": \"Speed (1994)\",\n    \"id\": 377,\n    \"genres\": [\n      \"Action\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 1650\n  },\n  {\n    \"title\": \"Indiana Jones and the Last Crusade (1989)\",\n    \"id\": 1291,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 1628\n  },\n  {\n    \"title\": \"Lethal Weapon (1987)\",\n    \"id\": 2000,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 1627\n  },\n  {\n    \"title\": \"Monty Python and the Holy Grail (1974)\",\n    \"id\": 1136,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1599\n  },\n  {\n    \"title\": \"Toy Story 2 (1999)\",\n    \"id\": 3114,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 1585\n  },\n  {\n    \"title\": \"When Harry Met Sally... (1989)\",\n    \"id\": 1307,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 1568\n  },\n  {\n    \"title\": \"Good Will Hunting (1997)\",\n    \"id\": 1704,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1548\n  },\n  {\n    \"title\": \"Titanic (1997)\",\n    \"id\": 1721,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 1546\n  },\n  {\n    \"title\": \"Breakfast Club, The (1985)\",\n    \"id\": 1968,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 1539\n  },\n  {\n    \"title\": \"Mission: Impossible (1996)\",\n    \"id\": 648,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Mystery\"\n    ],\n    \"count\": 1527\n  },\n  {\n    \"title\": \"Election (1999)\",\n    \"id\": 2599,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1522\n  },\n  {\n    \"title\": \"Twelve Monkeys (1995)\",\n    \"id\": 32,\n    \"genres\": [\n      \"Drama\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1511\n  },\n  {\n    \"title\": \"X-Men (2000)\",\n    \"id\": 3793,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1511\n  },\n  {\n    \"title\": \"Beetlejuice (1988)\",\n    \"id\": 2174,\n    \"genres\": [\n      \"Comedy\",\n      \"Fantasy\"\n    ],\n    \"count\": 1495\n  },\n  {\n    \"title\": \"Big (1988)\",\n    \"id\": 2797,\n    \"genres\": [\n      \"Comedy\",\n      \"Fantasy\"\n    ],\n    \"count\": 1491\n  },\n  {\n    \"title\": \"Edward Scissorhands (1990)\",\n    \"id\": 2291,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 1473\n  },\n  {\n    \"title\": \"Ferris Bueller's Day Off (1986)\",\n    \"id\": 2918,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1473\n  },\n  {\n    \"title\": \"Dances with Wolves (1990)\",\n    \"id\": 590,\n    \"genres\": [\n      \"Adventure\",\n      \"Drama\",\n      \"Western\"\n    ],\n    \"count\": 1451\n  },\n  {\n    \"title\": \"Fight Club (1999)\",\n    \"id\": 2959,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1451\n  },\n  {\n    \"title\": \"Close Encounters of the Third Kind (1977)\",\n    \"id\": 3471,\n    \"genres\": [\n      \"Drama\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1451\n  },\n  {\n    \"title\": \"Star Trek: The Wrath of Khan (1982)\",\n    \"id\": 1374,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1448\n  },\n  {\n    \"title\": \"Raising Arizona (1987)\",\n    \"id\": 1394,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1434\n  },\n  {\n    \"title\": \"Austin Powers: The Spy Who Shagged Me (1999)\",\n    \"id\": 2683,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1434\n  },\n  {\n    \"title\": \"Batman (1989)\",\n    \"id\": 592,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 1431\n  },\n  {\n    \"title\": \"As Good As It Gets (1997)\",\n    \"id\": 1784,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 1424\n  },\n  {\n    \"title\": \"Face/Off (1997)\",\n    \"id\": 1573,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 1421\n  },\n  {\n    \"title\": \"Butch Cassidy and the Sundance Kid (1969)\",\n    \"id\": 1304,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Western\"\n    ],\n    \"count\": 1419\n  },\n  {\n    \"title\": \"Thelma & Louise (1991)\",\n    \"id\": 3418,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 1417\n  },\n  {\n    \"title\": \"Clerks (1994)\",\n    \"id\": 223,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1412\n  },\n  {\n    \"title\": \"True Lies (1994)\",\n    \"id\": 380,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 1400\n  },\n  {\n    \"title\": \"American Pie (1999)\",\n    \"id\": 2706,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1389\n  },\n  {\n    \"title\": \"Amadeus (1984)\",\n    \"id\": 1225,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1382\n  },\n  {\n    \"title\": \"Contact (1997)\",\n    \"id\": 1584,\n    \"genres\": [\n      \"Drama\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1382\n  },\n  {\n    \"title\": \"Fifth Element, The (1997)\",\n    \"id\": 1527,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1377\n  },\n  {\n    \"title\": \"High Fidelity (2000)\",\n    \"id\": 3481,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1373\n  },\n  {\n    \"title\": \"There's Something About Mary (1998)\",\n    \"id\": 1923,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1371\n  },\n  {\n    \"title\": \"Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb (1963)\",\n    \"id\": 750,\n    \"genres\": [\n      \"Sci-Fi\",\n      \"War\"\n    ],\n    \"count\": 1367\n  },\n  {\n    \"title\": \"Arachnophobia (1990)\",\n    \"id\": 2699,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 1367\n  },\n  {\n    \"title\": \"Clueless (1995)\",\n    \"id\": 39,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 1362\n  },\n  {\n    \"title\": \"Get Shorty (1995)\",\n    \"id\": 21,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 1356\n  },\n  {\n    \"title\": \"Jerry Maguire (1996)\",\n    \"id\": 1393,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 1353\n  },\n  {\n    \"title\": \"Christmas Story, A (1983)\",\n    \"id\": 2804,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 1352\n  },\n  {\n    \"title\": \"Aladdin (1992)\",\n    \"id\": 588,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 1351\n  },\n  {\n    \"title\": \"Romancing the Stone (1984)\",\n    \"id\": 2406,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 1345\n  },\n  {\n    \"title\": \"Blues Brothers, The (1980)\",\n    \"id\": 1220,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 1341\n  },\n  {\n    \"title\": \"Rock, The (1996)\",\n    \"id\": 733,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Thriller\"\n    ],\n    \"count\": 1340\n  },\n  {\n    \"title\": \"Annie Hall (1977)\",\n    \"id\": 1230,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 1334\n  },\n  {\n    \"title\": \"Talented Mr. Ripley, The (1999)\",\n    \"id\": 3176,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 1331\n  },\n  {\n    \"title\": \"Rain Man (1988)\",\n    \"id\": 1961,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1330\n  },\n  {\n    \"title\": \"Chicken Run (2000)\",\n    \"id\": 3751,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 1329\n  },\n  {\n    \"title\": \"Fish Called Wanda, A (1988)\",\n    \"id\": 1079,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1326\n  },\n  {\n    \"title\": \"North by Northwest (1959)\",\n    \"id\": 908,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 1315\n  },\n  {\n    \"title\": \"Erin Brockovich (2000)\",\n    \"id\": 3408,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1315\n  },\n  {\n    \"title\": \"Willy Wonka and the Chocolate Factory (1971)\",\n    \"id\": 1073,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Comedy\",\n      \"Fantasy\"\n    ],\n    \"count\": 1313\n  },\n  {\n    \"title\": \"Mission: Impossible 2 (2000)\",\n    \"id\": 3623,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 1313\n  },\n  {\n    \"title\": \"Star Trek: First Contact (1996)\",\n    \"id\": 1356,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1310\n  },\n  {\n    \"title\": \"Predator (1987)\",\n    \"id\": 3527,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 1297\n  },\n  {\n    \"title\": \"South Park: Bigger, Longer and Uncut (1999)\",\n    \"id\": 2700,\n    \"genres\": [\n      \"Animation\",\n      \"Comedy\"\n    ],\n    \"count\": 1269\n  },\n  {\n    \"title\": \"Psycho (1960)\",\n    \"id\": 1219,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 1263\n  },\n  {\n    \"title\": \"Graduate, The (1967)\",\n    \"id\": 1247,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 1261\n  },\n  {\n    \"title\": \"Reservoir Dogs (1992)\",\n    \"id\": 1089,\n    \"genres\": [\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 1259\n  },\n  {\n    \"title\": \"Lost World: Jurassic Park, The (1997)\",\n    \"id\": 1544,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 1255\n  },\n  {\n    \"title\": \"Full Metal Jacket (1987)\",\n    \"id\": 1222,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 1254\n  },\n  {\n    \"title\": \"Apollo 13 (1995)\",\n    \"id\": 150,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1251\n  },\n  {\n    \"title\": \"Taxi Driver (1976)\",\n    \"id\": 111,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 1240\n  },\n  {\n    \"title\": \"Patriot, The (2000)\",\n    \"id\": 3753,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 1240\n  },\n  {\n    \"title\": \"Blair Witch Project, The (1999)\",\n    \"id\": 2710,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 1237\n  },\n  {\n    \"title\": \"Four Weddings and a Funeral (1994)\",\n    \"id\": 357,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 1233\n  },\n  {\n    \"title\": \"Rocky Horror Picture Show, The (1975)\",\n    \"id\": 2657,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\",\n      \"Musical\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1233\n  },\n  {\n    \"title\": \"Crying Game, The (1992)\",\n    \"id\": 1094,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\",\n      \"War\"\n    ],\n    \"count\": 1229\n  },\n  {\n    \"title\": \"Clockwork Orange, A (1971)\",\n    \"id\": 1206,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 1229\n  },\n  {\n    \"title\": \"Robocop (1987)\",\n    \"id\": 2985,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1229\n  },\n  {\n    \"title\": \"Superman (1978)\",\n    \"id\": 2640,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1222\n  },\n  {\n    \"title\": \"Green Mile, The (1999)\",\n    \"id\": 3147,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 1222\n  },\n  {\n    \"title\": \"Rushmore (1998)\",\n    \"id\": 2395,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1220\n  },\n  {\n    \"title\": \"Animal House (1978)\",\n    \"id\": 3421,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1207\n  },\n  {\n    \"title\": \"Austin Powers: International Man of Mystery (1997)\",\n    \"id\": 1517,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1205\n  },\n  {\n    \"title\": \"Full Monty, The (1997)\",\n    \"id\": 1641,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1199\n  },\n  {\n    \"title\": \"Young Frankenstein (1974)\",\n    \"id\": 1278,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 1193\n  },\n  {\n    \"title\": \"Chinatown (1974)\",\n    \"id\": 1252,\n    \"genres\": [\n      \"Film-Noir\",\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 1185\n  },\n  {\n    \"title\": \"Mask, The (1994)\",\n    \"id\": 367,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\",\n      \"Fantasy\"\n    ],\n    \"count\": 1179\n  },\n  {\n    \"title\": \"Sling Blade (1996)\",\n    \"id\": 1358,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 1177\n  },\n  {\n    \"title\": \"Apocalypse Now (1979)\",\n    \"id\": 1208,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 1176\n  },\n  {\n    \"title\": \"Starship Troopers (1997)\",\n    \"id\": 1676,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\",\n      \"War\"\n    ],\n    \"count\": 1163\n  },\n  {\n    \"title\": \"Splash (1984)\",\n    \"id\": 2100,\n    \"genres\": [\n      \"Comedy\",\n      \"Fantasy\",\n      \"Romance\"\n    ],\n    \"count\": 1163\n  },\n  {\n    \"title\": \"Planet of the Apes (1968)\",\n    \"id\": 2529,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1162\n  },\n  {\n    \"title\": \"Back to the Future Part II (1989)\",\n    \"id\": 2011,\n    \"genres\": [\n      \"Comedy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1158\n  },\n  {\n    \"title\": \"Pleasantville (1998)\",\n    \"id\": 2321,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1158\n  },\n  {\n    \"title\": \"Gone with the Wind (1939)\",\n    \"id\": 920,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\",\n      \"War\"\n    ],\n    \"count\": 1156\n  },\n  {\n    \"title\": \"Life Is Beautiful (La Vita \\u00e8 bella) (1997)\",\n    \"id\": 2324,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 1152\n  },\n  {\n    \"title\": \"Back to the Future Part III (1990)\",\n    \"id\": 2012,\n    \"genres\": [\n      \"Comedy\",\n      \"Sci-Fi\",\n      \"Western\"\n    ],\n    \"count\": 1148\n  },\n  {\n    \"title\": \"Platoon (1986)\",\n    \"id\": 1090,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 1143\n  },\n  {\n    \"title\": \"Seven (Se7en) (1995)\",\n    \"id\": 47,\n    \"genres\": [\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 1137\n  },\n  {\n    \"title\": \"Grosse Pointe Blank (1997)\",\n    \"id\": 1500,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\"\n    ],\n    \"count\": 1136\n  },\n  {\n    \"title\": \"Die Hard 2 (1990)\",\n    \"id\": 1370,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 1135\n  },\n  {\n    \"title\": \"Rocky (1976)\",\n    \"id\": 1954,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 1129\n  },\n  {\n    \"title\": \"Boogie Nights (1997)\",\n    \"id\": 1673,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1128\n  },\n  {\n    \"title\": \"Indiana Jones and the Temple of Doom (1984)\",\n    \"id\": 2115,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 1127\n  },\n  {\n    \"title\": \"Untouchables, The (1987)\",\n    \"id\": 2194,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 1127\n  },\n  {\n    \"title\": \"Star Trek IV: The Voyage Home (1986)\",\n    \"id\": 1376,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1126\n  },\n  {\n    \"title\": \"Ghost (1990)\",\n    \"id\": 587,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 1124\n  },\n  {\n    \"title\": \"Lion King, The (1994)\",\n    \"id\": 364,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 1121\n  },\n  {\n    \"title\": \"Wayne's World (1992)\",\n    \"id\": 3253,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1120\n  },\n  {\n    \"title\": \"Blazing Saddles (1974)\",\n    \"id\": 3671,\n    \"genres\": [\n      \"Comedy\",\n      \"Western\"\n    ],\n    \"count\": 1119\n  },\n  {\n    \"title\": \"This Is Spinal Tap (1984)\",\n    \"id\": 1288,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Musical\"\n    ],\n    \"count\": 1118\n  },\n  {\n    \"title\": \"Stargate (1994)\",\n    \"id\": 316,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1116\n  },\n  {\n    \"title\": \"Citizen Kane (1941)\",\n    \"id\": 923,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1116\n  },\n  {\n    \"title\": \"Magnolia (1999)\",\n    \"id\": 3160,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1113\n  },\n  {\n    \"title\": \"Glory (1989)\",\n    \"id\": 1242,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 1112\n  },\n  {\n    \"title\": \"Twister (1996)\",\n    \"id\": 736,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 1110\n  },\n  {\n    \"title\": \"Armageddon (1998)\",\n    \"id\": 1917,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 1110\n  },\n  {\n    \"title\": \"M*A*S*H (1970)\",\n    \"id\": 1294,\n    \"genres\": [\n      \"Comedy\",\n      \"War\"\n    ],\n    \"count\": 1099\n  },\n  {\n    \"title\": \"League of Their Own, A (1992)\",\n    \"id\": 3255,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 1099\n  },\n  {\n    \"title\": \"Big Lebowski, The (1998)\",\n    \"id\": 1732,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\",\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 1097\n  },\n  {\n    \"title\": \"Bull Durham (1988)\",\n    \"id\": 3361,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1095\n  },\n  {\n    \"title\": \"Sneakers (1992)\",\n    \"id\": 1396,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1091\n  },\n  {\n    \"title\": \"Thomas Crown Affair, The (1999)\",\n    \"id\": 2763,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 1089\n  },\n  {\n    \"title\": \"Shining, The (1980)\",\n    \"id\": 1258,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 1087\n  },\n  {\n    \"title\": \"My Cousin Vinny (1992)\",\n    \"id\": 2302,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1086\n  },\n  {\n    \"title\": \"Good Morning, Vietnam (1987)\",\n    \"id\": 3448,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 1082\n  },\n  {\n    \"title\": \"Gattaca (1997)\",\n    \"id\": 1653,\n    \"genres\": [\n      \"Drama\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 1077\n  },\n  {\n    \"title\": \"Air Force One (1997)\",\n    \"id\": 1608,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 1076\n  },\n  {\n    \"title\": \"Mars Attacks! (1996)\",\n    \"id\": 1391,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Sci-Fi\",\n      \"War\"\n    ],\n    \"count\": 1074\n  },\n  {\n    \"title\": \"Run Lola Run (Lola rennt) (1998)\",\n    \"id\": 2692,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Romance\"\n    ],\n    \"count\": 1072\n  },\n  {\n    \"title\": \"Fly, The (1986)\",\n    \"id\": 2455,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1071\n  },\n  {\n    \"title\": \"Sleepy Hollow (1999)\",\n    \"id\": 3081,\n    \"genres\": [\n      \"Horror\",\n      \"Romance\"\n    ],\n    \"count\": 1066\n  },\n  {\n    \"title\": \"Mad Max (1979)\",\n    \"id\": 3702,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1066\n  },\n  {\n    \"title\": \"Few Good Men, A (1992)\",\n    \"id\": 2268,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 1061\n  },\n  {\n    \"title\": \"Beauty and the Beast (1991)\",\n    \"id\": 595,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 1060\n  },\n  {\n    \"title\": \"Clear and Present Danger (1994)\",\n    \"id\": 349,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Thriller\"\n    ],\n    \"count\": 1059\n  },\n  {\n    \"title\": \"African Queen, The (1951)\",\n    \"id\": 969,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Romance\",\n      \"War\"\n    ],\n    \"count\": 1057\n  },\n  {\n    \"title\": \"Patriot Games (1992)\",\n    \"id\": 3256,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 1052\n  },\n  {\n    \"title\": \"Rear Window (1954)\",\n    \"id\": 904,\n    \"genres\": [\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 1050\n  },\n  {\n    \"title\": \"Sting, The (1973)\",\n    \"id\": 1234,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\"\n    ],\n    \"count\": 1049\n  },\n  {\n    \"title\": \"Pretty Woman (1990)\",\n    \"id\": 597,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 1046\n  },\n  {\n    \"title\": \"Witness (1985)\",\n    \"id\": 1674,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 1046\n  },\n  {\n    \"title\": \"Honey, I Shrunk the Kids (1989)\",\n    \"id\": 2054,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Comedy\",\n      \"Fantasy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1045\n  },\n  {\n    \"title\": \"Maltese Falcon, The (1941)\",\n    \"id\": 913,\n    \"genres\": [\n      \"Film-Noir\",\n      \"Mystery\"\n    ],\n    \"count\": 1043\n  },\n  {\n    \"title\": \"Little Mermaid, The (1989)\",\n    \"id\": 2081,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Comedy\",\n      \"Musical\",\n      \"Romance\"\n    ],\n    \"count\": 1035\n  },\n  {\n    \"title\": \"Mad Max 2 (a.k.a. The Road Warrior) (1981)\",\n    \"id\": 3703,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1034\n  },\n  {\n    \"title\": \"American President, The (1995)\",\n    \"id\": 11,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 1033\n  },\n  {\n    \"title\": \"Batman Returns (1992)\",\n    \"id\": 1377,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Comedy\",\n      \"Crime\"\n    ],\n    \"count\": 1031\n  },\n  {\n    \"title\": \"Three Kings (1999)\",\n    \"id\": 2890,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 1021\n  },\n  {\n    \"title\": \"Perfect Storm, The (2000)\",\n    \"id\": 3755,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Thriller\"\n    ],\n    \"count\": 1013\n  },\n  {\n    \"title\": \"Mary Poppins (1964)\",\n    \"id\": 1028,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 1011\n  },\n  {\n    \"title\": \"Lethal Weapon 2 (1989)\",\n    \"id\": 2001,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 1011\n  },\n  {\n    \"title\": \"Top Gun (1986)\",\n    \"id\": 1101,\n    \"genres\": [\n      \"Action\",\n      \"Romance\"\n    ],\n    \"count\": 1010\n  },\n  {\n    \"title\": \"Time Bandits (1981)\",\n    \"id\": 2968,\n    \"genres\": [\n      \"Adventure\",\n      \"Fantasy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1010\n  },\n  {\n    \"title\": \"Mummy, The (1999)\",\n    \"id\": 2617,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 1007\n  },\n  {\n    \"title\": \"Cocoon (1985)\",\n    \"id\": 2407,\n    \"genres\": [\n      \"Comedy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1006\n  },\n  {\n    \"title\": \"Truman Show, The (1998)\",\n    \"id\": 1682,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1005\n  },\n  {\n    \"title\": \"Boat, The (Das Boot) (1981)\",\n    \"id\": 1233,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 1001\n  },\n  {\n    \"title\": \"Star Trek VI: The Undiscovered Country (1991)\",\n    \"id\": 1372,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 1001\n  },\n  {\n    \"title\": \"Unforgiven (1992)\",\n    \"id\": 1266,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 997\n  },\n  {\n    \"title\": \"Nightmare Before Christmas, The (1993)\",\n    \"id\": 551,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 996\n  },\n  {\n    \"title\": \"X-Files: Fight the Future, The (1998)\",\n    \"id\": 1909,\n    \"genres\": [\n      \"Mystery\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 996\n  },\n  {\n    \"title\": \"Almost Famous (2000)\",\n    \"id\": 3897,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 994\n  },\n  {\n    \"title\": \"Star Trek: Generations (1994)\",\n    \"id\": 329,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 990\n  },\n  {\n    \"title\": \"American Graffiti (1973)\",\n    \"id\": 3363,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 990\n  },\n  {\n    \"title\": \"English Patient, The (1996)\",\n    \"id\": 1183,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\",\n      \"War\"\n    ],\n    \"count\": 989\n  },\n  {\n    \"title\": \"Dogma (1999)\",\n    \"id\": 3052,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 989\n  },\n  {\n    \"title\": \"Enemy of the State (1998)\",\n    \"id\": 2353,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 988\n  },\n  {\n    \"title\": \"Sleepless in Seattle (1993)\",\n    \"id\": 539,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 986\n  },\n  {\n    \"title\": \"Mask of Zorro, The (1998)\",\n    \"id\": 2006,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Romance\"\n    ],\n    \"count\": 983\n  },\n  {\n    \"title\": \"Moonstruck (1987)\",\n    \"id\": 3072,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 982\n  },\n  {\n    \"title\": \"Leaving Las Vegas (1995)\",\n    \"id\": 25,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 980\n  },\n  {\n    \"title\": \"Player, The (1992)\",\n    \"id\": 2289,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 978\n  },\n  {\n    \"title\": \"Last of the Mohicans, The (1992)\",\n    \"id\": 1408,\n    \"genres\": [\n      \"Action\",\n      \"Romance\",\n      \"War\"\n    ],\n    \"count\": 975\n  },\n  {\n    \"title\": \"In the Line of Fire (1993)\",\n    \"id\": 474,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 972\n  },\n  {\n    \"title\": \"Tron (1982)\",\n    \"id\": 2105,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Fantasy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 970\n  },\n  {\n    \"title\": \"Caddyshack (1980)\",\n    \"id\": 3552,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 967\n  },\n  {\n    \"title\": \"Chasing Amy (1997)\",\n    \"id\": 1639,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 966\n  },\n  {\n    \"title\": \"Con Air (1997)\",\n    \"id\": 1552,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Thriller\"\n    ],\n    \"count\": 954\n  },\n  {\n    \"title\": \"Dave (1993)\",\n    \"id\": 440,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 948\n  },\n  {\n    \"title\": \"Nutty Professor, The (1996)\",\n    \"id\": 788,\n    \"genres\": [\n      \"Comedy\",\n      \"Fantasy\",\n      \"Romance\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 948\n  },\n  {\n    \"title\": \"Army of Darkness (1993)\",\n    \"id\": 1215,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Comedy\",\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 945\n  },\n  {\n    \"title\": \"Eyes Wide Shut (1999)\",\n    \"id\": 2712,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 945\n  },\n  {\n    \"title\": \"Fisher King, The (1991)\",\n    \"id\": 3108,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 945\n  },\n  {\n    \"title\": \"Fantasia (1940)\",\n    \"id\": 1282,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 943\n  },\n  {\n    \"title\": \"Heat (1995)\",\n    \"id\": 6,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 940\n  },\n  {\n    \"title\": \"Bridge on the River Kwai, The (1957)\",\n    \"id\": 1250,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 938\n  },\n  {\n    \"title\": \"Elizabeth (1998)\",\n    \"id\": 2336,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 938\n  },\n  {\n    \"title\": \"Little Shop of Horrors (1986)\",\n    \"id\": 2746,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\",\n      \"Musical\"\n    ],\n    \"count\": 936\n  },\n  {\n    \"title\": \"Bowfinger (1999)\",\n    \"id\": 2770,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 935\n  },\n  {\n    \"title\": \"Cool Hand Luke (1967)\",\n    \"id\": 1276,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 930\n  },\n  {\n    \"title\": \"Dead Man Walking (1995)\",\n    \"id\": 36,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 928\n  },\n  {\n    \"title\": \"To Kill a Mockingbird (1962)\",\n    \"id\": 1207,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 928\n  },\n  {\n    \"title\": \"Ed Wood (1994)\",\n    \"id\": 235,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 927\n  },\n  {\n    \"title\": \"Star Trek III: The Search for Spock (1984)\",\n    \"id\": 1375,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 927\n  },\n  {\n    \"title\": \"Monty Python's Life of Brian (1979)\",\n    \"id\": 1080,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 926\n  },\n  {\n    \"title\": \"Alien\\u00b3 (1992)\",\n    \"id\": 1320,\n    \"genres\": [\n      \"Action\",\n      \"Horror\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 925\n  },\n  {\n    \"title\": \"Professional, The (a.k.a. Leon: The Professional) (1994)\",\n    \"id\": 293,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 923\n  },\n  {\n    \"title\": \"From Dusk Till Dawn (1996)\",\n    \"id\": 70,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Crime\",\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 914\n  },\n  {\n    \"title\": \"Say Anything... (1989)\",\n    \"id\": 2248,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 914\n  },\n  {\n    \"title\": \"Brazil (1985)\",\n    \"id\": 1199,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 913\n  },\n  {\n    \"title\": \"Crocodile Dundee (1986)\",\n    \"id\": 2470,\n    \"genres\": [\n      \"Adventure\",\n      \"Comedy\"\n    ],\n    \"count\": 912\n  },\n  {\n    \"title\": \"Wedding Singer, The (1998)\",\n    \"id\": 1777,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 909\n  },\n  {\n    \"title\": \"Out of Sight (1998)\",\n    \"id\": 1912,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Romance\"\n    ],\n    \"count\": 908\n  },\n  {\n    \"title\": \"U-571 (2000)\",\n    \"id\": 3555,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 908\n  },\n  {\n    \"title\": \"Wag the Dog (1997)\",\n    \"id\": 1747,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 907\n  },\n  {\n    \"title\": \"Vertigo (1958)\",\n    \"id\": 903,\n    \"genres\": [\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 905\n  },\n  {\n    \"title\": \"Wild Wild West (1999)\",\n    \"id\": 2701,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\",\n      \"Western\"\n    ],\n    \"count\": 902\n  },\n  {\n    \"title\": \"Spaceballs (1987)\",\n    \"id\": 3033,\n    \"genres\": [\n      \"Comedy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 899\n  },\n  {\n    \"title\": \"Goldfinger (1964)\",\n    \"id\": 2947,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 897\n  },\n  {\n    \"title\": \"Deep Impact (1998)\",\n    \"id\": 1876,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 895\n  },\n  {\n    \"title\": \"GoldenEye (1995)\",\n    \"id\": 10,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Thriller\"\n    ],\n    \"count\": 888\n  },\n  {\n    \"title\": \"Scream (1996)\",\n    \"id\": 1407,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 886\n  },\n  {\n    \"title\": \"Fast Times at Ridgemont High (1982)\",\n    \"id\": 3210,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 886\n  },\n  {\n    \"title\": \"Grifters, The (1990)\",\n    \"id\": 1179,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Film-Noir\"\n    ],\n    \"count\": 885\n  },\n  {\n    \"title\": \"Exorcist, The (1973)\",\n    \"id\": 1997,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 885\n  },\n  {\n    \"title\": \"Escape from New York (1981)\",\n    \"id\": 1129,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 884\n  },\n  {\n    \"title\": \"Insider, The (1999)\",\n    \"id\": 3006,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 884\n  },\n  {\n    \"title\": \"Misery (1990)\",\n    \"id\": 3499,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 884\n  },\n  {\n    \"title\": \"Sound of Music, The (1965)\",\n    \"id\": 1035,\n    \"genres\": [\n      \"Musical\"\n    ],\n    \"count\": 882\n  },\n  {\n    \"title\": \"Wrong Trousers, The (1993)\",\n    \"id\": 1148,\n    \"genres\": [\n      \"Animation\",\n      \"Comedy\"\n    ],\n    \"count\": 882\n  },\n  {\n    \"title\": \"Game, The (1997)\",\n    \"id\": 1625,\n    \"genres\": [\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 882\n  },\n  {\n    \"title\": \"Dark City (1998)\",\n    \"id\": 1748,\n    \"genres\": [\n      \"Film-Noir\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 874\n  },\n  {\n    \"title\": \"Man on the Moon (1999)\",\n    \"id\": 3174,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 874\n  },\n  {\n    \"title\": \"Office Space (1999)\",\n    \"id\": 2502,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 872\n  },\n  {\n    \"title\": \"Backdraft (1991)\",\n    \"id\": 3107,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 869\n  },\n  {\n    \"title\": \"Starman (1984)\",\n    \"id\": 3699,\n    \"genres\": [\n      \"Adventure\",\n      \"Drama\",\n      \"Romance\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 865\n  },\n  {\n    \"title\": \"Lady and the Tramp (1955)\",\n    \"id\": 2080,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Comedy\",\n      \"Musical\",\n      \"Romance\"\n    ],\n    \"count\": 864\n  },\n  {\n    \"title\": \"Tomorrow Never Dies (1997)\",\n    \"id\": 1722,\n    \"genres\": [\n      \"Action\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 862\n  },\n  {\n    \"title\": \"World Is Not Enough, The (1999)\",\n    \"id\": 3082,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 862\n  },\n  {\n    \"title\": \"Meet the Parents (2000)\",\n    \"id\": 3948,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 862\n  },\n  {\n    \"title\": \"French Connection, The (1971)\",\n    \"id\": 1953,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 861\n  },\n  {\n    \"title\": \"Pee-wee's Big Adventure (1985)\",\n    \"id\": 3608,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 861\n  },\n  {\n    \"title\": \"Dead Poets Society (1989)\",\n    \"id\": 1246,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 855\n  },\n  {\n    \"title\": \"Mystery Men (1999)\",\n    \"id\": 2723,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Comedy\"\n    ],\n    \"count\": 853\n  },\n  {\n    \"title\": \"Risky Business (1983)\",\n    \"id\": 2915,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 852\n  },\n  {\n    \"title\": \"Boys Don't Cry (1999)\",\n    \"id\": 2908,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 850\n  },\n  {\n    \"title\": \"Superman II (1980)\",\n    \"id\": 2641,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 846\n  },\n  {\n    \"title\": \"Desperately Seeking Susan (1985)\",\n    \"id\": 2369,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 840\n  },\n  {\n    \"title\": \"Trading Places (1983)\",\n    \"id\": 3039,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 839\n  },\n  {\n    \"title\": \"Diner (1982)\",\n    \"id\": 3543,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 839\n  },\n  {\n    \"title\": \"Mrs. Doubtfire (1993)\",\n    \"id\": 500,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 838\n  },\n  {\n    \"title\": \"Big Chill, The (1983)\",\n    \"id\": 2352,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 838\n  },\n  {\n    \"title\": \"You've Got Mail (1998)\",\n    \"id\": 2424,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 838\n  },\n  {\n    \"title\": \"Sense and Sensibility (1995)\",\n    \"id\": 17,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 835\n  },\n  {\n    \"title\": \"Some Like It Hot (1959)\",\n    \"id\": 910,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\"\n    ],\n    \"count\": 832\n  },\n  {\n    \"title\": \"Lawrence of Arabia (1962)\",\n    \"id\": 1204,\n    \"genres\": [\n      \"Adventure\",\n      \"War\"\n    ],\n    \"count\": 831\n  },\n  {\n    \"title\": \"Devil's Advocate, The (1997)\",\n    \"id\": 1645,\n    \"genres\": [\n      \"Crime\",\n      \"Horror\",\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 826\n  },\n  {\n    \"title\": \"Die Hard: With a Vengeance (1995)\",\n    \"id\": 165,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 825\n  },\n  {\n    \"title\": \"L.A. Story (1991)\",\n    \"id\": 2108,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 825\n  },\n  {\n    \"title\": \"Firm, The (1993)\",\n    \"id\": 454,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 824\n  },\n  {\n    \"title\": \"Good, The Bad and The Ugly, The (1966)\",\n    \"id\": 1201,\n    \"genres\": [\n      \"Action\",\n      \"Western\"\n    ],\n    \"count\": 822\n  },\n  {\n    \"title\": \"Frequency (2000)\",\n    \"id\": 3510,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 821\n  },\n  {\n    \"title\": \"My Best Friend's Wedding (1997)\",\n    \"id\": 1569,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 820\n  },\n  {\n    \"title\": \"Grease (1978)\",\n    \"id\": 1380,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\",\n      \"Romance\"\n    ],\n    \"count\": 817\n  },\n  {\n    \"title\": \"What About Bob? (1991)\",\n    \"id\": 3809,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 817\n  },\n  {\n    \"title\": \"Conspiracy Theory (1997)\",\n    \"id\": 1597,\n    \"genres\": [\n      \"Action\",\n      \"Mystery\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 811\n  },\n  {\n    \"title\": \"Parenthood (1989)\",\n    \"id\": 3526,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 811\n  },\n  {\n    \"title\": \"Blade (1998)\",\n    \"id\": 2167,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Horror\"\n    ],\n    \"count\": 809\n  },\n  {\n    \"title\": \"Grumpy Old Men (1993)\",\n    \"id\": 3450,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 809\n  },\n  {\n    \"title\": \"Star Trek: Insurrection (1998)\",\n    \"id\": 2393,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 807\n  },\n  {\n    \"title\": \"Ronin (1998)\",\n    \"id\": 2278,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 806\n  },\n  {\n    \"title\": \"Heathers (1989)\",\n    \"id\": 1285,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 803\n  },\n  {\n    \"title\": \"Thin Red Line, The (1998)\",\n    \"id\": 2427,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 803\n  },\n  {\n    \"title\": \"Maverick (1994)\",\n    \"id\": 368,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Western\"\n    ],\n    \"count\": 802\n  },\n  {\n    \"title\": \"Lethal Weapon 3 (1992)\",\n    \"id\": 2002,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 802\n  },\n  {\n    \"title\": \"Willow (1988)\",\n    \"id\": 2193,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Fantasy\"\n    ],\n    \"count\": 802\n  },\n  {\n    \"title\": \"Fatal Attraction (1987)\",\n    \"id\": 3101,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 802\n  },\n  {\n    \"title\": \"Negotiator, The (1998)\",\n    \"id\": 2058,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 799\n  },\n  {\n    \"title\": \"Roger & Me (1989)\",\n    \"id\": 2064,\n    \"genres\": [\n      \"Comedy\",\n      \"Documentary\"\n    ],\n    \"count\": 798\n  },\n  {\n    \"title\": \"Poltergeist (1982)\",\n    \"id\": 1994,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 795\n  },\n  {\n    \"title\": \"Mission to Mars (2000)\",\n    \"id\": 3354,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 793\n  },\n  {\n    \"title\": \"Star Trek: The Motion Picture (1979)\",\n    \"id\": 1371,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 792\n  },\n  {\n    \"title\": \"Big Daddy (1999)\",\n    \"id\": 2694,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 791\n  },\n  {\n    \"title\": \"Shanghai Noon (2000)\",\n    \"id\": 3624,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 791\n  },\n  {\n    \"title\": \"Dune (1984)\",\n    \"id\": 2021,\n    \"genres\": [\n      \"Fantasy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 789\n  },\n  {\n    \"title\": \"Broadcast News (1987)\",\n    \"id\": 2243,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 789\n  },\n  {\n    \"title\": \"Swingers (1996)\",\n    \"id\": 1060,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 786\n  },\n  {\n    \"title\": \"Double Jeopardy (1999)\",\n    \"id\": 2881,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 786\n  },\n  {\n    \"title\": \"Do the Right Thing (1989)\",\n    \"id\": 3424,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 781\n  },\n  {\n    \"title\": \"Notting Hill (1999)\",\n    \"id\": 2671,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 780\n  },\n  {\n    \"title\": \"Alien: Resurrection (1997)\",\n    \"id\": 1690,\n    \"genres\": [\n      \"Action\",\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 779\n  },\n  {\n    \"title\": \"Batman Forever (1995)\",\n    \"id\": 153,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Comedy\",\n      \"Crime\"\n    ],\n    \"count\": 777\n  },\n  {\n    \"title\": \"Entrapment (1999)\",\n    \"id\": 2605,\n    \"genres\": [\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 777\n  },\n  {\n    \"title\": \"Gremlins (1984)\",\n    \"id\": 2003,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 776\n  },\n  {\n    \"title\": \"Godfather: Part III, The (1990)\",\n    \"id\": 2023,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 774\n  },\n  {\n    \"title\": \"Dangerous Liaisons (1988)\",\n    \"id\": 2020,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 773\n  },\n  {\n    \"title\": \"Payback (1999)\",\n    \"id\": 2490,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 773\n  },\n  {\n    \"title\": \"Like Water for Chocolate (Como agua para chocolate) (1992)\",\n    \"id\": 265,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 772\n  },\n  {\n    \"title\": \"From Russia with Love (1963)\",\n    \"id\": 2948,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 771\n  },\n  {\n    \"title\": \"Field of Dreams (1989)\",\n    \"id\": 1302,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 768\n  },\n  {\n    \"title\": \"Ace Ventura: Pet Detective (1994)\",\n    \"id\": 344,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 766\n  },\n  {\n    \"title\": \"Kingpin (1996)\",\n    \"id\": 785,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 765\n  },\n  {\n    \"title\": \"Manchurian Candidate, The (1962)\",\n    \"id\": 1267,\n    \"genres\": [\n      \"Film-Noir\",\n      \"Thriller\"\n    ],\n    \"count\": 765\n  },\n  {\n    \"title\": \"Midnight Run (1988)\",\n    \"id\": 3104,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Comedy\",\n      \"Crime\"\n    ],\n    \"count\": 765\n  },\n  {\n    \"title\": \"Dog Day Afternoon (1975)\",\n    \"id\": 3362,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 765\n  },\n  {\n    \"title\": \"Snow White and the Seven Dwarfs (1937)\",\n    \"id\": 594,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 763\n  },\n  {\n    \"title\": \"West Side Story (1961)\",\n    \"id\": 1947,\n    \"genres\": [\n      \"Musical\",\n      \"Romance\"\n    ],\n    \"count\": 761\n  },\n  {\n    \"title\": \"Crimson Tide (1995)\",\n    \"id\": 161,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\",\n      \"War\"\n    ],\n    \"count\": 760\n  },\n  {\n    \"title\": \"King Kong (1933)\",\n    \"id\": 2366,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Horror\"\n    ],\n    \"count\": 756\n  },\n  {\n    \"title\": \"JFK (1991)\",\n    \"id\": 3386,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 756\n  },\n  {\n    \"title\": \"Romy and Michele's High School Reunion (1997)\",\n    \"id\": 1513,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 755\n  },\n  {\n    \"title\": \"Trainspotting (1996)\",\n    \"id\": 778,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 751\n  },\n  {\n    \"title\": \"Singin' in the Rain (1952)\",\n    \"id\": 899,\n    \"genres\": [\n      \"Musical\",\n      \"Romance\"\n    ],\n    \"count\": 751\n  },\n  {\n    \"title\": \"Basic Instinct (1992)\",\n    \"id\": 1092,\n    \"genres\": [\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 751\n  },\n  {\n    \"title\": \"Repo Man (1984)\",\n    \"id\": 1965,\n    \"genres\": [\n      \"Comedy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 751\n  },\n  {\n    \"title\": \"Right Stuff, The (1983)\",\n    \"id\": 1231,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 750\n  },\n  {\n    \"title\": \"13th Warrior, The (1999)\",\n    \"id\": 2826,\n    \"genres\": [\n      \"Action\",\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 750\n  },\n  {\n    \"title\": \"Simple Plan, A (1998)\",\n    \"id\": 2391,\n    \"genres\": [\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 745\n  },\n  {\n    \"title\": \"Sleeper (1973)\",\n    \"id\": 1077,\n    \"genres\": [\n      \"Comedy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 744\n  },\n  {\n    \"title\": \"Muppet Movie, The (1979)\",\n    \"id\": 3396,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 744\n  },\n  {\n    \"title\": \"Excalibur (1981)\",\n    \"id\": 2872,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"Fantasy\",\n      \"Romance\"\n    ],\n    \"count\": 742\n  },\n  {\n    \"title\": \"Highlander (1986)\",\n    \"id\": 1275,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 741\n  },\n  {\n    \"title\": \"Thing, The (1982)\",\n    \"id\": 2288,\n    \"genres\": [\n      \"Action\",\n      \"Horror\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 741\n  },\n  {\n    \"title\": \"Stripes (1981)\",\n    \"id\": 1663,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 740\n  },\n  {\n    \"title\": \"Interview with the Vampire (1994)\",\n    \"id\": 253,\n    \"genres\": [\n      \"Drama\",\n      \"Horror\"\n    ],\n    \"count\": 738\n  },\n  {\n    \"title\": \"Rocketeer, The (1991)\",\n    \"id\": 2094,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 736\n  },\n  {\n    \"title\": \"Crow, The (1994)\",\n    \"id\": 353,\n    \"genres\": [\n      \"Action\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 734\n  },\n  {\n    \"title\": \"Outbreak (1995)\",\n    \"id\": 292,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 733\n  },\n  {\n    \"title\": \"Nikita (La Femme Nikita) (1990)\",\n    \"id\": 1249,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 733\n  },\n  {\n    \"title\": \"Birds, The (1963)\",\n    \"id\": 1333,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 733\n  },\n  {\n    \"title\": \"Killing Fields, The (1984)\",\n    \"id\": 1299,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 732\n  },\n  {\n    \"title\": \"Running Man, The (1987)\",\n    \"id\": 3698,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 730\n  },\n  {\n    \"title\": \"It's a Wonderful Life (1946)\",\n    \"id\": 953,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 729\n  },\n  {\n    \"title\": \"Deliverance (1972)\",\n    \"id\": 2871,\n    \"genres\": [\n      \"Adventure\",\n      \"Thriller\"\n    ],\n    \"count\": 729\n  },\n  {\n    \"title\": \"Jackie Brown (1997)\",\n    \"id\": 1729,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 724\n  },\n  {\n    \"title\": \"Go (1999)\",\n    \"id\": 2580,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 723\n  },\n  {\n    \"title\": \"Hook (1991)\",\n    \"id\": 3489,\n    \"genres\": [\n      \"Adventure\",\n      \"Fantasy\"\n    ],\n    \"count\": 722\n  },\n  {\n    \"title\": \"Mad Max Beyond Thunderdome (1985)\",\n    \"id\": 3704,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 722\n  },\n  {\n    \"title\": \"Searching for Bobby Fischer (1993)\",\n    \"id\": 529,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 720\n  },\n  {\n    \"title\": \"Donnie Brasco (1997)\",\n    \"id\": 1466,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 719\n  },\n  {\n    \"title\": \"Sixteen Candles (1984)\",\n    \"id\": 2144,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 719\n  },\n  {\n    \"title\": \"Manhattan (1979)\",\n    \"id\": 1244,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 718\n  },\n  {\n    \"title\": \"Hoop Dreams (1994)\",\n    \"id\": 246,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 716\n  },\n  {\n    \"title\": \"Goonies, The (1985)\",\n    \"id\": 2005,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Fantasy\"\n    ],\n    \"count\": 716\n  },\n  {\n    \"title\": \"Night of the Living Dead (1968)\",\n    \"id\": 968,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 715\n  },\n  {\n    \"title\": \"Addams Family, The (1991)\",\n    \"id\": 2124,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 714\n  },\n  {\n    \"title\": \"Rosemary's Baby (1968)\",\n    \"id\": 2160,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 712\n  },\n  {\n    \"title\": \"Cider House Rules, The (1999)\",\n    \"id\": 3148,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 711\n  },\n  {\n    \"title\": \"Dark Crystal, The (1982)\",\n    \"id\": 2140,\n    \"genres\": [\n      \"Children's\",\n      \"Fantasy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 710\n  },\n  {\n    \"title\": \"Dirty Dozen, The (1967)\",\n    \"id\": 2944,\n    \"genres\": [\n      \"Action\",\n      \"War\"\n    ],\n    \"count\": 707\n  },\n  {\n    \"title\": \"Legends of the Fall (1994)\",\n    \"id\": 266,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\",\n      \"War\",\n      \"Western\"\n    ],\n    \"count\": 705\n  },\n  {\n    \"title\": \"Quiz Show (1994)\",\n    \"id\": 300,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 705\n  },\n  {\n    \"title\": \"Ben-Hur (1959)\",\n    \"id\": 1287,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Drama\"\n    ],\n    \"count\": 704\n  },\n  {\n    \"title\": \"Waking Ned Devine (1998)\",\n    \"id\": 2359,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 703\n  },\n  {\n    \"title\": \"Jumanji (1995)\",\n    \"id\": 2,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Fantasy\"\n    ],\n    \"count\": 701\n  },\n  {\n    \"title\": \"F/X (1986)\",\n    \"id\": 3763,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 701\n  },\n  {\n    \"title\": \"Natural Born Killers (1994)\",\n    \"id\": 288,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 700\n  },\n  {\n    \"title\": \"Deer Hunter, The (1978)\",\n    \"id\": 1263,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 700\n  },\n  {\n    \"title\": \"10 Things I Hate About You (1999)\",\n    \"id\": 2572,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 700\n  },\n  {\n    \"title\": \"NeverEnding Story, The (1984)\",\n    \"id\": 2161,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Fantasy\"\n    ],\n    \"count\": 699\n  },\n  {\n    \"title\": \"Harold and Maude (1971)\",\n    \"id\": 1235,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 697\n  },\n  {\n    \"title\": \"Great Escape, The (1963)\",\n    \"id\": 1262,\n    \"genres\": [\n      \"Adventure\",\n      \"War\"\n    ],\n    \"count\": 696\n  },\n  {\n    \"title\": \"Gods Must Be Crazy, The (1980)\",\n    \"id\": 2150,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 695\n  },\n  {\n    \"title\": \"What's Eating Gilbert Grape (1993)\",\n    \"id\": 337,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 693\n  },\n  {\n    \"title\": \"Pretty in Pink (1986)\",\n    \"id\": 2145,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 693\n  },\n  {\n    \"title\": \"Naked Gun: From the Files of Police Squad!, The (1988)\",\n    \"id\": 3868,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 692\n  },\n  {\n    \"title\": \"General's Daughter, The (1999)\",\n    \"id\": 2688,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 691\n  },\n  {\n    \"title\": \"Network (1976)\",\n    \"id\": 3504,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 688\n  },\n  {\n    \"title\": \"Dirty Dancing (1987)\",\n    \"id\": 1088,\n    \"genres\": [\n      \"Musical\",\n      \"Romance\"\n    ],\n    \"count\": 687\n  },\n  {\n    \"title\": \"Cape Fear (1991)\",\n    \"id\": 1343,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 687\n  },\n  {\n    \"title\": \"Cliffhanger (1993)\",\n    \"id\": 434,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Crime\"\n    ],\n    \"count\": 686\n  },\n  {\n    \"title\": \"Bonnie and Clyde (1967)\",\n    \"id\": 1084,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 686\n  },\n  {\n    \"title\": \"Casino (1995)\",\n    \"id\": 16,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 682\n  },\n  {\n    \"title\": \"Happy Gilmore (1996)\",\n    \"id\": 104,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 682\n  },\n  {\n    \"title\": \"Birdcage, The (1996)\",\n    \"id\": 141,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 682\n  },\n  {\n    \"title\": \"So I Married an Axe Murderer (1993)\",\n    \"id\": 543,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 682\n  },\n  {\n    \"title\": \"Doors, The (1991)\",\n    \"id\": 1093,\n    \"genres\": [\n      \"Drama\",\n      \"Musical\"\n    ],\n    \"count\": 680\n  },\n  {\n    \"title\": \"Awakenings (1990)\",\n    \"id\": 3105,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 680\n  },\n  {\n    \"title\": \"Truth About Cats & Dogs, The (1996)\",\n    \"id\": 708,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 679\n  },\n  {\n    \"title\": \"Raging Bull (1980)\",\n    \"id\": 1228,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 677\n  },\n  {\n    \"title\": \"Breakfast at Tiffany's (1961)\",\n    \"id\": 902,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 676\n  },\n  {\n    \"title\": \"Strictly Ballroom (1992)\",\n    \"id\": 1188,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 676\n  },\n  {\n    \"title\": \"Home Alone (1990)\",\n    \"id\": 586,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 675\n  },\n  {\n    \"title\": \"Iron Giant, The (1999)\",\n    \"id\": 2761,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 674\n  },\n  {\n    \"title\": \"While You Were Sleeping (1995)\",\n    \"id\": 339,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 673\n  },\n  {\n    \"title\": \"Jewel of the Nile, The (1985)\",\n    \"id\": 2405,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 673\n  },\n  {\n    \"title\": \"Arsenic and Old Lace (1944)\",\n    \"id\": 1269,\n    \"genres\": [\n      \"Comedy\",\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 672\n  },\n  {\n    \"title\": \"Bram Stoker's Dracula (1992)\",\n    \"id\": 1339,\n    \"genres\": [\n      \"Horror\",\n      \"Romance\"\n    ],\n    \"count\": 672\n  },\n  {\n    \"title\": \"Gandhi (1982)\",\n    \"id\": 1293,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 671\n  },\n  {\n    \"title\": \"Lethal Weapon 4 (1998)\",\n    \"id\": 1918,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 669\n  },\n  {\n    \"title\": \"Much Ado About Nothing (1993)\",\n    \"id\": 497,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 667\n  },\n  {\n    \"title\": \"Lost in Space (1998)\",\n    \"id\": 1831,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 667\n  },\n  {\n    \"title\": \"White Men Can't Jump (1992)\",\n    \"id\": 3263,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 667\n  },\n  {\n    \"title\": \"Jacob's Ladder (1990)\",\n    \"id\": 3476,\n    \"genres\": [\n      \"Horror\",\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 667\n  },\n  {\n    \"title\": \"Liar Liar (1997)\",\n    \"id\": 1485,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 666\n  },\n  {\n    \"title\": \"Sister Act (1992)\",\n    \"id\": 3247,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\"\n    ],\n    \"count\": 666\n  },\n  {\n    \"title\": \"Bulworth (1998)\",\n    \"id\": 1883,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 664\n  },\n  {\n    \"title\": \"Jungle Book, The (1967)\",\n    \"id\": 2078,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 664\n  },\n  {\n    \"title\": \"Midnight Cowboy (1969)\",\n    \"id\": 1952,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 663\n  },\n  {\n    \"title\": \"Dumb & Dumber (1994)\",\n    \"id\": 231,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 660\n  },\n  {\n    \"title\": \"True Romance (1993)\",\n    \"id\": 555,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Romance\"\n    ],\n    \"count\": 658\n  },\n  {\n    \"title\": \"Producers, The (1968)\",\n    \"id\": 2300,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 658\n  },\n  {\n    \"title\": \"Cell, The (2000)\",\n    \"id\": 3863,\n    \"genres\": [\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 658\n  },\n  {\n    \"title\": \"Close Shave, A (1995)\",\n    \"id\": 745,\n    \"genres\": [\n      \"Animation\",\n      \"Comedy\",\n      \"Thriller\"\n    ],\n    \"count\": 657\n  },\n  {\n    \"title\": \"Best in Show (2000)\",\n    \"id\": 3911,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 657\n  },\n  {\n    \"title\": \"Blue Velvet (1986)\",\n    \"id\": 2076,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 654\n  },\n  {\n    \"title\": \"Driving Miss Daisy (1989)\",\n    \"id\": 1962,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 652\n  },\n  {\n    \"title\": \"Moonraker (1979)\",\n    \"id\": 3638,\n    \"genres\": [\n      \"Action\",\n      \"Romance\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 652\n  },\n  {\n    \"title\": \"Waterworld (1995)\",\n    \"id\": 208,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 651\n  },\n  {\n    \"title\": \"Runaway Bride (1999)\",\n    \"id\": 2724,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 650\n  },\n  {\n    \"title\": \"Dr. No (1962)\",\n    \"id\": 2949,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 646\n  },\n  {\n    \"title\": \"Patton (1970)\",\n    \"id\": 1272,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 645\n  },\n  {\n    \"title\": \"Antz (1998)\",\n    \"id\": 2294,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 645\n  },\n  {\n    \"title\": \"Dazed and Confused (1993)\",\n    \"id\": 441,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 643\n  },\n  {\n    \"title\": \"Predator 2 (1990)\",\n    \"id\": 3697,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 643\n  },\n  {\n    \"title\": \"Santa Clause, The (1994)\",\n    \"id\": 317,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\",\n      \"Fantasy\"\n    ],\n    \"count\": 641\n  },\n  {\n    \"title\": \"Demolition Man (1993)\",\n    \"id\": 442,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 640\n  },\n  {\n    \"title\": \"American History X (1998)\",\n    \"id\": 2329,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 640\n  },\n  {\n    \"title\": \"Bone Collector, The (1999)\",\n    \"id\": 3005,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 640\n  },\n  {\n    \"title\": \"Broken Arrow (1996)\",\n    \"id\": 95,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 638\n  },\n  {\n    \"title\": \"My Fair Lady (1964)\",\n    \"id\": 914,\n    \"genres\": [\n      \"Musical\",\n      \"Romance\"\n    ],\n    \"count\": 636\n  },\n  {\n    \"title\": \"Last Emperor, The (1987)\",\n    \"id\": 1960,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 636\n  },\n  {\n    \"title\": \"Westworld (1973)\",\n    \"id\": 2527,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\",\n      \"Thriller\",\n      \"Western\"\n    ],\n    \"count\": 636\n  },\n  {\n    \"title\": \"Chariots of Fire (1981)\",\n    \"id\": 1957,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 634\n  },\n  {\n    \"title\": \"Arlington Road (1999)\",\n    \"id\": 2707,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 634\n  },\n  {\n    \"title\": \"Lone Star (1996)\",\n    \"id\": 800,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 630\n  },\n  {\n    \"title\": \"American Psycho (2000)\",\n    \"id\": 3535,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 630\n  },\n  {\n    \"title\": \"Midnight in the Garden of Good and Evil (1997)\",\n    \"id\": 1711,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\",\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 628\n  },\n  {\n    \"title\": \"Seven Samurai (The Magnificent Seven) (Shichinin no samurai) (1954)\",\n    \"id\": 2019,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 628\n  },\n  {\n    \"title\": \"Invasion of the Body Snatchers (1956)\",\n    \"id\": 2664,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 628\n  },\n  {\n    \"title\": \"Mr. Mom (1983)\",\n    \"id\": 3591,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 628\n  },\n  {\n    \"title\": \"Blood Simple (1984)\",\n    \"id\": 3683,\n    \"genres\": [\n      \"Drama\",\n      \"Film-Noir\"\n    ],\n    \"count\": 628\n  },\n  {\n    \"title\": \"Rush Hour (1998)\",\n    \"id\": 2273,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 627\n  },\n  {\n    \"title\": \"Wild Things (1998)\",\n    \"id\": 1805,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 626\n  },\n  {\n    \"title\": \"Out of Africa (1985)\",\n    \"id\": 1959,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 626\n  },\n  {\n    \"title\": \"Logan's Run (1976)\",\n    \"id\": 2528,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 626\n  },\n  {\n    \"title\": \"Bodyguard, The (1992)\",\n    \"id\": 3257,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 626\n  },\n  {\n    \"title\": \"Lock, Stock & Two Smoking Barrels (1998)\",\n    \"id\": 2542,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 625\n  },\n  {\n    \"title\": \"Powder (1995)\",\n    \"id\": 24,\n    \"genres\": [\n      \"Drama\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 624\n  },\n  {\n    \"title\": \"Never Been Kissed (1999)\",\n    \"id\": 2581,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 624\n  },\n  {\n    \"title\": \"Dick Tracy (1990)\",\n    \"id\": 2616,\n    \"genres\": [\n      \"Action\",\n      \"Crime\"\n    ],\n    \"count\": 624\n  },\n  {\n    \"title\": \"River Runs Through It, A (1992)\",\n    \"id\": 3100,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 623\n  },\n  {\n    \"title\": \"Poseidon Adventure, The (1972)\",\n    \"id\": 2013,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 622\n  },\n  {\n    \"title\": \"Heavy Metal (1981)\",\n    \"id\": 610,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Animation\",\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 618\n  },\n  {\n    \"title\": \"12 Angry Men (1957)\",\n    \"id\": 1203,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 616\n  },\n  {\n    \"title\": \"Analyze This (1999)\",\n    \"id\": 2539,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 616\n  },\n  {\n    \"title\": \"Tin Cup (1996)\",\n    \"id\": 852,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 615\n  },\n  {\n    \"title\": \"Lawnmower Man, The (1992)\",\n    \"id\": 1037,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 615\n  },\n  {\n    \"title\": \"Cinema Paradiso (1988)\",\n    \"id\": 1172,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 615\n  },\n  {\n    \"title\": \"Star Trek V: The Final Frontier (1989)\",\n    \"id\": 1373,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 613\n  },\n  {\n    \"title\": \"Pi (1998)\",\n    \"id\": 1921,\n    \"genres\": [\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 613\n  },\n  {\n    \"title\": \"Scary Movie (2000)\",\n    \"id\": 3785,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 613\n  },\n  {\n    \"title\": \"Dragonheart (1996)\",\n    \"id\": 653,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Fantasy\"\n    ],\n    \"count\": 612\n  },\n  {\n    \"title\": \"Civil Action, A (1998)\",\n    \"id\": 2433,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 612\n  },\n  {\n    \"title\": \"Adventures of Buckaroo Bonzai Across the 8th Dimension, The (1984)\",\n    \"id\": 3070,\n    \"genres\": [\n      \"Adventure\",\n      \"Comedy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 611\n  },\n  {\n    \"title\": \"Piano, The (1993)\",\n    \"id\": 509,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 608\n  },\n  {\n    \"title\": \"Opposite of Sex, The (1998)\",\n    \"id\": 1885,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 608\n  },\n  {\n    \"title\": \"Batman & Robin (1997)\",\n    \"id\": 1562,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Crime\"\n    ],\n    \"count\": 606\n  },\n  {\n    \"title\": \"Strange Days (1995)\",\n    \"id\": 198,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 605\n  },\n  {\n    \"title\": \"Whole Nine Yards, The (2000)\",\n    \"id\": 3301,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\"\n    ],\n    \"count\": 602\n  },\n  {\n    \"title\": \"Hudsucker Proxy, The (1994)\",\n    \"id\": 471,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 599\n  },\n  {\n    \"title\": \"Fletch (1985)\",\n    \"id\": 2371,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 599\n  },\n  {\n    \"title\": \"Deep Blue Sea (1999)\",\n    \"id\": 2722,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 598\n  },\n  {\n    \"title\": \"Duck Soup (1933)\",\n    \"id\": 1256,\n    \"genres\": [\n      \"Comedy\",\n      \"War\"\n    ],\n    \"count\": 594\n  },\n  {\n    \"title\": \"Peter Pan (1953)\",\n    \"id\": 2087,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Fantasy\",\n      \"Musical\"\n    ],\n    \"count\": 594\n  },\n  {\n    \"title\": \"Fried Green Tomatoes (1991)\",\n    \"id\": 1271,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 593\n  },\n  {\n    \"title\": \"Being There (1979)\",\n    \"id\": 1292,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 591\n  },\n  {\n    \"title\": \"Little Big Man (1970)\",\n    \"id\": 3037,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Western\"\n    ],\n    \"count\": 591\n  },\n  {\n    \"title\": \"Boiler Room (2000)\",\n    \"id\": 3298,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 590\n  },\n  {\n    \"title\": \"Hoosiers (1986)\",\n    \"id\": 3360,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 590\n  },\n  {\n    \"title\": \"Bambi (1942)\",\n    \"id\": 2018,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 589\n  },\n  {\n    \"title\": \"October Sky (1999)\",\n    \"id\": 2501,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 588\n  },\n  {\n    \"title\": \"Gone in 60 Seconds (2000)\",\n    \"id\": 3717,\n    \"genres\": [\n      \"Action\",\n      \"Crime\"\n    ],\n    \"count\": 584\n  },\n  {\n    \"title\": \"Big Trouble in Little China (1986)\",\n    \"id\": 3740,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 584\n  },\n  {\n    \"title\": \"Red Violin, The (Le Violon rouge) (1998)\",\n    \"id\": 2686,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 583\n  },\n  {\n    \"title\": \"Dead Again (1991)\",\n    \"id\": 3044,\n    \"genres\": [\n      \"Mystery\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 583\n  },\n  {\n    \"title\": \"Philadelphia Story, The (1940)\",\n    \"id\": 898,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 582\n  },\n  {\n    \"title\": \"Prince of Egypt, The (1998)\",\n    \"id\": 2394,\n    \"genres\": [\n      \"Animation\",\n      \"Musical\"\n    ],\n    \"count\": 579\n  },\n  {\n    \"title\": \"Gods and Monsters (1998)\",\n    \"id\": 2333,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 578\n  },\n  {\n    \"title\": \"Cinderella (1950)\",\n    \"id\": 1022,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 577\n  },\n  {\n    \"title\": \"Scream 3 (2000)\",\n    \"id\": 3273,\n    \"genres\": [\n      \"Horror\",\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 577\n  },\n  {\n    \"title\": \"20,000 Leagues Under the Sea (1954)\",\n    \"id\": 1019,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Fantasy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 575\n  },\n  {\n    \"title\": \"Hot Shots! Part Deux (1993)\",\n    \"id\": 466,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"War\"\n    ],\n    \"count\": 573\n  },\n  {\n    \"title\": \"Jerk, The (1979)\",\n    \"id\": 2109,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 573\n  },\n  {\n    \"title\": \"Bound (1996)\",\n    \"id\": 866,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 572\n  },\n  {\n    \"title\": \"Conan the Barbarian (1982)\",\n    \"id\": 1587,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 572\n  },\n  {\n    \"title\": \"Glengarry Glen Ross (1992)\",\n    \"id\": 1095,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 571\n  },\n  {\n    \"title\": \"Rumble in the Bronx (1995)\",\n    \"id\": 112,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Crime\"\n    ],\n    \"count\": 570\n  },\n  {\n    \"title\": \"Net, The (1995)\",\n    \"id\": 185,\n    \"genres\": [\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 569\n  },\n  {\n    \"title\": \"Three Amigos! (1986)\",\n    \"id\": 2478,\n    \"genres\": [\n      \"Comedy\",\n      \"Western\"\n    ],\n    \"count\": 569\n  },\n  {\n    \"title\": \"Dumbo (1941)\",\n    \"id\": 1029,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 568\n  },\n  {\n    \"title\": \"Carrie (1976)\",\n    \"id\": 1345,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 567\n  },\n  {\n    \"title\": \"Congo (1995)\",\n    \"id\": 160,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Mystery\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 565\n  },\n  {\n    \"title\": \"101 Dalmatians (1961)\",\n    \"id\": 2085,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 565\n  },\n  {\n    \"title\": \"War of the Worlds, The (1953)\",\n    \"id\": 2662,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\",\n      \"War\"\n    ],\n    \"count\": 565\n  },\n  {\n    \"title\": \"Ghostbusters II (1989)\",\n    \"id\": 2717,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 565\n  },\n  {\n    \"title\": \"Judge Dredd (1995)\",\n    \"id\": 173,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 564\n  },\n  {\n    \"title\": \"Ransom (1996)\",\n    \"id\": 832,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 564\n  },\n  {\n    \"title\": \"Doctor Zhivago (1965)\",\n    \"id\": 2067,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\",\n      \"War\"\n    ],\n    \"count\": 564\n  },\n  {\n    \"title\": \"End of Days (1999)\",\n    \"id\": 3113,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 564\n  },\n  {\n    \"title\": \"Space Jam (1996)\",\n    \"id\": 673,\n    \"genres\": [\n      \"Adventure\",\n      \"Animation\",\n      \"Children's\",\n      \"Comedy\",\n      \"Fantasy\"\n    ],\n    \"count\": 563\n  },\n  {\n    \"title\": \"Saint, The (1997)\",\n    \"id\": 1479,\n    \"genres\": [\n      \"Action\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 563\n  },\n  {\n    \"title\": \"Sex, Lies, and Videotape (1989)\",\n    \"id\": 1186,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 562\n  },\n  {\n    \"title\": \"Young Guns (1988)\",\n    \"id\": 1378,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Western\"\n    ],\n    \"count\": 562\n  },\n  {\n    \"title\": \"Philadelphia (1993)\",\n    \"id\": 508,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 561\n  },\n  {\n    \"title\": \"Room with a View, A (1986)\",\n    \"id\": 1296,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 560\n  },\n  {\n    \"title\": \"Me, Myself and Irene (2000)\",\n    \"id\": 3752,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 560\n  },\n  {\n    \"title\": \"Day the Earth Stood Still, The (1951)\",\n    \"id\": 1253,\n    \"genres\": [\n      \"Drama\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 559\n  },\n  {\n    \"title\": \"Cruel Intentions (1999)\",\n    \"id\": 2541,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 559\n  },\n  {\n    \"title\": \"Nurse Betty (2000)\",\n    \"id\": 3893,\n    \"genres\": [\n      \"Comedy\",\n      \"Thriller\"\n    ],\n    \"count\": 559\n  },\n  {\n    \"title\": \"Airplane II: The Sequel (1982)\",\n    \"id\": 2792,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 558\n  },\n  {\n    \"title\": \"Commitments, The (1991)\",\n    \"id\": 3060,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 558\n  },\n  {\n    \"title\": \"G.I. Jane (1997)\",\n    \"id\": 1586,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 557\n  },\n  {\n    \"title\": \"Bringing Out the Dead (1999)\",\n    \"id\": 2976,\n    \"genres\": [\n      \"Drama\",\n      \"Horror\"\n    ],\n    \"count\": 557\n  },\n  {\n    \"title\": \"Scent of a Woman (1992)\",\n    \"id\": 3252,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 555\n  },\n  {\n    \"title\": \"Rob Roy (1995)\",\n    \"id\": 151,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\",\n      \"War\"\n    ],\n    \"count\": 554\n  },\n  {\n    \"title\": \"Labyrinth (1986)\",\n    \"id\": 1967,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Fantasy\"\n    ],\n    \"count\": 554\n  },\n  {\n    \"title\": \"Tombstone (1993)\",\n    \"id\": 553,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 553\n  },\n  {\n    \"title\": \"Double Indemnity (1944)\",\n    \"id\": 3435,\n    \"genres\": [\n      \"Crime\",\n      \"Film-Noir\"\n    ],\n    \"count\": 551\n  },\n  {\n    \"title\": \"Species (1995)\",\n    \"id\": 196,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 550\n  },\n  {\n    \"title\": \"American Werewolf in London, An (1981)\",\n    \"id\": 1321,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 549\n  },\n  {\n    \"title\": \"Vacation (1983)\",\n    \"id\": 2795,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 549\n  },\n  {\n    \"title\": \"Evil Dead II (Dead By Dawn) (1987)\",\n    \"id\": 1261,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 548\n  },\n  {\n    \"title\": \"Shine (1996)\",\n    \"id\": 1357,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 548\n  },\n  {\n    \"title\": \"Natural, The (1984)\",\n    \"id\": 3098,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 548\n  },\n  {\n    \"title\": \"Prizzi's Honor (1985)\",\n    \"id\": 3685,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 545\n  },\n  {\n    \"title\": \"To Die For (1995)\",\n    \"id\": 45,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 544\n  },\n  {\n    \"title\": \"Pitch Black (2000)\",\n    \"id\": 3300,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 542\n  },\n  {\n    \"title\": \"Ladyhawke (1985)\",\n    \"id\": 3479,\n    \"genres\": [\n      \"Adventure\",\n      \"Fantasy\",\n      \"Romance\"\n    ],\n    \"count\": 542\n  },\n  {\n    \"title\": \"Honeymoon in Vegas (1992)\",\n    \"id\": 3614,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 542\n  },\n  {\n    \"title\": \"Mr. Holland's Opus (1995)\",\n    \"id\": 62,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 541\n  },\n  {\n    \"title\": \"Big Sleep, The (1946)\",\n    \"id\": 1284,\n    \"genres\": [\n      \"Film-Noir\",\n      \"Mystery\"\n    ],\n    \"count\": 541\n  },\n  {\n    \"title\": \"Sphere (1998)\",\n    \"id\": 1779,\n    \"genres\": [\n      \"Adventure\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 541\n  },\n  {\n    \"title\": \"Adventures in Babysitting (1987)\",\n    \"id\": 2133,\n    \"genres\": [\n      \"Adventure\",\n      \"Comedy\"\n    ],\n    \"count\": 541\n  },\n  {\n    \"title\": \"Desperado (1995)\",\n    \"id\": 163,\n    \"genres\": [\n      \"Action\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 540\n  },\n  {\n    \"title\": \"Flight of the Navigator (1986)\",\n    \"id\": 2046,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 536\n  },\n  {\n    \"title\": \"Titan A.E. (2000)\",\n    \"id\": 3745,\n    \"genres\": [\n      \"Adventure\",\n      \"Animation\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 535\n  },\n  {\n    \"title\": \"Teenage Mutant Ninja Turtles (1990)\",\n    \"id\": 3438,\n    \"genres\": [\n      \"Action\",\n      \"Children's\",\n      \"Fantasy\"\n    ],\n    \"count\": 534\n  },\n  {\n    \"title\": \"Arthur (1981)\",\n    \"id\": 3524,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 534\n  },\n  {\n    \"title\": \"Coneheads (1993)\",\n    \"id\": 435,\n    \"genres\": [\n      \"Comedy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 533\n  },\n  {\n    \"title\": \"Pushing Tin (1999)\",\n    \"id\": 2598,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 533\n  },\n  {\n    \"title\": \"Terms of Endearment (1983)\",\n    \"id\": 1958,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 530\n  },\n  {\n    \"title\": \"Three Musketeers, The (1993)\",\n    \"id\": 552,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Comedy\"\n    ],\n    \"count\": 529\n  },\n  {\n    \"title\": \"That Thing You Do! (1996)\",\n    \"id\": 1042,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 528\n  },\n  {\n    \"title\": \"Doctor Dolittle (1998)\",\n    \"id\": 1911,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 527\n  },\n  {\n    \"title\": \"James and the Giant Peach (1996)\",\n    \"id\": 661,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 525\n  },\n  {\n    \"title\": \"Alice in Wonderland (1951)\",\n    \"id\": 1032,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 525\n  },\n  {\n    \"title\": \"Falling Down (1993)\",\n    \"id\": 3020,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 524\n  },\n  {\n    \"title\": \"Babe: Pig in the City (1998)\",\n    \"id\": 2384,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 523\n  },\n  {\n    \"title\": \"Fistful of Dollars, A (1964)\",\n    \"id\": 2951,\n    \"genres\": [\n      \"Action\",\n      \"Western\"\n    ],\n    \"count\": 522\n  },\n  {\n    \"title\": \"Grapes of Wrath, The (1940)\",\n    \"id\": 3095,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 521\n  },\n  {\n    \"title\": \"Stuart Little (1999)\",\n    \"id\": 3157,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 520\n  },\n  {\n    \"title\": \"Breaking Away (1979)\",\n    \"id\": 3359,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 520\n  },\n  {\n    \"title\": \"On the Waterfront (1954)\",\n    \"id\": 1945,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 519\n  },\n  {\n    \"title\": \"Muriel's Wedding (1994)\",\n    \"id\": 342,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 518\n  },\n  {\n    \"title\": \"Hard Day's Night, A (1964)\",\n    \"id\": 2863,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 518\n  },\n  {\n    \"title\": \"Scrooged (1988)\",\n    \"id\": 3087,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 516\n  },\n  {\n    \"title\": \"Client, The (1994)\",\n    \"id\": 350,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 515\n  },\n  {\n    \"title\": \"Christmas Vacation (1989)\",\n    \"id\": 2423,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 515\n  },\n  {\n    \"title\": \"Touch of Evil (1958)\",\n    \"id\": 1248,\n    \"genres\": [\n      \"Crime\",\n      \"Film-Noir\",\n      \"Thriller\"\n    ],\n    \"count\": 514\n  },\n  {\n    \"title\": \"Working Girl (1988)\",\n    \"id\": 2245,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 514\n  },\n  {\n    \"title\": \"Faculty, The (1998)\",\n    \"id\": 2428,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 513\n  },\n  {\n    \"title\": \"Buffy the Vampire Slayer (1992)\",\n    \"id\": 3264,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 512\n  },\n  {\n    \"title\": \"Escape from L.A. (1996)\",\n    \"id\": 849,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 511\n  },\n  {\n    \"title\": \"Sleeping Beauty (1959)\",\n    \"id\": 2096,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 511\n  },\n  {\n    \"title\": \"Superman III (1983)\",\n    \"id\": 2642,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 511\n  },\n  {\n    \"title\": \"Weird Science (1985)\",\n    \"id\": 2134,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 510\n  },\n  {\n    \"title\": \"Hurricane, The (1999)\",\n    \"id\": 3178,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 509\n  },\n  {\n    \"title\": \"Ice Storm, The (1997)\",\n    \"id\": 1635,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 507\n  },\n  {\n    \"title\": \"Ordinary People (1980)\",\n    \"id\": 1956,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 507\n  },\n  {\n    \"title\": \"Halloween (1978)\",\n    \"id\": 1982,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 506\n  },\n  {\n    \"title\": \"Peggy Sue Got Married (1986)\",\n    \"id\": 2469,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 506\n  },\n  {\n    \"title\": \"28 Days (2000)\",\n    \"id\": 3534,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 505\n  },\n  {\n    \"title\": \"Body Heat (1981)\",\n    \"id\": 2917,\n    \"genres\": [\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 504\n  },\n  {\n    \"title\": \"Alien Nation (1988)\",\n    \"id\": 3701,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 504\n  },\n  {\n    \"title\": \"Guns of Navarone, The (1961)\",\n    \"id\": 3654,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 502\n  },\n  {\n    \"title\": \"Postino, Il (The Postman) (1994)\",\n    \"id\": 58,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 501\n  },\n  {\n    \"title\": \"Emma (1996)\",\n    \"id\": 838,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 501\n  },\n  {\n    \"title\": \"Last Action Hero (1993)\",\n    \"id\": 485,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 500\n  },\n  {\n    \"title\": \"Private Parts (1997)\",\n    \"id\": 1476,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 499\n  },\n  {\n    \"title\": \"Godzilla (1998)\",\n    \"id\": 1882,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 499\n  },\n  {\n    \"title\": \"Pinocchio (1940)\",\n    \"id\": 596,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 498\n  },\n  {\n    \"title\": \"Event Horizon (1997)\",\n    \"id\": 1590,\n    \"genres\": [\n      \"Action\",\n      \"Mystery\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 498\n  },\n  {\n    \"title\": \"Charlotte's Web (1973)\",\n    \"id\": 2137,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 497\n  },\n  {\n    \"title\": \"Dead Calm (1989)\",\n    \"id\": 3203,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 497\n  },\n  {\n    \"title\": \"Wonder Boys (2000)\",\n    \"id\": 3317,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 496\n  },\n  {\n    \"title\": \"Snake Eyes (1998)\",\n    \"id\": 2126,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 495\n  },\n  {\n    \"title\": \"Serpico (1973)\",\n    \"id\": 3735,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 495\n  },\n  {\n    \"title\": \"Spanish Prisoner, The (1997)\",\n    \"id\": 1834,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 493\n  },\n  {\n    \"title\": \"Karate Kid, The (1984)\",\n    \"id\": 2420,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 493\n  },\n  {\n    \"title\": \"Robocop 2 (1990)\",\n    \"id\": 2986,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 493\n  },\n  {\n    \"title\": \"Cop Land (1997)\",\n    \"id\": 1589,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 492\n  },\n  {\n    \"title\": \"Crumb (1994)\",\n    \"id\": 162,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 491\n  },\n  {\n    \"title\": \"Mulan (1998)\",\n    \"id\": 1907,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 490\n  },\n  {\n    \"title\": \"Keeping the Faith (2000)\",\n    \"id\": 3536,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 489\n  },\n  {\n    \"title\": \"Stir of Echoes (1999)\",\n    \"id\": 2841,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 487\n  },\n  {\n    \"title\": \"Spy Who Loved Me, The (1977)\",\n    \"id\": 3635,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 487\n  },\n  {\n    \"title\": \"Primal Fear (1996)\",\n    \"id\": 628,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 485\n  },\n  {\n    \"title\": \"Easy Rider (1969)\",\n    \"id\": 3168,\n    \"genres\": [\n      \"Adventure\",\n      \"Drama\"\n    ],\n    \"count\": 485\n  },\n  {\n    \"title\": \"Streetcar Named Desire, A (1951)\",\n    \"id\": 1104,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 483\n  },\n  {\n    \"title\": \"Any Given Sunday (1999)\",\n    \"id\": 3173,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 483\n  },\n  {\n    \"title\": \"Mariachi, El (1992)\",\n    \"id\": 3267,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 483\n  },\n  {\n    \"title\": \"Strangers on a Train (1951)\",\n    \"id\": 2186,\n    \"genres\": [\n      \"Film-Noir\",\n      \"Thriller\"\n    ],\n    \"count\": 482\n  },\n  {\n    \"title\": \"Benny & Joon (1993)\",\n    \"id\": 1441,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 481\n  },\n  {\n    \"title\": \"Third Man, The (1949)\",\n    \"id\": 1212,\n    \"genres\": [\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 480\n  },\n  {\n    \"title\": \"Fly, The (1958)\",\n    \"id\": 2454,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 480\n  },\n  {\n    \"title\": \"Howards End (1992)\",\n    \"id\": 3260,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 480\n  },\n  {\n    \"title\": \"William Shakespeare's Romeo and Juliet (1996)\",\n    \"id\": 1059,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 479\n  },\n  {\n    \"title\": \"Purple Rose of Cairo, The (1985)\",\n    \"id\": 2065,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 479\n  },\n  {\n    \"title\": \"Grumpier Old Men (1995)\",\n    \"id\": 3,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 478\n  },\n  {\n    \"title\": \"Welcome to the Dollhouse (1995)\",\n    \"id\": 562,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 478\n  },\n  {\n    \"title\": \"Space Cowboys (2000)\",\n    \"id\": 3827,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 478\n  },\n  {\n    \"title\": \"Heavenly Creatures (1994)\",\n    \"id\": 247,\n    \"genres\": [\n      \"Drama\",\n      \"Fantasy\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 477\n  },\n  {\n    \"title\": \"Karate Kid, Part II, The (1986)\",\n    \"id\": 2421,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Drama\"\n    ],\n    \"count\": 477\n  },\n  {\n    \"title\": \"Forever Young (1992)\",\n    \"id\": 3269,\n    \"genres\": [\n      \"Adventure\",\n      \"Romance\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 477\n  },\n  {\n    \"title\": \"Rudy (1993)\",\n    \"id\": 524,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 476\n  },\n  {\n    \"title\": \"Haunting, The (1999)\",\n    \"id\": 2719,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 476\n  },\n  {\n    \"title\": \"Beavis and Butt-head Do America (1996)\",\n    \"id\": 1405,\n    \"genres\": [\n      \"Animation\",\n      \"Comedy\"\n    ],\n    \"count\": 475\n  },\n  {\n    \"title\": \"Spawn (1997)\",\n    \"id\": 1591,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 475\n  },\n  {\n    \"title\": \"I Know What You Did Last Summer (1997)\",\n    \"id\": 1644,\n    \"genres\": [\n      \"Horror\",\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 475\n  },\n  {\n    \"title\": \"Ruthless People (1986)\",\n    \"id\": 2463,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 475\n  },\n  {\n    \"title\": \"Malcolm X (1992)\",\n    \"id\": 3246,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 475\n  },\n  {\n    \"title\": \"Red Dawn (1984)\",\n    \"id\": 3441,\n    \"genres\": [\n      \"Action\",\n      \"War\"\n    ],\n    \"count\": 475\n  },\n  {\n    \"title\": \"Secrets & Lies (1996)\",\n    \"id\": 1041,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 474\n  },\n  {\n    \"title\": \"Joy Luck Club, The (1993)\",\n    \"id\": 1678,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 474\n  },\n  {\n    \"title\": \"Patch Adams (1998)\",\n    \"id\": 2431,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 474\n  },\n  {\n    \"title\": \"Limey, The (1999)\",\n    \"id\": 2912,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 474\n  },\n  {\n    \"title\": \"Grand Day Out, A (1992)\",\n    \"id\": 1223,\n    \"genres\": [\n      \"Animation\",\n      \"Comedy\"\n    ],\n    \"count\": 473\n  },\n  {\n    \"title\": \"Better Off Dead... (1985)\",\n    \"id\": 1257,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 472\n  },\n  {\n    \"title\": \"Apostle, The (1997)\",\n    \"id\": 1694,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 471\n  },\n  {\n    \"title\": \"Six Days Seven Nights (1998)\",\n    \"id\": 1894,\n    \"genres\": [\n      \"Adventure\",\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 471\n  },\n  {\n    \"title\": \"Popeye (1980)\",\n    \"id\": 2088,\n    \"genres\": [\n      \"Adventure\",\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 471\n  },\n  {\n    \"title\": \"Sunset Blvd. (a.k.a. Sunset Boulevard) (1950)\",\n    \"id\": 922,\n    \"genres\": [\n      \"Film-Noir\"\n    ],\n    \"count\": 470\n  },\n  {\n    \"title\": \"2010 (1984)\",\n    \"id\": 2311,\n    \"genres\": [\n      \"Mystery\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 470\n  },\n  {\n    \"title\": \"Adventures of Priscilla, Queen of the Desert, The (1994)\",\n    \"id\": 345,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 469\n  },\n  {\n    \"title\": \"Hercules (1997)\",\n    \"id\": 1566,\n    \"genres\": [\n      \"Adventure\",\n      \"Animation\",\n      \"Children's\",\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 469\n  },\n  {\n    \"title\": \"Mosquito Coast, The (1986)\",\n    \"id\": 2734,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 466\n  },\n  {\n    \"title\": \"Sliding Doors (1998)\",\n    \"id\": 1680,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 464\n  },\n  {\n    \"title\": \"Addams Family Values (1993)\",\n    \"id\": 410,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 463\n  },\n  {\n    \"title\": \"Escape from the Planet of the Apes (1971)\",\n    \"id\": 2533,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 463\n  },\n  {\n    \"title\": \"What Lies Beneath (2000)\",\n    \"id\": 3798,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 463\n  },\n  {\n    \"title\": \"Waiting for Guffman (1996)\",\n    \"id\": 1449,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 462\n  },\n  {\n    \"title\": \"Under Siege (1992)\",\n    \"id\": 1385,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 461\n  },\n  {\n    \"title\": \"Key Largo (1948)\",\n    \"id\": 3334,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Film-Noir\",\n      \"Thriller\"\n    ],\n    \"count\": 461\n  },\n  {\n    \"title\": \"Man with the Golden Gun, The (1974)\",\n    \"id\": 3639,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 461\n  },\n  {\n    \"title\": \"Eraser (1996)\",\n    \"id\": 786,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 460\n  },\n  {\n    \"title\": \"Crimes and Misdemeanors (1989)\",\n    \"id\": 2973,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 460\n  },\n  {\n    \"title\": \"Golden Child, The (1986)\",\n    \"id\": 2735,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Comedy\"\n    ],\n    \"count\": 459\n  },\n  {\n    \"title\": \"Final Destination (2000)\",\n    \"id\": 3409,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 459\n  },\n  {\n    \"title\": \"Sabrina (1995)\",\n    \"id\": 7,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 458\n  },\n  {\n    \"title\": \"Johnny Mnemonic (1995)\",\n    \"id\": 172,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 458\n  },\n  {\n    \"title\": \"Elephant Man, The (1980)\",\n    \"id\": 2313,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 457\n  },\n  {\n    \"title\": \"Mystery Science Theater 3000: The Movie (1996)\",\n    \"id\": 671,\n    \"genres\": [\n      \"Comedy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 456\n  },\n  {\n    \"title\": \"People vs. Larry Flynt, The (1996)\",\n    \"id\": 1120,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 456\n  },\n  {\n    \"title\": \"Bullets Over Broadway (1994)\",\n    \"id\": 348,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 455\n  },\n  {\n    \"title\": \"Scream 2 (1997)\",\n    \"id\": 1717,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 455\n  },\n  {\n    \"title\": \"Return to Me (2000)\",\n    \"id\": 3512,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 454\n  },\n  {\n    \"title\": \"My Left Foot (1989)\",\n    \"id\": 1185,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 453\n  },\n  {\n    \"title\": \"Treasure of the Sierra Madre, The (1948)\",\n    \"id\": 1254,\n    \"genres\": [\n      \"Adventure\"\n    ],\n    \"count\": 453\n  },\n  {\n    \"title\": \"Fantasia 2000 (1999)\",\n    \"id\": 3159,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 453\n  },\n  {\n    \"title\": \"Conversation, The (1974)\",\n    \"id\": 3730,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 453\n  },\n  {\n    \"title\": \"Akira (1988)\",\n    \"id\": 1274,\n    \"genres\": [\n      \"Adventure\",\n      \"Animation\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 452\n  },\n  {\n    \"title\": \"Kramer Vs. Kramer (1979)\",\n    \"id\": 1955,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 452\n  },\n  {\n    \"title\": \"Mallrats (1995)\",\n    \"id\": 180,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 451\n  },\n  {\n    \"title\": \"Ghost and the Darkness, The (1996)\",\n    \"id\": 1049,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 451\n  },\n  {\n    \"title\": \"Nightmare on Elm Street, A (1984)\",\n    \"id\": 1347,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 451\n  },\n  {\n    \"title\": \"And Now for Something Completely Different (1971)\",\n    \"id\": 2788,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 451\n  },\n  {\n    \"title\": \"Remains of the Day, The (1993)\",\n    \"id\": 515,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 450\n  },\n  {\n    \"title\": \"Big Night (1996)\",\n    \"id\": 994,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 450\n  },\n  {\n    \"title\": \"Color Purple, The (1985)\",\n    \"id\": 2739,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 450\n  },\n  {\n    \"title\": \"Hustler, The (1961)\",\n    \"id\": 3468,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 450\n  },\n  {\n    \"title\": \"U.S. Marshalls (1998)\",\n    \"id\": 1792,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 448\n  },\n  {\n    \"title\": \"Mickey Blue Eyes (1999)\",\n    \"id\": 2805,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 448\n  },\n  {\n    \"title\": \"Angel Heart (1987)\",\n    \"id\": 3706,\n    \"genres\": [\n      \"Film-Noir\",\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 448\n  },\n  {\n    \"title\": \"Creepshow (1982)\",\n    \"id\": 3016,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 447\n  },\n  {\n    \"title\": \"Pacific Heights (1990)\",\n    \"id\": 3219,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 447\n  },\n  {\n    \"title\": \"Defending Your Life (1991)\",\n    \"id\": 3358,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 446\n  },\n  {\n    \"title\": \"Notorious (1946)\",\n    \"id\": 930,\n    \"genres\": [\n      \"Film-Noir\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 445\n  },\n  {\n    \"title\": \"Rocky II (1979)\",\n    \"id\": 2409,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 445\n  },\n  {\n    \"title\": \"Hairspray (1988)\",\n    \"id\": 2926,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 445\n  },\n  {\n    \"title\": \"To Catch a Thief (1955)\",\n    \"id\": 933,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 443\n  },\n  {\n    \"title\": \"Clue (1985)\",\n    \"id\": 2413,\n    \"genres\": [\n      \"Comedy\",\n      \"Mystery\"\n    ],\n    \"count\": 443\n  },\n  {\n    \"title\": \"Cabaret (1972)\",\n    \"id\": 3545,\n    \"genres\": [\n      \"Musical\",\n      \"War\"\n    ],\n    \"count\": 443\n  },\n  {\n    \"title\": \"Real Genius (1985)\",\n    \"id\": 1297,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 442\n  },\n  {\n    \"title\": \"Tarzan (1999)\",\n    \"id\": 2687,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 442\n  },\n  {\n    \"title\": \"Courage Under Fire (1996)\",\n    \"id\": 647,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 441\n  },\n  {\n    \"title\": \"Toys (1992)\",\n    \"id\": 2253,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Fantasy\"\n    ],\n    \"count\": 440\n  },\n  {\n    \"title\": \"Wallace & Gromit: The Best of Aardman Animation (1996)\",\n    \"id\": 720,\n    \"genres\": [\n      \"Animation\"\n    ],\n    \"count\": 438\n  },\n  {\n    \"title\": \"Lord of the Rings, The (1978)\",\n    \"id\": 2116,\n    \"genres\": [\n      \"Adventure\",\n      \"Animation\",\n      \"Children's\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 438\n  },\n  {\n    \"title\": \"Single White Female (1992)\",\n    \"id\": 3274,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 438\n  },\n  {\n    \"title\": \"Remember the Titans (2000)\",\n    \"id\": 3916,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 437\n  },\n  {\n    \"title\": \"Timecop (1994)\",\n    \"id\": 379,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 436\n  },\n  {\n    \"title\": \"Footloose (1984)\",\n    \"id\": 3791,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 435\n  },\n  {\n    \"title\": \"Long Kiss Goodnight, The (1996)\",\n    \"id\": 1047,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 434\n  },\n  {\n    \"title\": \"Victor/Victoria (1982)\",\n    \"id\": 1081,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 434\n  },\n  {\n    \"title\": \"Howard the Duck (1986)\",\n    \"id\": 2450,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 434\n  },\n  {\n    \"title\": \"Brothers McMullen, The (1995)\",\n    \"id\": 144,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 433\n  },\n  {\n    \"title\": \"Wayne's World 2 (1993)\",\n    \"id\": 3254,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 433\n  },\n  {\n    \"title\": \"Mighty Aphrodite (1995)\",\n    \"id\": 52,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 431\n  },\n  {\n    \"title\": \"Girl, Interrupted (1999)\",\n    \"id\": 3186,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 431\n  },\n  {\n    \"title\": \"Naked Gun 2 1/2: The Smell of Fear, The (1991)\",\n    \"id\": 3869,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 431\n  },\n  {\n    \"title\": \"Dick (1999)\",\n    \"id\": 2759,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 430\n  },\n  {\n    \"title\": \"Straight Story, The (1999)\",\n    \"id\": 2966,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 429\n  },\n  {\n    \"title\": \"Singles (1992)\",\n    \"id\": 3261,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 429\n  },\n  {\n    \"title\": \"Reality Bites (1994)\",\n    \"id\": 372,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 428\n  },\n  {\n    \"title\": \"In & Out (1997)\",\n    \"id\": 1614,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 428\n  },\n  {\n    \"title\": \"Phenomenon (1996)\",\n    \"id\": 802,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 427\n  },\n  {\n    \"title\": \"Henry V (1989)\",\n    \"id\": 1224,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 426\n  },\n  {\n    \"title\": \"Spartacus (1960)\",\n    \"id\": 2728,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 426\n  },\n  {\n    \"title\": \"Roman Holiday (1953)\",\n    \"id\": 916,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 425\n  },\n  {\n    \"title\": \"For Your Eyes Only (1981)\",\n    \"id\": 2989,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 425\n  },\n  {\n    \"title\": \"Beach, The (2000)\",\n    \"id\": 3285,\n    \"genres\": [\n      \"Adventure\",\n      \"Drama\"\n    ],\n    \"count\": 423\n  },\n  {\n    \"title\": \"Soylent Green (1973)\",\n    \"id\": 2009,\n    \"genres\": [\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 422\n  },\n  {\n    \"title\": \"Live and Let Die (1973)\",\n    \"id\": 2991,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 422\n  },\n  {\n    \"title\": \"Lake Placid (1999)\",\n    \"id\": 2713,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 421\n  },\n  {\n    \"title\": \"Mister Roberts (1955)\",\n    \"id\": 3035,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 421\n  },\n  {\n    \"title\": \"Shaft (2000)\",\n    \"id\": 3744,\n    \"genres\": [\n      \"Action\",\n      \"Crime\"\n    ],\n    \"count\": 421\n  },\n  {\n    \"title\": \"Days of Thunder (1990)\",\n    \"id\": 1100,\n    \"genres\": [\n      \"Action\",\n      \"Romance\"\n    ],\n    \"count\": 420\n  },\n  {\n    \"title\": \"Waterboy, The (1998)\",\n    \"id\": 2335,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 420\n  },\n  {\n    \"title\": \"Rules of Engagement (2000)\",\n    \"id\": 3513,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 420\n  },\n  {\n    \"title\": \"Stigmata (1999)\",\n    \"id\": 2840,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 419\n  },\n  {\n    \"title\": \"Road Trip (2000)\",\n    \"id\": 3617,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 419\n  },\n  {\n    \"title\": \"Quick and the Dead, The (1995)\",\n    \"id\": 303,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Western\"\n    ],\n    \"count\": 418\n  },\n  {\n    \"title\": \"Brady Bunch Movie, The (1995)\",\n    \"id\": 585,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 418\n  },\n  {\n    \"title\": \"Money Pit, The (1986)\",\n    \"id\": 2375,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 418\n  },\n  {\n    \"title\": \"Arrival, The (1996)\",\n    \"id\": 748,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 417\n  },\n  {\n    \"title\": \"Apartment, The (1960)\",\n    \"id\": 909,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 417\n  },\n  {\n    \"title\": \"Executive Decision (1996)\",\n    \"id\": 494,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 416\n  },\n  {\n    \"title\": \"Craft, The (1996)\",\n    \"id\": 724,\n    \"genres\": [\n      \"Drama\",\n      \"Horror\"\n    ],\n    \"count\": 416\n  },\n  {\n    \"title\": \"Ever After: A Cinderella Story (1998)\",\n    \"id\": 2125,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 416\n  },\n  {\n    \"title\": \"Drugstore Cowboy (1989)\",\n    \"id\": 3019,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 416\n  },\n  {\n    \"title\": \"Bob Roberts (1992)\",\n    \"id\": 1171,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 415\n  },\n  {\n    \"title\": \"Midsummer Night's Dream, A (1999)\",\n    \"id\": 2622,\n    \"genres\": [\n      \"Comedy\",\n      \"Fantasy\"\n    ],\n    \"count\": 415\n  },\n  {\n    \"title\": \"Crocodile Dundee II (1988)\",\n    \"id\": 2471,\n    \"genres\": [\n      \"Adventure\",\n      \"Comedy\"\n    ],\n    \"count\": 414\n  },\n  {\n    \"title\": \"Bringing Up Baby (1938)\",\n    \"id\": 955,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 413\n  },\n  {\n    \"title\": \"Police Academy (1984)\",\n    \"id\": 2378,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 413\n  },\n  {\n    \"title\": \"Flatliners (1990)\",\n    \"id\": 3686,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 413\n  },\n  {\n    \"title\": \"Naked Gun 33 1/3: The Final Insult (1994)\",\n    \"id\": 370,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 412\n  },\n  {\n    \"title\": \"Tommy Boy (1995)\",\n    \"id\": 333,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 410\n  },\n  {\n    \"title\": \"She's All That (1999)\",\n    \"id\": 2485,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 410\n  },\n  {\n    \"title\": \"eXistenZ (1999)\",\n    \"id\": 2600,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 410\n  },\n  {\n    \"title\": \"Omen, The (1976)\",\n    \"id\": 1350,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 409\n  },\n  {\n    \"title\": \"Steel Magnolias (1989)\",\n    \"id\": 3844,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 408\n  },\n  {\n    \"title\": \"Private Benjamin (1980)\",\n    \"id\": 1135,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 407\n  },\n  {\n    \"title\": \"Frankenstein (1931)\",\n    \"id\": 2648,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 406\n  },\n  {\n    \"title\": \"On Golden Pond (1981)\",\n    \"id\": 1124,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 405\n  },\n  {\n    \"title\": \"Hollow Man (2000)\",\n    \"id\": 3826,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 405\n  },\n  {\n    \"title\": \"American Tail, An (1986)\",\n    \"id\": 2141,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 404\n  },\n  {\n    \"title\": \"Thunderball (1965)\",\n    \"id\": 2993,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 404\n  },\n  {\n    \"title\": \"City of Lost Children, The (1995)\",\n    \"id\": 29,\n    \"genres\": [\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 403\n  },\n  {\n    \"title\": \"All About Eve (1950)\",\n    \"id\": 926,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 403\n  },\n  {\n    \"title\": \"High Noon (1952)\",\n    \"id\": 1283,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 403\n  },\n  {\n    \"title\": \"Far and Away (1992)\",\n    \"id\": 3259,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 403\n  },\n  {\n    \"title\": \"Rebel Without a Cause (1955)\",\n    \"id\": 1103,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 402\n  },\n  {\n    \"title\": \"Perfect Murder, A (1998)\",\n    \"id\": 1892,\n    \"genres\": [\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 401\n  },\n  {\n    \"title\": \"Rambo: First Blood Part II (1985)\",\n    \"id\": 2402,\n    \"genres\": [\n      \"Action\",\n      \"War\"\n    ],\n    \"count\": 401\n  },\n  {\n    \"title\": \"Rocky III (1982)\",\n    \"id\": 2410,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 401\n  },\n  {\n    \"title\": \"Marathon Man (1976)\",\n    \"id\": 3551,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 400\n  },\n  {\n    \"title\": \"Anaconda (1997)\",\n    \"id\": 1499,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Thriller\"\n    ],\n    \"count\": 399\n  },\n  {\n    \"title\": \"Yellow Submarine (1968)\",\n    \"id\": 2857,\n    \"genres\": [\n      \"Animation\",\n      \"Musical\"\n    ],\n    \"count\": 399\n  },\n  {\n    \"title\": \"Snow Falling on Cedars (1999)\",\n    \"id\": 3185,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 398\n  },\n  {\n    \"title\": \"His Girl Friday (1940)\",\n    \"id\": 951,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 397\n  },\n  {\n    \"title\": \"First Blood (1982)\",\n    \"id\": 2403,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 397\n  },\n  {\n    \"title\": \"Peacemaker, The (1997)\",\n    \"id\": 1616,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\",\n      \"War\"\n    ],\n    \"count\": 395\n  },\n  {\n    \"title\": \"Kiss the Girls (1997)\",\n    \"id\": 1620,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 395\n  },\n  {\n    \"title\": \"Summer of Sam (1999)\",\n    \"id\": 2702,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 395\n  },\n  {\n    \"title\": \"All About My Mother (Todo Sobre Mi Madre) (1999)\",\n    \"id\": 3083,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 394\n  },\n  {\n    \"title\": \"Stalag 17 (1953)\",\n    \"id\": 3196,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 394\n  },\n  {\n    \"title\": \"City Slickers II: The Legend of Curly's Gold (1994)\",\n    \"id\": 432,\n    \"genres\": [\n      \"Comedy\",\n      \"Western\"\n    ],\n    \"count\": 392\n  },\n  {\n    \"title\": \"Cool Runnings (1993)\",\n    \"id\": 1020,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 392\n  },\n  {\n    \"title\": \"Michael (1996)\",\n    \"id\": 1409,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 392\n  },\n  {\n    \"title\": \"Man in the Iron Mask, The (1998)\",\n    \"id\": 1801,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 391\n  },\n  {\n    \"title\": \"Thirteenth Floor, The (1999)\",\n    \"id\": 2672,\n    \"genres\": [\n      \"Drama\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 391\n  },\n  {\n    \"title\": \"Year of Living Dangerously (1982)\",\n    \"id\": 2919,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 391\n  },\n  {\n    \"title\": \"Hunchback of Notre Dame, The (1996)\",\n    \"id\": 783,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 390\n  },\n  {\n    \"title\": \"Ideal Husband, An (1999)\",\n    \"id\": 2690,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 390\n  },\n  {\n    \"title\": \"Ace Ventura: When Nature Calls (1995)\",\n    \"id\": 19,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 389\n  },\n  {\n    \"title\": \"Serial Mom (1994)\",\n    \"id\": 532,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\",\n      \"Horror\"\n    ],\n    \"count\": 389\n  },\n  {\n    \"title\": \"Odd Couple, The (1968)\",\n    \"id\": 3507,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 389\n  },\n  {\n    \"title\": \"Fallen (1998)\",\n    \"id\": 1754,\n    \"genres\": [\n      \"Action\",\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 388\n  },\n  {\n    \"title\": \"Metropolis (1926)\",\n    \"id\": 2010,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 388\n  },\n  {\n    \"title\": \"Contender, The (2000)\",\n    \"id\": 3952,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 388\n  },\n  {\n    \"title\": \"Bridges of Madison County, The (1995)\",\n    \"id\": 105,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 387\n  },\n  {\n    \"title\": \"Pink Floyd - The Wall (1982)\",\n    \"id\": 1298,\n    \"genres\": [\n      \"Drama\",\n      \"Musical\",\n      \"War\"\n    ],\n    \"count\": 387\n  },\n  {\n    \"title\": \"Hope Floats (1998)\",\n    \"id\": 1888,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 387\n  },\n  {\n    \"title\": \"Don Juan DeMarco (1995)\",\n    \"id\": 224,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 386\n  },\n  {\n    \"title\": \"Barbarella (1968)\",\n    \"id\": 674,\n    \"genres\": [\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 386\n  },\n  {\n    \"title\": \"Rebecca (1940)\",\n    \"id\": 928,\n    \"genres\": [\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 386\n  },\n  {\n    \"title\": \"Siege, The (1998)\",\n    \"id\": 2334,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 386\n  },\n  {\n    \"title\": \"Cable Guy, The (1996)\",\n    \"id\": 784,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 385\n  },\n  {\n    \"title\": \"Return of the Pink Panther, The (1974)\",\n    \"id\": 1125,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 385\n  },\n  {\n    \"title\": \"European Vacation (1985)\",\n    \"id\": 2794,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 385\n  },\n  {\n    \"title\": \"Licence to Kill (1989)\",\n    \"id\": 2990,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 385\n  },\n  {\n    \"title\": \"Madness of King George, The (1994)\",\n    \"id\": 272,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 383\n  },\n  {\n    \"title\": \"Mr. Smith Goes to Washington (1939)\",\n    \"id\": 954,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 383\n  },\n  {\n    \"title\": \"Drop Dead Gorgeous (1999)\",\n    \"id\": 2718,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 383\n  },\n  {\n    \"title\": \"Bicentennial Man (1999)\",\n    \"id\": 3156,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 383\n  },\n  {\n    \"title\": \"Pocahontas (1995)\",\n    \"id\": 48,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Musical\",\n      \"Romance\"\n    ],\n    \"count\": 382\n  },\n  {\n    \"title\": \"Rising Sun (1993)\",\n    \"id\": 517,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 382\n  },\n  {\n    \"title\": \"Hamlet (1996)\",\n    \"id\": 1411,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 382\n  },\n  {\n    \"title\": \"Sweet Hereafter, The (1997)\",\n    \"id\": 1719,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 381\n  },\n  {\n    \"title\": \"Honey, I Blew Up the Kid (1992)\",\n    \"id\": 2053,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 381\n  },\n  {\n    \"title\": \"Absolute Power (1997)\",\n    \"id\": 1459,\n    \"genres\": [\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 380\n  },\n  {\n    \"title\": \"Absent Minded Professor, The (1961)\",\n    \"id\": 2015,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\",\n      \"Fantasy\"\n    ],\n    \"count\": 380\n  },\n  {\n    \"title\": \"Miracle on 34th Street (1947)\",\n    \"id\": 2398,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 380\n  },\n  {\n    \"title\": \"Bird on a Wire (1990)\",\n    \"id\": 3705,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 380\n  },\n  {\n    \"title\": \"Devil in a Blue Dress (1995)\",\n    \"id\": 164,\n    \"genres\": [\n      \"Crime\",\n      \"Film-Noir\",\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 379\n  },\n  {\n    \"title\": \"Dial M for Murder (1954)\",\n    \"id\": 1086,\n    \"genres\": [\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 379\n  },\n  {\n    \"title\": \"Young Sherlock Holmes (1985)\",\n    \"id\": 2414,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Mystery\"\n    ],\n    \"count\": 379\n  },\n  {\n    \"title\": \"Verdict, The (1982)\",\n    \"id\": 3068,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 379\n  },\n  {\n    \"title\": \"Copycat (1995)\",\n    \"id\": 22,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 378\n  },\n  {\n    \"title\": \"Adventures of Robin Hood, The (1938)\",\n    \"id\": 940,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 378\n  },\n  {\n    \"title\": \"Rocky IV (1985)\",\n    \"id\": 2411,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 375\n  },\n  {\n    \"title\": \"Universal Soldier (1992)\",\n    \"id\": 2808,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 375\n  },\n  {\n    \"title\": \"Kelly's Heroes (1970)\",\n    \"id\": 3836,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"War\"\n    ],\n    \"count\": 375\n  },\n  {\n    \"title\": \"Robin Hood: Men in Tights (1993)\",\n    \"id\": 520,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 374\n  },\n  {\n    \"title\": \"It Happened One Night (1934)\",\n    \"id\": 905,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 374\n  },\n  {\n    \"title\": \"Sleepers (1996)\",\n    \"id\": 1061,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 374\n  },\n  {\n    \"title\": \"Jumpin' Jack Flash (1986)\",\n    \"id\": 2468,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 374\n  },\n  {\n    \"title\": \"Weekend at Bernie's (1989)\",\n    \"id\": 1091,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 373\n  },\n  {\n    \"title\": \"Forbidden Planet (1956)\",\n    \"id\": 1301,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 373\n  },\n  {\n    \"title\": \"Inspector Gadget (1999)\",\n    \"id\": 2720,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 373\n  },\n  {\n    \"title\": \"Bananas (1971)\",\n    \"id\": 1078,\n    \"genres\": [\n      \"Comedy\",\n      \"War\"\n    ],\n    \"count\": 372\n  },\n  {\n    \"title\": \"Smoke Signals (1998)\",\n    \"id\": 1914,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 372\n  },\n  {\n    \"title\": \"Gremlins 2: The New Batch (1990)\",\n    \"id\": 2004,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 372\n  },\n  {\n    \"title\": \"King and I, The (1956)\",\n    \"id\": 2565,\n    \"genres\": [\n      \"Musical\"\n    ],\n    \"count\": 372\n  },\n  {\n    \"title\": \"History of the World: Part I (1981)\",\n    \"id\": 2301,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 371\n  },\n  {\n    \"title\": \"Jaws 2 (1978)\",\n    \"id\": 1388,\n    \"genres\": [\n      \"Action\",\n      \"Horror\"\n    ],\n    \"count\": 370\n  },\n  {\n    \"title\": \"Carlito's Way (1993)\",\n    \"id\": 431,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 369\n  },\n  {\n    \"title\": \"Young Guns II (1990)\",\n    \"id\": 1379,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Western\"\n    ],\n    \"count\": 369\n  },\n  {\n    \"title\": \"Secret of NIMH, The (1982)\",\n    \"id\": 2139,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 369\n  },\n  {\n    \"title\": \"Small Time Crooks (2000)\",\n    \"id\": 3618,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 369\n  },\n  {\n    \"title\": \"Delicatessen (1991)\",\n    \"id\": 1175,\n    \"genres\": [\n      \"Comedy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 368\n  },\n  {\n    \"title\": \"I.Q. (1994)\",\n    \"id\": 252,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 367\n  },\n  {\n    \"title\": \"Speed 2: Cruise Control (1997)\",\n    \"id\": 1556,\n    \"genres\": [\n      \"Action\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 367\n  },\n  {\n    \"title\": \"Dead Men Don't Wear Plaid (1982)\",\n    \"id\": 2110,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 367\n  },\n  {\n    \"title\": \"City of Angels (1998)\",\n    \"id\": 1835,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 366\n  },\n  {\n    \"title\": \"Alive (1993)\",\n    \"id\": 3250,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 366\n  },\n  {\n    \"title\": \"Sabrina (1954)\",\n    \"id\": 915,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 365\n  },\n  {\n    \"title\": \"Married to the Mob (1988)\",\n    \"id\": 2247,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 365\n  },\n  {\n    \"title\": \"Beneath the Planet of the Apes (1970)\",\n    \"id\": 2530,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 365\n  },\n  {\n    \"title\": \"Outlaw Josey Wales, The (1976)\",\n    \"id\": 3508,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 365\n  },\n  {\n    \"title\": \"Soapdish (1991)\",\n    \"id\": 3712,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 365\n  },\n  {\n    \"title\": \"101 Dalmatians (1996)\",\n    \"id\": 1367,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 364\n  },\n  {\n    \"title\": \"Small Soldiers (1998)\",\n    \"id\": 1920,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Fantasy\",\n      \"War\"\n    ],\n    \"count\": 364\n  },\n  {\n    \"title\": \"From Here to Eternity (1953)\",\n    \"id\": 1944,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\",\n      \"War\"\n    ],\n    \"count\": 364\n  },\n  {\n    \"title\": \"Cyrano de Bergerac (1990)\",\n    \"id\": 1277,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 363\n  },\n  {\n    \"title\": \"Dead Zone, The (1983)\",\n    \"id\": 2118,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 363\n  },\n  {\n    \"title\": \"Bad Boys (1995)\",\n    \"id\": 145,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 362\n  },\n  {\n    \"title\": \"First Knight (1995)\",\n    \"id\": 168,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 362\n  },\n  {\n    \"title\": \"Seven Years in Tibet (1997)\",\n    \"id\": 1619,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 362\n  },\n  {\n    \"title\": \"Flashdance (1983)\",\n    \"id\": 2942,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 362\n  },\n  {\n    \"title\": \"Cape Fear (1962)\",\n    \"id\": 1344,\n    \"genres\": [\n      \"Film-Noir\",\n      \"Thriller\"\n    ],\n    \"count\": 361\n  },\n  {\n    \"title\": \"Mighty Ducks, The (1992)\",\n    \"id\": 2082,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 361\n  },\n  {\n    \"title\": \"Blast from the Past (1999)\",\n    \"id\": 2496,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 360\n  },\n  {\n    \"title\": \"Bachelor Party (1984)\",\n    \"id\": 3525,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 360\n  },\n  {\n    \"title\": \"Breaker Morant (1980)\",\n    \"id\": 3811,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 360\n  },\n  {\n    \"title\": \"Five Easy Pieces (1970)\",\n    \"id\": 3201,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 359\n  },\n  {\n    \"title\": \"Tank Girl (1995)\",\n    \"id\": 327,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Musical\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 358\n  },\n  {\n    \"title\": \"Miller's Crossing (1990)\",\n    \"id\": 1245,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 358\n  },\n  {\n    \"title\": \"Conquest of the Planet of the Apes (1972)\",\n    \"id\": 2532,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 358\n  },\n  {\n    \"title\": \"Great Muppet Caper, The (1981)\",\n    \"id\": 3397,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 358\n  },\n  {\n    \"title\": \"Fabulous Baker Boys, The (1989)\",\n    \"id\": 3684,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 358\n  },\n  {\n    \"title\": \"Indian in the Cupboard, The (1995)\",\n    \"id\": 60,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Fantasy\"\n    ],\n    \"count\": 357\n  },\n  {\n    \"title\": \"Ran (1985)\",\n    \"id\": 1217,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 357\n  },\n  {\n    \"title\": \"Little Shop of Horrors, The (1960)\",\n    \"id\": 2747,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 356\n  },\n  {\n    \"title\": \"Harvey (1950)\",\n    \"id\": 3088,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 356\n  },\n  {\n    \"title\": \"Romeo and Juliet (1968)\",\n    \"id\": 3668,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 356\n  },\n  {\n    \"title\": \"Billy Madison (1995)\",\n    \"id\": 216,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 355\n  },\n  {\n    \"title\": \"Amityville Horror, The (1979)\",\n    \"id\": 1327,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 355\n  },\n  {\n    \"title\": \"Legend (1985)\",\n    \"id\": 2143,\n    \"genres\": [\n      \"Adventure\",\n      \"Fantasy\",\n      \"Romance\"\n    ],\n    \"count\": 355\n  },\n  {\n    \"title\": \"What Dreams May Come (1998)\",\n    \"id\": 2297,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 355\n  },\n  {\n    \"title\": \"No Way Out (1987)\",\n    \"id\": 3505,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 355\n  },\n  {\n    \"title\": \"Breakdown (1997)\",\n    \"id\": 1518,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 354\n  },\n  {\n    \"title\": \"Buffalo 66 (1998)\",\n    \"id\": 1916,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 354\n  },\n  {\n    \"title\": \"Back to School (1986)\",\n    \"id\": 2416,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 354\n  },\n  {\n    \"title\": \"For Love of the Game (1999)\",\n    \"id\": 2861,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 354\n  },\n  {\n    \"title\": \"Last Picture Show, The (1971)\",\n    \"id\": 3152,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 354\n  },\n  {\n    \"title\": \"Primary Colors (1998)\",\n    \"id\": 1810,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 353\n  },\n  {\n    \"title\": \"Women on the Verge of a Nervous Breakdown (1988)\",\n    \"id\": 3067,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 353\n  },\n  {\n    \"title\": \"Lord of the Flies (1963)\",\n    \"id\": 3461,\n    \"genres\": [\n      \"Adventure\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 353\n  },\n  {\n    \"title\": \"On Her Majesty's Secret Service (1969)\",\n    \"id\": 3633,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 353\n  },\n  {\n    \"title\": \"Beverly Hills Cop III (1994)\",\n    \"id\": 420,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 352\n  },\n  {\n    \"title\": \"St. Elmo's Fire (1985)\",\n    \"id\": 2146,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 352\n  },\n  {\n    \"title\": \"Longest Day, The (1962)\",\n    \"id\": 3062,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 352\n  },\n  {\n    \"title\": \"Local Hero (1983)\",\n    \"id\": 1238,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 351\n  },\n  {\n    \"title\": \"Amistad (1997)\",\n    \"id\": 1693,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 351\n  },\n  {\n    \"title\": \"River Wild, The (1994)\",\n    \"id\": 376,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 350\n  },\n  {\n    \"title\": \"Englishman Who Went Up a Hill, But Came Down a Mountain, The (1995)\",\n    \"id\": 468,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 350\n  },\n  {\n    \"title\": \"Super Mario Bros. (1993)\",\n    \"id\": 546,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Children's\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 350\n  },\n  {\n    \"title\": \"Shall We Dance? (Shall We Dansu?) (1996)\",\n    \"id\": 1537,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 350\n  },\n  {\n    \"title\": \"Guess Who's Coming to Dinner (1967)\",\n    \"id\": 3451,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 350\n  },\n  {\n    \"title\": \"Dolores Claiborne (1994)\",\n    \"id\": 230,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 349\n  },\n  {\n    \"title\": \"Mimic (1997)\",\n    \"id\": 1603,\n    \"genres\": [\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 348\n  },\n  {\n    \"title\": \"In the Heat of the Night (1967)\",\n    \"id\": 1950,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 348\n  },\n  {\n    \"title\": \"Fantastic Voyage (1966)\",\n    \"id\": 3927,\n    \"genres\": [\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 348\n  },\n  {\n    \"title\": \"Eat Drink Man Woman (1994)\",\n    \"id\": 232,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 346\n  },\n  {\n    \"title\": \"Ref, The (1994)\",\n    \"id\": 514,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 346\n  },\n  {\n    \"title\": \"Grand Canyon (1991)\",\n    \"id\": 2112,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 346\n  },\n  {\n    \"title\": \"Pelican Brief, The (1993)\",\n    \"id\": 2803,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 346\n  },\n  {\n    \"title\": \"Blue Lagoon, The (1980)\",\n    \"id\": 2950,\n    \"genres\": [\n      \"Adventure\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 346\n  },\n  {\n    \"title\": \"Rounders (1998)\",\n    \"id\": 2231,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 345\n  },\n  {\n    \"title\": \"Heaven Can Wait (1978)\",\n    \"id\": 2779,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 345\n  },\n  {\n    \"title\": \"Princess Mononoke, The (Mononoke Hime) (1997)\",\n    \"id\": 3000,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Animation\"\n    ],\n    \"count\": 345\n  },\n  {\n    \"title\": \"Dinosaur (2000)\",\n    \"id\": 3615,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 345\n  },\n  {\n    \"title\": \"Time to Kill, A (1996)\",\n    \"id\": 805,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 344\n  },\n  {\n    \"title\": \"Robin Hood: Prince of Thieves (1991)\",\n    \"id\": 1027,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 344\n  },\n  {\n    \"title\": \"Sophie's Choice (1982)\",\n    \"id\": 1096,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 344\n  },\n  {\n    \"title\": \"Meet Joe Black (1998)\",\n    \"id\": 2340,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 344\n  },\n  {\n    \"title\": \"American in Paris, An (1951)\",\n    \"id\": 900,\n    \"genres\": [\n      \"Musical\",\n      \"Romance\"\n    ],\n    \"count\": 343\n  },\n  {\n    \"title\": \"Color of Money, The (1986)\",\n    \"id\": 2474,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 343\n  },\n  {\n    \"title\": \"Robin Hood (1973)\",\n    \"id\": 3034,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 343\n  },\n  {\n    \"title\": \"Battlefield Earth (2000)\",\n    \"id\": 3593,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 342\n  },\n  {\n    \"title\": \"Home Alone 2: Lost in New York (1992)\",\n    \"id\": 2953,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 341\n  },\n  {\n    \"title\": \"House on Haunted Hill, The (1999)\",\n    \"id\": 2995,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 341\n  },\n  {\n    \"title\": \"Messenger: The Story of Joan of Arc, The (1999)\",\n    \"id\": 3053,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 341\n  },\n  {\n    \"title\": \"Muppets Take Manhattan, The (1984)\",\n    \"id\": 3398,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 341\n  },\n  {\n    \"title\": \"Hand That Rocks the Cradle, The (1992)\",\n    \"id\": 3249,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 340\n  },\n  {\n    \"title\": \"Death Becomes Her (1992)\",\n    \"id\": 3258,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 339\n  },\n  {\n    \"title\": \"Porky's (1981)\",\n    \"id\": 3688,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 339\n  },\n  {\n    \"title\": \"Secret Garden, The (1993)\",\n    \"id\": 531,\n    \"genres\": [\n      \"Children's\",\n      \"Drama\"\n    ],\n    \"count\": 338\n  },\n  {\n    \"title\": \"Frighteners, The (1996)\",\n    \"id\": 799,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 338\n  },\n  {\n    \"title\": \"Blob, The (1958)\",\n    \"id\": 1334,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 338\n  },\n  {\n    \"title\": \"Dreamscape (1984)\",\n    \"id\": 3770,\n    \"genres\": [\n      \"Adventure\",\n      \"Crime\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 338\n  },\n  {\n    \"title\": \"Jackal, The (1997)\",\n    \"id\": 1687,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 337\n  },\n  {\n    \"title\": \"Manhattan Murder Mystery (1993)\",\n    \"id\": 492,\n    \"genres\": [\n      \"Comedy\",\n      \"Mystery\"\n    ],\n    \"count\": 335\n  },\n  {\n    \"title\": \"Three Colors: Red (1994)\",\n    \"id\": 306,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 334\n  },\n  {\n    \"title\": \"Anna and the King (1999)\",\n    \"id\": 3155,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 333\n  },\n  {\n    \"title\": \"Superman IV: The Quest for Peace (1987)\",\n    \"id\": 2643,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 332\n  },\n  {\n    \"title\": \"Children of a Lesser God (1986)\",\n    \"id\": 2312,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 331\n  },\n  {\n    \"title\": \"Romeo Must Die (2000)\",\n    \"id\": 3452,\n    \"genres\": [\n      \"Action\",\n      \"Romance\"\n    ],\n    \"count\": 331\n  },\n  {\n    \"title\": \"My Life as a Dog (Mitt liv som hund) (1985)\",\n    \"id\": 1300,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 330\n  },\n  {\n    \"title\": \"Little Voice (1998)\",\n    \"id\": 2390,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 330\n  },\n  {\n    \"title\": \"Phantasm (1979)\",\n    \"id\": 2901,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 330\n  },\n  {\n    \"title\": \"Circle of Friends (1995)\",\n    \"id\": 222,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 329\n  },\n  {\n    \"title\": \"Astronaut's Wife, The (1999)\",\n    \"id\": 2827,\n    \"genres\": [\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 328\n  },\n  {\n    \"title\": \"Of Mice and Men (1992)\",\n    \"id\": 3271,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 328\n  },\n  {\n    \"title\": \"Virgin Suicides, The (1999)\",\n    \"id\": 3556,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 328\n  },\n  {\n    \"title\": \"Stand and Deliver (1987)\",\n    \"id\": 3071,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 327\n  },\n  {\n    \"title\": \"Hard-Boiled (Lashou shentan) (1992)\",\n    \"id\": 3265,\n    \"genres\": [\n      \"Action\",\n      \"Crime\"\n    ],\n    \"count\": 327\n  },\n  {\n    \"title\": \"French Kiss (1995)\",\n    \"id\": 236,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 326\n  },\n  {\n    \"title\": \"About Last Night... (1986)\",\n    \"id\": 2262,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 326\n  },\n  {\n    \"title\": \"Rosencrantz and Guildenstern Are Dead (1990)\",\n    \"id\": 1243,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 325\n  },\n  {\n    \"title\": \"Nashville (1975)\",\n    \"id\": 2303,\n    \"genres\": [\n      \"Drama\",\n      \"Musical\"\n    ],\n    \"count\": 325\n  },\n  {\n    \"title\": \"View to a Kill, A (1985)\",\n    \"id\": 2376,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 325\n  },\n  {\n    \"title\": \"Papillon (1973)\",\n    \"id\": 3198,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 325\n  },\n  {\n    \"title\": \"For a Few Dollars More (1965)\",\n    \"id\": 3681,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 325\n  },\n  {\n    \"title\": \"Island of Dr. Moreau, The (1996)\",\n    \"id\": 880,\n    \"genres\": [\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 324\n  },\n  {\n    \"title\": \"Name of the Rose, The (1986)\",\n    \"id\": 2467,\n    \"genres\": [\n      \"Mystery\"\n    ],\n    \"count\": 324\n  },\n  {\n    \"title\": \"Pete's Dragon (1977)\",\n    \"id\": 1030,\n    \"genres\": [\n      \"Adventure\",\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 323\n  },\n  {\n    \"title\": \"Goodbye Girl, The (1977)\",\n    \"id\": 3244,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 322\n  },\n  {\n    \"title\": \"Hackers (1995)\",\n    \"id\": 170,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 321\n  },\n  {\n    \"title\": \"Dante's Peak (1997)\",\n    \"id\": 1438,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 321\n  },\n  {\n    \"title\": \"Rainmaker, The (1997)\",\n    \"id\": 1672,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 321\n  },\n  {\n    \"title\": \"Prince of Tides, The (1991)\",\n    \"id\": 3528,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 321\n  },\n  {\n    \"title\": \"Happiness (1998)\",\n    \"id\": 2318,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 320\n  },\n  {\n    \"title\": \"True Grit (1969)\",\n    \"id\": 3494,\n    \"genres\": [\n      \"Adventure\",\n      \"Western\"\n    ],\n    \"count\": 320\n  },\n  {\n    \"title\": \"Friday (1995)\",\n    \"id\": 69,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 319\n  },\n  {\n    \"title\": \"Bedknobs and Broomsticks (1971)\",\n    \"id\": 1031,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 319\n  },\n  {\n    \"title\": \"Replacement Killers, The (1998)\",\n    \"id\": 1769,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 319\n  },\n  {\n    \"title\": \"Three Days of the Condor (1975)\",\n    \"id\": 2819,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 319\n  },\n  {\n    \"title\": \"Flirting With Disaster (1996)\",\n    \"id\": 125,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 318\n  },\n  {\n    \"title\": \"Drop Dead Fred (1991)\",\n    \"id\": 1126,\n    \"genres\": [\n      \"Comedy\",\n      \"Fantasy\"\n    ],\n    \"count\": 317\n  },\n  {\n    \"title\": \"Pump Up the Volume (1990)\",\n    \"id\": 1268,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 317\n  },\n  {\n    \"title\": \"Friday the 13th (1980)\",\n    \"id\": 1974,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 317\n  },\n  {\n    \"title\": \"High Plains Drifter (1972)\",\n    \"id\": 2921,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 317\n  },\n  {\n    \"title\": \"All That Jazz (1979)\",\n    \"id\": 2971,\n    \"genres\": [\n      \"Musical\"\n    ],\n    \"count\": 317\n  },\n  {\n    \"title\": \"Pecker (1998)\",\n    \"id\": 2282,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 316\n  },\n  {\n    \"title\": \"Six Degrees of Separation (1993)\",\n    \"id\": 538,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 315\n  },\n  {\n    \"title\": \"Meatballs (1979)\",\n    \"id\": 3040,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 315\n  },\n  {\n    \"title\": \"Ghost in the Shell (Kokaku kidotai) (1995)\",\n    \"id\": 741,\n    \"genres\": [\n      \"Animation\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 314\n  },\n  {\n    \"title\": \"Ghost Dog: The Way of the Samurai (1999)\",\n    \"id\": 3328,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 314\n  },\n  {\n    \"title\": \"In the Name of the Father (1993)\",\n    \"id\": 475,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 312\n  },\n  {\n    \"title\": \"Man with Two Brains, The (1983)\",\n    \"id\": 2111,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 312\n  },\n  {\n    \"title\": \"Pale Rider (1985)\",\n    \"id\": 2401,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 312\n  },\n  {\n    \"title\": \"Battle for the Planet of the Apes (1973)\",\n    \"id\": 2531,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 312\n  },\n  {\n    \"title\": \"Mortal Kombat (1995)\",\n    \"id\": 44,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 311\n  },\n  {\n    \"title\": \"Man Who Would Be King, The (1975)\",\n    \"id\": 1303,\n    \"genres\": [\n      \"Adventure\"\n    ],\n    \"count\": 310\n  },\n  {\n    \"title\": \"King Kong (1976)\",\n    \"id\": 2367,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Horror\"\n    ],\n    \"count\": 309\n  },\n  {\n    \"title\": \"Perfect World, A (1993)\",\n    \"id\": 507,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 308\n  },\n  {\n    \"title\": \"M (1931)\",\n    \"id\": 1260,\n    \"genres\": [\n      \"Crime\",\n      \"Film-Noir\",\n      \"Thriller\"\n    ],\n    \"count\": 308\n  },\n  {\n    \"title\": \"Deuce Bigalow: Male Gigolo (1999)\",\n    \"id\": 3146,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 308\n  },\n  {\n    \"title\": \"In the Company of Men (1997)\",\n    \"id\": 1594,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 307\n  },\n  {\n    \"title\": \"Some Folks Call It a Sling Blade (1993)\",\n    \"id\": 678,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 306\n  },\n  {\n    \"title\": \"Charade (1963)\",\n    \"id\": 911,\n    \"genres\": [\n      \"Comedy\",\n      \"Mystery\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 306\n  },\n  {\n    \"title\": \"Can't Hardly Wait (1998)\",\n    \"id\": 1895,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 306\n  },\n  {\n    \"title\": \"Tequila Sunrise (1988)\",\n    \"id\": 2802,\n    \"genres\": [\n      \"Action\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 306\n  },\n  {\n    \"title\": \"Fright Night (1985)\",\n    \"id\": 2867,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 306\n  },\n  {\n    \"title\": \"Air America (1990)\",\n    \"id\": 3841,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 306\n  },\n  {\n    \"title\": \"Watership Down (1978)\",\n    \"id\": 2138,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Drama\",\n      \"Fantasy\"\n    ],\n    \"count\": 305\n  },\n  {\n    \"title\": \"Pet Sematary (1989)\",\n    \"id\": 2513,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 305\n  },\n  {\n    \"title\": \"Modern Times (1936)\",\n    \"id\": 3462,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 305\n  },\n  {\n    \"title\": \"Kentucky Fried Movie, The (1977)\",\n    \"id\": 3760,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 305\n  },\n  {\n    \"title\": \"Shane (1953)\",\n    \"id\": 3871,\n    \"genres\": [\n      \"Drama\",\n      \"Western\"\n    ],\n    \"count\": 305\n  },\n  {\n    \"title\": \"Father of the Bride (1950)\",\n    \"id\": 934,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 304\n  },\n  {\n    \"title\": \"Anastasia (1997)\",\n    \"id\": 1688,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 304\n  },\n  {\n    \"title\": \"Atlantic City (1980)\",\n    \"id\": 2130,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 304\n  },\n  {\n    \"title\": \"Sid and Nancy (1986)\",\n    \"id\": 2348,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 304\n  },\n  {\n    \"title\": \"Topsy-Turvy (1999)\",\n    \"id\": 3163,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 304\n  },\n  {\n    \"title\": \"Big Kahuna, The (2000)\",\n    \"id\": 3566,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 304\n  },\n  {\n    \"title\": \"Freejack (1992)\",\n    \"id\": 3802,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 304\n  },\n  {\n    \"title\": \"Requiem for a Dream (2000)\",\n    \"id\": 3949,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 304\n  },\n  {\n    \"title\": \"Grease 2 (1982)\",\n    \"id\": 1381,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\",\n      \"Romance\"\n    ],\n    \"count\": 303\n  },\n  {\n    \"title\": \"'burbs, The (1989)\",\n    \"id\": 2072,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 303\n  },\n  {\n    \"title\": \"My Favorite Year (1982)\",\n    \"id\": 921,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 302\n  },\n  {\n    \"title\": \"Flubber (1997)\",\n    \"id\": 1702,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\",\n      \"Fantasy\"\n    ],\n    \"count\": 302\n  },\n  {\n    \"title\": \"Klute (1971)\",\n    \"id\": 1964,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 302\n  },\n  {\n    \"title\": \"EDtv (1999)\",\n    \"id\": 2567,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 302\n  },\n  {\n    \"title\": \"Skulls, The (2000)\",\n    \"id\": 3484,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 302\n  },\n  {\n    \"title\": \"Short Cuts (1993)\",\n    \"id\": 535,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 301\n  },\n  {\n    \"title\": \"Old Yeller (1957)\",\n    \"id\": 1012,\n    \"genres\": [\n      \"Children's\",\n      \"Drama\"\n    ],\n    \"count\": 301\n  },\n  {\n    \"title\": \"Zero Effect (1998)\",\n    \"id\": 1845,\n    \"genres\": [\n      \"Comedy\",\n      \"Thriller\"\n    ],\n    \"count\": 301\n  },\n  {\n    \"title\": \"Godzilla (Gojira) (1954)\",\n    \"id\": 2363,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 301\n  },\n  {\n    \"title\": \"Rambo III (1988)\",\n    \"id\": 2404,\n    \"genres\": [\n      \"Action\",\n      \"War\"\n    ],\n    \"count\": 301\n  },\n  {\n    \"title\": \"Secret of Roan Inish, The (1994)\",\n    \"id\": 314,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 300\n  },\n  {\n    \"title\": \"Barb Wire (1996)\",\n    \"id\": 737,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 300\n  },\n  {\n    \"title\": \"Stepford Wives, The (1975)\",\n    \"id\": 2346,\n    \"genres\": [\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 300\n  },\n  {\n    \"title\": \"Heartbreak Ridge (1986)\",\n    \"id\": 2476,\n    \"genres\": [\n      \"Action\",\n      \"War\"\n    ],\n    \"count\": 300\n  },\n  {\n    \"title\": \"My Dog Skip (1999)\",\n    \"id\": 3189,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 300\n  },\n  {\n    \"title\": \"Seventh Seal, The (Sjunde inseglet, Det) (1957)\",\n    \"id\": 1237,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 299\n  },\n  {\n    \"title\": \"Menace II Society (1993)\",\n    \"id\": 493,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 298\n  },\n  {\n    \"title\": \"Striptease (1996)\",\n    \"id\": 762,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\"\n    ],\n    \"count\": 298\n  },\n  {\n    \"title\": \"Fear and Loathing in Las Vegas (1998)\",\n    \"id\": 1884,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 298\n  },\n  {\n    \"title\": \"Ninth Gate, The (2000)\",\n    \"id\": 3355,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 298\n  },\n  {\n    \"title\": \"Harry and the Hendersons (1987)\",\n    \"id\": 3388,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 298\n  },\n  {\n    \"title\": \"Jackie Chan's First Strike (1996)\",\n    \"id\": 1429,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 297\n  },\n  {\n    \"title\": \"Who's Afraid of Virginia Woolf? (1966)\",\n    \"id\": 2132,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 297\n  },\n  {\n    \"title\": \"Father of the Bride Part II (1995)\",\n    \"id\": 5,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 296\n  },\n  {\n    \"title\": \"Age of Innocence, The (1993)\",\n    \"id\": 412,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 295\n  },\n  {\n    \"title\": \"First Wives Club, The (1996)\",\n    \"id\": 830,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 295\n  },\n  {\n    \"title\": \"Tin Men (1987)\",\n    \"id\": 1395,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 295\n  },\n  {\n    \"title\": \"Black Hole, The (1979)\",\n    \"id\": 2034,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 295\n  },\n  {\n    \"title\": \"I Still Know What You Did Last Summer (1998)\",\n    \"id\": 2338,\n    \"genres\": [\n      \"Horror\",\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 295\n  },\n  {\n    \"title\": \"American Movie (1999)\",\n    \"id\": 3007,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 295\n  },\n  {\n    \"title\": \"Midnight Express (1978)\",\n    \"id\": 3498,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 295\n  },\n  {\n    \"title\": \"American Gigolo (1980)\",\n    \"id\": 3649,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 295\n  },\n  {\n    \"title\": \"Kalifornia (1993)\",\n    \"id\": 481,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 294\n  },\n  {\n    \"title\": \"Night Shift (1982)\",\n    \"id\": 2518,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 294\n  },\n  {\n    \"title\": \"Flamingo Kid, The (1984)\",\n    \"id\": 3308,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 294\n  },\n  {\n    \"title\": \"Sword in the Stone, The (1963)\",\n    \"id\": 1025,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 293\n  },\n  {\n    \"title\": \"Encino Man (1992)\",\n    \"id\": 3243,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 293\n  },\n  {\n    \"title\": \"Laura (1944)\",\n    \"id\": 942,\n    \"genres\": [\n      \"Crime\",\n      \"Film-Noir\",\n      \"Mystery\"\n    ],\n    \"count\": 292\n  },\n  {\n    \"title\": \"Thin Man, The (1934)\",\n    \"id\": 950,\n    \"genres\": [\n      \"Mystery\"\n    ],\n    \"count\": 292\n  },\n  {\n    \"title\": \"Escape to Witch Mountain (1975)\",\n    \"id\": 1009,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Fantasy\"\n    ],\n    \"count\": 291\n  },\n  {\n    \"title\": \"Sommersby (1993)\",\n    \"id\": 2875,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\",\n      \"Romance\"\n    ],\n    \"count\": 291\n  },\n  {\n    \"title\": \"Help! (1965)\",\n    \"id\": 2946,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 291\n  },\n  {\n    \"title\": \"Soldier (1998)\",\n    \"id\": 2322,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\",\n      \"Thriller\",\n      \"War\"\n    ],\n    \"count\": 290\n  },\n  {\n    \"title\": \"George of the Jungle (1997)\",\n    \"id\": 1588,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 289\n  },\n  {\n    \"title\": \"Reindeer Games (2000)\",\n    \"id\": 3316,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 289\n  },\n  {\n    \"title\": \"Paper Chase, The (1973)\",\n    \"id\": 3733,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 289\n  },\n  {\n    \"title\": \"Devil's Own, The (1997)\",\n    \"id\": 1488,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"Thriller\",\n      \"War\"\n    ],\n    \"count\": 288\n  },\n  {\n    \"title\": \"Avengers, The (1998)\",\n    \"id\": 2153,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 288\n  },\n  {\n    \"title\": \"Sweet and Lowdown (1999)\",\n    \"id\": 3129,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 288\n  },\n  {\n    \"title\": \"Fox and the Hound, The (1981)\",\n    \"id\": 1033,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 287\n  },\n  {\n    \"title\": \"Mystery, Alaska (1999)\",\n    \"id\": 2889,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 287\n  },\n  {\n    \"title\": \"Shadowlands (1993)\",\n    \"id\": 534,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 286\n  },\n  {\n    \"title\": \"2 Days in the Valley (1996)\",\n    \"id\": 999,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 286\n  },\n  {\n    \"title\": \"Karate Kid III, The (1989)\",\n    \"id\": 2422,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Drama\"\n    ],\n    \"count\": 286\n  },\n  {\n    \"title\": \"Little Women (1994)\",\n    \"id\": 261,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 285\n  },\n  {\n    \"title\": \"Free Willy (1993)\",\n    \"id\": 455,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Drama\"\n    ],\n    \"count\": 285\n  },\n  {\n    \"title\": \"Somewhere in Time (1980)\",\n    \"id\": 1286,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 285\n  },\n  {\n    \"title\": \"Radio Days (1987)\",\n    \"id\": 2750,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 285\n  },\n  {\n    \"title\": \"Everything You Always Wanted to Know About Sex (1972)\",\n    \"id\": 3812,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 285\n  },\n  {\n    \"title\": \"Deconstructing Harry (1997)\",\n    \"id\": 1701,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 284\n  },\n  {\n    \"title\": \"Fly II, The (1989)\",\n    \"id\": 2456,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 284\n  },\n  {\n    \"title\": \"Hidden, The (1987)\",\n    \"id\": 3576,\n    \"genres\": [\n      \"Action\",\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 284\n  },\n  {\n    \"title\": \"Evita (1996)\",\n    \"id\": 1416,\n    \"genres\": [\n      \"Drama\",\n      \"Musical\"\n    ],\n    \"count\": 282\n  },\n  {\n    \"title\": \"Murder at 1600 (1997)\",\n    \"id\": 1422,\n    \"genres\": [\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 281\n  },\n  {\n    \"title\": \"Night of the Comet (1984)\",\n    \"id\": 2613,\n    \"genres\": [\n      \"Action\",\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 281\n  },\n  {\n    \"title\": \"Them! (1954)\",\n    \"id\": 2287,\n    \"genres\": [\n      \"Sci-Fi\",\n      \"Thriller\",\n      \"War\"\n    ],\n    \"count\": 280\n  },\n  {\n    \"title\": \"Thomas Crown Affair, The (1968)\",\n    \"id\": 2764,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 280\n  },\n  {\n    \"title\": \"South Pacific (1958)\",\n    \"id\": 2941,\n    \"genres\": [\n      \"Musical\",\n      \"Romance\",\n      \"War\"\n    ],\n    \"count\": 280\n  },\n  {\n    \"title\": \"Story of Us, The (1999)\",\n    \"id\": 2961,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 280\n  },\n  {\n    \"title\": \"Cat Ballou (1965)\",\n    \"id\": 3873,\n    \"genres\": [\n      \"Comedy\",\n      \"Western\"\n    ],\n    \"count\": 280\n  },\n  {\n    \"title\": \"Hud (1963)\",\n    \"id\": 3467,\n    \"genres\": [\n      \"Drama\",\n      \"Western\"\n    ],\n    \"count\": 279\n  },\n  {\n    \"title\": \"Hellraiser (1987)\",\n    \"id\": 3917,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 279\n  },\n  {\n    \"title\": \"Aristocats, The (1970)\",\n    \"id\": 616,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 278\n  },\n  {\n    \"title\": \"Nineteen Eighty-Four (1984)\",\n    \"id\": 2117,\n    \"genres\": [\n      \"Drama\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 278\n  },\n  {\n    \"title\": \"When We Were Kings (1996)\",\n    \"id\": 1147,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 277\n  },\n  {\n    \"title\": \"Raise the Red Lantern (1991)\",\n    \"id\": 1280,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 277\n  },\n  {\n    \"title\": \"Mumford (1999)\",\n    \"id\": 2883,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 277\n  },\n  {\n    \"title\": \"Where the Heart Is (2000)\",\n    \"id\": 3565,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 277\n  },\n  {\n    \"title\": \"Swiss Family Robinson (1960)\",\n    \"id\": 1017,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 276\n  },\n  {\n    \"title\": \"Return to Oz (1985)\",\n    \"id\": 2093,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Fantasy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 276\n  },\n  {\n    \"title\": \"Agnes of God (1985)\",\n    \"id\": 3251,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 276\n  },\n  {\n    \"title\": \"Taking of Pelham One Two Three, The (1974)\",\n    \"id\": 3384,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 276\n  },\n  {\n    \"title\": \"Mulholland Falls (1996)\",\n    \"id\": 707,\n    \"genres\": [\n      \"Crime\",\n      \"Film-Noir\",\n      \"Thriller\"\n    ],\n    \"count\": 275\n  },\n  {\n    \"title\": \"Holiday Inn (1942)\",\n    \"id\": 3061,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 275\n  },\n  {\n    \"title\": \"Gold Rush, The (1925)\",\n    \"id\": 3629,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 275\n  },\n  {\n    \"title\": \"Everyone Says I Love You (1996)\",\n    \"id\": 1057,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\",\n      \"Romance\"\n    ],\n    \"count\": 274\n  },\n  {\n    \"title\": \"Nightmare on Elm Street Part 2: Freddy's Revenge, A (1985)\",\n    \"id\": 1969,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 274\n  },\n  {\n    \"title\": \"Rocky V (1990)\",\n    \"id\": 2412,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 274\n  },\n  {\n    \"title\": \"Creature Comforts (1990)\",\n    \"id\": 3429,\n    \"genres\": [\n      \"Animation\",\n      \"Comedy\"\n    ],\n    \"count\": 274\n  },\n  {\n    \"title\": \"Auntie Mame (1958)\",\n    \"id\": 3548,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 274\n  },\n  {\n    \"title\": \"Shallow Grave (1994)\",\n    \"id\": 319,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 273\n  },\n  {\n    \"title\": \"Supercop (1992)\",\n    \"id\": 861,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 273\n  },\n  {\n    \"title\": \"Christine (1983)\",\n    \"id\": 2517,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 273\n  },\n  {\n    \"title\": \"Swamp Thing (1982)\",\n    \"id\": 2668,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 273\n  },\n  {\n    \"title\": \"Shampoo (1975)\",\n    \"id\": 3099,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 273\n  },\n  {\n    \"title\": \"Wolf (1994)\",\n    \"id\": 382,\n    \"genres\": [\n      \"Drama\",\n      \"Horror\"\n    ],\n    \"count\": 272\n  },\n  {\n    \"title\": \"8MM (1999)\",\n    \"id\": 2505,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 272\n  },\n  {\n    \"title\": \"Cat on a Hot Tin Roof (1958)\",\n    \"id\": 971,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 271\n  },\n  {\n    \"title\": \"Iron Eagle (1986)\",\n    \"id\": 2815,\n    \"genres\": [\n      \"Action\",\n      \"War\"\n    ],\n    \"count\": 271\n  },\n  {\n    \"title\": \"City Lights (1931)\",\n    \"id\": 3307,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 271\n  },\n  {\n    \"title\": \"Empire Records (1995)\",\n    \"id\": 3477,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 271\n  },\n  {\n    \"title\": \"White Christmas (1954)\",\n    \"id\": 3675,\n    \"genres\": [\n      \"Musical\"\n    ],\n    \"count\": 271\n  },\n  {\n    \"title\": \"Wyatt Earp (1994)\",\n    \"id\": 383,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 270\n  },\n  {\n    \"title\": \"Body Snatchers (1993)\",\n    \"id\": 426,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 269\n  },\n  {\n    \"title\": \"Around the World in 80 Days (1956)\",\n    \"id\": 952,\n    \"genres\": [\n      \"Adventure\",\n      \"Comedy\"\n    ],\n    \"count\": 269\n  },\n  {\n    \"title\": \"Thin Blue Line, The (1988)\",\n    \"id\": 1189,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 269\n  },\n  {\n    \"title\": \"Murphy's Romance (1985)\",\n    \"id\": 3501,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 269\n  },\n  {\n    \"title\": \"Hamlet (1990)\",\n    \"id\": 3723,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 269\n  },\n  {\n    \"title\": \"It Could Happen to You (1994)\",\n    \"id\": 361,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 268\n  },\n  {\n    \"title\": \"All Quiet on the Western Front (1930)\",\n    \"id\": 1927,\n    \"genres\": [\n      \"War\"\n    ],\n    \"count\": 268\n  },\n  {\n    \"title\": \"Nightmare on Elm Street 3: Dream Warriors, A (1987)\",\n    \"id\": 1970,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 268\n  },\n  {\n    \"title\": \"Parent Trap, The (1998)\",\n    \"id\": 2059,\n    \"genres\": [\n      \"Children's\",\n      \"Drama\"\n    ],\n    \"count\": 268\n  },\n  {\n    \"title\": \"Inherit the Wind (1960)\",\n    \"id\": 3469,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 268\n  },\n  {\n    \"title\": \"Flintstones, The (1994)\",\n    \"id\": 355,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 267\n  },\n  {\n    \"title\": \"Breaking the Waves (1996)\",\n    \"id\": 1354,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 267\n  },\n  {\n    \"title\": \"Slums of Beverly Hills, The (1998)\",\n    \"id\": 2155,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 267\n  },\n  {\n    \"title\": \"Eye of the Beholder (1999)\",\n    \"id\": 3238,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 267\n  },\n  {\n    \"title\": \"Firestarter (1984)\",\n    \"id\": 3708,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 267\n  },\n  {\n    \"title\": \"Unbearable Lightness of Being, The (1988)\",\n    \"id\": 1295,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 266\n  },\n  {\n    \"title\": \"Fools Rush In (1997)\",\n    \"id\": 1457,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 266\n  },\n  {\n    \"title\": \"Ulee's Gold (1997)\",\n    \"id\": 1633,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 266\n  },\n  {\n    \"title\": \"Varsity Blues (1999)\",\n    \"id\": 2447,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 266\n  },\n  {\n    \"title\": \"Towering Inferno, The (1974)\",\n    \"id\": 2524,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 266\n  },\n  {\n    \"title\": \"Instinct (1999)\",\n    \"id\": 2676,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 266\n  },\n  {\n    \"title\": \"Bamba, La (1987)\",\n    \"id\": 3478,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 266\n  },\n  {\n    \"title\": \"Smilla's Sense of Snow (1997)\",\n    \"id\": 1480,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 265\n  },\n  {\n    \"title\": \"Mrs. Brown (Her Majesty, Mrs. Brown) (1997)\",\n    \"id\": 1643,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 265\n  },\n  {\n    \"title\": \"American Werewolf in Paris, An (1997)\",\n    \"id\": 2793,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 265\n  },\n  {\n    \"title\": \"Where Eagles Dare (1969)\",\n    \"id\": 3366,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"War\"\n    ],\n    \"count\": 265\n  },\n  {\n    \"title\": \"Wild Bunch, The (1969)\",\n    \"id\": 599,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 264\n  },\n  {\n    \"title\": \"Kids in the Hall: Brain Candy (1996)\",\n    \"id\": 663,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 264\n  },\n  {\n    \"title\": \"Mercury Rising (1998)\",\n    \"id\": 1833,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 264\n  },\n  {\n    \"title\": \"Tender Mercies (1983)\",\n    \"id\": 2070,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 264\n  },\n  {\n    \"title\": \"Psycho (1998)\",\n    \"id\": 2389,\n    \"genres\": [\n      \"Crime\",\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 264\n  },\n  {\n    \"title\": \"Loaded Weapon 1 (1993)\",\n    \"id\": 3208,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 264\n  },\n  {\n    \"title\": \"Bronx Tale, A (1993)\",\n    \"id\": 428,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 263\n  },\n  {\n    \"title\": \"Great Expectations (1998)\",\n    \"id\": 1735,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 263\n  },\n  {\n    \"title\": \"Medicine Man (1992)\",\n    \"id\": 2822,\n    \"genres\": [\n      \"Adventure\",\n      \"Romance\"\n    ],\n    \"count\": 263\n  },\n  {\n    \"title\": \"Candyman (1992)\",\n    \"id\": 1342,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 262\n  },\n  {\n    \"title\": \"Muppet Christmas Carol, The (1992)\",\n    \"id\": 2083,\n    \"genres\": [\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 262\n  },\n  {\n    \"title\": \"Immortal Beloved (1994)\",\n    \"id\": 249,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 261\n  },\n  {\n    \"title\": \"Guys and Dolls (1955)\",\n    \"id\": 3549,\n    \"genres\": [\n      \"Musical\"\n    ],\n    \"count\": 260\n  },\n  {\n    \"title\": \"Casper (1995)\",\n    \"id\": 158,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 259\n  },\n  {\n    \"title\": \"Mis\\u00e9rables, Les (1998)\",\n    \"id\": 1873,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 259\n  },\n  {\n    \"title\": \"54 (1998)\",\n    \"id\": 2188,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 259\n  },\n  {\n    \"title\": \"Cocoon: The Return (1988)\",\n    \"id\": 2408,\n    \"genres\": [\n      \"Comedy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 259\n  },\n  {\n    \"title\": \"Running Scared (1986)\",\n    \"id\": 2457,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 259\n  },\n  {\n    \"title\": \"Cradle Will Rock, The (1999)\",\n    \"id\": 3145,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 259\n  },\n  {\n    \"title\": \"Red Rock West (1992)\",\n    \"id\": 373,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 258\n  },\n  {\n    \"title\": \"Parent Trap, The (1961)\",\n    \"id\": 1013,\n    \"genres\": [\n      \"Children's\",\n      \"Drama\"\n    ],\n    \"count\": 258\n  },\n  {\n    \"title\": \"7th Voyage of Sinbad, The (1958)\",\n    \"id\": 3153,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Fantasy\"\n    ],\n    \"count\": 258\n  },\n  {\n    \"title\": \"Forget Paris (1995)\",\n    \"id\": 237,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 257\n  },\n  {\n    \"title\": \"Corrina, Corrina (1994)\",\n    \"id\": 351,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 257\n  },\n  {\n    \"title\": \"Diva (1981)\",\n    \"id\": 1264,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"Mystery\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 257\n  },\n  {\n    \"title\": \"Muppet Treasure Island (1996)\",\n    \"id\": 107,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 256\n  },\n  {\n    \"title\": \"Last Man Standing (1996)\",\n    \"id\": 996,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"Western\"\n    ],\n    \"count\": 256\n  },\n  {\n    \"title\": \"Outsiders, The (1983)\",\n    \"id\": 2114,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 256\n  },\n  {\n    \"title\": \"Disclosure (1994)\",\n    \"id\": 225,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 254\n  },\n  {\n    \"title\": \"Farewell My Concubine (1993)\",\n    \"id\": 446,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 254\n  },\n  {\n    \"title\": \"Last Temptation of Christ, The (1988)\",\n    \"id\": 2022,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 254\n  },\n  {\n    \"title\": \"Bottle Rocket (1996)\",\n    \"id\": 101,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 253\n  },\n  {\n    \"title\": \"39 Steps, The (1935)\",\n    \"id\": 965,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 253\n  },\n  {\n    \"title\": \"Falcon and the Snowman, The (1984)\",\n    \"id\": 3169,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 253\n  },\n  {\n    \"title\": \"Man Who Knew Too Little, The (1997)\",\n    \"id\": 1689,\n    \"genres\": [\n      \"Comedy\",\n      \"Mystery\"\n    ],\n    \"count\": 252\n  },\n  {\n    \"title\": \"Take the Money and Run (1969)\",\n    \"id\": 1963,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 252\n  },\n  {\n    \"title\": \"Bachelor, The (1999)\",\n    \"id\": 3004,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 252\n  },\n  {\n    \"title\": \"Bicycle Thief, The (Ladri di biciclette) (1948)\",\n    \"id\": 3089,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 252\n  },\n  {\n    \"title\": \"Wes Craven's New Nightmare (1994)\",\n    \"id\": 366,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 251\n  },\n  {\n    \"title\": \"Top Hat (1935)\",\n    \"id\": 945,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\",\n      \"Romance\"\n    ],\n    \"count\": 251\n  },\n  {\n    \"title\": \"Presidio, The (1988)\",\n    \"id\": 3197,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 251\n  },\n  {\n    \"title\": \"Teenage Mutant Ninja Turtles II: The Secret of the Ooze (1991)\",\n    \"id\": 3439,\n    \"genres\": [\n      \"Action\",\n      \"Children's\",\n      \"Fantasy\"\n    ],\n    \"count\": 251\n  },\n  {\n    \"title\": \"U Turn (1997)\",\n    \"id\": 1627,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Mystery\"\n    ],\n    \"count\": 250\n  },\n  {\n    \"title\": \"Random Hearts (1999)\",\n    \"id\": 2906,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 250\n  },\n  {\n    \"title\": \"Plan 9 from Outer Space (1958)\",\n    \"id\": 1924,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 249\n  },\n  {\n    \"title\": \"Rescuers, The (1977)\",\n    \"id\": 2090,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 249\n  },\n  {\n    \"title\": \"Renaissance Man (1994)\",\n    \"id\": 516,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 248\n  },\n  {\n    \"title\": \"Re-Animator (1985)\",\n    \"id\": 3018,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 248\n  },\n  {\n    \"title\": \"Nutty Professor II: The Klumps (2000)\",\n    \"id\": 3821,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 248\n  },\n  {\n    \"title\": \"Angels in the Outfield (1994)\",\n    \"id\": 1021,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 247\n  },\n  {\n    \"title\": \"Texas Chainsaw Massacre, The (1974)\",\n    \"id\": 2459,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 247\n  },\n  {\n    \"title\": \"Detroit Rock City (1999)\",\n    \"id\": 2772,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 246\n  },\n  {\n    \"title\": \"Brother from Another Planet, The (1984)\",\n    \"id\": 3700,\n    \"genres\": [\n      \"Drama\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 246\n  },\n  {\n    \"title\": \"Blowup (1966)\",\n    \"id\": 3788,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 246\n  },\n  {\n    \"title\": \"Airheads (1994)\",\n    \"id\": 413,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 245\n  },\n  {\n    \"title\": \"Cujo (1983)\",\n    \"id\": 2121,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 245\n  },\n  {\n    \"title\": \"Searchers, The (1956)\",\n    \"id\": 3365,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 245\n  },\n  {\n    \"title\": \"F/X 2 (1992)\",\n    \"id\": 3764,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 245\n  },\n  {\n    \"title\": \"Tampopo (1986)\",\n    \"id\": 3819,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 245\n  },\n  {\n    \"title\": \"Mary Shelley's Frankenstein (1994)\",\n    \"id\": 273,\n    \"genres\": [\n      \"Drama\",\n      \"Horror\"\n    ],\n    \"count\": 244\n  },\n  {\n    \"title\": \"Killer, The (Die xue shuang xiong) (1989)\",\n    \"id\": 1218,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 244\n  },\n  {\n    \"title\": \"Gung Ho (1986)\",\n    \"id\": 2374,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 244\n  },\n  {\n    \"title\": \"Jagged Edge (1985)\",\n    \"id\": 3102,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 244\n  },\n  {\n    \"title\": \"Three Colors: Blue (1993)\",\n    \"id\": 307,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 243\n  },\n  {\n    \"title\": \"Richard III (1995)\",\n    \"id\": 41,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 242\n  },\n  {\n    \"title\": \"Love Bug, The (1969)\",\n    \"id\": 1010,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 242\n  },\n  {\n    \"title\": \"Wings of Desire (Der Himmel \\u00fcber Berlin) (1987)\",\n    \"id\": 1211,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 242\n  },\n  {\n    \"title\": \"Nightmare on Elm Street 4: The Dream Master, A (1988)\",\n    \"id\": 1971,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 242\n  },\n  {\n    \"title\": \"Needful Things (1993)\",\n    \"id\": 2120,\n    \"genres\": [\n      \"Drama\",\n      \"Horror\"\n    ],\n    \"count\": 242\n  },\n  {\n    \"title\": \"Once Upon a Time in America (1984)\",\n    \"id\": 1227,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 241\n  },\n  {\n    \"title\": \"Hang 'em High (1967)\",\n    \"id\": 2922,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 241\n  },\n  {\n    \"title\": \"Badlands (1973)\",\n    \"id\": 3741,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 241\n  },\n  {\n    \"title\": \"Replacements, The (2000)\",\n    \"id\": 3861,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 241\n  },\n  {\n    \"title\": \"Addicted to Love (1997)\",\n    \"id\": 1541,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 240\n  },\n  {\n    \"title\": \"Funny Farm (1988)\",\n    \"id\": 2796,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 240\n  },\n  {\n    \"title\": \"Jungle Book, The (1994)\",\n    \"id\": 362,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Romance\"\n    ],\n    \"count\": 239\n  },\n  {\n    \"title\": \"Multiplicity (1996)\",\n    \"id\": 719,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 239\n  },\n  {\n    \"title\": \"Child's Play (1988)\",\n    \"id\": 1991,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 239\n  },\n  {\n    \"title\": \"Rescuers Down Under, The (1990)\",\n    \"id\": 2089,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 239\n  },\n  {\n    \"title\": \"My Blue Heaven (1990)\",\n    \"id\": 2249,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 239\n  },\n  {\n    \"title\": \"Stop Making Sense (1984)\",\n    \"id\": 2859,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 239\n  },\n  {\n    \"title\": \"Nosferatu (Nosferatu, eine Symphonie des Grauens) (1922)\",\n    \"id\": 1348,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 238\n  },\n  {\n    \"title\": \"Brassed Off (1996)\",\n    \"id\": 1542,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 238\n  },\n  {\n    \"title\": \"Doctor Dolittle (1967)\",\n    \"id\": 2135,\n    \"genres\": [\n      \"Adventure\",\n      \"Musical\"\n    ],\n    \"count\": 238\n  },\n  {\n    \"title\": \"My Bodyguard (1980)\",\n    \"id\": 2240,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 238\n  },\n  {\n    \"title\": \"Fletch Lives (1989)\",\n    \"id\": 2372,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 238\n  },\n  {\n    \"title\": \"Dracula (1931)\",\n    \"id\": 2644,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 238\n  },\n  {\n    \"title\": \"Chain Reaction (1996)\",\n    \"id\": 836,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Thriller\"\n    ],\n    \"count\": 237\n  },\n  {\n    \"title\": \"Relic, The (1997)\",\n    \"id\": 879,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 237\n  },\n  {\n    \"title\": \"Nine 1/2 Weeks (1986)\",\n    \"id\": 3707,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 237\n  },\n  {\n    \"title\": \"White Squall (1996)\",\n    \"id\": 86,\n    \"genres\": [\n      \"Adventure\",\n      \"Drama\"\n    ],\n    \"count\": 236\n  },\n  {\n    \"title\": \"My Own Private Idaho (1991)\",\n    \"id\": 1611,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 236\n  },\n  {\n    \"title\": \"Best Years of Our Lives, The (1946)\",\n    \"id\": 1939,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 236\n  },\n  {\n    \"title\": \"Practical Magic (1998)\",\n    \"id\": 2316,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 236\n  },\n  {\n    \"title\": \"Exotica (1994)\",\n    \"id\": 233,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 235\n  },\n  {\n    \"title\": \"Forces of Nature (1999)\",\n    \"id\": 2558,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 235\n  },\n  {\n    \"title\": \"Cookie's Fortune (1999)\",\n    \"id\": 2583,\n    \"genres\": [\n      \"Mystery\"\n    ],\n    \"count\": 235\n  },\n  {\n    \"title\": \"Croupier (1998)\",\n    \"id\": 3783,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 235\n  },\n  {\n    \"title\": \"Homeward Bound: The Incredible Journey (1993)\",\n    \"id\": 1015,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 234\n  },\n  {\n    \"title\": \"Battleship Potemkin, The (Bronenosets Potyomkin) (1925)\",\n    \"id\": 3742,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 234\n  },\n  {\n    \"title\": \"Bring It On (2000)\",\n    \"id\": 3882,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 234\n  },\n  {\n    \"title\": \"Nobody's Fool (1994)\",\n    \"id\": 281,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 233\n  },\n  {\n    \"title\": \"She's the One (1996)\",\n    \"id\": 804,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 233\n  },\n  {\n    \"title\": \"Children of the Corn (1984)\",\n    \"id\": 2122,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 233\n  },\n  {\n    \"title\": \"Shadow of a Doubt (1943)\",\n    \"id\": 2203,\n    \"genres\": [\n      \"Film-Noir\",\n      \"Thriller\"\n    ],\n    \"count\": 233\n  },\n  {\n    \"title\": \"Cube (1997)\",\n    \"id\": 2232,\n    \"genres\": [\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 233\n  },\n  {\n    \"title\": \"Fly Away Home (1996)\",\n    \"id\": 986,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 232\n  },\n  {\n    \"title\": \"Apple Dumpling Gang, The (1975)\",\n    \"id\": 1007,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\",\n      \"Western\"\n    ],\n    \"count\": 232\n  },\n  {\n    \"title\": \"Beverly Hills Ninja (1997)\",\n    \"id\": 1431,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 232\n  },\n  {\n    \"title\": \"Muppets From Space (1999)\",\n    \"id\": 2709,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 232\n  },\n  {\n    \"title\": \"Batman: Mask of the Phantasm (1993)\",\n    \"id\": 3213,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 232\n  },\n  {\n    \"title\": \"Invisible Man, The (1933)\",\n    \"id\": 3932,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 232\n  },\n  {\n    \"title\": \"Howling, The (1980)\",\n    \"id\": 1130,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 231\n  },\n  {\n    \"title\": \"Transformers: The Movie, The (1986)\",\n    \"id\": 1205,\n    \"genres\": [\n      \"Action\",\n      \"Animation\",\n      \"Children's\",\n      \"Sci-Fi\",\n      \"Thriller\",\n      \"War\"\n    ],\n    \"count\": 231\n  },\n  {\n    \"title\": \"Mouse Hunt (1997)\",\n    \"id\": 1713,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 231\n  },\n  {\n    \"title\": \"Clan of the Cave Bear, The (1986)\",\n    \"id\": 2147,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 231\n  },\n  {\n    \"title\": \"Paths of Glory (1957)\",\n    \"id\": 1178,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 230\n  },\n  {\n    \"title\": \"Some Kind of Wonderful (1987)\",\n    \"id\": 1290,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 230\n  },\n  {\n    \"title\": \"Blues Brothers 2000 (1998)\",\n    \"id\": 1772,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 230\n  },\n  {\n    \"title\": \"Buena Vista Social Club (1999)\",\n    \"id\": 2677,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 230\n  },\n  {\n    \"title\": \"Nick of Time (1995)\",\n    \"id\": 89,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 229\n  },\n  {\n    \"title\": \"Mission, The (1986)\",\n    \"id\": 2745,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 229\n  },\n  {\n    \"title\": \"Milk Money (1994)\",\n    \"id\": 276,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 228\n  },\n  {\n    \"title\": \"Fearless (1993)\",\n    \"id\": 448,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 228\n  },\n  {\n    \"title\": \"With Honors (1994)\",\n    \"id\": 450,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 228\n  },\n  {\n    \"title\": \"8 1/2 (1963)\",\n    \"id\": 1251,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 228\n  },\n  {\n    \"title\": \"Edge, The (1997)\",\n    \"id\": 1615,\n    \"genres\": [\n      \"Adventure\",\n      \"Thriller\"\n    ],\n    \"count\": 227\n  },\n  {\n    \"title\": \"Deep Rising (1998)\",\n    \"id\": 1762,\n    \"genres\": [\n      \"Action\",\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 227\n  },\n  {\n    \"title\": \"Bell, Book and Candle (1958)\",\n    \"id\": 3516,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 227\n  },\n  {\n    \"title\": \"One Fine Day (1996)\",\n    \"id\": 605,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 226\n  },\n  {\n    \"title\": \"Quiet Man, The (1952)\",\n    \"id\": 1226,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 226\n  },\n  {\n    \"title\": \"Urban Legend (1998)\",\n    \"id\": 2279,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 226\n  },\n  {\n    \"title\": \"Mutiny on the Bounty (1935)\",\n    \"id\": 1931,\n    \"genres\": [\n      \"Adventure\"\n    ],\n    \"count\": 225\n  },\n  {\n    \"title\": \"Police Academy 2: Their First Assignment (1985)\",\n    \"id\": 2379,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 225\n  },\n  {\n    \"title\": \"Brighton Beach Memoirs (1986)\",\n    \"id\": 2736,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 225\n  },\n  {\n    \"title\": \"Angela's Ashes (1999)\",\n    \"id\": 3179,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 225\n  },\n  {\n    \"title\": \"Mis\\u00e9rables, Les (1995)\",\n    \"id\": 73,\n    \"genres\": [\n      \"Drama\",\n      \"Musical\"\n    ],\n    \"count\": 224\n  },\n  {\n    \"title\": \"Trekkies (1997)\",\n    \"id\": 2693,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 224\n  },\n  {\n    \"title\": \"Omega Man, The (1971)\",\n    \"id\": 3032,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 224\n  },\n  {\n    \"title\": \"Santa Claus: The Movie (1985)\",\n    \"id\": 2399,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Fantasy\"\n    ],\n    \"count\": 223\n  },\n  {\n    \"title\": \"Birdy (1984)\",\n    \"id\": 3342,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 223\n  },\n  {\n    \"title\": \"What Ever Happened to Baby Jane? (1962)\",\n    \"id\": 3546,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 223\n  },\n  {\n    \"title\": \"Creature From the Black Lagoon, The (1954)\",\n    \"id\": 3930,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 223\n  },\n  {\n    \"title\": \"Jingle All the Way (1996)\",\n    \"id\": 1359,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 222\n  },\n  {\n    \"title\": \"Nutty Professor, The (1963)\",\n    \"id\": 2136,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 222\n  },\n  {\n    \"title\": \"Jeremiah Johnson (1972)\",\n    \"id\": 3074,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 222\n  },\n  {\n    \"title\": \"Bloodsport (1988)\",\n    \"id\": 3444,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 222\n  },\n  {\n    \"title\": \"Shaft (1971)\",\n    \"id\": 3729,\n    \"genres\": [\n      \"Action\",\n      \"Crime\"\n    ],\n    \"count\": 222\n  },\n  {\n    \"title\": \"Dead Presidents (1995)\",\n    \"id\": 42,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 221\n  },\n  {\n    \"title\": \"Virtuosity (1995)\",\n    \"id\": 338,\n    \"genres\": [\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 221\n  },\n  {\n    \"title\": \"Blown Away (1994)\",\n    \"id\": 423,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 221\n  },\n  {\n    \"title\": \"Winnie the Pooh and the Blustery Day (1968)\",\n    \"id\": 1023,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 221\n  },\n  {\n    \"title\": \"Lolita (1997)\",\n    \"id\": 2025,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 221\n  },\n  {\n    \"title\": \"Song of the South (1946)\",\n    \"id\": 2099,\n    \"genres\": [\n      \"Adventure\",\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 221\n  },\n  {\n    \"title\": \"Kids (1995)\",\n    \"id\": 175,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 220\n  },\n  {\n    \"title\": \"Manon of the Spring (Manon des sources) (1986)\",\n    \"id\": 1132,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 220\n  },\n  {\n    \"title\": \"Palmetto (1998)\",\n    \"id\": 1783,\n    \"genres\": [\n      \"Film-Noir\",\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 220\n  },\n  {\n    \"title\": \"Run Silent, Run Deep (1958)\",\n    \"id\": 2670,\n    \"genres\": [\n      \"War\"\n    ],\n    \"count\": 220\n  },\n  {\n    \"title\": \"Way We Were, The (1973)\",\n    \"id\": 3194,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 220\n  },\n  {\n    \"title\": \"Death Wish (1974)\",\n    \"id\": 3430,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 220\n  },\n  {\n    \"title\": \"Walk in the Clouds, A (1995)\",\n    \"id\": 207,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 219\n  },\n  {\n    \"title\": \"Citizen Ruth (1996)\",\n    \"id\": 1392,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 219\n  },\n  {\n    \"title\": \"Man for All Seasons, A (1966)\",\n    \"id\": 1949,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 219\n  },\n  {\n    \"title\": \"Henry: Portrait of a Serial Killer (1990)\",\n    \"id\": 2159,\n    \"genres\": [\n      \"Crime\",\n      \"Horror\"\n    ],\n    \"count\": 219\n  },\n  {\n    \"title\": \"Village of the Damned (1960)\",\n    \"id\": 2553,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 219\n  },\n  {\n    \"title\": \"Titanic (1953)\",\n    \"id\": 3404,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 219\n  },\n  {\n    \"title\": \"Heart and Souls (1993)\",\n    \"id\": 3466,\n    \"genres\": [\n      \"Comedy\",\n      \"Fantasy\"\n    ],\n    \"count\": 219\n  },\n  {\n    \"title\": \"Love and Death (1975)\",\n    \"id\": 3814,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 219\n  },\n  {\n    \"title\": \"Junior (1994)\",\n    \"id\": 256,\n    \"genres\": [\n      \"Comedy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 218\n  },\n  {\n    \"title\": \"Matilda (1996)\",\n    \"id\": 837,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 218\n  },\n  {\n    \"title\": \"Wings of the Dove, The (1997)\",\n    \"id\": 1683,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 218\n  },\n  {\n    \"title\": \"Showgirls (1995)\",\n    \"id\": 193,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 217\n  },\n  {\n    \"title\": \"Cold Comfort Farm (1995)\",\n    \"id\": 728,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 217\n  },\n  {\n    \"title\": \"Indecent Proposal (1993)\",\n    \"id\": 2269,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 217\n  },\n  {\n    \"title\": \"Very Bad Things (1998)\",\n    \"id\": 2387,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\"\n    ],\n    \"count\": 217\n  },\n  {\n    \"title\": \"Hard Target (1993)\",\n    \"id\": 464,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 216\n  },\n  {\n    \"title\": \"Hellraiser: Bloodline (1996)\",\n    \"id\": 611,\n    \"genres\": [\n      \"Action\",\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 216\n  },\n  {\n    \"title\": \"Jean de Florette (1986)\",\n    \"id\": 1131,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 216\n  },\n  {\n    \"title\": \"Great Dictator, The (1940)\",\n    \"id\": 1281,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 216\n  },\n  {\n    \"title\": \"Bride of Frankenstein (1935)\",\n    \"id\": 1340,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 216\n  },\n  {\n    \"title\": \"Vegas Vacation (1997)\",\n    \"id\": 1461,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 216\n  },\n  {\n    \"title\": \"Nell (1994)\",\n    \"id\": 282,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 215\n  },\n  {\n    \"title\": \"East of Eden (1955)\",\n    \"id\": 949,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 215\n  },\n  {\n    \"title\": \"Freddy's Dead: The Final Nightmare (1991)\",\n    \"id\": 1973,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 215\n  },\n  {\n    \"title\": \"All Dogs Go to Heaven (1989)\",\n    \"id\": 2123,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 215\n  },\n  {\n    \"title\": \"Central Station (Central do Brasil) (1998)\",\n    \"id\": 2357,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 215\n  },\n  {\n    \"title\": \"Reds (1981)\",\n    \"id\": 2929,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 215\n  },\n  {\n    \"title\": \"Yojimbo (1961)\",\n    \"id\": 3030,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Western\"\n    ],\n    \"count\": 215\n  },\n  {\n    \"title\": \"Jungle Fever (1991)\",\n    \"id\": 3426,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 215\n  },\n  {\n    \"title\": \"Mod Squad, The (1999)\",\n    \"id\": 2568,\n    \"genres\": [\n      \"Action\",\n      \"Crime\"\n    ],\n    \"count\": 214\n  },\n  {\n    \"title\": \"Smoke (1995)\",\n    \"id\": 194,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 213\n  },\n  {\n    \"title\": \"Operation Dumbo Drop (1995)\",\n    \"id\": 688,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Comedy\",\n      \"War\"\n    ],\n    \"count\": 213\n  },\n  {\n    \"title\": \"Suicide Kings (1997)\",\n    \"id\": 1799,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 213\n  },\n  {\n    \"title\": \"Oliver! (1968)\",\n    \"id\": 1951,\n    \"genres\": [\n      \"Musical\"\n    ],\n    \"count\": 213\n  },\n  {\n    \"title\": \"Something Wicked This Way Comes (1983)\",\n    \"id\": 2097,\n    \"genres\": [\n      \"Children's\",\n      \"Horror\"\n    ],\n    \"count\": 213\n  },\n  {\n    \"title\": \"Lifeboat (1944)\",\n    \"id\": 2202,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\",\n      \"War\"\n    ],\n    \"count\": 213\n  },\n  {\n    \"title\": \"Superstar (1999)\",\n    \"id\": 2907,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 213\n  },\n  {\n    \"title\": \"Places in the Heart (1984)\",\n    \"id\": 3111,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 213\n  },\n  {\n    \"title\": \"Murder in the First (1995)\",\n    \"id\": 280,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 212\n  },\n  {\n    \"title\": \"Paper, The (1994)\",\n    \"id\": 371,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 212\n  },\n  {\n    \"title\": \"Man Without a Face, The (1993)\",\n    \"id\": 491,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 212\n  },\n  {\n    \"title\": \"Up in Smoke (1978)\",\n    \"id\": 1194,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 212\n  },\n  {\n    \"title\": \"Never Cry Wolf (1983)\",\n    \"id\": 3347,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 212\n  },\n  {\n    \"title\": \"Nine Months (1995)\",\n    \"id\": 186,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 211\n  },\n  {\n    \"title\": \"When a Man Loves a Woman (1994)\",\n    \"id\": 381,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 211\n  },\n  {\n    \"title\": \"Object of My Affection, The (1998)\",\n    \"id\": 1821,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 211\n  },\n  {\n    \"title\": \"Tommy (1975)\",\n    \"id\": 2877,\n    \"genres\": [\n      \"Drama\",\n      \"Musical\"\n    ],\n    \"count\": 211\n  },\n  {\n    \"title\": \"Jaws 3-D (1983)\",\n    \"id\": 1389,\n    \"genres\": [\n      \"Action\",\n      \"Horror\"\n    ],\n    \"count\": 210\n  },\n  {\n    \"title\": \"Horse Whisperer, The (1998)\",\n    \"id\": 1727,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 210\n  },\n  {\n    \"title\": \"Blind Date (1987)\",\n    \"id\": 3394,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 210\n  },\n  {\n    \"title\": \"She's Gotta Have It (1986)\",\n    \"id\": 3422,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 210\n  },\n  {\n    \"title\": \"Magnum Force (1973)\",\n    \"id\": 3682,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 210\n  },\n  {\n    \"title\": \"Swimming with Sharks (1995)\",\n    \"id\": 322,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 209\n  },\n  {\n    \"title\": \"Aladdin and the King of Thieves (1996)\",\n    \"id\": 1064,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 209\n  },\n  {\n    \"title\": \"Madonna: Truth or Dare (1991)\",\n    \"id\": 1191,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 209\n  },\n  {\n    \"title\": \"Barcelona (1994)\",\n    \"id\": 417,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 208\n  },\n  {\n    \"title\": \"McCabe & Mrs. Miller (1971)\",\n    \"id\": 3093,\n    \"genres\": [\n      \"Drama\",\n      \"Western\"\n    ],\n    \"count\": 208\n  },\n  {\n    \"title\": \"Tao of Steve, The (2000)\",\n    \"id\": 3852,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 208\n  },\n  {\n    \"title\": \"Sirens (1994)\",\n    \"id\": 537,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 207\n  },\n  {\n    \"title\": \"Idle Hands (1999)\",\n    \"id\": 2606,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 207\n  },\n  {\n    \"title\": \"Son in Law (1993)\",\n    \"id\": 542,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 206\n  },\n  {\n    \"title\": \"Spellbound (1945)\",\n    \"id\": 931,\n    \"genres\": [\n      \"Mystery\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 206\n  },\n  {\n    \"title\": \"Hard Rain (1998)\",\n    \"id\": 1752,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 206\n  },\n  {\n    \"title\": \"Attack of the Killer Tomatoes! (1980)\",\n    \"id\": 2163,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 206\n  },\n  {\n    \"title\": \"General, The (1927)\",\n    \"id\": 3022,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 206\n  },\n  {\n    \"title\": \"Shop Around the Corner, The (1940)\",\n    \"id\": 3097,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 206\n  },\n  {\n    \"title\": \"Adventures of Rocky and Bullwinkle, The (2000)\",\n    \"id\": 3754,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 206\n  },\n  {\n    \"title\": \"Abbott and Costello Meet Frankenstein (1948)\",\n    \"id\": 3928,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 206\n  },\n  {\n    \"title\": \"Living in Oblivion (1995)\",\n    \"id\": 176,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 205\n  },\n  {\n    \"title\": \"Marty (1955)\",\n    \"id\": 1946,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 205\n  },\n  {\n    \"title\": \"Jack Frost (1998)\",\n    \"id\": 2392,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 205\n  },\n  {\n    \"title\": \"Daylight (1996)\",\n    \"id\": 798,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Thriller\"\n    ],\n    \"count\": 204\n  },\n  {\n    \"title\": \"Boys from Brazil, The (1978)\",\n    \"id\": 3204,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 204\n  },\n  {\n    \"title\": \"To Wong Foo, Thanks for Everything! Julie Newmar (1995)\",\n    \"id\": 203,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 203\n  },\n  {\n    \"title\": \"Under Siege 2: Dark Territory (1995)\",\n    \"id\": 204,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 203\n  },\n  {\n    \"title\": \"Highlander III: The Sorcerer (1994)\",\n    \"id\": 405,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 203\n  },\n  {\n    \"title\": \"Shadow, The (1994)\",\n    \"id\": 533,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 203\n  },\n  {\n    \"title\": \"To Sir with Love (1967)\",\n    \"id\": 3296,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 203\n  },\n  {\n    \"title\": \"Three Colors: White (1994)\",\n    \"id\": 308,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 202\n  },\n  {\n    \"title\": \"Robocop 3 (1993)\",\n    \"id\": 519,\n    \"genres\": [\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 202\n  },\n  {\n    \"title\": \"Simon Birch (1998)\",\n    \"id\": 2236,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 202\n  },\n  {\n    \"title\": \"Sister Act 2: Back in the Habit (1993)\",\n    \"id\": 3248,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 202\n  },\n  {\n    \"title\": \"My Life in Pink (Ma vie en rose) (1997)\",\n    \"id\": 1734,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 201\n  },\n  {\n    \"title\": \"American Tail: Fievel Goes West, An (1991)\",\n    \"id\": 2142,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 201\n  },\n  {\n    \"title\": \"Blame It on Rio (1984)\",\n    \"id\": 2259,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 201\n  },\n  {\n    \"title\": \"Runaway Train (1985)\",\n    \"id\": 2344,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 201\n  },\n  {\n    \"title\": \"Lolita (1962)\",\n    \"id\": 2729,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 201\n  },\n  {\n    \"title\": \"Coyote Ugly (2000)\",\n    \"id\": 3825,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 201\n  },\n  {\n    \"title\": \"Bean (1997)\",\n    \"id\": 1665,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 200\n  },\n  {\n    \"title\": \"Nightmare on Elm Street 5: The Dream Child, A (1989)\",\n    \"id\": 1972,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 200\n  },\n  {\n    \"title\": \"Black Cauldron, The (1985)\",\n    \"id\": 2033,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 200\n  },\n  {\n    \"title\": \"End of the Affair, The (1999)\",\n    \"id\": 3125,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 200\n  },\n  {\n    \"title\": \"Cutting Edge, The (1992)\",\n    \"id\": 3270,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 200\n  },\n  {\n    \"title\": \"Dancer in the Dark (2000)\",\n    \"id\": 3910,\n    \"genres\": [\n      \"Drama\",\n      \"Musical\"\n    ],\n    \"count\": 200\n  },\n  {\n    \"title\": \"Friday the 13th Part 3: 3D (1982)\",\n    \"id\": 1976,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 199\n  },\n  {\n    \"title\": \"Halloween: H20 (1998)\",\n    \"id\": 2107,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 199\n  },\n  {\n    \"title\": \"Lady Vanishes, The (1938)\",\n    \"id\": 2208,\n    \"genres\": [\n      \"Comedy\",\n      \"Mystery\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 199\n  },\n  {\n    \"title\": \"Out-of-Towners, The (1999)\",\n    \"id\": 2574,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 199\n  },\n  {\n    \"title\": \"Teaching Mrs. Tingle (1999)\",\n    \"id\": 2806,\n    \"genres\": [\n      \"Comedy\",\n      \"Thriller\"\n    ],\n    \"count\": 199\n  },\n  {\n    \"title\": \"Buddy Holly Story, The (1978)\",\n    \"id\": 2866,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 199\n  },\n  {\n    \"title\": \"...And Justice for All (1979)\",\n    \"id\": 3420,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 199\n  },\n  {\n    \"title\": \"Force 10 from Navarone (1978)\",\n    \"id\": 3519,\n    \"genres\": [\n      \"Action\",\n      \"War\"\n    ],\n    \"count\": 199\n  },\n  {\n    \"title\": \"Eraserhead (1977)\",\n    \"id\": 3676,\n    \"genres\": [\n      \"Drama\",\n      \"Horror\"\n    ],\n    \"count\": 199\n  },\n  {\n    \"title\": \"Anatomy of a Murder (1959)\",\n    \"id\": 3801,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 199\n  },\n  {\n    \"title\": \"Cook the Thief His Wife & Her Lover, The (1989)\",\n    \"id\": 1173,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 198\n  },\n  {\n    \"title\": \"Half Baked (1998)\",\n    \"id\": 1753,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 198\n  },\n  {\n    \"title\": \"Species II (1998)\",\n    \"id\": 1862,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 198\n  },\n  {\n    \"title\": \"Tom Jones (1963)\",\n    \"id\": 1948,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 198\n  },\n  {\n    \"title\": \"Metropolitan (1990)\",\n    \"id\": 1966,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 198\n  },\n  {\n    \"title\": \"Cat from Outer Space, The (1978)\",\n    \"id\": 2038,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 198\n  },\n  {\n    \"title\": \"Affliction (1997)\",\n    \"id\": 2439,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 198\n  },\n  {\n    \"title\": \"Steamboat Willie (1940)\",\n    \"id\": 2102,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 197\n  },\n  {\n    \"title\": \"Next Stop, Wonderland (1998)\",\n    \"id\": 2171,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 196\n  },\n  {\n    \"title\": \"Police Academy 3: Back in Training (1986)\",\n    \"id\": 2380,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 196\n  },\n  {\n    \"title\": \"Boys on the Side (1995)\",\n    \"id\": 218,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 195\n  },\n  {\n    \"title\": \"What's Love Got to Do with It? (1993)\",\n    \"id\": 477,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 195\n  },\n  {\n    \"title\": \"Halloween II (1981)\",\n    \"id\": 1983,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 195\n  },\n  {\n    \"title\": \"Butcher's Wife, The (1991)\",\n    \"id\": 2266,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 195\n  },\n  {\n    \"title\": \"Great Santini, The (1979)\",\n    \"id\": 3135,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 195\n  },\n  {\n    \"title\": \"Coming Home (1978)\",\n    \"id\": 3724,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 195\n  },\n  {\n    \"title\": \"Angels and Insects (1995)\",\n    \"id\": 85,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 194\n  },\n  {\n    \"title\": \"Boomerang (1992)\",\n    \"id\": 122,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 194\n  },\n  {\n    \"title\": \"Christmas Carol, A (1938)\",\n    \"id\": 1099,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 194\n  },\n  {\n    \"title\": \"Night on Earth (1991)\",\n    \"id\": 1279,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 194\n  },\n  {\n    \"title\": \"House of Yes, The (1997)\",\n    \"id\": 1648,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 194\n  },\n  {\n    \"title\": \"Tora! Tora! Tora! (1970)\",\n    \"id\": 3066,\n    \"genres\": [\n      \"War\"\n    ],\n    \"count\": 194\n  },\n  {\n    \"title\": \"In the Mouth of Madness (1995)\",\n    \"id\": 407,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 193\n  },\n  {\n    \"title\": \"D2: The Mighty Ducks (1994)\",\n    \"id\": 2042,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 193\n  },\n  {\n    \"title\": \"Soul Man (1986)\",\n    \"id\": 2473,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 193\n  },\n  {\n    \"title\": \"Barefoot in the Park (1967)\",\n    \"id\": 2870,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 193\n  },\n  {\n    \"title\": \"Black Sheep (1996)\",\n    \"id\": 88,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 192\n  },\n  {\n    \"title\": \"Me Myself I (2000)\",\n    \"id\": 3515,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 192\n  },\n  {\n    \"title\": \"Rope (1948)\",\n    \"id\": 2176,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 191\n  },\n  {\n    \"title\": \"Missing in Action (1984)\",\n    \"id\": 3766,\n    \"genres\": [\n      \"Action\",\n      \"War\"\n    ],\n    \"count\": 191\n  },\n  {\n    \"title\": \"Prophecy, The (1995)\",\n    \"id\": 188,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 190\n  },\n  {\n    \"title\": \"Spice World (1997)\",\n    \"id\": 1760,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 190\n  },\n  {\n    \"title\": \"Nighthawks (1981)\",\n    \"id\": 3029,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 190\n  },\n  {\n    \"title\": \"Drowning Mona (2000)\",\n    \"id\": 3324,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 190\n  },\n  {\n    \"title\": \"Thunderbolt and Lightfoot (1974)\",\n    \"id\": 3769,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 190\n  },\n  {\n    \"title\": \"Very Brady Sequel, A (1996)\",\n    \"id\": 818,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 189\n  },\n  {\n    \"title\": \"Meet Me in St. Louis (1944)\",\n    \"id\": 918,\n    \"genres\": [\n      \"Musical\"\n    ],\n    \"count\": 189\n  },\n  {\n    \"title\": \"Affair to Remember, An (1957)\",\n    \"id\": 932,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 189\n  },\n  {\n    \"title\": \"Kolya (1996)\",\n    \"id\": 1446,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 189\n  },\n  {\n    \"title\": \"Fast, Cheap & Out of Control (1997)\",\n    \"id\": 1649,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 189\n  },\n  {\n    \"title\": \"Big Hit, The (1998)\",\n    \"id\": 1866,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 189\n  },\n  {\n    \"title\": \"Red Sonja (1985)\",\n    \"id\": 2373,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 189\n  },\n  {\n    \"title\": \"Virus (1999)\",\n    \"id\": 2448,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 189\n  },\n  {\n    \"title\": \"Dead Ringers (1988)\",\n    \"id\": 2551,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 189\n  },\n  {\n    \"title\": \"My Science Project (1985)\",\n    \"id\": 2615,\n    \"genres\": [\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 189\n  },\n  {\n    \"title\": \"My Man Godfrey (1936)\",\n    \"id\": 947,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 188\n  },\n  {\n    \"title\": \"Mirror Has Two Faces, The (1996)\",\n    \"id\": 1353,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 188\n  },\n  {\n    \"title\": \"Teenage Mutant Ninja Turtles III (1993)\",\n    \"id\": 3440,\n    \"genres\": [\n      \"Action\",\n      \"Children's\",\n      \"Fantasy\"\n    ],\n    \"count\": 188\n  },\n  {\n    \"title\": \"Mystery Train (1989)\",\n    \"id\": 3521,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 188\n  },\n  {\n    \"title\": \"Canadian Bacon (1994)\",\n    \"id\": 157,\n    \"genres\": [\n      \"Comedy\",\n      \"War\"\n    ],\n    \"count\": 187\n  },\n  {\n    \"title\": \"Cabin Boy (1994)\",\n    \"id\": 429,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 187\n  },\n  {\n    \"title\": \"Gaslight (1944)\",\n    \"id\": 906,\n    \"genres\": [\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 187\n  },\n  {\n    \"title\": \"Gigi (1958)\",\n    \"id\": 938,\n    \"genres\": [\n      \"Musical\"\n    ],\n    \"count\": 187\n  },\n  {\n    \"title\": \"Apt Pupil (1998)\",\n    \"id\": 2320,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 187\n  },\n  {\n    \"title\": \"400 Blows, The (Les Quatre cents coups) (1959)\",\n    \"id\": 2731,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 187\n  },\n  {\n    \"title\": \"Return from Witch Mountain (1978)\",\n    \"id\": 2091,\n    \"genres\": [\n      \"Children's\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 186\n  },\n  {\n    \"title\": \"Hero (1992)\",\n    \"id\": 2252,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 186\n  },\n  {\n    \"title\": \"Hard 8 (a.k.a. Sydney, a.k.a. Hard Eight) (1996)\",\n    \"id\": 2952,\n    \"genres\": [\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 186\n  },\n  {\n    \"title\": \"Saving Grace (2000)\",\n    \"id\": 3831,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 186\n  },\n  {\n    \"title\": \"Malice (1993)\",\n    \"id\": 490,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 185\n  },\n  {\n    \"title\": \"Swing Kids (1993)\",\n    \"id\": 2106,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 185\n  },\n  {\n    \"title\": \"Outrageous Fortune (1987)\",\n    \"id\": 2752,\n    \"genres\": [\n      \"Comedy\",\n      \"Mystery\"\n    ],\n    \"count\": 185\n  },\n  {\n    \"title\": \"Happy, Texas (1999)\",\n    \"id\": 2891,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 185\n  },\n  {\n    \"title\": \"Action Jackson (1988)\",\n    \"id\": 3710,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 185\n  },\n  {\n    \"title\": \"But I'm a Cheerleader (1999)\",\n    \"id\": 3786,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 184\n  },\n  {\n    \"title\": \"Richie Rich (1994)\",\n    \"id\": 374,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 183\n  },\n  {\n    \"title\": \"Halloween: The Curse of Michael Myers (1995)\",\n    \"id\": 891,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 183\n  },\n  {\n    \"title\": \"Hocus Pocus (1993)\",\n    \"id\": 2052,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 183\n  },\n  {\n    \"title\": \"BASEketball (1998)\",\n    \"id\": 2060,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 183\n  },\n  {\n    \"title\": \"Lifeforce (1985)\",\n    \"id\": 2377,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 183\n  },\n  {\n    \"title\": \"Haunting, The (1963)\",\n    \"id\": 2550,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 183\n  },\n  {\n    \"title\": \"Beautiful Girls (1996)\",\n    \"id\": 94,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 182\n  },\n  {\n    \"title\": \"Koyaanisqatsi (1983)\",\n    \"id\": 1289,\n    \"genres\": [\n      \"Documentary\",\n      \"War\"\n    ],\n    \"count\": 182\n  },\n  {\n    \"title\": \"Poltergeist II: The Other Side (1986)\",\n    \"id\": 1995,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 182\n  },\n  {\n    \"title\": \"Center Stage (2000)\",\n    \"id\": 3594,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 182\n  },\n  {\n    \"title\": \"Toxic Avenger, The (1985)\",\n    \"id\": 3693,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 182\n  },\n  {\n    \"title\": \"Supergirl (1984)\",\n    \"id\": 3877,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Fantasy\"\n    ],\n    \"count\": 182\n  },\n  {\n    \"title\": \"Basketball Diaries, The (1995)\",\n    \"id\": 147,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 181\n  },\n  {\n    \"title\": \"Volcano (1997)\",\n    \"id\": 1515,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 181\n  },\n  {\n    \"title\": \"200 Cigarettes (1999)\",\n    \"id\": 2504,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 181\n  },\n  {\n    \"title\": \"King of New York (1990)\",\n    \"id\": 1785,\n    \"genres\": [\n      \"Action\",\n      \"Crime\"\n    ],\n    \"count\": 180\n  },\n  {\n    \"title\": \"Freaky Friday (1977)\",\n    \"id\": 2014,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 180\n  },\n  {\n    \"title\": \"Mighty Joe Young (1998)\",\n    \"id\": 2429,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Drama\"\n    ],\n    \"count\": 180\n  },\n  {\n    \"title\": \"What Planet Are You From? (2000)\",\n    \"id\": 3326,\n    \"genres\": [\n      \"Comedy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 180\n  },\n  {\n    \"title\": \"Big Momma's House (2000)\",\n    \"id\": 3646,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 180\n  },\n  {\n    \"title\": \"Voyage to the Bottom of the Sea (1961)\",\n    \"id\": 3926,\n    \"genres\": [\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 180\n  },\n  {\n    \"title\": \"Persuasion (1995)\",\n    \"id\": 28,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 179\n  },\n  {\n    \"title\": \"Another Stakeout (1993)\",\n    \"id\": 415,\n    \"genres\": [\n      \"Comedy\",\n      \"Thriller\"\n    ],\n    \"count\": 179\n  },\n  {\n    \"title\": \"Robert A. Heinlein's The Puppet Masters (1994)\",\n    \"id\": 512,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 179\n  },\n  {\n    \"title\": \"Eve's Bayou (1997)\",\n    \"id\": 1660,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 179\n  },\n  {\n    \"title\": \"Man Who Knew Too Much, The (1956)\",\n    \"id\": 2183,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 179\n  },\n  {\n    \"title\": \"Living Out Loud (1998)\",\n    \"id\": 2331,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 179\n  },\n  {\n    \"title\": \"Airport (1970)\",\n    \"id\": 2520,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 179\n  },\n  {\n    \"title\": \"Stop! Or My Mom Will Shoot (1992)\",\n    \"id\": 3268,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 179\n  },\n  {\n    \"title\": \"Hamlet (2000)\",\n    \"id\": 3598,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 179\n  },\n  {\n    \"title\": \"Way of the Gun, The (2000)\",\n    \"id\": 3896,\n    \"genres\": [\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 179\n  },\n  {\n    \"title\": \"Screamers (1995)\",\n    \"id\": 76,\n    \"genres\": [\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 178\n  },\n  {\n    \"title\": \"No Escape (1994)\",\n    \"id\": 504,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 178\n  },\n  {\n    \"title\": \"Threesome (1994)\",\n    \"id\": 550,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 178\n  },\n  {\n    \"title\": \"Crush, The (1993)\",\n    \"id\": 3835,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 178\n  },\n  {\n    \"title\": \"Borrowers, The (1997)\",\n    \"id\": 1848,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Comedy\",\n      \"Fantasy\"\n    ],\n    \"count\": 177\n  },\n  {\n    \"title\": \"NeverEnding Story II: The Next Chapter, The (1990)\",\n    \"id\": 2162,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Fantasy\"\n    ],\n    \"count\": 177\n  },\n  {\n    \"title\": \"Matewan (1987)\",\n    \"id\": 3090,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 177\n  },\n  {\n    \"title\": \"Born Yesterday (1950)\",\n    \"id\": 3341,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 177\n  },\n  {\n    \"title\": \"Mr. Saturday Night (1992)\",\n    \"id\": 3500,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 177\n  },\n  {\n    \"title\": \"Killing Zoe (1994)\",\n    \"id\": 482,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 176\n  },\n  {\n    \"title\": \"Kundun (1997)\",\n    \"id\": 1730,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 176\n  },\n  {\n    \"title\": \"Boy Who Could Fly, The (1986)\",\n    \"id\": 2453,\n    \"genres\": [\n      \"Drama\",\n      \"Fantasy\"\n    ],\n    \"count\": 176\n  },\n  {\n    \"title\": \"Hanging Up (2000)\",\n    \"id\": 3299,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 176\n  },\n  {\n    \"title\": \"East is East (1999)\",\n    \"id\": 3538,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 176\n  },\n  {\n    \"title\": \"Bronco Billy (1980)\",\n    \"id\": 3834,\n    \"genres\": [\n      \"Adventure\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 176\n  },\n  {\n    \"title\": \"Spanking the Monkey (1994)\",\n    \"id\": 574,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 175\n  },\n  {\n    \"title\": \"Shall We Dance? (1937)\",\n    \"id\": 1066,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\",\n      \"Romance\"\n    ],\n    \"count\": 175\n  },\n  {\n    \"title\": \"Seventh Sign, The (1988)\",\n    \"id\": 2263,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 175\n  },\n  {\n    \"title\": \"Wing Commander (1999)\",\n    \"id\": 2549,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 175\n  },\n  {\n    \"title\": \"Bear, The (1988)\",\n    \"id\": 3412,\n    \"genres\": [\n      \"Adventure\"\n    ],\n    \"count\": 175\n  },\n  {\n    \"title\": \"Thinner (1996)\",\n    \"id\": 742,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 174\n  },\n  {\n    \"title\": \"Funny Face (1957)\",\n    \"id\": 901,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 174\n  },\n  {\n    \"title\": \"Cat People (1982)\",\n    \"id\": 1346,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 174\n  },\n  {\n    \"title\": \"Police Academy 4: Citizens on Patrol (1987)\",\n    \"id\": 2381,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 174\n  },\n  {\n    \"title\": \"Quest for Fire (1981)\",\n    \"id\": 3036,\n    \"genres\": [\n      \"Adventure\"\n    ],\n    \"count\": 174\n  },\n  {\n    \"title\": \"Once Upon a Time in the West (1969)\",\n    \"id\": 1209,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 173\n  },\n  {\n    \"title\": \"Vampires (1998)\",\n    \"id\": 2328,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 173\n  },\n  {\n    \"title\": \"Who's Harry Crumb? (1989)\",\n    \"id\": 3387,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 173\n  },\n  {\n    \"title\": \"Little Princess, A (1995)\",\n    \"id\": 262,\n    \"genres\": [\n      \"Children's\",\n      \"Drama\"\n    ],\n    \"count\": 172\n  },\n  {\n    \"title\": \"Mr. Nice Guy (1997)\",\n    \"id\": 1858,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 172\n  },\n  {\n    \"title\": \"Last Detail, The (1973)\",\n    \"id\": 3200,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 172\n  },\n  {\n    \"title\": \"Jack (1996)\",\n    \"id\": 765,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 171\n  },\n  {\n    \"title\": \"Ghost and Mrs. Muir, The (1947)\",\n    \"id\": 943,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 171\n  },\n  {\n    \"title\": \"Friday the 13th Part 2 (1981)\",\n    \"id\": 1975,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 171\n  },\n  {\n    \"title\": \"And the Band Played On (1993)\",\n    \"id\": 2071,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 171\n  },\n  {\n    \"title\": \"Ravenous (1999)\",\n    \"id\": 2560,\n    \"genres\": [\n      \"Drama\",\n      \"Horror\"\n    ],\n    \"count\": 171\n  },\n  {\n    \"title\": \"Brokedown Palace (1999)\",\n    \"id\": 2771,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 171\n  },\n  {\n    \"title\": \"Bad Lieutenant (1992)\",\n    \"id\": 3272,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 171\n  },\n  {\n    \"title\": \"House Party (1990)\",\n    \"id\": 3773,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 171\n  },\n  {\n    \"title\": \"Waiting to Exhale (1995)\",\n    \"id\": 4,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 170\n  },\n  {\n    \"title\": \"Miracle on 34th Street (1994)\",\n    \"id\": 277,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 170\n  },\n  {\n    \"title\": \"Michael Collins (1996)\",\n    \"id\": 991,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 170\n  },\n  {\n    \"title\": \"Fog, The (1980)\",\n    \"id\": 1128,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 170\n  },\n  {\n    \"title\": \"Message in a Bottle (1999)\",\n    \"id\": 2497,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 170\n  },\n  {\n    \"title\": \"Sliver (1993)\",\n    \"id\": 540,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 169\n  },\n  {\n    \"title\": \"Freeway (1996)\",\n    \"id\": 1034,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 169\n  },\n  {\n    \"title\": \"Friday the 13th: The Final Chapter (1984)\",\n    \"id\": 1977,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 169\n  },\n  {\n    \"title\": \"Deep End of the Ocean, The (1999)\",\n    \"id\": 2546,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 169\n  },\n  {\n    \"title\": \"Barry Lyndon (1975)\",\n    \"id\": 2730,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 169\n  },\n  {\n    \"title\": \"On the Town (1949)\",\n    \"id\": 3606,\n    \"genres\": [\n      \"Musical\"\n    ],\n    \"count\": 169\n  },\n  {\n    \"title\": \"Belle de jour (1967)\",\n    \"id\": 154,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 168\n  },\n  {\n    \"title\": \"Goofy Movie, A (1995)\",\n    \"id\": 239,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 168\n  },\n  {\n    \"title\": \"Once Were Warriors (1994)\",\n    \"id\": 290,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 168\n  },\n  {\n    \"title\": \"Mother (1996)\",\n    \"id\": 1414,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 168\n  },\n  {\n    \"title\": \"Fierce Creatures (1997)\",\n    \"id\": 1425,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 168\n  },\n  {\n    \"title\": \"Paulie (1998)\",\n    \"id\": 1806,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 168\n  },\n  {\n    \"title\": \"Next Friday (1999)\",\n    \"id\": 3177,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 168\n  },\n  {\n    \"title\": \"Road to El Dorado, The (2000)\",\n    \"id\": 3483,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 168\n  },\n  {\n    \"title\": \"Everest (1998)\",\n    \"id\": 1797,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 167\n  },\n  {\n    \"title\": \"Picnic at Hanging Rock (1975)\",\n    \"id\": 1913,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 167\n  },\n  {\n    \"title\": \"Suspicion (1941)\",\n    \"id\": 2206,\n    \"genres\": [\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 167\n  },\n  {\n    \"title\": \"Mona Lisa (1986)\",\n    \"id\": 2349,\n    \"genres\": [\n      \"Comedy\",\n      \"Thriller\"\n    ],\n    \"count\": 167\n  },\n  {\n    \"title\": \"North Dallas Forty (1979)\",\n    \"id\": 3506,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 167\n  },\n  {\n    \"title\": \"Hellbound: Hellraiser II (1988)\",\n    \"id\": 3918,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 167\n  },\n  {\n    \"title\": \"Restoration (1995)\",\n    \"id\": 43,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 166\n  },\n  {\n    \"title\": \"How to Make an American Quilt (1995)\",\n    \"id\": 46,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 166\n  },\n  {\n    \"title\": \"Things to Do in Denver when You're Dead (1995)\",\n    \"id\": 81,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 166\n  },\n  {\n    \"title\": \"Boxing Helena (1993)\",\n    \"id\": 427,\n    \"genres\": [\n      \"Mystery\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 166\n  },\n  {\n    \"title\": \"House (1986)\",\n    \"id\": 2148,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 166\n  },\n  {\n    \"title\": \"Adventures of Milo and Otis, The (1986)\",\n    \"id\": 2846,\n    \"genres\": [\n      \"Children's\"\n    ],\n    \"count\": 166\n  },\n  {\n    \"title\": \"Carnal Knowledge (1971)\",\n    \"id\": 3167,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 166\n  },\n  {\n    \"title\": \"Damien: Omen II (1978)\",\n    \"id\": 2789,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 165\n  },\n  {\n    \"title\": \"Grand Illusion (Grande illusion, La) (1937)\",\n    \"id\": 3134,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 165\n  },\n  {\n    \"title\": \"Seven Days in May (1964)\",\n    \"id\": 3634,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 165\n  },\n  {\n    \"title\": \"Kid, The (2000)\",\n    \"id\": 3784,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 165\n  },\n  {\n    \"title\": \"Something to Talk About (1995)\",\n    \"id\": 195,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 164\n  },\n  {\n    \"title\": \"Great Race, The (1965)\",\n    \"id\": 1083,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 164\n  },\n  {\n    \"title\": \"Clockwatchers (1997)\",\n    \"id\": 1875,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 164\n  },\n  {\n    \"title\": \"Baby Geniuses (1999)\",\n    \"id\": 2555,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 164\n  },\n  {\n    \"title\": \"Soldier's Story, A (1984)\",\n    \"id\": 2852,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 164\n  },\n  {\n    \"title\": \"Operation Condor (Feiying gaiwak) (1990)\",\n    \"id\": 2879,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Comedy\"\n    ],\n    \"count\": 164\n  },\n  {\n    \"title\": \"Psycho II (1983)\",\n    \"id\": 2902,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 164\n  },\n  {\n    \"title\": \"Return of Jafar, The (1993)\",\n    \"id\": 2092,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 163\n  },\n  {\n    \"title\": \"Other Sister, The (1999)\",\n    \"id\": 2506,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 163\n  },\n  {\n    \"title\": \"Mommie Dearest (1981)\",\n    \"id\": 2639,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 163\n  },\n  {\n    \"title\": \"Outside Providence (1999)\",\n    \"id\": 2836,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 163\n  },\n  {\n    \"title\": \"Titus (1999)\",\n    \"id\": 3181,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 163\n  },\n  {\n    \"title\": \"Twin Peaks: Fire Walk with Me (1992)\",\n    \"id\": 3262,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 163\n  },\n  {\n    \"title\": \"Ghosts of Mississippi (1996)\",\n    \"id\": 1401,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 162\n  },\n  {\n    \"title\": \"Selena (1997)\",\n    \"id\": 1487,\n    \"genres\": [\n      \"Drama\",\n      \"Musical\"\n    ],\n    \"count\": 162\n  },\n  {\n    \"title\": \"Picture Perfect (1997)\",\n    \"id\": 1593,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 162\n  },\n  {\n    \"title\": \"Babes in Toyland (1961)\",\n    \"id\": 2017,\n    \"genres\": [\n      \"Children's\",\n      \"Fantasy\",\n      \"Musical\"\n    ],\n    \"count\": 162\n  },\n  {\n    \"title\": \"Mummy, The (1932)\",\n    \"id\": 2633,\n    \"genres\": [\n      \"Horror\",\n      \"Romance\"\n    ],\n    \"count\": 162\n  },\n  {\n    \"title\": \"Cat's Eye (1985)\",\n    \"id\": 2787,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 162\n  },\n  {\n    \"title\": \"Village of the Damned (1995)\",\n    \"id\": 332,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 161\n  },\n  {\n    \"title\": \"To Be or Not to Be (1942)\",\n    \"id\": 946,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 161\n  },\n  {\n    \"title\": \"Disturbing Behavior (1998)\",\n    \"id\": 2026,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 161\n  },\n  {\n    \"title\": \"One Crazy Summer (1986)\",\n    \"id\": 2261,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 161\n  },\n  {\n    \"title\": \"Monkey Shines (1988)\",\n    \"id\": 2900,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 161\n  },\n  {\n    \"title\": \"Sullivan's Travels (1942)\",\n    \"id\": 2936,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 161\n  },\n  {\n    \"title\": \"Piranha (1978)\",\n    \"id\": 3024,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 161\n  },\n  {\n    \"title\": \"Anywhere But Here (1999)\",\n    \"id\": 3051,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 161\n  },\n  {\n    \"title\": \"Supernova (2000)\",\n    \"id\": 3190,\n    \"genres\": [\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 161\n  },\n  {\n    \"title\": \"Dracula: Dead and Loving It (1995)\",\n    \"id\": 12,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 160\n  },\n  {\n    \"title\": \"Money Train (1995)\",\n    \"id\": 20,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 160\n  },\n  {\n    \"title\": \"Before Sunrise (1995)\",\n    \"id\": 215,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 160\n  },\n  {\n    \"title\": \"He Got Game (1998)\",\n    \"id\": 1840,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 160\n  },\n  {\n    \"title\": \"Hilary and Jackie (1998)\",\n    \"id\": 2442,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 160\n  },\n  {\n    \"title\": \"Problem Child (1990)\",\n    \"id\": 2798,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 160\n  },\n  {\n    \"title\": \"Indochine (1992)\",\n    \"id\": 2943,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 160\n  },\n  {\n    \"title\": \"Next Best Thing, The (2000)\",\n    \"id\": 3325,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 160\n  },\n  {\n    \"title\": \"Hunger, The (1983)\",\n    \"id\": 3550,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 160\n  },\n  {\n    \"title\": \"Paradise Lost: The Child Murders at Robin Hood Hills (1996)\",\n    \"id\": 1361,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 159\n  },\n  {\n    \"title\": \"Only You (1994)\",\n    \"id\": 289,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 158\n  },\n  {\n    \"title\": \"Return of Martin Guerre, The (Retour de Martin Guerre, Le) (1982)\",\n    \"id\": 1150,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 158\n  },\n  {\n    \"title\": \"Darby O'Gill and the Little People (1959)\",\n    \"id\": 2043,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Fantasy\"\n    ],\n    \"count\": 158\n  },\n  {\n    \"title\": \"Pink Flamingos (1972)\",\n    \"id\": 2361,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 158\n  },\n  {\n    \"title\": \"Four Rooms (1995)\",\n    \"id\": 18,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 157\n  },\n  {\n    \"title\": \"Thirty-Two Short Films About Glenn Gould (1993)\",\n    \"id\": 549,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 157\n  },\n  {\n    \"title\": \"Muse, The (1999)\",\n    \"id\": 2829,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 157\n  },\n  {\n    \"title\": \"She-Devil (1989)\",\n    \"id\": 3392,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 157\n  },\n  {\n    \"title\": \"Puppet Master (1989)\",\n    \"id\": 3660,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 157\n  },\n  {\n    \"title\": \"Beverly Hillbillies, The (1993)\",\n    \"id\": 419,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 156\n  },\n  {\n    \"title\": \"Spitfire Grill, The (1996)\",\n    \"id\": 848,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 156\n  },\n  {\n    \"title\": \"Shaggy Dog, The (1959)\",\n    \"id\": 1016,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 156\n  },\n  {\n    \"title\": \"McHale's Navy (1997)\",\n    \"id\": 1445,\n    \"genres\": [\n      \"Comedy\",\n      \"War\"\n    ],\n    \"count\": 156\n  },\n  {\n    \"title\": \"Corruptor, The (1999)\",\n    \"id\": 2540,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 156\n  },\n  {\n    \"title\": \"Benji (1974)\",\n    \"id\": 3672,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 156\n  },\n  {\n    \"title\": \"Pawnbroker, The (1965)\",\n    \"id\": 3789,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 156\n  },\n  {\n    \"title\": \"Next Karate Kid, The (1994)\",\n    \"id\": 502,\n    \"genres\": [\n      \"Action\",\n      \"Children's\"\n    ],\n    \"count\": 155\n  },\n  {\n    \"title\": \"Butcher Boy, The (1998)\",\n    \"id\": 1699,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 155\n  },\n  {\n    \"title\": \"Liberty Heights (1999)\",\n    \"id\": 3078,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 155\n  },\n  {\n    \"title\": \"Lucas (1986)\",\n    \"id\": 3480,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 155\n  },\n  {\n    \"title\": \"Postman Always Rings Twice, The (1981)\",\n    \"id\": 3529,\n    \"genres\": [\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 155\n  },\n  {\n    \"title\": \"Candidate, The (1972)\",\n    \"id\": 1082,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 154\n  },\n  {\n    \"title\": \"Home Alone 3 (1997)\",\n    \"id\": 1707,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 154\n  },\n  {\n    \"title\": \"Journey of Natty Gann, The (1985)\",\n    \"id\": 2077,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 154\n  },\n  {\n    \"title\": \"Man Bites Dog (C'est arriv\\u00e9 pr\\u00e8s de chez vous) (1992)\",\n    \"id\": 3266,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 154\n  },\n  {\n    \"title\": \"Nixon (1995)\",\n    \"id\": 14,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 153\n  },\n  {\n    \"title\": \"Specialist, The (1994)\",\n    \"id\": 315,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 153\n  },\n  {\n    \"title\": \"Dead Man (1995)\",\n    \"id\": 714,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 153\n  },\n  {\n    \"title\": \"Alligator (1980)\",\n    \"id\": 2525,\n    \"genres\": [\n      \"Action\",\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 153\n  },\n  {\n    \"title\": \"Cowboy Way, The (1994)\",\n    \"id\": 438,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 152\n  },\n  {\n    \"title\": \"Program, The (1993)\",\n    \"id\": 511,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 152\n  },\n  {\n    \"title\": \"Twelfth Night (1996)\",\n    \"id\": 892,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 152\n  },\n  {\n    \"title\": \"In the Army Now (1994)\",\n    \"id\": 473,\n    \"genres\": [\n      \"Comedy\",\n      \"War\"\n    ],\n    \"count\": 151\n  },\n  {\n    \"title\": \"Tales from the Crypt Presents: Bordello of Blood (1996)\",\n    \"id\": 842,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 151\n  },\n  {\n    \"title\": \"Love and Death on Long Island (1997)\",\n    \"id\": 1794,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 151\n  },\n  {\n    \"title\": \"Asphalt Jungle, The (1950)\",\n    \"id\": 3364,\n    \"genres\": [\n      \"Crime\",\n      \"Film-Noir\"\n    ],\n    \"count\": 151\n  },\n  {\n    \"title\": \"Golden Voyage of Sinbad, The (1974)\",\n    \"id\": 3771,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 151\n  },\n  {\n    \"title\": \"Police Academy 5: Assignment: Miami Beach (1988)\",\n    \"id\": 2382,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 150\n  },\n  {\n    \"title\": \"Tales from the Hood (1995)\",\n    \"id\": 330,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 149\n  },\n  {\n    \"title\": \"Road to Wellville, The (1994)\",\n    \"id\": 518,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 149\n  },\n  {\n    \"title\": \"Police Academy 6: City Under Siege (1989)\",\n    \"id\": 2383,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 149\n  },\n  {\n    \"title\": \"They Shoot Horses, Don't They? (1969)\",\n    \"id\": 3011,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 149\n  },\n  {\n    \"title\": \"Endless Summer, The (1966)\",\n    \"id\": 3653,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 149\n  },\n  {\n    \"title\": \"Beautician and the Beast, The (1997)\",\n    \"id\": 1453,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 148\n  },\n  {\n    \"title\": \"Rapture, The (1991)\",\n    \"id\": 2024,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 148\n  },\n  {\n    \"title\": \"Jules and Jim (Jules et Jim) (1961)\",\n    \"id\": 2732,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 148\n  },\n  {\n    \"title\": \"Iron Eagle II (1988)\",\n    \"id\": 2816,\n    \"genres\": [\n      \"Action\",\n      \"War\"\n    ],\n    \"count\": 148\n  },\n  {\n    \"title\": \"Near Dark (1987)\",\n    \"id\": 3727,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 148\n  },\n  {\n    \"title\": \"One False Move (1991)\",\n    \"id\": 3728,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 148\n  },\n  {\n    \"title\": \"Jesus' Son (1999)\",\n    \"id\": 3747,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 148\n  },\n  {\n    \"title\": \"Exit to Eden (1994)\",\n    \"id\": 234,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 147\n  },\n  {\n    \"title\": \"Enchanted April (1991)\",\n    \"id\": 1177,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 147\n  },\n  {\n    \"title\": \"Passion Fish (1992)\",\n    \"id\": 1187,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 147\n  },\n  {\n    \"title\": \"Maximum Overdrive (1986)\",\n    \"id\": 2119,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 147\n  },\n  {\n    \"title\": \"Trouble with Harry, The (1955)\",\n    \"id\": 2184,\n    \"genres\": [\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 147\n  },\n  {\n    \"title\": \"Bride of Chucky (1998)\",\n    \"id\": 2315,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 147\n  },\n  {\n    \"title\": \"Original Kings of Comedy, The (2000)\",\n    \"id\": 3865,\n    \"genres\": [\n      \"Comedy\",\n      \"Documentary\"\n    ],\n    \"count\": 147\n  },\n  {\n    \"title\": \"Bank Dick, The (1940)\",\n    \"id\": 3929,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 147\n  },\n  {\n    \"title\": \"Cutthroat Island (1995)\",\n    \"id\": 15,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Romance\"\n    ],\n    \"count\": 146\n  },\n  {\n    \"title\": \"Looking for Richard (1996)\",\n    \"id\": 1050,\n    \"genres\": [\n      \"Documentary\",\n      \"Drama\"\n    ],\n    \"count\": 146\n  },\n  {\n    \"title\": \"Down by Law (1986)\",\n    \"id\": 1273,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 146\n  },\n  {\n    \"title\": \"Jungle2Jungle (a.k.a. Jungle 2 Jungle) (1997)\",\n    \"id\": 1474,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 146\n  },\n  {\n    \"title\": \"Home Fries (1998)\",\n    \"id\": 2385,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 146\n  },\n  {\n    \"title\": \"Stepmom (1998)\",\n    \"id\": 2432,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 146\n  },\n  {\n    \"title\": \"Winslow Boy, The (1998)\",\n    \"id\": 2611,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 146\n  },\n  {\n    \"title\": \"Angus (1995)\",\n    \"id\": 700,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 145\n  },\n  {\n    \"title\": \"Fan, The (1996)\",\n    \"id\": 782,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 145\n  },\n  {\n    \"title\": \"Lost Highway (1997)\",\n    \"id\": 1464,\n    \"genres\": [\n      \"Mystery\"\n    ],\n    \"count\": 145\n  },\n  {\n    \"title\": \"8 Heads in a Duffel Bag (1997)\",\n    \"id\": 1503,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 145\n  },\n  {\n    \"title\": \"Condorman (1981)\",\n    \"id\": 2041,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 145\n  },\n  {\n    \"title\": \"Lady Eve, The (1941)\",\n    \"id\": 2935,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 145\n  },\n  {\n    \"title\": \"Mansfield Park (1999)\",\n    \"id\": 3079,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 145\n  },\n  {\n    \"title\": \"Kid in King Arthur's Court, A (1995)\",\n    \"id\": 258,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Comedy\",\n      \"Fantasy\",\n      \"Romance\"\n    ],\n    \"count\": 144\n  },\n  {\n    \"title\": \"Tales From the Crypt Presents: Demon Knight (1995)\",\n    \"id\": 328,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 144\n  },\n  {\n    \"title\": \"Oliver & Company (1988)\",\n    \"id\": 709,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 144\n  },\n  {\n    \"title\": \"Booty Call (1997)\",\n    \"id\": 1468,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 144\n  },\n  {\n    \"title\": \"Return to Paradise (1998)\",\n    \"id\": 2166,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 144\n  },\n  {\n    \"title\": \"Night at the Roxbury, A (1998)\",\n    \"id\": 2296,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 144\n  },\n  {\n    \"title\": \"Mighty Joe Young (1949)\",\n    \"id\": 2430,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Drama\"\n    ],\n    \"count\": 144\n  },\n  {\n    \"title\": \"Love Letter, The (1999)\",\n    \"id\": 2629,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 144\n  },\n  {\n    \"title\": \"Coma (1978)\",\n    \"id\": 3015,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 144\n  },\n  {\n    \"title\": \"Night to Remember, A (1958)\",\n    \"id\": 3405,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 144\n  },\n  {\n    \"title\": \"Art of War, The (2000)\",\n    \"id\": 3879,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 144\n  },\n  {\n    \"title\": \"Bio-Dome (1996)\",\n    \"id\": 65,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 143\n  },\n  {\n    \"title\": \"Priest (1994)\",\n    \"id\": 299,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 143\n  },\n  {\n    \"title\": \"Getaway, The (1994)\",\n    \"id\": 459,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 143\n  },\n  {\n    \"title\": \"Raisin in the Sun, A (1961)\",\n    \"id\": 3350,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 143\n  },\n  {\n    \"title\": \"Solaris (Solyaris) (1972)\",\n    \"id\": 3503,\n    \"genres\": [\n      \"Drama\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 143\n  },\n  {\n    \"title\": \"Godzilla 2000 (Gojira ni-sen mireniamu) (1999)\",\n    \"id\": 3864,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 143\n  },\n  {\n    \"title\": \"D3: The Mighty Ducks (1996)\",\n    \"id\": 1005,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 142\n  },\n  {\n    \"title\": \"Major League: Back to the Minors (1998)\",\n    \"id\": 1863,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 142\n  },\n  {\n    \"title\": \"Emerald Forest, The (1985)\",\n    \"id\": 2370,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Drama\"\n    ],\n    \"count\": 142\n  },\n  {\n    \"title\": \"Dangerous Minds (1995)\",\n    \"id\": 31,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 141\n  },\n  {\n    \"title\": \"Higher Learning (1995)\",\n    \"id\": 358,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 141\n  },\n  {\n    \"title\": \"Color of Night (1994)\",\n    \"id\": 436,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 141\n  },\n  {\n    \"title\": \"Celluloid Closet, The (1995)\",\n    \"id\": 581,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 141\n  },\n  {\n    \"title\": \"Substitute, The (1996)\",\n    \"id\": 694,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 141\n  },\n  {\n    \"title\": \"Crash (1996)\",\n    \"id\": 1483,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 141\n  },\n  {\n    \"title\": \"Rugrats Movie, The (1998)\",\n    \"id\": 2354,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 141\n  },\n  {\n    \"title\": \"Dreamlife of Angels, The (La Vie r\\u00eav\\u00e9e des anges) (1998)\",\n    \"id\": 2575,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 141\n  },\n  {\n    \"title\": \"Someone to Watch Over Me (1987)\",\n    \"id\": 2956,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 141\n  },\n  {\n    \"title\": \"Kagemusha (1980)\",\n    \"id\": 3091,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 141\n  },\n  {\n    \"title\": \"Volunteers (1985)\",\n    \"id\": 3385,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 141\n  },\n  {\n    \"title\": \"Waking the Dead (1999)\",\n    \"id\": 3457,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 141\n  },\n  {\n    \"title\": \"Porky's II: The Next Day (1983)\",\n    \"id\": 3689,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 141\n  },\n  {\n    \"title\": \"Major Payne (1994)\",\n    \"id\": 267,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 140\n  },\n  {\n    \"title\": \"Speechless (1994)\",\n    \"id\": 378,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 140\n  },\n  {\n    \"title\": \"Sgt. Bilko (1996)\",\n    \"id\": 637,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 140\n  },\n  {\n    \"title\": \"Trip to Bountiful, The (1985)\",\n    \"id\": 2069,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 140\n  },\n  {\n    \"title\": \"How Stella Got Her Groove Back (1998)\",\n    \"id\": 2154,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 140\n  },\n  {\n    \"title\": \"52 Pick-Up (1986)\",\n    \"id\": 2475,\n    \"genres\": [\n      \"Action\",\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 140\n  },\n  {\n    \"title\": \"Up Close and Personal (1996)\",\n    \"id\": 140,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 139\n  },\n  {\n    \"title\": \"Lord of Illusions (1995)\",\n    \"id\": 177,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 139\n  },\n  {\n    \"title\": \"20 Dates (1998)\",\n    \"id\": 2492,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 139\n  },\n  {\n    \"title\": \"SLC Punk! (1998)\",\n    \"id\": 2596,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 139\n  },\n  {\n    \"title\": \"Taming of the Shrew, The (1967)\",\n    \"id\": 3028,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 139\n  },\n  {\n    \"title\": \"Poison Ivy (1992)\",\n    \"id\": 3063,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 139\n  },\n  {\n    \"title\": \"Eyes of Laura Mars (1978)\",\n    \"id\": 3445,\n    \"genres\": [\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 139\n  },\n  {\n    \"title\": \"Houseguest (1994)\",\n    \"id\": 248,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 138\n  },\n  {\n    \"title\": \"Pillow Book, The (1995)\",\n    \"id\": 1554,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 138\n  },\n  {\n    \"title\": \"Loser (2000)\",\n    \"id\": 3616,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 138\n  },\n  {\n    \"title\": \"Easy Money (1983)\",\n    \"id\": 3846,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 138\n  },\n  {\n    \"title\": \"Terminal Velocity (1994)\",\n    \"id\": 548,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 137\n  },\n  {\n    \"title\": \"Postman, The (1997)\",\n    \"id\": 1726,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 137\n  },\n  {\n    \"title\": \"Allan Quartermain and the Lost City of Gold (1987)\",\n    \"id\": 2748,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 137\n  },\n  {\n    \"title\": \"Pollyanna (1960)\",\n    \"id\": 1014,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 136\n  },\n  {\n    \"title\": \"Until the End of the World (Bis ans Ende der Welt) (1991)\",\n    \"id\": 1306,\n    \"genres\": [\n      \"Drama\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 136\n  },\n  {\n    \"title\": \"Walkabout (1971)\",\n    \"id\": 1419,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 136\n  },\n  {\n    \"title\": \"King Kong vs. Godzilla (Kingukongu tai Gojira) (1962)\",\n    \"id\": 2365,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 136\n  },\n  {\n    \"title\": \"Children of the Damned (1963)\",\n    \"id\": 2554,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 136\n  },\n  {\n    \"title\": \"Mildred Pierce (1945)\",\n    \"id\": 2612,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 136\n  },\n  {\n    \"title\": \"Pok\\u00e9mon: The First Movie (1998)\",\n    \"id\": 3054,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 136\n  },\n  {\n    \"title\": \"Pagemaster, The (1994)\",\n    \"id\": 558,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Animation\",\n      \"Children's\",\n      \"Fantasy\"\n    ],\n    \"count\": 135\n  },\n  {\n    \"title\": \"True Crime (1995)\",\n    \"id\": 695,\n    \"genres\": [\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 135\n  },\n  {\n    \"title\": \"Crow: City of Angels, The (1996)\",\n    \"id\": 839,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 135\n  },\n  {\n    \"title\": \"Herbie Rides Again (1974)\",\n    \"id\": 1011,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 135\n  },\n  {\n    \"title\": \"Last Days of Disco, The (1998)\",\n    \"id\": 1836,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 135\n  },\n  {\n    \"title\": \"Dirty Work (1998)\",\n    \"id\": 2195,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 135\n  },\n  {\n    \"title\": \"Thing From Another World, The (1951)\",\n    \"id\": 2660,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 135\n  },\n  {\n    \"title\": \"Frances (1982)\",\n    \"id\": 2757,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 135\n  },\n  {\n    \"title\": \"Highlander: Endgame (2000)\",\n    \"id\": 3889,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Fantasy\"\n    ],\n    \"count\": 135\n  },\n  {\n    \"title\": \"Runaway (1984)\",\n    \"id\": 3937,\n    \"genres\": [\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 135\n  },\n  {\n    \"title\": \"Mortal Kombat: Annihilation (1997)\",\n    \"id\": 1681,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 134\n  },\n  {\n    \"title\": \"Mighty, The (1998)\",\n    \"id\": 2310,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 134\n  },\n  {\n    \"title\": \"Wolf Man, The (1941)\",\n    \"id\": 2654,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 134\n  },\n  {\n    \"title\": \"Beyond the Mat (2000)\",\n    \"id\": 3327,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 134\n  },\n  {\n    \"title\": \"Place in the Sun, A (1951)\",\n    \"id\": 3475,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 134\n  },\n  {\n    \"title\": \"Down Periscope (1996)\",\n    \"id\": 135,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 133\n  },\n  {\n    \"title\": \"Judgment Night (1993)\",\n    \"id\": 479,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 133\n  },\n  {\n    \"title\": \"High Art (1998)\",\n    \"id\": 1897,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 133\n  },\n  {\n    \"title\": \"Child's Play 2 (1990)\",\n    \"id\": 1992,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 133\n  },\n  {\n    \"title\": \"Saturn 3 (1979)\",\n    \"id\": 2851,\n    \"genres\": [\n      \"Adventure\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 133\n  },\n  {\n    \"title\": \"Drunken Master (Zui quan) (1979)\",\n    \"id\": 2924,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 133\n  },\n  {\n    \"title\": \"Music of the Heart (1999)\",\n    \"id\": 2996,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 133\n  },\n  {\n    \"title\": \"Against All Odds (1984)\",\n    \"id\": 3206,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 133\n  },\n  {\n    \"title\": \"War Room, The (1993)\",\n    \"id\": 556,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 132\n  },\n  {\n    \"title\": \"Ninotchka (1939)\",\n    \"id\": 936,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 132\n  },\n  {\n    \"title\": \"Tie Me Up! Tie Me Down! (1990)\",\n    \"id\": 1190,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 132\n  },\n  {\n    \"title\": \"Joe's Apartment (1996)\",\n    \"id\": 829,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 131\n  },\n  {\n    \"title\": \"Old Man and the Sea, The (1958)\",\n    \"id\": 1085,\n    \"genres\": [\n      \"Adventure\",\n      \"Drama\"\n    ],\n    \"count\": 131\n  },\n  {\n    \"title\": \"Inventing the Abbotts (1997)\",\n    \"id\": 1498,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 131\n  },\n  {\n    \"title\": \"Great Mouse Detective, The (1986)\",\n    \"id\": 2048,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 131\n  },\n  {\n    \"title\": \"Meteor (1979)\",\n    \"id\": 2526,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 131\n  },\n  {\n    \"title\": \"True Crime (1999)\",\n    \"id\": 2561,\n    \"genres\": [\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 131\n  },\n  {\n    \"title\": \"Giant (1956)\",\n    \"id\": 948,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 130\n  },\n  {\n    \"title\": \"Dead Man on Campus (1998)\",\n    \"id\": 2169,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 130\n  },\n  {\n    \"title\": \"Armed and Dangerous (1986)\",\n    \"id\": 2458,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\"\n    ],\n    \"count\": 130\n  },\n  {\n    \"title\": \"Killing, The (1956)\",\n    \"id\": 2726,\n    \"genres\": [\n      \"Crime\",\n      \"Film-Noir\"\n    ],\n    \"count\": 130\n  },\n  {\n    \"title\": \"Distinguished Gentleman, The (1992)\",\n    \"id\": 3120,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 130\n  },\n  {\n    \"title\": \"U2: Rattle and Hum (1988)\",\n    \"id\": 3142,\n    \"genres\": [\n      \"Documentary\",\n      \"Musical\"\n    ],\n    \"count\": 130\n  },\n  {\n    \"title\": \"Drop Zone (1994)\",\n    \"id\": 227,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 129\n  },\n  {\n    \"title\": \"Striking Distance (1993)\",\n    \"id\": 544,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 129\n  },\n  {\n    \"title\": \"Double Life of Veronique, The (La Double Vie de V\\u00e9ronique) (1991)\",\n    \"id\": 1176,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 129\n  },\n  {\n    \"title\": \"Halloween 4: The Return of Michael Myers (1988)\",\n    \"id\": 1985,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 129\n  },\n  {\n    \"title\": \"Mafia! (1998)\",\n    \"id\": 2027,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\"\n    ],\n    \"count\": 129\n  },\n  {\n    \"title\": \"Class (1983)\",\n    \"id\": 2241,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 129\n  },\n  {\n    \"title\": \"Tigger Movie, The (2000)\",\n    \"id\": 3287,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 129\n  },\n  {\n    \"title\": \"Breathless (1983)\",\n    \"id\": 3584,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 129\n  },\n  {\n    \"title\": \"City Hall (1996)\",\n    \"id\": 100,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 128\n  },\n  {\n    \"title\": \"Fear (1996)\",\n    \"id\": 662,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 128\n  },\n  {\n    \"title\": \"Wishmaster (1997)\",\n    \"id\": 1623,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 128\n  },\n  {\n    \"title\": \"Computer Wore Tennis Shoes, The (1970)\",\n    \"id\": 2040,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 128\n  },\n  {\n    \"title\": \"Fanny and Alexander (1982)\",\n    \"id\": 2068,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 128\n  },\n  {\n    \"title\": \"Gods Must Be Crazy II, The (1989)\",\n    \"id\": 2151,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 128\n  },\n  {\n    \"title\": \"Man Who Knew Too Much, The (1934)\",\n    \"id\": 2212,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 128\n  },\n  {\n    \"title\": \"Blue Streak (1999)\",\n    \"id\": 2860,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 128\n  },\n  {\n    \"title\": \"Dying Young (1991)\",\n    \"id\": 3436,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 128\n  },\n  {\n    \"title\": \"Flintstones in Viva Rock Vegas, The (2000)\",\n    \"id\": 3564,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 128\n  },\n  {\n    \"title\": \"Ready to Wear (Pret-A-Porter) (1994)\",\n    \"id\": 305,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 127\n  },\n  {\n    \"title\": \"I Love Trouble (1994)\",\n    \"id\": 360,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 127\n  },\n  {\n    \"title\": \"Basquiat (1996)\",\n    \"id\": 851,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 127\n  },\n  {\n    \"title\": \"Marked for Death (1990)\",\n    \"id\": 1382,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 127\n  },\n  {\n    \"title\": \"Halloween 5: The Revenge of Michael Myers (1989)\",\n    \"id\": 1986,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 127\n  },\n  {\n    \"title\": \"Exorcist III, The (1990)\",\n    \"id\": 1999,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 127\n  },\n  {\n    \"title\": \"Bad Seed, The (1956)\",\n    \"id\": 2967,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 127\n  },\n  {\n    \"title\": \"Time Code (2000)\",\n    \"id\": 3571,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 127\n  },\n  {\n    \"title\": \"Girlfight (2000)\",\n    \"id\": 3915,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 127\n  },\n  {\n    \"title\": \"Assassins (1995)\",\n    \"id\": 23,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 126\n  },\n  {\n    \"title\": \"Three Caballeros, The (1945)\",\n    \"id\": 1024,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 126\n  },\n  {\n    \"title\": \"Rosewood (1997)\",\n    \"id\": 1465,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 126\n  },\n  {\n    \"title\": \"Velvet Goldmine (1998)\",\n    \"id\": 2337,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 126\n  },\n  {\n    \"title\": \"Playing by Heart (1998)\",\n    \"id\": 2443,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 126\n  },\n  {\n    \"title\": \"Clay Pigeons (1998)\",\n    \"id\": 2280,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 125\n  },\n  {\n    \"title\": \"Aces: Iron Eagle III (1992)\",\n    \"id\": 2817,\n    \"genres\": [\n      \"Action\",\n      \"War\"\n    ],\n    \"count\": 125\n  },\n  {\n    \"title\": \"Surviving the Game (1994)\",\n    \"id\": 547,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Thriller\"\n    ],\n    \"count\": 124\n  },\n  {\n    \"title\": \"Daytrippers, The (1996)\",\n    \"id\": 1484,\n    \"genres\": [\n      \"Mystery\"\n    ],\n    \"count\": 124\n  },\n  {\n    \"title\": \"Mr. Death: The Rise and Fall of Fred A. Leuchter Jr. (1999)\",\n    \"id\": 3182,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 124\n  },\n  {\n    \"title\": \"Stranger Than Paradise (1984)\",\n    \"id\": 3925,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 124\n  },\n  {\n    \"title\": \"Burnt By the Sun (Utomlyonnye solntsem) (1994)\",\n    \"id\": 213,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 123\n  },\n  {\n    \"title\": \"Phantom, The (1996)\",\n    \"id\": 761,\n    \"genres\": [\n      \"Adventure\"\n    ],\n    \"count\": 123\n  },\n  {\n    \"title\": \"That Darn Cat! (1965)\",\n    \"id\": 1018,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\",\n      \"Mystery\"\n    ],\n    \"count\": 123\n  },\n  {\n    \"title\": \"Wrongfully Accused (1998)\",\n    \"id\": 2170,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 123\n  },\n  {\n    \"title\": \"My Favorite Martian (1999)\",\n    \"id\": 2498,\n    \"genres\": [\n      \"Comedy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 123\n  },\n  {\n    \"title\": \"Lost Weekend, The (1945)\",\n    \"id\": 1938,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 122\n  },\n  {\n    \"title\": \"Tea with Mussolini (1999)\",\n    \"id\": 2436,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 122\n  },\n  {\n    \"title\": \"Down to You (2000)\",\n    \"id\": 3225,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 122\n  },\n  {\n    \"title\": \"Snow Day (2000)\",\n    \"id\": 3286,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 122\n  },\n  {\n    \"title\": \"Gypsy (1962)\",\n    \"id\": 3604,\n    \"genres\": [\n      \"Musical\"\n    ],\n    \"count\": 122\n  },\n  {\n    \"title\": \"Porky's Revenge (1985)\",\n    \"id\": 3690,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 122\n  },\n  {\n    \"title\": \"Last Supper, The (1995)\",\n    \"id\": 627,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 121\n  },\n  {\n    \"title\": \"Mrs. Winterbourne (1996)\",\n    \"id\": 691,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 121\n  },\n  {\n    \"title\": \"Harriet the Spy (1996)\",\n    \"id\": 801,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 121\n  },\n  {\n    \"title\": \"Extreme Measures (1996)\",\n    \"id\": 1003,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 121\n  },\n  {\n    \"title\": \"Paris, Texas (1984)\",\n    \"id\": 1305,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 121\n  },\n  {\n    \"title\": \"My Fellow Americans (1996)\",\n    \"id\": 1390,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 121\n  },\n  {\n    \"title\": \"Stardust Memories (1980)\",\n    \"id\": 2290,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 121\n  },\n  {\n    \"title\": \"Godzilla (Gojira) (1984)\",\n    \"id\": 2364,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 121\n  },\n  {\n    \"title\": \"Dudley Do-Right (1999)\",\n    \"id\": 2828,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 121\n  },\n  {\n    \"title\": \"Blue Hawaii (1961)\",\n    \"id\": 3600,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 121\n  },\n  {\n    \"title\": \"Chuck & Buck (2000)\",\n    \"id\": 3794,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 121\n  },\n  {\n    \"title\": \"Bed of Roses (1996)\",\n    \"id\": 74,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 120\n  },\n  {\n    \"title\": \"Backbeat (1993)\",\n    \"id\": 346,\n    \"genres\": [\n      \"Drama\",\n      \"Musical\"\n    ],\n    \"count\": 120\n  },\n  {\n    \"title\": \"Kazaam (1996)\",\n    \"id\": 810,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\",\n      \"Fantasy\"\n    ],\n    \"count\": 120\n  },\n  {\n    \"title\": \"Communion (1989)\",\n    \"id\": 3758,\n    \"genres\": [\n      \"Drama\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 120\n  },\n  {\n    \"title\": \"Crooklyn (1994)\",\n    \"id\": 352,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 119\n  },\n  {\n    \"title\": \"Newton Boys, The (1998)\",\n    \"id\": 1804,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 119\n  },\n  {\n    \"title\": \"All the King's Men (1949)\",\n    \"id\": 1942,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 119\n  },\n  {\n    \"title\": \"Orgazmo (1997)\",\n    \"id\": 2325,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 119\n  },\n  {\n    \"title\": \"Jawbreaker (1999)\",\n    \"id\": 2500,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 119\n  },\n  {\n    \"title\": \"Earthquake (1974)\",\n    \"id\": 2535,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 119\n  },\n  {\n    \"title\": \"Drive Me Crazy (1999)\",\n    \"id\": 2888,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 119\n  },\n  {\n    \"title\": \"Love and Basketball (2000)\",\n    \"id\": 3554,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 119\n  },\n  {\n    \"title\": \"Marnie (1964)\",\n    \"id\": 2181,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 118\n  },\n  {\n    \"title\": \"Jakob the Liar (1999)\",\n    \"id\": 2882,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 118\n  },\n  {\n    \"title\": \"Days of Heaven (1978)\",\n    \"id\": 2932,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 118\n  },\n  {\n    \"title\": \"Peter's Friends (1992)\",\n    \"id\": 3045,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 118\n  },\n  {\n    \"title\": \"Bonfire of the Vanities (1990)\",\n    \"id\": 3130,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 118\n  },\n  {\n    \"title\": \"Umbrellas of Cherbourg, The (Parapluies de Cherbourg, Les) (1964)\",\n    \"id\": 199,\n    \"genres\": [\n      \"Drama\",\n      \"Musical\"\n    ],\n    \"count\": 117\n  },\n  {\n    \"title\": \"Romeo Is Bleeding (1993)\",\n    \"id\": 521,\n    \"genres\": [\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 117\n  },\n  {\n    \"title\": \"Ruby in Paradise (1993)\",\n    \"id\": 523,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 117\n  },\n  {\n    \"title\": \"Celebration, The (Festen) (1998)\",\n    \"id\": 2360,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 117\n  },\n  {\n    \"title\": \"Gulliver's Travels (1939)\",\n    \"id\": 2899,\n    \"genres\": [\n      \"Adventure\",\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 117\n  },\n  {\n    \"title\": \"Autumn in New York (2000)\",\n    \"id\": 3824,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 117\n  },\n  {\n    \"title\": \"Clockers (1995)\",\n    \"id\": 159,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 116\n  },\n  {\n    \"title\": \"April Fool's Day (1986)\",\n    \"id\": 1330,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 116\n  },\n  {\n    \"title\": \"Life Less Ordinary, A (1997)\",\n    \"id\": 1658,\n    \"genres\": [\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 116\n  },\n  {\n    \"title\": \"Madeline (1998)\",\n    \"id\": 1919,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 116\n  },\n  {\n    \"title\": \"Texas Chainsaw Massacre 2, The (1986)\",\n    \"id\": 2460,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 116\n  },\n  {\n    \"title\": \"Pet Sematary II (1992)\",\n    \"id\": 2514,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 116\n  },\n  {\n    \"title\": \"Jennifer 8 (1992)\",\n    \"id\": 3557,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 116\n  },\n  {\n    \"title\": \"Chungking Express (1994)\",\n    \"id\": 123,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\",\n      \"Romance\"\n    ],\n    \"count\": 115\n  },\n  {\n    \"title\": \"Simply Irresistible (1999)\",\n    \"id\": 2491,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 115\n  },\n  {\n    \"title\": \"Juror, The (1996)\",\n    \"id\": 79,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 114\n  },\n  {\n    \"title\": \"Just Cause (1995)\",\n    \"id\": 257,\n    \"genres\": [\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 114\n  },\n  {\n    \"title\": \"Vanya on 42nd Street (1994)\",\n    \"id\": 334,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 114\n  },\n  {\n    \"title\": \"Orlando (1993)\",\n    \"id\": 506,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 114\n  },\n  {\n    \"title\": \"Friday the 13th Part V: A New Beginning (1985)\",\n    \"id\": 1978,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 114\n  },\n  {\n    \"title\": \"Free Willy 2: The Adventure Home (1995)\",\n    \"id\": 169,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Drama\"\n    ],\n    \"count\": 113\n  },\n  {\n    \"title\": \"Mad City (1997)\",\n    \"id\": 1667,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 113\n  },\n  {\n    \"title\": \"Henry Fool (1997)\",\n    \"id\": 1904,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 113\n  },\n  {\n    \"title\": \"Hamlet (1948)\",\n    \"id\": 1941,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 113\n  },\n  {\n    \"title\": \"Exorcist II: The Heretic (1977)\",\n    \"id\": 1998,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 113\n  },\n  {\n    \"title\": \"Shaggy D.A., The (1976)\",\n    \"id\": 2095,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 113\n  },\n  {\n    \"title\": \"Gate, The (1987)\",\n    \"id\": 2451,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 113\n  },\n  {\n    \"title\": \"Baby... Secret of the Lost Legend (1985)\",\n    \"id\": 3401,\n    \"genres\": [\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 113\n  },\n  {\n    \"title\": \"Sleepwalkers (1992)\",\n    \"id\": 3709,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 113\n  },\n  {\n    \"title\": \"Stuart Saves His Family (1995)\",\n    \"id\": 312,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 112\n  },\n  {\n    \"title\": \"Preacher's Wife, The (1996)\",\n    \"id\": 1363,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 112\n  },\n  {\n    \"title\": \"Air Bud (1997)\",\n    \"id\": 1592,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 112\n  },\n  {\n    \"title\": \"Friday the 13th Part VI: Jason Lives (1986)\",\n    \"id\": 1979,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 112\n  },\n  {\n    \"title\": \"Poltergeist III (1988)\",\n    \"id\": 1996,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 112\n  },\n  {\n    \"title\": \"One True Thing (1998)\",\n    \"id\": 2272,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 112\n  },\n  {\n    \"title\": \"American Pop (1981)\",\n    \"id\": 3725,\n    \"genres\": [\n      \"Animation\",\n      \"Musical\"\n    ],\n    \"count\": 112\n  },\n  {\n    \"title\": \"Pumpkinhead (1988)\",\n    \"id\": 3840,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 112\n  },\n  {\n    \"title\": \"Phantom of the Opera, The (1943)\",\n    \"id\": 3936,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 112\n  },\n  {\n    \"title\": \"Don't Be a Menace to South Central While Drinking Your Juice in the Hood (1996)\",\n    \"id\": 63,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 111\n  },\n  {\n    \"title\": \"Desperate Measures (1998)\",\n    \"id\": 1598,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 111\n  },\n  {\n    \"title\": \"Nights of Cabiria (Le Notti di Cabiria) (1957)\",\n    \"id\": 2351,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 111\n  },\n  {\n    \"title\": \"Best Man, The (1999)\",\n    \"id\": 2975,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 111\n  },\n  {\n    \"title\": \"Murder, My Sweet (1944)\",\n    \"id\": 1069,\n    \"genres\": [\n      \"Film-Noir\",\n      \"Thriller\"\n    ],\n    \"count\": 110\n  },\n  {\n    \"title\": \"Navigator: A Mediaeval Odyssey, The (1988)\",\n    \"id\": 2173,\n    \"genres\": [\n      \"Adventure\",\n      \"Fantasy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 110\n  },\n  {\n    \"title\": \"Nothing in Common (1986)\",\n    \"id\": 2418,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 110\n  },\n  {\n    \"title\": \"Flawless (1999)\",\n    \"id\": 3115,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 110\n  },\n  {\n    \"title\": \"Kull the Conqueror (1997)\",\n    \"id\": 1606,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 109\n  },\n  {\n    \"title\": \"MatchMaker, The (1997)\",\n    \"id\": 1629,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 109\n  },\n  {\n    \"title\": \"Red Corner (1997)\",\n    \"id\": 1686,\n    \"genres\": [\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 109\n  },\n  {\n    \"title\": \"Apple Dumpling Gang Rides Again, The (1979)\",\n    \"id\": 2016,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\",\n      \"Western\"\n    ],\n    \"count\": 109\n  },\n  {\n    \"title\": \"Your Friends and Neighbors (1998)\",\n    \"id\": 2165,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 109\n  },\n  {\n    \"title\": \"Death and the Maiden (1994)\",\n    \"id\": 229,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 108\n  },\n  {\n    \"title\": \"To Gillian on Her 37th Birthday (1996)\",\n    \"id\": 1043,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 108\n  },\n  {\n    \"title\": \"Crucible, The (1996)\",\n    \"id\": 1366,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 108\n  },\n  {\n    \"title\": \"Krippendorf's Tribe (1998)\",\n    \"id\": 1855,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 108\n  },\n  {\n    \"title\": \"Melvin and Howard (1980)\",\n    \"id\": 2988,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 108\n  },\n  {\n    \"title\": \"Splendor in the Grass (1961)\",\n    \"id\": 3330,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 108\n  },\n  {\n    \"title\": \"Little Rascals, The (1994)\",\n    \"id\": 575,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 107\n  },\n  {\n    \"title\": \"Spy Hard (1996)\",\n    \"id\": 743,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 107\n  },\n  {\n    \"title\": \"How Green Was My Valley (1941)\",\n    \"id\": 1935,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 107\n  },\n  {\n    \"title\": \"Pork Chop Hill (1959)\",\n    \"id\": 2669,\n    \"genres\": [\n      \"War\"\n    ],\n    \"count\": 107\n  },\n  {\n    \"title\": \"Pit and the Pendulum (1961)\",\n    \"id\": 2782,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 107\n  },\n  {\n    \"title\": \"Gilda (1946)\",\n    \"id\": 2940,\n    \"genres\": [\n      \"Film-Noir\"\n    ],\n    \"count\": 107\n  },\n  {\n    \"title\": \"Carnival of Souls (1962)\",\n    \"id\": 3627,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 107\n  },\n  {\n    \"title\": \"Flesh and Bone (1993)\",\n    \"id\": 451,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\",\n      \"Romance\"\n    ],\n    \"count\": 106\n  },\n  {\n    \"title\": \"Solo (1996)\",\n    \"id\": 692,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 106\n  },\n  {\n    \"title\": \"Out of the Past (1947)\",\n    \"id\": 2066,\n    \"genres\": [\n      \"Film-Noir\"\n    ],\n    \"count\": 106\n  },\n  {\n    \"title\": \"Sinbad and the Eye of the Tiger (1977)\",\n    \"id\": 3807,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 106\n  },\n  {\n    \"title\": \"Duets (2000)\",\n    \"id\": 3901,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 106\n  },\n  {\n    \"title\": \"Party Girl (1995)\",\n    \"id\": 187,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 105\n  },\n  {\n    \"title\": \"Street Fighter (1994)\",\n    \"id\": 393,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 105\n  },\n  {\n    \"title\": \"Blue Sky (1994)\",\n    \"id\": 425,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 105\n  },\n  {\n    \"title\": \"Heaven & Earth (1993)\",\n    \"id\": 465,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 105\n  },\n  {\n    \"title\": \"Fled (1996)\",\n    \"id\": 809,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 105\n  },\n  {\n    \"title\": \"Gay Divorcee, The (1934)\",\n    \"id\": 907,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\",\n      \"Romance\"\n    ],\n    \"count\": 105\n  },\n  {\n    \"title\": \"Mark of Zorro, The (1940)\",\n    \"id\": 941,\n    \"genres\": [\n      \"Adventure\"\n    ],\n    \"count\": 105\n  },\n  {\n    \"title\": \"Paris Is Burning (1990)\",\n    \"id\": 1192,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 105\n  },\n  {\n    \"title\": \"Halloween III: Season of the Witch (1983)\",\n    \"id\": 1984,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 105\n  },\n  {\n    \"title\": \"Rage: Carrie 2, The (1999)\",\n    \"id\": 2548,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 105\n  },\n  {\n    \"title\": \"Captain Horatio Hornblower (1951)\",\n    \"id\": 3406,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"War\"\n    ],\n    \"count\": 105\n  },\n  {\n    \"title\": \"Bulletproof (1996)\",\n    \"id\": 886,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 104\n  },\n  {\n    \"title\": \"Marvin's Room (1996)\",\n    \"id\": 1399,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 104\n  },\n  {\n    \"title\": \"Homegrown (1998)\",\n    \"id\": 1824,\n    \"genres\": [\n      \"Comedy\",\n      \"Thriller\"\n    ],\n    \"count\": 104\n  },\n  {\n    \"title\": \"Herbie Goes Bananas (1980)\",\n    \"id\": 2050,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 104\n  },\n  {\n    \"title\": \"Beloved (1998)\",\n    \"id\": 2314,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 104\n  },\n  {\n    \"title\": \"Palm Beach Story, The (1942)\",\n    \"id\": 2937,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 104\n  },\n  {\n    \"title\": \"Creepshow 2 (1987)\",\n    \"id\": 3017,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 104\n  },\n  {\n    \"title\": \"Missing in Action 2: The Beginning (1985)\",\n    \"id\": 3767,\n    \"genres\": [\n      \"Action\",\n      \"War\"\n    ],\n    \"count\": 104\n  },\n  {\n    \"title\": \"Return of the Fly (1959)\",\n    \"id\": 3923,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 104\n  },\n  {\n    \"title\": \"Stealing Beauty (1996)\",\n    \"id\": 781,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 103\n  },\n  {\n    \"title\": \"Foxfire (1996)\",\n    \"id\": 835,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 103\n  },\n  {\n    \"title\": \"Love! Valour! Compassion! (1997)\",\n    \"id\": 1535,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 103\n  },\n  {\n    \"title\": \"House II: The Second Story (1987)\",\n    \"id\": 2149,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 103\n  },\n  {\n    \"title\": \"Three to Tango (1999)\",\n    \"id\": 2978,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 103\n  },\n  {\n    \"title\": \"East-West (Est-ouest) (1999)\",\n    \"id\": 3357,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 103\n  },\n  {\n    \"title\": \"Anchors Aweigh (1945)\",\n    \"id\": 3599,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 103\n  },\n  {\n    \"title\": \"Sudden Death (1995)\",\n    \"id\": 9,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 102\n  },\n  {\n    \"title\": \"Kiss of Death (1995)\",\n    \"id\": 259,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 102\n  },\n  {\n    \"title\": \"Low Down Dirty Shame, A (1994)\",\n    \"id\": 387,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 102\n  },\n  {\n    \"title\": \"Believers, The (1987)\",\n    \"id\": 1332,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 102\n  },\n  {\n    \"title\": \"Big One, The (1997)\",\n    \"id\": 1827,\n    \"genres\": [\n      \"Comedy\",\n      \"Documentary\"\n    ],\n    \"count\": 102\n  },\n  {\n    \"title\": \"Walk on the Moon, A (1999)\",\n    \"id\": 2570,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 102\n  },\n  {\n    \"title\": \"Life (1999)\",\n    \"id\": 2587,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 102\n  },\n  {\n    \"title\": \"After Life (1998)\",\n    \"id\": 2624,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 102\n  },\n  {\n    \"title\": \"Dracula (1958)\",\n    \"id\": 2645,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 102\n  },\n  {\n    \"title\": \"Dark Half, The (1993)\",\n    \"id\": 2898,\n    \"genres\": [\n      \"Horror\",\n      \"Mystery\"\n    ],\n    \"count\": 102\n  },\n  {\n    \"title\": \"Suddenly, Last Summer (1959)\",\n    \"id\": 3872,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 102\n  },\n  {\n    \"title\": \"Cemetery Man (Dellamorte Dellamore) (1994)\",\n    \"id\": 735,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 101\n  },\n  {\n    \"title\": \"Glimmer Man, The (1996)\",\n    \"id\": 1004,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 101\n  },\n  {\n    \"title\": \"Two Girls and a Guy (1997)\",\n    \"id\": 1816,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 101\n  },\n  {\n    \"title\": \"Herbie Goes to Monte Carlo (1977)\",\n    \"id\": 2051,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 101\n  },\n  {\n    \"title\": \"Maurice (1987)\",\n    \"id\": 3094,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 101\n  },\n  {\n    \"title\": \"Flying Tigers (1942)\",\n    \"id\": 3628,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 101\n  },\n  {\n    \"title\": \"Fighting Seabees, The (1944)\",\n    \"id\": 3643,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 101\n  },\n  {\n    \"title\": \"Othello (1995)\",\n    \"id\": 26,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 100\n  },\n  {\n    \"title\": \"Safe (1995)\",\n    \"id\": 190,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 100\n  },\n  {\n    \"title\": \"Fatal Instinct (1993)\",\n    \"id\": 445,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 100\n  },\n  {\n    \"title\": \"Little Big League (1994)\",\n    \"id\": 569,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 100\n  },\n  {\n    \"title\": \"I Shot Andy Warhol (1996)\",\n    \"id\": 766,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 100\n  },\n  {\n    \"title\": \"Blue Angel, The (Blaue Engel, Der) (1930)\",\n    \"id\": 978,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 100\n  },\n  {\n    \"title\": \"Garden of Finzi-Contini, The (Giardino dei Finzi-Contini, Il) (1970)\",\n    \"id\": 1362,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 100\n  },\n  {\n    \"title\": \"Metro (1997)\",\n    \"id\": 1432,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 100\n  },\n  {\n    \"title\": \"Dangerous Beauty (1998)\",\n    \"id\": 1758,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 100\n  },\n  {\n    \"title\": \"Tales from the Darkside: The Movie (1990)\",\n    \"id\": 2327,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 100\n  },\n  {\n    \"title\": \"Children of Paradise (Les enfants du paradis) (1945)\",\n    \"id\": 2920,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 100\n  },\n  {\n    \"title\": \"Pok\\u00e9mon the Movie 2000 (2000)\",\n    \"id\": 3799,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 100\n  },\n  {\n    \"title\": \"Get Carter (2000)\",\n    \"id\": 3946,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 100\n  },\n  {\n    \"title\": \"Balto (1995)\",\n    \"id\": 13,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 99\n  },\n  {\n    \"title\": \"Home for the Holidays (1995)\",\n    \"id\": 57,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 99\n  },\n  {\n    \"title\": \"NeverEnding Story III, The (1994)\",\n    \"id\": 126,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Fantasy\"\n    ],\n    \"count\": 99\n  },\n  {\n    \"title\": \"Friday the 13th Part VIII: Jason Takes Manhattan (1989)\",\n    \"id\": 1981,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 99\n  },\n  {\n    \"title\": \"Hurlyburly (1998)\",\n    \"id\": 2435,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 99\n  },\n  {\n    \"title\": \"Problem Child 2 (1991)\",\n    \"id\": 2799,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 99\n  },\n  {\n    \"title\": \"Brief Encounter (1946)\",\n    \"id\": 2927,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 99\n  },\n  {\n    \"title\": \"For Whom the Bell Tolls (1943)\",\n    \"id\": 897,\n    \"genres\": [\n      \"Adventure\",\n      \"War\"\n    ],\n    \"count\": 98\n  },\n  {\n    \"title\": \"Trees Lounge (1996)\",\n    \"id\": 1051,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 98\n  },\n  {\n    \"title\": \"Wrong Man, The (1956)\",\n    \"id\": 2182,\n    \"genres\": [\n      \"Drama\",\n      \"Film-Noir\",\n      \"Thriller\"\n    ],\n    \"count\": 98\n  },\n  {\n    \"title\": \"Fantastic Planet, The (La Plan\\u00e8te sauvage) (1973)\",\n    \"id\": 2495,\n    \"genres\": [\n      \"Animation\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 98\n  },\n  {\n    \"title\": \"Psycho III (1986)\",\n    \"id\": 2903,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 98\n  },\n  {\n    \"title\": \"Fitzcarraldo (1982)\",\n    \"id\": 2970,\n    \"genres\": [\n      \"Adventure\",\n      \"Drama\"\n    ],\n    \"count\": 98\n  },\n  {\n    \"title\": \"Assault on Precinct 13 (1976)\",\n    \"id\": 3726,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 98\n  },\n  {\n    \"title\": \"Lawnmower Man 2: Beyond Cyberspace (1996)\",\n    \"id\": 66,\n    \"genres\": [\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 97\n  },\n  {\n    \"title\": \"Boys Life (1995)\",\n    \"id\": 388,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 97\n  },\n  {\n    \"title\": \"Made in America (1993)\",\n    \"id\": 489,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 97\n  },\n  {\n    \"title\": \"Lost Horizon (1937)\",\n    \"id\": 944,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 97\n  },\n  {\n    \"title\": \"Davy Crockett, King of the Wild Frontier (1955)\",\n    \"id\": 1008,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 97\n  },\n  {\n    \"title\": \"Child's Play 3 (1992)\",\n    \"id\": 1993,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 97\n  },\n  {\n    \"title\": \"Swept Away (Travolti da un insolito destino nell'azzurro mare d'Agosto) (1975)\",\n    \"id\": 2239,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 97\n  },\n  {\n    \"title\": \"American Flyers (1985)\",\n    \"id\": 3214,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 97\n  },\n  {\n    \"title\": \"Shakes the Clown (1991)\",\n    \"id\": 3544,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 97\n  },\n  {\n    \"title\": \"Fair Game (1995)\",\n    \"id\": 71,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 96\n  },\n  {\n    \"title\": \"Ruling Class, The (1972)\",\n    \"id\": 1162,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 96\n  },\n  {\n    \"title\": \"Blank Check (1994)\",\n    \"id\": 2036,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 96\n  },\n  {\n    \"title\": \"Celebrity (1998)\",\n    \"id\": 2356,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 96\n  },\n  {\n    \"title\": \"House on Haunted Hill (1958)\",\n    \"id\": 2519,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 96\n  },\n  {\n    \"title\": \"Who's That Girl? (1987)\",\n    \"id\": 3391,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 96\n  },\n  {\n    \"title\": \"Max Dugan Returns (1983)\",\n    \"id\": 3497,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 96\n  },\n  {\n    \"title\": \"Bamboozled (2000)\",\n    \"id\": 3943,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 96\n  },\n  {\n    \"title\": \"Gingerbread Man, The (1998)\",\n    \"id\": 1841,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 95\n  },\n  {\n    \"title\": \"Frenzy (1972)\",\n    \"id\": 2178,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 95\n  },\n  {\n    \"title\": \"Pope of Greenwich Village, The (1984)\",\n    \"id\": 2347,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 95\n  },\n  {\n    \"title\": \"Twin Falls Idaho (1999)\",\n    \"id\": 2725,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 95\n  },\n  {\n    \"title\": \"On the Beach (1959)\",\n    \"id\": 3379,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 95\n  },\n  {\n    \"title\": \"Black Beauty (1994)\",\n    \"id\": 421,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 94\n  },\n  {\n    \"title\": \"Naked (1993)\",\n    \"id\": 501,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 94\n  },\n  {\n    \"title\": \"Microcosmos (Microcosmos: Le peuple de l'herbe) (1996)\",\n    \"id\": 1111,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 94\n  },\n  {\n    \"title\": \"Tin Drum, The (Blechtrommel, Die) (1979)\",\n    \"id\": 1161,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 94\n  },\n  {\n    \"title\": \"Dear God (1996)\",\n    \"id\": 1167,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 94\n  },\n  {\n    \"title\": \"Albino Alligator (1996)\",\n    \"id\": 1352,\n    \"genres\": [\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 94\n  },\n  {\n    \"title\": \"That Darn Cat! (1997)\",\n    \"id\": 1460,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\",\n      \"Mystery\"\n    ],\n    \"count\": 94\n  },\n  {\n    \"title\": \"It Came from Outer Space (1953)\",\n    \"id\": 2661,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 94\n  },\n  {\n    \"title\": \"Things Change (1988)\",\n    \"id\": 3613,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 94\n  },\n  {\n    \"title\": \"Watcher, The (2000)\",\n    \"id\": 3895,\n    \"genres\": [\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 94\n  },\n  {\n    \"title\": \"Kicking and Screaming (1995)\",\n    \"id\": 72,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 93\n  },\n  {\n    \"title\": \"Jeffrey (1995)\",\n    \"id\": 171,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 93\n  },\n  {\n    \"title\": \"Mighty Morphin Power Rangers: The Movie (1995)\",\n    \"id\": 181,\n    \"genres\": [\n      \"Action\",\n      \"Children's\"\n    ],\n    \"count\": 93\n  },\n  {\n    \"title\": \"Fresh (1994)\",\n    \"id\": 456,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 93\n  },\n  {\n    \"title\": \"Candyman: Farewell to the Flesh (1995)\",\n    \"id\": 606,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 93\n  },\n  {\n    \"title\": \"Foreign Correspondent (1940)\",\n    \"id\": 929,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 93\n  },\n  {\n    \"title\": \"Set It Off (1996)\",\n    \"id\": 998,\n    \"genres\": [\n      \"Action\",\n      \"Crime\"\n    ],\n    \"count\": 93\n  },\n  {\n    \"title\": \"Newsies (1992)\",\n    \"id\": 2084,\n    \"genres\": [\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 93\n  },\n  {\n    \"title\": \"Tough Guys (1986)\",\n    \"id\": 2472,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 93\n  },\n  {\n    \"title\": \"Mummy, The (1959)\",\n    \"id\": 2634,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 93\n  },\n  {\n    \"title\": \"Irma la Douce (1963)\",\n    \"id\": 3076,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 93\n  },\n  {\n    \"title\": \"My Man Godfrey (1957)\",\n    \"id\": 3096,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 93\n  },\n  {\n    \"title\": \"Hands on a Hard Body (1996)\",\n    \"id\": 2330,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 92\n  },\n  {\n    \"title\": \"Boondock Saints, The (1999)\",\n    \"id\": 3275,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 92\n  },\n  {\n    \"title\": \"Dersu Uzala (1974)\",\n    \"id\": 3470,\n    \"genres\": [\n      \"Adventure\",\n      \"Drama\"\n    ],\n    \"count\": 92\n  },\n  {\n    \"title\": \"Black and White (1999)\",\n    \"id\": 3509,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 92\n  },\n  {\n    \"title\": \"Hollywood Knights, The (1980)\",\n    \"id\": 3619,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 92\n  },\n  {\n    \"title\": \"Puppet Master III: Toulon's Revenge (1991)\",\n    \"id\": 3662,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 92\n  },\n  {\n    \"title\": \"Urban Legends: Final Cut (2000)\",\n    \"id\": 3908,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 92\n  },\n  {\n    \"title\": \"Jade (1995)\",\n    \"id\": 132,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 91\n  },\n  {\n    \"title\": \"Great White Hype, The (1996)\",\n    \"id\": 725,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 91\n  },\n  {\n    \"title\": \"Mediterraneo (1991)\",\n    \"id\": 1184,\n    \"genres\": [\n      \"Comedy\",\n      \"War\"\n    ],\n    \"count\": 91\n  },\n  {\n    \"title\": \"Men With Guns (1997)\",\n    \"id\": 1788,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 91\n  },\n  {\n    \"title\": \"Gentleman's Agreement (1947)\",\n    \"id\": 1940,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 91\n  },\n  {\n    \"title\": \"Friday the 13th Part VII: The New Blood (1988)\",\n    \"id\": 1980,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 91\n  },\n  {\n    \"title\": \"Prom Night (1980)\",\n    \"id\": 1987,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 91\n  },\n  {\n    \"title\": \"Incredible Journey, The (1963)\",\n    \"id\": 2057,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 91\n  },\n  {\n    \"title\": \"Oscar and Lucinda (a.k.a. Oscar & Lucinda) (1997)\",\n    \"id\": 2801,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 91\n  },\n  {\n    \"title\": \"Universal Soldier: The Return (1999)\",\n    \"id\": 2807,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 91\n  },\n  {\n    \"title\": \"Man and a Woman, A (Un Homme et une Femme) (1966)\",\n    \"id\": 2969,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 91\n  },\n  {\n    \"title\": \"Star Is Born, A (1937)\",\n    \"id\": 3217,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 91\n  },\n  {\n    \"title\": \"Air Up There, The (1994)\",\n    \"id\": 414,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 90\n  },\n  {\n    \"title\": \"Beautiful Thing (1996)\",\n    \"id\": 1046,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 90\n  },\n  {\n    \"title\": \"Trust (1990)\",\n    \"id\": 1236,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 90\n  },\n  {\n    \"title\": \"Evening Star, The (1996)\",\n    \"id\": 1410,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 90\n  },\n  {\n    \"title\": \"Playing God (1997)\",\n    \"id\": 1647,\n    \"genres\": [\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 90\n  },\n  {\n    \"title\": \"Holy Man (1998)\",\n    \"id\": 2306,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 90\n  },\n  {\n    \"title\": \"I Saw What You Did (1965)\",\n    \"id\": 2856,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 90\n  },\n  {\n    \"title\": \"Man with the Golden Arm, The (1955)\",\n    \"id\": 3678,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 90\n  },\n  {\n    \"title\": \"Antonia's Line (Antonia) (1995)\",\n    \"id\": 82,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 89\n  },\n  {\n    \"title\": \"Vampire in Brooklyn (1995)\",\n    \"id\": 93,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 89\n  },\n  {\n    \"title\": \"Bad Girls (1994)\",\n    \"id\": 416,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 89\n  },\n  {\n    \"title\": \"Moll Flanders (1996)\",\n    \"id\": 650,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 89\n  },\n  {\n    \"title\": \"Saboteur (1942)\",\n    \"id\": 2204,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 89\n  },\n  {\n    \"title\": \"Caligula (1980)\",\n    \"id\": 2862,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 89\n  },\n  {\n    \"title\": \"Here on Earth (2000)\",\n    \"id\": 3453,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 89\n  },\n  {\n    \"title\": \"Phantasm II (1988)\",\n    \"id\": 3837,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 89\n  },\n  {\n    \"title\": \"Mary Reilly (1996)\",\n    \"id\": 92,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 88\n  },\n  {\n    \"title\": \"Fathers' Day (1997)\",\n    \"id\": 1526,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 88\n  },\n  {\n    \"title\": \"Excess Baggage (1997)\",\n    \"id\": 1605,\n    \"genres\": [\n      \"Adventure\",\n      \"Romance\"\n    ],\n    \"count\": 88\n  },\n  {\n    \"title\": \"Rocket Man (1997)\",\n    \"id\": 1646,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 88\n  },\n  {\n    \"title\": \"Switchback (1997)\",\n    \"id\": 1661,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 88\n  },\n  {\n    \"title\": \"Limbo (1999)\",\n    \"id\": 2682,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 88\n  },\n  {\n    \"title\": \"42 Up (1998)\",\n    \"id\": 3077,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 88\n  },\n  {\n    \"title\": \"My Life (1993)\",\n    \"id\": 3502,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 88\n  },\n  {\n    \"title\": \"Burglar (1987)\",\n    \"id\": 3715,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 88\n  },\n  {\n    \"title\": \"Beyond Rangoon (1995)\",\n    \"id\": 155,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 87\n  },\n  {\n    \"title\": \"Geronimo: An American Legend (1993)\",\n    \"id\": 458,\n    \"genres\": [\n      \"Drama\",\n      \"Western\"\n    ],\n    \"count\": 87\n  },\n  {\n    \"title\": \"Big Blue, The (Le Grand Bleu) (1988)\",\n    \"id\": 1216,\n    \"genres\": [\n      \"Adventure\",\n      \"Romance\"\n    ],\n    \"count\": 87\n  },\n  {\n    \"title\": \"FairyTale: A True Story (1997)\",\n    \"id\": 1654,\n    \"genres\": [\n      \"Children's\",\n      \"Drama\",\n      \"Fantasy\"\n    ],\n    \"count\": 87\n  },\n  {\n    \"title\": \"Wood, The (1999)\",\n    \"id\": 2714,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 87\n  },\n  {\n    \"title\": \"Morning After, The (1986)\",\n    \"id\": 2749,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 87\n  },\n  {\n    \"title\": \"Sesame Street Presents Follow That Bird (1985)\",\n    \"id\": 3399,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 87\n  },\n  {\n    \"title\": \"Sunshine (1999)\",\n    \"id\": 3720,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 87\n  },\n  {\n    \"title\": \"Hellraiser III: Hell on Earth (1992)\",\n    \"id\": 3919,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 87\n  },\n  {\n    \"title\": \"Queen Margot (La Reine Margot) (1994)\",\n    \"id\": 302,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 86\n  },\n  {\n    \"title\": \"Go Fish (1994)\",\n    \"id\": 461,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 86\n  },\n  {\n    \"title\": \"Betrayed (1988)\",\n    \"id\": 3370,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 86\n  },\n  {\n    \"title\": \"Unstrung Heroes (1995)\",\n    \"id\": 205,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 85\n  },\n  {\n    \"title\": \"Feeling Minnesota (1996)\",\n    \"id\": 697,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 85\n  },\n  {\n    \"title\": \"Soul Food (1997)\",\n    \"id\": 1621,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 85\n  },\n  {\n    \"title\": \"Wilde (1997)\",\n    \"id\": 2437,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 85\n  },\n  {\n    \"title\": \"Peeping Tom (1960)\",\n    \"id\": 2488,\n    \"genres\": [\n      \"Drama\",\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 85\n  },\n  {\n    \"title\": \"Trick (1999)\",\n    \"id\": 2721,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 85\n  },\n  {\n    \"title\": \"Incredibly True Adventure of Two Girls in Love, The (1995)\",\n    \"id\": 3046,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 85\n  },\n  {\n    \"title\": \"Death Wish II (1982)\",\n    \"id\": 3431,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 85\n  },\n  {\n    \"title\": \"Lords of Flatbush, The (1974)\",\n    \"id\": 3590,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 85\n  },\n  {\n    \"title\": \"Long Walk Home, The (1990)\",\n    \"id\": 3713,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 85\n  },\n  {\n    \"title\": \"War, The (1994)\",\n    \"id\": 340,\n    \"genres\": [\n      \"Adventure\",\n      \"Drama\"\n    ],\n    \"count\": 84\n  },\n  {\n    \"title\": \"Women, The (1939)\",\n    \"id\": 927,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 84\n  },\n  {\n    \"title\": \"She's So Lovely (1997)\",\n    \"id\": 1600,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 84\n  },\n  {\n    \"title\": \"How to Be a Player (1997)\",\n    \"id\": 1640,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 84\n  },\n  {\n    \"title\": \"Grand Hotel (1932)\",\n    \"id\": 1929,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 84\n  },\n  {\n    \"title\": \"Airport 1975 (1974)\",\n    \"id\": 2521,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 84\n  },\n  {\n    \"title\": \"Twelve Chairs, The (1970)\",\n    \"id\": 3622,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 84\n  },\n  {\n    \"title\": \"Romper Stomper (1992)\",\n    \"id\": 522,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 83\n  },\n  {\n    \"title\": \"Family Plot (1976)\",\n    \"id\": 2177,\n    \"genres\": [\n      \"Comedy\",\n      \"Thriller\"\n    ],\n    \"count\": 83\n  },\n  {\n    \"title\": \"We're No Angels (1989)\",\n    \"id\": 2264,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 83\n  },\n  {\n    \"title\": \"Nothing But Trouble (1991)\",\n    \"id\": 2265,\n    \"genres\": [\n      \"Adventure\",\n      \"Comedy\"\n    ],\n    \"count\": 83\n  },\n  {\n    \"title\": \"Minus Man, The (1999)\",\n    \"id\": 2844,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 83\n  },\n  {\n    \"title\": \"White Sands (1992)\",\n    \"id\": 3810,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 83\n  },\n  {\n    \"title\": \"Interiors (1978)\",\n    \"id\": 3813,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 83\n  },\n  {\n    \"title\": \"Unzipped (1995)\",\n    \"id\": 206,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 82\n  },\n  {\n    \"title\": \"Jane Eyre (1996)\",\n    \"id\": 613,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 82\n  },\n  {\n    \"title\": \"Meet John Doe (1941)\",\n    \"id\": 973,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 82\n  },\n  {\n    \"title\": \"SubUrbia (1997)\",\n    \"id\": 1454,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 82\n  },\n  {\n    \"title\": \"Welcome To Sarajevo (1997)\",\n    \"id\": 1670,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 82\n  },\n  {\n    \"title\": \"Afterglow (1997)\",\n    \"id\": 1733,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 82\n  },\n  {\n    \"title\": \"Mrs. Miniver (1942)\",\n    \"id\": 1936,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 82\n  },\n  {\n    \"title\": \"Long Goodbye, The (1973)\",\n    \"id\": 2511,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 82\n  },\n  {\n    \"title\": \"Frankenstein Meets the Wolf Man (1943)\",\n    \"id\": 2651,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 82\n  },\n  {\n    \"title\": \"Raven, The (1963)\",\n    \"id\": 2780,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 82\n  },\n  {\n    \"title\": \"Felicia's Journey (1999)\",\n    \"id\": 3055,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 82\n  },\n  {\n    \"title\": \"Gossip (2000)\",\n    \"id\": 3553,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 82\n  },\n  {\n    \"title\": \"Puppet Master II (1990)\",\n    \"id\": 3661,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 82\n  },\n  {\n    \"title\": \"Eyes of Tammy Faye, The (2000)\",\n    \"id\": 3859,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 82\n  },\n  {\n    \"title\": \"Jury Duty (1995)\",\n    \"id\": 174,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 81\n  },\n  {\n    \"title\": \"Even Cowgirls Get the Blues (1993)\",\n    \"id\": 444,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 81\n  },\n  {\n    \"title\": \"Picnic (1955)\",\n    \"id\": 982,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 81\n  },\n  {\n    \"title\": \"Get on the Bus (1996)\",\n    \"id\": 1054,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 81\n  },\n  {\n    \"title\": \"Amos & Andrew (1993)\",\n    \"id\": 1440,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 81\n  },\n  {\n    \"title\": \"Kama Sutra: A Tale of Love (1996)\",\n    \"id\": 1475,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 81\n  },\n  {\n    \"title\": \"Fire Down Below (1997)\",\n    \"id\": 1626,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 81\n  },\n  {\n    \"title\": \"Mephisto (1981)\",\n    \"id\": 2075,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 81\n  },\n  {\n    \"title\": \"Dinner Game, The (Le D\\u00eener de cons) (1998)\",\n    \"id\": 2696,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 81\n  },\n  {\n    \"title\": \"Ipcress File, The (1965)\",\n    \"id\": 2983,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 81\n  },\n  {\n    \"title\": \"March of the Wooden Soldiers (a.k.a. Laurel & Hardy in Toyland) (1934)\",\n    \"id\": 3086,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 81\n  },\n  {\n    \"title\": \"Braddock: Missing in Action III (1988)\",\n    \"id\": 3768,\n    \"genres\": [\n      \"Action\",\n      \"War\"\n    ],\n    \"count\": 81\n  },\n  {\n    \"title\": \"Farinelli: il castrato (1994)\",\n    \"id\": 242,\n    \"genres\": [\n      \"Drama\",\n      \"Musical\"\n    ],\n    \"count\": 80\n  },\n  {\n    \"title\": \"Mixed Nuts (1994)\",\n    \"id\": 275,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 80\n  },\n  {\n    \"title\": \"Two if by Sea (1996)\",\n    \"id\": 64,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 79\n  },\n  {\n    \"title\": \"Young Poisoner's Handbook, The (1995)\",\n    \"id\": 117,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 79\n  },\n  {\n    \"title\": \"Love Affair (1994)\",\n    \"id\": 270,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 79\n  },\n  {\n    \"title\": \"Pallbearer, The (1996)\",\n    \"id\": 612,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 79\n  },\n  {\n    \"title\": \"Graveyard Shift (1990)\",\n    \"id\": 2113,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 79\n  },\n  {\n    \"title\": \"Dance with Me (1998)\",\n    \"id\": 2168,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 79\n  },\n  {\n    \"title\": \"Young Doctors in Love (1982)\",\n    \"id\": 2255,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 79\n  },\n  {\n    \"title\": \"Airport '77 (1977)\",\n    \"id\": 2522,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 79\n  },\n  {\n    \"title\": \"Crimes of the Heart (1986)\",\n    \"id\": 2738,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 79\n  },\n  {\n    \"title\": \"Mo' Better Blues (1990)\",\n    \"id\": 3425,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 79\n  },\n  {\n    \"title\": \"Girl 6 (1996)\",\n    \"id\": 639,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 78\n  },\n  {\n    \"title\": \"High School High (1996)\",\n    \"id\": 833,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 78\n  },\n  {\n    \"title\": \"Chamber, The (1996)\",\n    \"id\": 1006,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 78\n  },\n  {\n    \"title\": \"Odessa File, The (1974)\",\n    \"id\": 3230,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 78\n  },\n  {\n    \"title\": \"Family Thing, A (1996)\",\n    \"id\": 635,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 77\n  },\n  {\n    \"title\": \"First Kid (1996)\",\n    \"id\": 881,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 77\n  },\n  {\n    \"title\": \"Little Princess, The (1939)\",\n    \"id\": 917,\n    \"genres\": [\n      \"Children's\",\n      \"Drama\"\n    ],\n    \"count\": 77\n  },\n  {\n    \"title\": \"Nightwatch (1997)\",\n    \"id\": 1355,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 77\n  },\n  {\n    \"title\": \"Phantoms (1998)\",\n    \"id\": 1655,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 77\n  },\n  {\n    \"title\": \"Firestorm (1998)\",\n    \"id\": 1744,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Thriller\"\n    ],\n    \"count\": 77\n  },\n  {\n    \"title\": \"You Can't Take It With You (1938)\",\n    \"id\": 1934,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 77\n  },\n  {\n    \"title\": \"Seven Beauties (Pasqualino Settebellezze) (1976)\",\n    \"id\": 2238,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 77\n  },\n  {\n    \"title\": \"Yards, The (1999)\",\n    \"id\": 2769,\n    \"genres\": [\n      \"Crime\",\n      \"Mystery\"\n    ],\n    \"count\": 77\n  },\n  {\n    \"title\": \"Razor's Edge, The (1984)\",\n    \"id\": 2928,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 77\n  },\n  {\n    \"title\": \"Woman in the Dunes (Suna no onna) (1964)\",\n    \"id\": 3224,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 77\n  },\n  {\n    \"title\": \"Prick Up Your Ears (1987)\",\n    \"id\": 3547,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 77\n  },\n  {\n    \"title\": \"Carnosaur (1993)\",\n    \"id\": 3572,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 77\n  },\n  {\n    \"title\": \"Sugarland Express, The (1974)\",\n    \"id\": 3738,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 77\n  },\n  {\n    \"title\": \"Boys and Girls (2000)\",\n    \"id\": 3743,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 77\n  },\n  {\n    \"title\": \"Dunston Checks In (1996)\",\n    \"id\": 87,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 76\n  },\n  {\n    \"title\": \"Night Falls on Manhattan (1997)\",\n    \"id\": 1404,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 76\n  },\n  {\n    \"title\": \"Billy's Hollywood Screen Kiss (1997)\",\n    \"id\": 2029,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 76\n  },\n  {\n    \"title\": \"Son of Flubber (1963)\",\n    \"id\": 2098,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 76\n  },\n  {\n    \"title\": \"Permanent Midnight (1998)\",\n    \"id\": 2271,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 76\n  },\n  {\n    \"title\": \"Scarlet Letter, The (1995)\",\n    \"id\": 191,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 75\n  },\n  {\n    \"title\": \"Blue Chips (1994)\",\n    \"id\": 424,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 75\n  },\n  {\n    \"title\": \"All Dogs Go to Heaven 2 (1996)\",\n    \"id\": 631,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 75\n  },\n  {\n    \"title\": \"Hoodlum (1997)\",\n    \"id\": 1601,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Film-Noir\"\n    ],\n    \"count\": 75\n  },\n  {\n    \"title\": \"Shanghai Triad (Yao a yao yao dao waipo qiao) (1995)\",\n    \"id\": 30,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 74\n  },\n  {\n    \"title\": \"King of the Hill (1993)\",\n    \"id\": 483,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 74\n  },\n  {\n    \"title\": \"Heavy (1995)\",\n    \"id\": 764,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 74\n  },\n  {\n    \"title\": \"Indian Summer (a.k.a. Alive & Kicking) (1996)\",\n    \"id\": 1642,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 74\n  },\n  {\n    \"title\": \"Mortal Thoughts (1991)\",\n    \"id\": 2267,\n    \"genres\": [\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 74\n  },\n  {\n    \"title\": \"Impostors, The (1998)\",\n    \"id\": 2295,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 74\n  },\n  {\n    \"title\": \"Heartburn (1986)\",\n    \"id\": 2417,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 74\n  },\n  {\n    \"title\": \"At First Sight (1999)\",\n    \"id\": 2445,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 74\n  },\n  {\n    \"title\": \"Face in the Crowd, A (1957)\",\n    \"id\": 3038,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 74\n  },\n  {\n    \"title\": \"School Daze (1988)\",\n    \"id\": 3423,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 74\n  },\n  {\n    \"title\": \"Coogan's Bluff (1968)\",\n    \"id\": 3427,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 74\n  },\n  {\n    \"title\": \"Ponette (1996)\",\n    \"id\": 1545,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 73\n  },\n  {\n    \"title\": \"Meatballs Part II (1984)\",\n    \"id\": 3041,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 73\n  },\n  {\n    \"title\": \"Ride with the Devil (1999)\",\n    \"id\": 3117,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\",\n      \"War\"\n    ],\n    \"count\": 73\n  },\n  {\n    \"title\": \"Crimson Pirate, The (1952)\",\n    \"id\": 3417,\n    \"genres\": [\n      \"Adventure\",\n      \"Comedy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 73\n  },\n  {\n    \"title\": \"8 Seconds (1994)\",\n    \"id\": 408,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 72\n  },\n  {\n    \"title\": \"Withnail and I (1987)\",\n    \"id\": 1202,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 72\n  },\n  {\n    \"title\": \"Kurt & Courtney (1998)\",\n    \"id\": 1856,\n    \"genres\": [\n      \"Documentary\",\n      \"Musical\"\n    ],\n    \"count\": 72\n  },\n  {\n    \"title\": \"Dancer, Texas Pop. 81 (1998)\",\n    \"id\": 1870,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 72\n  },\n  {\n    \"title\": \"Haunted Honeymoon (1986)\",\n    \"id\": 2786,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 72\n  },\n  {\n    \"title\": \"Operation Condor 2 (Longxiong hudi) (1990)\",\n    \"id\": 2880,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Comedy\"\n    ],\n    \"count\": 72\n  },\n  {\n    \"title\": \"Omega Code, The (1999)\",\n    \"id\": 2965,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 72\n  },\n  {\n    \"title\": \"Pal Joey (1957)\",\n    \"id\": 3199,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\",\n      \"Romance\"\n    ],\n    \"count\": 72\n  },\n  {\n    \"title\": \"Death Wish 3 (1985)\",\n    \"id\": 3432,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 72\n  },\n  {\n    \"title\": \"Good Earth, The (1937)\",\n    \"id\": 3447,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 72\n  },\n  {\n    \"title\": \"Duel in the Sun (1946)\",\n    \"id\": 3792,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 72\n  },\n  {\n    \"title\": \"Little Buddha (1993)\",\n    \"id\": 365,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 71\n  },\n  {\n    \"title\": \"Fear of a Black Hat (1993)\",\n    \"id\": 449,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 71\n  },\n  {\n    \"title\": \"Diabolique (1996)\",\n    \"id\": 640,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 71\n  },\n  {\n    \"title\": \"Band Wagon, The (1953)\",\n    \"id\": 935,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 71\n  },\n  {\n    \"title\": \"Prefontaine (1997)\",\n    \"id\": 1442,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 71\n  },\n  {\n    \"title\": \"Trial and Error (1997)\",\n    \"id\": 1550,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 71\n  },\n  {\n    \"title\": \"Six-String Samurai (1998)\",\n    \"id\": 2275,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 71\n  },\n  {\n    \"title\": \"Earth Vs. the Flying Saucers (1956)\",\n    \"id\": 2665,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 71\n  },\n  {\n    \"title\": \"Under the Rainbow (1981)\",\n    \"id\": 3048,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 71\n  },\n  {\n    \"title\": \"Where the Buffalo Roam (1980)\",\n    \"id\": 3235,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 71\n  },\n  {\n    \"title\": \"Bride of the Monster (1956)\",\n    \"id\": 3340,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 71\n  },\n  {\n    \"title\": \"Stay Tuned (1992)\",\n    \"id\": 3669,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 71\n  },\n  {\n    \"title\": \"Fury, The (1978)\",\n    \"id\": 3732,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 71\n  },\n  {\n    \"title\": \"Carrington (1995)\",\n    \"id\": 35,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 70\n  },\n  {\n    \"title\": \"Braindead (1992)\",\n    \"id\": 1241,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 70\n  },\n  {\n    \"title\": \"Governess, The (1998)\",\n    \"id\": 2062,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 70\n  },\n  {\n    \"title\": \"Soldier's Daughter Never Cries, A (1998)\",\n    \"id\": 2276,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 70\n  },\n  {\n    \"title\": \"'Night Mother (1986)\",\n    \"id\": 3112,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 70\n  },\n  {\n    \"title\": \"Play it to the Bone (1999)\",\n    \"id\": 3180,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 70\n  },\n  {\n    \"title\": \"Girl on the Bridge, The (La Fille sur le Pont) (1999)\",\n    \"id\": 3822,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 70\n  },\n  {\n    \"title\": \"Broken Hearts Club, The (2000)\",\n    \"id\": 3914,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 70\n  },\n  {\n    \"title\": \"Addiction, The (1995)\",\n    \"id\": 152,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 69\n  },\n  {\n    \"title\": \"House of the Spirits, The (1993)\",\n    \"id\": 469,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 69\n  },\n  {\n    \"title\": \"North (1994)\",\n    \"id\": 505,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 69\n  },\n  {\n    \"title\": \"Mother Night (1996)\",\n    \"id\": 893,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 69\n  },\n  {\n    \"title\": \"Grace of My Heart (1996)\",\n    \"id\": 988,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 69\n  },\n  {\n    \"title\": \"My Giant (1998)\",\n    \"id\": 1839,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 69\n  },\n  {\n    \"title\": \"Air Bud: Golden Receiver (1998)\",\n    \"id\": 2152,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 69\n  },\n  {\n    \"title\": \"Extremities (1986)\",\n    \"id\": 2419,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 69\n  },\n  {\n    \"title\": \"Xiu Xiu: The Sent-Down Girl (Tian yu) (1998)\",\n    \"id\": 2621,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 69\n  },\n  {\n    \"title\": \"Sanjuro (1962)\",\n    \"id\": 2905,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 69\n  },\n  {\n    \"title\": \"Repulsion (1965)\",\n    \"id\": 3075,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 69\n  },\n  {\n    \"title\": \"Quatermass and the Pit (1967)\",\n    \"id\": 3658,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 69\n  },\n  {\n    \"title\": \"Butterfly (La Lengua de las Mariposas) (2000)\",\n    \"id\": 3746,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 69\n  },\n  {\n    \"title\": \"Cecil B. Demented (2000)\",\n    \"id\": 3858,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 69\n  },\n  {\n    \"title\": \"Woman on Top (2000)\",\n    \"id\": 3909,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 69\n  },\n  {\n    \"title\": \"Tom and Huck (1995)\",\n    \"id\": 8,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 68\n  },\n  {\n    \"title\": \"If Lucy Fell (1996)\",\n    \"id\": 118,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 68\n  },\n  {\n    \"title\": \"Scout, The (1994)\",\n    \"id\": 528,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 68\n  },\n  {\n    \"title\": \"Quest for Camelot (1998)\",\n    \"id\": 1881,\n    \"genres\": [\n      \"Adventure\",\n      \"Animation\",\n      \"Children's\",\n      \"Fantasy\"\n    ],\n    \"count\": 68\n  },\n  {\n    \"title\": \"Get Real (1998)\",\n    \"id\": 2607,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 68\n  },\n  {\n    \"title\": \"Better Than Chocolate (1999)\",\n    \"id\": 2774,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 68\n  },\n  {\n    \"title\": \"Stanley & Iris (1990)\",\n    \"id\": 3103,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 68\n  },\n  {\n    \"title\": \"Dorado, El (1967)\",\n    \"id\": 3487,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 68\n  },\n  {\n    \"title\": \"I Dreamed of Africa (2000)\",\n    \"id\": 3579,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 68\n  },\n  {\n    \"title\": \"Eye for an Eye (1996)\",\n    \"id\": 61,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 67\n  },\n  {\n    \"title\": \"Cobb (1994)\",\n    \"id\": 354,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 67\n  },\n  {\n    \"title\": \"For Love or Money (1993)\",\n    \"id\": 453,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 67\n  },\n  {\n    \"title\": \"Radioland Murders (1994)\",\n    \"id\": 513,\n    \"genres\": [\n      \"Comedy\",\n      \"Mystery\",\n      \"Romance\"\n    ],\n    \"count\": 67\n  },\n  {\n    \"title\": \"Homeward Bound II: Lost in San Francisco (1996)\",\n    \"id\": 609,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 67\n  },\n  {\n    \"title\": \"Bread and Chocolate (Pane e cioccolata) (1973)\",\n    \"id\": 615,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 67\n  },\n  {\n    \"title\": \"Steel (1997)\",\n    \"id\": 1599,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 67\n  },\n  {\n    \"title\": \"King of Masks, The (Bian Lian) (1996)\",\n    \"id\": 2609,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 67\n  },\n  {\n    \"title\": \"Pajama Game, The (1957)\",\n    \"id\": 2874,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 67\n  },\n  {\n    \"title\": \"Bats (1999)\",\n    \"id\": 2974,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 67\n  },\n  {\n    \"title\": \"Fatal Beauty (1987)\",\n    \"id\": 3716,\n    \"genres\": [\n      \"Action\",\n      \"Crime\"\n    ],\n    \"count\": 67\n  },\n  {\n    \"title\": \"Blue in the Face (1995)\",\n    \"id\": 156,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 66\n  },\n  {\n    \"title\": \"Jerky Boys, The (1994)\",\n    \"id\": 255,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 66\n  },\n  {\n    \"title\": \"Fandango (1985)\",\n    \"id\": 2073,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 66\n  },\n  {\n    \"title\": \"Twin Dragons (Shuang long hui) (1992)\",\n    \"id\": 2582,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 66\n  },\n  {\n    \"title\": \"Black Mask (Hak hap) (1996)\",\n    \"id\": 2625,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 66\n  },\n  {\n    \"title\": \"Tumbleweeds (1999)\",\n    \"id\": 3118,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 66\n  },\n  {\n    \"title\": \"Stealing Home (1988)\",\n    \"id\": 3138,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 66\n  },\n  {\n    \"title\": \"Cry in the Dark, A (1988)\",\n    \"id\": 3211,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 66\n  },\n  {\n    \"title\": \"My Chauffeur (1986)\",\n    \"id\": 3491,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 66\n  },\n  {\n    \"title\": \"Ready to Rumble (2000)\",\n    \"id\": 3511,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 66\n  },\n  {\n    \"title\": \"Clara's Heart (1988)\",\n    \"id\": 3714,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 66\n  },\n  {\n    \"title\": \"Five Senses, The (1999)\",\n    \"id\": 3795,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 66\n  },\n  {\n    \"title\": \"Wonderland (1999)\",\n    \"id\": 3823,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 66\n  },\n  {\n    \"title\": \"Faraway, So Close (In Weiter Ferne, So Nah!) (1993)\",\n    \"id\": 3920,\n    \"genres\": [\n      \"Drama\",\n      \"Fantasy\"\n    ],\n    \"count\": 66\n  },\n  {\n    \"title\": \"Free Willy 3: The Rescue (1997)\",\n    \"id\": 1595,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Drama\"\n    ],\n    \"count\": 65\n  },\n  {\n    \"title\": \"Money Talks (1997)\",\n    \"id\": 1604,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 65\n  },\n  {\n    \"title\": \"Polish Wedding (1998)\",\n    \"id\": 2007,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 65\n  },\n  {\n    \"title\": \"Son of Frankenstein (1939)\",\n    \"id\": 2649,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 65\n  },\n  {\n    \"title\": \"Red Sorghum (Hong Gao Liang) (1987)\",\n    \"id\": 2972,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 65\n  },\n  {\n    \"title\": \"Bride of Re-Animator (1990)\",\n    \"id\": 3013,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 65\n  },\n  {\n    \"title\": \"Meatballs III (1987)\",\n    \"id\": 3042,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 65\n  },\n  {\n    \"title\": \"Destination Moon (1950)\",\n    \"id\": 3375,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 65\n  },\n  {\n    \"title\": \"Color of Paradise, The (Rang-e Khoda) (1999)\",\n    \"id\": 3456,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 65\n  },\n  {\n    \"title\": \"Night of the Creeps (1986)\",\n    \"id\": 3696,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 65\n  },\n  {\n    \"title\": \"Babysitter, The (1995)\",\n    \"id\": 217,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 64\n  },\n  {\n    \"title\": \"Cops and Robbersons (1994)\",\n    \"id\": 437,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 64\n  },\n  {\n    \"title\": \"Life with Mikey (1993)\",\n    \"id\": 486,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 64\n  },\n  {\n    \"title\": \"Alphaville (1965)\",\n    \"id\": 680,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 64\n  },\n  {\n    \"title\": \"Body Snatcher, The (1945)\",\n    \"id\": 1337,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 64\n  },\n  {\n    \"title\": \"Pest, The (1997)\",\n    \"id\": 1456,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 64\n  },\n  {\n    \"title\": \"Mr. Magoo (1997)\",\n    \"id\": 1731,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 64\n  },\n  {\n    \"title\": \"Odd Couple II, The (1998)\",\n    \"id\": 1837,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 64\n  },\n  {\n    \"title\": \"Friends & Lovers (1999)\",\n    \"id\": 2589,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 64\n  },\n  {\n    \"title\": \"Lost & Found (1999)\",\n    \"id\": 2597,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 64\n  },\n  {\n    \"title\": \"River, The (1984)\",\n    \"id\": 3109,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 64\n  },\n  {\n    \"title\": \"House Party 2 (1991)\",\n    \"id\": 3774,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 64\n  },\n  {\n    \"title\": \"Losing Isaiah (1995)\",\n    \"id\": 271,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 63\n  },\n  {\n    \"title\": \"Bastard Out of Carolina (1996)\",\n    \"id\": 1397,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 63\n  },\n  {\n    \"title\": \"In Love and War (1996)\",\n    \"id\": 1398,\n    \"genres\": [\n      \"Romance\",\n      \"War\"\n    ],\n    \"count\": 63\n  },\n  {\n    \"title\": \"Van, The (1996)\",\n    \"id\": 1482,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 63\n  },\n  {\n    \"title\": \"Star Kid (1997)\",\n    \"id\": 1750,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Fantasy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 63\n  },\n  {\n    \"title\": \"Going My Way (1944)\",\n    \"id\": 1937,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 63\n  },\n  {\n    \"title\": \"In Dreams (1999)\",\n    \"id\": 2446,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 63\n  },\n  {\n    \"title\": \"Tingler, The (1959)\",\n    \"id\": 2781,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 63\n  },\n  {\n    \"title\": \"Masque of the Red Death, The (1964)\",\n    \"id\": 2784,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 63\n  },\n  {\n    \"title\": \"Fright Night Part II (1989)\",\n    \"id\": 2868,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 63\n  },\n  {\n    \"title\": \"Big Country, The (1958)\",\n    \"id\": 3368,\n    \"genres\": [\n      \"Romance\",\n      \"Western\"\n    ],\n    \"count\": 63\n  },\n  {\n    \"title\": \"Shower (Xizhao) (1999)\",\n    \"id\": 3787,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 63\n  },\n  {\n    \"title\": \"Wonderful, Horrible Life of Leni Riefenstahl, The (Die Macht der Bilder) (1993)\",\n    \"id\": 363,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 62\n  },\n  {\n    \"title\": \"Lightning Jack (1994)\",\n    \"id\": 487,\n    \"genres\": [\n      \"Comedy\",\n      \"Western\"\n    ],\n    \"count\": 62\n  },\n  {\n    \"title\": \"Princess Caraboo (1994)\",\n    \"id\": 580,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 62\n  },\n  {\n    \"title\": \"Flipper (1996)\",\n    \"id\": 711,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 62\n  },\n  {\n    \"title\": \"For Richer or Poorer (1997)\",\n    \"id\": 1703,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 62\n  },\n  {\n    \"title\": \"Men Don't Leave (1990)\",\n    \"id\": 2250,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 62\n  },\n  {\n    \"title\": \"Leatherface: Texas Chainsaw Massacre III (1990)\",\n    \"id\": 2461,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 62\n  },\n  {\n    \"title\": \"Open Your Eyes (Abre los ojos) (1997)\",\n    \"id\": 2594,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 62\n  },\n  {\n    \"title\": \"Tarantula (1955)\",\n    \"id\": 2656,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 62\n  },\n  {\n    \"title\": \"Two Jakes, The (1990)\",\n    \"id\": 3141,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 62\n  },\n  {\n    \"title\": \"Kid, The (1921)\",\n    \"id\": 3310,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 62\n  },\n  {\n    \"title\": \"Gothic (1986)\",\n    \"id\": 3459,\n    \"genres\": [\n      \"Drama\",\n      \"Horror\"\n    ],\n    \"count\": 62\n  },\n  {\n    \"title\": \"Joe Gould's Secret (2000)\",\n    \"id\": 3514,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 62\n  },\n  {\n    \"title\": \"Decline of Western Civilization, The (1981)\",\n    \"id\": 3679,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 62\n  },\n  {\n    \"title\": \"Toxic Avenger, Part II, The (1989)\",\n    \"id\": 3694,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 62\n  },\n  {\n    \"title\": \"Now and Then (1995)\",\n    \"id\": 27,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 61\n  },\n  {\n    \"title\": \"Doom Generation, The (1995)\",\n    \"id\": 166,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 61\n  },\n  {\n    \"title\": \"Strawberry and Chocolate (Fresa y chocolate) (1993)\",\n    \"id\": 321,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 61\n  },\n  {\n    \"title\": \"To Live (Huozhe) (1994)\",\n    \"id\": 326,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 61\n  },\n  {\n    \"title\": \"Cronos (1992)\",\n    \"id\": 565,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 61\n  },\n  {\n    \"title\": \"Children of Heaven, The (Bacheha-Ye Aseman) (1997)\",\n    \"id\": 1900,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 61\n  },\n  {\n    \"title\": \"Greatest Show on Earth, The (1952)\",\n    \"id\": 1943,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 61\n  },\n  {\n    \"title\": \"Sheltering Sky, The (1990)\",\n    \"id\": 2283,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 61\n  },\n  {\n    \"title\": \"No Mercy (1986)\",\n    \"id\": 2741,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 61\n  },\n  {\n    \"title\": \"Final Conflict, The (a.k.a. Omen III: The Final Conflict) (1981)\",\n    \"id\": 2790,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 61\n  },\n  {\n    \"title\": \"Guinevere (1999)\",\n    \"id\": 2885,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 61\n  },\n  {\n    \"title\": \"Boiling Point (1993)\",\n    \"id\": 3165,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 61\n  },\n  {\n    \"title\": \"American Pimp (1999)\",\n    \"id\": 3718,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 61\n  },\n  {\n    \"title\": \"French Twist (Gazon maudit) (1995)\",\n    \"id\": 68,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 60\n  },\n  {\n    \"title\": \"Mr. Wrong (1996)\",\n    \"id\": 102,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 60\n  },\n  {\n    \"title\": \"American Buffalo (1996)\",\n    \"id\": 806,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 60\n  },\n  {\n    \"title\": \"Wild America (1997)\",\n    \"id\": 1582,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 60\n  },\n  {\n    \"title\": \"Almost Heroes (1998)\",\n    \"id\": 1887,\n    \"genres\": [\n      \"Adventure\",\n      \"Comedy\"\n    ],\n    \"count\": 60\n  },\n  {\n    \"title\": \"Down in the Delta (1998)\",\n    \"id\": 2434,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 60\n  },\n  {\n    \"title\": \"Poison Ivy: New Seduction (1997)\",\n    \"id\": 3064,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 60\n  },\n  {\n    \"title\": \"War Zone, The (1999)\",\n    \"id\": 3150,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 60\n  },\n  {\n    \"title\": \"Two Moon Juction (1988)\",\n    \"id\": 3577,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 60\n  },\n  {\n    \"title\": \"Screwed (2000)\",\n    \"id\": 3596,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 60\n  },\n  {\n    \"title\": \"Bless the Child (2000)\",\n    \"id\": 3857,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 60\n  },\n  {\n    \"title\": \"Faster Pussycat! Kill! Kill! (1965)\",\n    \"id\": 390,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 59\n  },\n  {\n    \"title\": \"Maya Lin: A Strong Clear Vision (1994)\",\n    \"id\": 759,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 59\n  },\n  {\n    \"title\": \"Trigger Effect, The (1996)\",\n    \"id\": 882,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 59\n  },\n  {\n    \"title\": \"Out to Sea (1997)\",\n    \"id\": 1581,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 59\n  },\n  {\n    \"title\": \"Jerry Springer: Ringmaster (1998)\",\n    \"id\": 2386,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 59\n  },\n  {\n    \"title\": \"Champ, The (1979)\",\n    \"id\": 3428,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 59\n  },\n  {\n    \"title\": \"Madame Sousatzka (1988)\",\n    \"id\": 3496,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 59\n  },\n  {\n    \"title\": \"Mrs. Parker and the Vicious Circle (1994)\",\n    \"id\": 369,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 58\n  },\n  {\n    \"title\": \"Bye Bye, Love (1995)\",\n    \"id\": 603,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 58\n  },\n  {\n    \"title\": \"Of Human Bondage (1934)\",\n    \"id\": 959,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 58\n  },\n  {\n    \"title\": \"Ridicule (1996)\",\n    \"id\": 1365,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 58\n  },\n  {\n    \"title\": \"Mrs. Dalloway (1997)\",\n    \"id\": 1684,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 58\n  },\n  {\n    \"title\": \"Torn Curtain (1966)\",\n    \"id\": 2180,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 58\n  },\n  {\n    \"title\": \"King Kong Lives (1986)\",\n    \"id\": 2368,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Horror\"\n    ],\n    \"count\": 58\n  },\n  {\n    \"title\": \"General, The (1998)\",\n    \"id\": 2425,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 58\n  },\n  {\n    \"title\": \"Mole People, The (1956)\",\n    \"id\": 2667,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 58\n  },\n  {\n    \"title\": \"Iron Eagle IV (1995)\",\n    \"id\": 2818,\n    \"genres\": [\n      \"Action\",\n      \"War\"\n    ],\n    \"count\": 58\n  },\n  {\n    \"title\": \"Puppet Master 4 (1993)\",\n    \"id\": 3663,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 58\n  },\n  {\n    \"title\": \"Class of Nuke 'Em High (1986)\",\n    \"id\": 3692,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 58\n  },\n  {\n    \"title\": \"MacKenna's Gold (1969)\",\n    \"id\": 3806,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 58\n  },\n  {\n    \"title\": \"Blink (1994)\",\n    \"id\": 422,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 57\n  },\n  {\n    \"title\": \"Walking and Talking (1996)\",\n    \"id\": 803,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 57\n  },\n  {\n    \"title\": \"Truth or Consequences, N.M. (1997)\",\n    \"id\": 1523,\n    \"genres\": [\n      \"Action\",\n      \"Crime\",\n      \"Romance\"\n    ],\n    \"count\": 57\n  },\n  {\n    \"title\": \"Blackbeard's Ghost (1968)\",\n    \"id\": 2035,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 57\n  },\n  {\n    \"title\": \"Bedroom Window, The (1987)\",\n    \"id\": 2753,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 57\n  },\n  {\n    \"title\": \"Conformist, The (Il Conformista) (1970)\",\n    \"id\": 2925,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 57\n  },\n  {\n    \"title\": \"Cup, The (Ph\\u00f6rpa) (1999)\",\n    \"id\": 3241,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 57\n  },\n  {\n    \"title\": \"Prince of the City (1981)\",\n    \"id\": 3734,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 57\n  },\n  {\n    \"title\": \"World of Apu, The (Apur Sansar) (1959)\",\n    \"id\": 670,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 56\n  },\n  {\n    \"title\": \"Farewell to Arms, A (1932)\",\n    \"id\": 976,\n    \"genres\": [\n      \"Romance\",\n      \"War\"\n    ],\n    \"count\": 56\n  },\n  {\n    \"title\": \"Amityville II: The Possession (1982)\",\n    \"id\": 1326,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 56\n  },\n  {\n    \"title\": \"Chill Factor (1999)\",\n    \"id\": 2835,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 56\n  },\n  {\n    \"title\": \"Holy Smoke (1999)\",\n    \"id\": 3127,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 56\n  },\n  {\n    \"title\": \"Blue Collar (1978)\",\n    \"id\": 3304,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 56\n  },\n  {\n    \"title\": \"Bridge at Remagen, The (1969)\",\n    \"id\": 3372,\n    \"genres\": [\n      \"Action\",\n      \"War\"\n    ],\n    \"count\": 56\n  },\n  {\n    \"title\": \"Brain That Wouldn't Die, The (1962)\",\n    \"id\": 3833,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 56\n  },\n  {\n    \"title\": \"My Crazy Life (Mi vida loca) (1993)\",\n    \"id\": 269,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 55\n  },\n  {\n    \"title\": \"Simple Twist of Fate, A (1994)\",\n    \"id\": 536,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 55\n  },\n  {\n    \"title\": \"Andre (1994)\",\n    \"id\": 577,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 55\n  },\n  {\n    \"title\": \"Stupids, The (1996)\",\n    \"id\": 747,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 55\n  },\n  {\n    \"title\": \"Body Parts (1991)\",\n    \"id\": 1336,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 55\n  },\n  {\n    \"title\": \"187 (1997)\",\n    \"id\": 1609,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 55\n  },\n  {\n    \"title\": \"Kiss Me, Guido (1997)\",\n    \"id\": 1612,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 55\n  },\n  {\n    \"title\": \"Senseless (1998)\",\n    \"id\": 1746,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 55\n  },\n  {\n    \"title\": \"Far Off Place, A (1993)\",\n    \"id\": 2045,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 55\n  },\n  {\n    \"title\": \"Effect of Gamma Rays on Man-in-the-Moon Marigolds, The (1972)\",\n    \"id\": 3069,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 55\n  },\n  {\n    \"title\": \"Nadine (1987)\",\n    \"id\": 3395,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 55\n  },\n  {\n    \"title\": \"Get Carter (1971)\",\n    \"id\": 3947,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 55\n  },\n  {\n    \"title\": \"Little Odessa (1994)\",\n    \"id\": 268,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 54\n  },\n  {\n    \"title\": \"Swan Princess, The (1994)\",\n    \"id\": 313,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 54\n  },\n  {\n    \"title\": \"Live Nude Girls (1995)\",\n    \"id\": 467,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 54\n  },\n  {\n    \"title\": \"Adventures of Pinocchio, The (1996)\",\n    \"id\": 828,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 54\n  },\n  {\n    \"title\": \"Inspector General, The (1949)\",\n    \"id\": 963,\n    \"genres\": [\n      \"Musical\"\n    ],\n    \"count\": 54\n  },\n  {\n    \"title\": \"Maximum Risk (1996)\",\n    \"id\": 990,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Thriller\"\n    ],\n    \"count\": 54\n  },\n  {\n    \"title\": \"Bad Taste (1987)\",\n    \"id\": 1255,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 54\n  },\n  {\n    \"title\": \"Wild Man Blues (1998)\",\n    \"id\": 1865,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 54\n  },\n  {\n    \"title\": \"Candleshoe (1977)\",\n    \"id\": 2037,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 54\n  },\n  {\n    \"title\": \"Sabotage (1936)\",\n    \"id\": 2210,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 54\n  },\n  {\n    \"title\": \"If.... (1968)\",\n    \"id\": 2285,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 54\n  },\n  {\n    \"title\": \"Return of the Texas Chainsaw Massacre, The (1994)\",\n    \"id\": 2462,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 54\n  },\n  {\n    \"title\": \"Breakfast of Champions (1999)\",\n    \"id\": 2507,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 54\n  },\n  {\n    \"title\": \"From the Hip (1987)\",\n    \"id\": 2751,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 54\n  },\n  {\n    \"title\": \"Life and Times of Hank Greenberg, The (1998)\",\n    \"id\": 3188,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 54\n  },\n  {\n    \"title\": \"End of Violence, The (1997)\",\n    \"id\": 3518,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 54\n  },\n  {\n    \"title\": \"Puppet Master 5: The Final Chapter (1994)\",\n    \"id\": 3664,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 54\n  },\n  {\n    \"title\": \"Tigerland (2000)\",\n    \"id\": 3950,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 54\n  },\n  {\n    \"title\": \"Before and After (1996)\",\n    \"id\": 113,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 53\n  },\n  {\n    \"title\": \"Burnt Offerings (1976)\",\n    \"id\": 1341,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 53\n  },\n  {\n    \"title\": \"Twilight (1998)\",\n    \"id\": 1791,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 53\n  },\n  {\n    \"title\": \"Tex (1982)\",\n    \"id\": 2104,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 53\n  },\n  {\n    \"title\": \"Glen or Glenda (1953)\",\n    \"id\": 2362,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 53\n  },\n  {\n    \"title\": \"It Came from Beneath the Sea (1955)\",\n    \"id\": 2663,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 53\n  },\n  {\n    \"title\": \"Hamlet (1964)\",\n    \"id\": 2820,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 53\n  },\n  {\n    \"title\": \"Repossessed (1990)\",\n    \"id\": 3031,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 53\n  },\n  {\n    \"title\": \"Beautiful People (1999)\",\n    \"id\": 3302,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 53\n  },\n  {\n    \"title\": \"Toxic Avenger Part III: The Last Temptation of Toxie, The (1989)\",\n    \"id\": 3695,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 53\n  },\n  {\n    \"title\": \"White Balloon, The (Badkonake Sefid ) (1995)\",\n    \"id\": 80,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 52\n  },\n  {\n    \"title\": \"White Man's Burden (1995)\",\n    \"id\": 209,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 52\n  },\n  {\n    \"title\": \"Horseman on the Roof, The (Hussard sur le toit, Le) (1995)\",\n    \"id\": 715,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 52\n  },\n  {\n    \"title\": \"'Til There Was You (1997)\",\n    \"id\": 779,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 52\n  },\n  {\n    \"title\": \"Stalker (1979)\",\n    \"id\": 1232,\n    \"genres\": [\n      \"Mystery\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 52\n  },\n  {\n    \"title\": \"Angel Baby (1995)\",\n    \"id\": 1428,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 52\n  },\n  {\n    \"title\": \"Children of the Revolution (1996)\",\n    \"id\": 1516,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 52\n  },\n  {\n    \"title\": \"Whatever (1998)\",\n    \"id\": 1922,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 52\n  },\n  {\n    \"title\": \"Devil and Max Devlin, The (1981)\",\n    \"id\": 2044,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 52\n  },\n  {\n    \"title\": \"Why Do Fools Fall In Love? (1998)\",\n    \"id\": 2190,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 52\n  },\n  {\n    \"title\": \"God Said 'Ha!' (1998)\",\n    \"id\": 2499,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 52\n  },\n  {\n    \"title\": \"Children of the Corn II: The Final Sacrifice (1993)\",\n    \"id\": 2515,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 52\n  },\n  {\n    \"title\": \"House of Frankenstein (1944)\",\n    \"id\": 2647,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 52\n  },\n  {\n    \"title\": \"Niagara (1953)\",\n    \"id\": 2939,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 52\n  },\n  {\n    \"title\": \"Not One Less (Yi ge dou bu neng shao) (1999)\",\n    \"id\": 3289,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 52\n  },\n  {\n    \"title\": \"Urbania (2000)\",\n    \"id\": 3903,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 52\n  },\n  {\n    \"title\": \"Celtic Pride (1996)\",\n    \"id\": 710,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 51\n  },\n  {\n    \"title\": \"Wild Reeds (1994)\",\n    \"id\": 896,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 51\n  },\n  {\n    \"title\": \"Angel and the Badman (1947)\",\n    \"id\": 964,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 51\n  },\n  {\n    \"title\": \"Turbulence (1997)\",\n    \"id\": 1427,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 51\n  },\n  {\n    \"title\": \"Prophecy II, The (1998)\",\n    \"id\": 1756,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 51\n  },\n  {\n    \"title\": \"Four Days in September (1997)\",\n    \"id\": 1759,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 51\n  },\n  {\n    \"title\": \"Black Dog (1998)\",\n    \"id\": 1869,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 51\n  },\n  {\n    \"title\": \"Hello Mary Lou: Prom Night II (1987)\",\n    \"id\": 1988,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 51\n  },\n  {\n    \"title\": \"Curse of Frankenstein, The (1957)\",\n    \"id\": 2652,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 51\n  },\n  {\n    \"title\": \"Last Night (1998)\",\n    \"id\": 3008,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 51\n  },\n  {\n    \"title\": \"Map of the World, A (1999)\",\n    \"id\": 3128,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 51\n  },\n  {\n    \"title\": \"Date with an Angel (1987)\",\n    \"id\": 3393,\n    \"genres\": [\n      \"Comedy\",\n      \"Fantasy\"\n    ],\n    \"count\": 51\n  },\n  {\n    \"title\": \"Trial, The (Le Proc\\u00e8s) (1963)\",\n    \"id\": 3416,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 51\n  },\n  {\n    \"title\": \"King Creole (1958)\",\n    \"id\": 3605,\n    \"genres\": [\n      \"Drama\",\n      \"Musical\"\n    ],\n    \"count\": 51\n  },\n  {\n    \"title\": \"Groove (2000)\",\n    \"id\": 3790,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 51\n  },\n  {\n    \"title\": \"Crossing Guard, The (1995)\",\n    \"id\": 78,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 50\n  },\n  {\n    \"title\": \"Heidi Fleiss: Hollywood Madam (1995)\",\n    \"id\": 99,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 50\n  },\n  {\n    \"title\": \"House Arrest (1996)\",\n    \"id\": 840,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 50\n  },\n  {\n    \"title\": \"Jude (1996)\",\n    \"id\": 1056,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 50\n  },\n  {\n    \"title\": \"Associate, The (1996)\",\n    \"id\": 1113,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 50\n  },\n  {\n    \"title\": \"Hear My Song (1991)\",\n    \"id\": 1180,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 50\n  },\n  {\n    \"title\": \"Audrey Rose (1977)\",\n    \"id\": 1331,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 50\n  },\n  {\n    \"title\": \"Whole Wide World, The (1996)\",\n    \"id\": 1413,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 50\n  },\n  {\n    \"title\": \"Thousand Acres, A (1997)\",\n    \"id\": 1624,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 50\n  },\n  {\n    \"title\": \"Washington Square (1997)\",\n    \"id\": 1650,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 50\n  },\n  {\n    \"title\": \"Vibes (1988)\",\n    \"id\": 2733,\n    \"genres\": [\n      \"Adventure\",\n      \"Comedy\"\n    ],\n    \"count\": 50\n  },\n  {\n    \"title\": \"Love Is a Many-Splendored Thing (1955)\",\n    \"id\": 3414,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 50\n  },\n  {\n    \"title\": \"Myth of Fingerprints, The (1997)\",\n    \"id\": 3620,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 50\n  },\n  {\n    \"title\": \"Blow-Out (La Grande Bouffe) (1973)\",\n    \"id\": 3655,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 50\n  },\n  {\n    \"title\": \"Lonely Are the Brave (1962)\",\n    \"id\": 3737,\n    \"genres\": [\n      \"Drama\",\n      \"Western\"\n    ],\n    \"count\": 50\n  },\n  {\n    \"title\": \"Moonlight and Valentino (1995)\",\n    \"id\": 182,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 49\n  },\n  {\n    \"title\": \"National Lampoon's Senior Trip (1995)\",\n    \"id\": 325,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 49\n  },\n  {\n    \"title\": \"Above the Rim (1994)\",\n    \"id\": 409,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 49\n  },\n  {\n    \"title\": \"Mr. Wonderful (1993)\",\n    \"id\": 499,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 49\n  },\n  {\n    \"title\": \"Gone Fishin' (1997)\",\n    \"id\": 870,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 49\n  },\n  {\n    \"title\": \"Gridlock'd (1997)\",\n    \"id\": 1447,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 49\n  },\n  {\n    \"title\": \"Schizopolis (1996)\",\n    \"id\": 1546,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 49\n  },\n  {\n    \"title\": \"Assignment, The (1997)\",\n    \"id\": 1631,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 49\n  },\n  {\n    \"title\": \"Insomnia (1997)\",\n    \"id\": 1889,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 49\n  },\n  {\n    \"title\": \"Wisdom (1986)\",\n    \"id\": 2260,\n    \"genres\": [\n      \"Action\",\n      \"Crime\"\n    ],\n    \"count\": 49\n  },\n  {\n    \"title\": \"Bustin' Loose (1981)\",\n    \"id\": 3014,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 49\n  },\n  {\n    \"title\": \"Glass Bottom Boat, The (1966)\",\n    \"id\": 3144,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 49\n  },\n  {\n    \"title\": \"Brandon Teena Story, The (1998)\",\n    \"id\": 3281,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 49\n  },\n  {\n    \"title\": \"G. I. Blues (1960)\",\n    \"id\": 3602,\n    \"genres\": [\n      \"Musical\"\n    ],\n    \"count\": 49\n  },\n  {\n    \"title\": \"8 1/2 Women (1999)\",\n    \"id\": 3626,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 49\n  },\n  {\n    \"title\": \"Our Town (1940)\",\n    \"id\": 3870,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 49\n  },\n  {\n    \"title\": \"Mad Love (1995)\",\n    \"id\": 179,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 48\n  },\n  {\n    \"title\": \"Poison Ivy II (1995)\",\n    \"id\": 291,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 48\n  },\n  {\n    \"title\": \"Red Firecracker, Green Firecracker (1994)\",\n    \"id\": 309,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 48\n  },\n  {\n    \"title\": \"Clean Slate (1994)\",\n    \"id\": 433,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 48\n  },\n  {\n    \"title\": \"Land Before Time III: The Time of the Great Giving (1995)\",\n    \"id\": 888,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 48\n  },\n  {\n    \"title\": \"City of Industry (1997)\",\n    \"id\": 1472,\n    \"genres\": [\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 48\n  },\n  {\n    \"title\": \"Double Team (1997)\",\n    \"id\": 1497,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 48\n  },\n  {\n    \"title\": \"Kissing a Fool (1998)\",\n    \"id\": 1854,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 48\n  },\n  {\n    \"title\": \"Tango (1998)\",\n    \"id\": 2573,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 48\n  },\n  {\n    \"title\": \"Lovers of the Arctic Circle, The (Los Amantes del C\\u00edrculo Polar) (1998)\",\n    \"id\": 2585,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 48\n  },\n  {\n    \"title\": \"Hideous Kinky (1998)\",\n    \"id\": 2590,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 48\n  },\n  {\n    \"title\": \"My Son the Fanatic (1998)\",\n    \"id\": 2697,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 48\n  },\n  {\n    \"title\": \"Deterrence (1998)\",\n    \"id\": 3318,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 48\n  },\n  {\n    \"title\": \"Bound for Glory (1976)\",\n    \"id\": 3371,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 48\n  },\n  {\n    \"title\": \"Death Wish 4: The Crackdown (1987)\",\n    \"id\": 3433,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 48\n  },\n  {\n    \"title\": \"King of Marvin Gardens, The (1972)\",\n    \"id\": 3588,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 48\n  },\n  {\n    \"title\": \"Amateur (1994)\",\n    \"id\": 149,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 47\n  },\n  {\n    \"title\": \"Guilty as Sin (1993)\",\n    \"id\": 463,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 47\n  },\n  {\n    \"title\": \"Mr. Jones (1993)\",\n    \"id\": 498,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 47\n  },\n  {\n    \"title\": \"Bloodsport 2 (1995)\",\n    \"id\": 667,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 47\n  },\n  {\n    \"title\": \"Pather Panchali (1955)\",\n    \"id\": 668,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 47\n  },\n  {\n    \"title\": \"Heaven's Prisoners (1996)\",\n    \"id\": 731,\n    \"genres\": [\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 47\n  },\n  {\n    \"title\": \"Larger Than Life (1996)\",\n    \"id\": 813,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 47\n  },\n  {\n    \"title\": \"3 Ninjas: High Noon On Mega Mountain (1998)\",\n    \"id\": 1739,\n    \"genres\": [\n      \"Action\",\n      \"Children's\"\n    ],\n    \"count\": 47\n  },\n  {\n    \"title\": \"Night Flier (1997)\",\n    \"id\": 1771,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 47\n  },\n  {\n    \"title\": \"Cousin Bette (1998)\",\n    \"id\": 1896,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 47\n  },\n  {\n    \"title\": \"Another Day in Paradise (1998)\",\n    \"id\": 2440,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 47\n  },\n  {\n    \"title\": \"Howling II: Your Sister Is a Werewolf (1985)\",\n    \"id\": 2655,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 47\n  },\n  {\n    \"title\": \"Autumn Tale, An (Conte d'automne) (1998)\",\n    \"id\": 2708,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 47\n  },\n  {\n    \"title\": \"For the Love of Benji (1977)\",\n    \"id\": 3674,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 47\n  },\n  {\n    \"title\": \"Decline of Western Civilization Part II: The Metal Years, The (1988)\",\n    \"id\": 3680,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 47\n  },\n  {\n    \"title\": \"Phantasm IV: Oblivion (1998)\",\n    \"id\": 3839,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 47\n  },\n  {\n    \"title\": \"Beach Party (1963)\",\n    \"id\": 3921,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 47\n  },\n  {\n    \"title\": \"My Family (1995)\",\n    \"id\": 279,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 46\n  },\n  {\n    \"title\": \"Bitter Moon (1992)\",\n    \"id\": 347,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 46\n  },\n  {\n    \"title\": \"Meet Wally Sparks (1997)\",\n    \"id\": 1439,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 46\n  },\n  {\n    \"title\": \"Castle, The (1997)\",\n    \"id\": 2618,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 46\n  },\n  {\n    \"title\": \"It Came from Hollywood (1982)\",\n    \"id\": 2659,\n    \"genres\": [\n      \"Comedy\",\n      \"Documentary\"\n    ],\n    \"count\": 46\n  },\n  {\n    \"title\": \"In Too Deep (1999)\",\n    \"id\": 2812,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 46\n  },\n  {\n    \"title\": \"Gun Shy (2000)\",\n    \"id\": 3276,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 46\n  },\n  {\n    \"title\": \"Hot Spot, The (1990)\",\n    \"id\": 3765,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 46\n  },\n  {\n    \"title\": \"Georgia (1995)\",\n    \"id\": 55,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 45\n  },\n  {\n    \"title\": \"Hideaway (1995)\",\n    \"id\": 240,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 45\n  },\n  {\n    \"title\": \"Chasers (1994)\",\n    \"id\": 564,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 45\n  },\n  {\n    \"title\": \"When the Cats Away (Chacun cherche son chat) (1996)\",\n    \"id\": 1571,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 45\n  },\n  {\n    \"title\": \"Wings (1927)\",\n    \"id\": 1925,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\",\n      \"War\"\n    ],\n    \"count\": 45\n  },\n  {\n    \"title\": \"Children of the Corn III (1994)\",\n    \"id\": 2516,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 45\n  },\n  {\n    \"title\": \"Crazy in Alabama (1999)\",\n    \"id\": 2977,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 45\n  },\n  {\n    \"title\": \"Third Miracle, The (1999)\",\n    \"id\": 3183,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 45\n  },\n  {\n    \"title\": \"They Might Be Giants (1971)\",\n    \"id\": 3284,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 45\n  },\n  {\n    \"title\": \"Mifune (Mifunes sidste sang) (1999)\",\n    \"id\": 3320,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 45\n  },\n  {\n    \"title\": \"Baraka (1992)\",\n    \"id\": 3677,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 45\n  },\n  {\n    \"title\": \"Love's Labour's Lost (2000)\",\n    \"id\": 3719,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 45\n  },\n  {\n    \"title\": \"Bait (2000)\",\n    \"id\": 3898,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 45\n  },\n  {\n    \"title\": \"Hate (Haine, La) (1995)\",\n    \"id\": 97,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 44\n  },\n  {\n    \"title\": \"I'll Do Anything (1994)\",\n    \"id\": 472,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 44\n  },\n  {\n    \"title\": \"Nothing to Lose (1994)\",\n    \"id\": 875,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 44\n  },\n  {\n    \"title\": \"Love in the Afternoon (1957)\",\n    \"id\": 937,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 44\n  },\n  {\n    \"title\": \"Turbo: A Power Rangers Movie (1997)\",\n    \"id\": 1495,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 44\n  },\n  {\n    \"title\": \"Warriors of Virtue (1997)\",\n    \"id\": 1525,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Children's\",\n      \"Fantasy\"\n    ],\n    \"count\": 44\n  },\n  {\n    \"title\": \"Wide Awake (1998)\",\n    \"id\": 1812,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 44\n  },\n  {\n    \"title\": \"Character (Karakter) (1997)\",\n    \"id\": 1860,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 44\n  },\n  {\n    \"title\": \"Without Limits (1998)\",\n    \"id\": 2237,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 44\n  },\n  {\n    \"title\": \"Dancing at Lughnasa (1998)\",\n    \"id\": 2341,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 44\n  },\n  {\n    \"title\": \"Gloria (1999)\",\n    \"id\": 2479,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 44\n  },\n  {\n    \"title\": \"Chopping Mall (a.k.a. Killbots) (1986)\",\n    \"id\": 2614,\n    \"genres\": [\n      \"Action\",\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 44\n  },\n  {\n    \"title\": \"Emperor and the Assassin, The (Jing ke ci qin wang) (1999)\",\n    \"id\": 3158,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 44\n  },\n  {\n    \"title\": \"Shaft's Big Score! (1972)\",\n    \"id\": 3782,\n    \"genres\": [\n      \"Action\",\n      \"Crime\"\n    ],\n    \"count\": 44\n  },\n  {\n    \"title\": \"Two Women (La Ciociara) (1961)\",\n    \"id\": 3808,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 44\n  },\n  {\n    \"title\": \"Picture Bride (1995)\",\n    \"id\": 301,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 43\n  },\n  {\n    \"title\": \"Alaska (1996)\",\n    \"id\": 808,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 43\n  },\n  {\n    \"title\": \"Bogus (1996)\",\n    \"id\": 885,\n    \"genres\": [\n      \"Children's\",\n      \"Drama\",\n      \"Fantasy\"\n    ],\n    \"count\": 43\n  },\n  {\n    \"title\": \"Hype! (1996)\",\n    \"id\": 1310,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 43\n  },\n  {\n    \"title\": \"Amityville 3-D (1983)\",\n    \"id\": 1323,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 43\n  },\n  {\n    \"title\": \"Portrait of a Lady, The (1996)\",\n    \"id\": 1417,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 43\n  },\n  {\n    \"title\": \"Leave It to Beaver (1997)\",\n    \"id\": 1602,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 43\n  },\n  {\n    \"title\": \"Bandit Queen (1994)\",\n    \"id\": 2284,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 43\n  },\n  {\n    \"title\": \"Fiendish Plot of Dr. Fu Manchu, The (1980)\",\n    \"id\": 2286,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 43\n  },\n  {\n    \"title\": \"Desert Bloom (1986)\",\n    \"id\": 2345,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 43\n  },\n  {\n    \"title\": \"Prancer (1989)\",\n    \"id\": 2400,\n    \"genres\": [\n      \"Children's\",\n      \"Drama\"\n    ],\n    \"count\": 43\n  },\n  {\n    \"title\": \"Free Enterprise (1998)\",\n    \"id\": 2681,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 43\n  },\n  {\n    \"title\": \"Thumbelina (1994)\",\n    \"id\": 2876,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 43\n  },\n  {\n    \"title\": \"Cross of Iron (1977)\",\n    \"id\": 3339,\n    \"genres\": [\n      \"War\"\n    ],\n    \"count\": 43\n  },\n  {\n    \"title\": \"Devil's Brigade, The (1968)\",\n    \"id\": 3367,\n    \"genres\": [\n      \"War\"\n    ],\n    \"count\": 43\n  },\n  {\n    \"title\": \"Whatever It Takes (2000)\",\n    \"id\": 3454,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 43\n  },\n  {\n    \"title\": \"Cutter's Way (1981)\",\n    \"id\": 3731,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 43\n  },\n  {\n    \"title\": \"H.O.T.S. (1979)\",\n    \"id\": 3804,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 43\n  },\n  {\n    \"title\": \"Spiral Staircase, The (1946)\",\n    \"id\": 3849,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 43\n  },\n  {\n    \"title\": \"Digimon: The Movie (2000)\",\n    \"id\": 3945,\n    \"genres\": [\n      \"Adventure\",\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 43\n  },\n  {\n    \"title\": \"Perez Family, The (1995)\",\n    \"id\": 294,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"Baby-Sitters Club, The (1995)\",\n    \"id\": 343,\n    \"genres\": [\n      \"Children's\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"In the Realm of the Senses (Ai no corrida) (1976)\",\n    \"id\": 495,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"Poetic Justice (1993)\",\n    \"id\": 510,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"My Favorite Season (1993)\",\n    \"id\": 621,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"Quest, The (1996)\",\n    \"id\": 704,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"Kansas City (1996)\",\n    \"id\": 869,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"Children of the Corn IV: The Gathering (1996)\",\n    \"id\": 1105,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"Paradise Road (1997)\",\n    \"id\": 1507,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"Hush (1998)\",\n    \"id\": 1798,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"Hot Lead and Cold Feet (1978)\",\n    \"id\": 2055,\n    \"genres\": [\n      \"Comedy\",\n      \"Western\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"Deadly Friend (1986)\",\n    \"id\": 2465,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"Firewalker (1986)\",\n    \"id\": 2477,\n    \"genres\": [\n      \"Adventure\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"Rollercoaster (1977)\",\n    \"id\": 2523,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"Son of Dracula (1943)\",\n    \"id\": 2653,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"Best Laid Plans (1999)\",\n    \"id\": 2842,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"Dog Park (1998)\",\n    \"id\": 2884,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"Plunkett & MaCleane (1999)\",\n    \"id\": 2893,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"Go West (1925)\",\n    \"id\": 3133,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"Private School (1983)\",\n    \"id\": 3691,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"Phantasm III: Lord of the Dead (1994)\",\n    \"id\": 3838,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 42\n  },\n  {\n    \"title\": \"Big Green, The (1995)\",\n    \"id\": 54,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 41\n  },\n  {\n    \"title\": \"Anne Frank Remembered (1995)\",\n    \"id\": 116,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 41\n  },\n  {\n    \"title\": \"Wild Bill (1995)\",\n    \"id\": 210,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 41\n  },\n  {\n    \"title\": \"Underneath, The (1995)\",\n    \"id\": 335,\n    \"genres\": [\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 41\n  },\n  {\n    \"title\": \"Great Day in Harlem, A (1994)\",\n    \"id\": 602,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 41\n  },\n  {\n    \"title\": \"I Went Down (1997)\",\n    \"id\": 1910,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Crime\"\n    ],\n    \"count\": 41\n  },\n  {\n    \"title\": \"Three Seasons (1999)\",\n    \"id\": 2610,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 41\n  },\n  {\n    \"title\": \"Besieged (L' Assedio) (1998)\",\n    \"id\": 2630,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 41\n  },\n  {\n    \"title\": \"Get Bruce (1999)\",\n    \"id\": 2689,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 41\n  },\n  {\n    \"title\": \"Lovers on the Bridge, The (Les Amants du Pont-Neuf) (1991)\",\n    \"id\": 2704,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 41\n  },\n  {\n    \"title\": \"Perfect Blue (1997)\",\n    \"id\": 2810,\n    \"genres\": [\n      \"Animation\",\n      \"Mystery\"\n    ],\n    \"count\": 41\n  },\n  {\n    \"title\": \"Othello (1952)\",\n    \"id\": 2848,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 41\n  },\n  {\n    \"title\": \"Rocketship X-M (1950)\",\n    \"id\": 3780,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 41\n  },\n  {\n    \"title\": \"Miami Rhapsody (1995)\",\n    \"id\": 278,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 40\n  },\n  {\n    \"title\": \"Bad Company (1995)\",\n    \"id\": 384,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 40\n  },\n  {\n    \"title\": \"Jason's Lyric (1994)\",\n    \"id\": 391,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 40\n  },\n  {\n    \"title\": \"Penny Serenade (1941)\",\n    \"id\": 956,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 40\n  },\n  {\n    \"title\": \"Bliss (1997)\",\n    \"id\": 987,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 40\n  },\n  {\n    \"title\": \"I'm Not Rappaport (1996)\",\n    \"id\": 1317,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 40\n  },\n  {\n    \"title\": \"Best Men (1997)\",\n    \"id\": 1473,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 40\n  },\n  {\n    \"title\": \"Dream With the Fishes (1997)\",\n    \"id\": 1563,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 40\n  },\n  {\n    \"title\": \"Barney's Great Adventure (1998)\",\n    \"id\": 1826,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 40\n  },\n  {\n    \"title\": \"Barefoot Executive, The (1971)\",\n    \"id\": 2032,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 40\n  },\n  {\n    \"title\": \"Doug's 1st Movie (1999)\",\n    \"id\": 2566,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 40\n  },\n  {\n    \"title\": \"Meatballs 4 (1992)\",\n    \"id\": 3043,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 40\n  },\n  {\n    \"title\": \"And God Created Woman (1988)\",\n    \"id\": 3343,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 40\n  },\n  {\n    \"title\": \"Monsieur Verdoux (1947)\",\n    \"id\": 3632,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 40\n  },\n  {\n    \"title\": \"Trouble in Paradise (1932)\",\n    \"id\": 3739,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 40\n  },\n  {\n    \"title\": \"I'm the One That I Want (2000)\",\n    \"id\": 3851,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 40\n  },\n  {\n    \"title\": \"Crew, The (2000)\",\n    \"id\": 3884,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 40\n  },\n  {\n    \"title\": \"Steal This Movie! (2000)\",\n    \"id\": 3886,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 40\n  },\n  {\n    \"title\": \"Beautiful (2000)\",\n    \"id\": 3912,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 40\n  },\n  {\n    \"title\": \"Two Family House (2000)\",\n    \"id\": 3951,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 40\n  },\n  {\n    \"title\": \"M. Butterfly (1993)\",\n    \"id\": 488,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 39\n  },\n  {\n    \"title\": \"Eddie (1996)\",\n    \"id\": 656,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 39\n  },\n  {\n    \"title\": \"One Night Stand (1997)\",\n    \"id\": 1668,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 39\n  },\n  {\n    \"title\": \"Topaz (1969)\",\n    \"id\": 2179,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 39\n  },\n  {\n    \"title\": \"House of Dracula (1945)\",\n    \"id\": 2646,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 39\n  },\n  {\n    \"title\": \"Band of the Hand (1986)\",\n    \"id\": 3442,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 39\n  },\n  {\n    \"title\": \"Funny Bones (1995)\",\n    \"id\": 3446,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 39\n  },\n  {\n    \"title\": \"Filth and the Fury, The (2000)\",\n    \"id\": 3539,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 39\n  },\n  {\n    \"title\": \"Big Carnival, The (1951)\",\n    \"id\": 3736,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 39\n  },\n  {\n    \"title\": \"Affair of Love, An (Une Liaison Pornographique) (1999)\",\n    \"id\": 3855,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 39\n  },\n  {\n    \"title\": \"Bikini Beach (1964)\",\n    \"id\": 3922,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 39\n  },\n  {\n    \"title\": \"Jefferson in Paris (1995)\",\n    \"id\": 254,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 38\n  },\n  {\n    \"title\": \"Widows' Peak (1994)\",\n    \"id\": 452,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 38\n  },\n  {\n    \"title\": \"Getting Even with Dad (1994)\",\n    \"id\": 460,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 38\n  },\n  {\n    \"title\": \"House Party 3 (1994)\",\n    \"id\": 470,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 38\n  },\n  {\n    \"title\": \"Flirt (1995)\",\n    \"id\": 846,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 38\n  },\n  {\n    \"title\": \"Tarzan and the Lost City (1998)\",\n    \"id\": 1867,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 38\n  },\n  {\n    \"title\": \"One Tough Cop (1998)\",\n    \"id\": 2307,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 38\n  },\n  {\n    \"title\": \"Otello (1986)\",\n    \"id\": 2744,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 38\n  },\n  {\n    \"title\": \"Guardian, The (1990)\",\n    \"id\": 2982,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 38\n  },\n  {\n    \"title\": \"Daddy Long Legs (1919)\",\n    \"id\": 3132,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 38\n  },\n  {\n    \"title\": \"Let's Get Harry (1986)\",\n    \"id\": 3389,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 38\n  },\n  {\n    \"title\": \"Death Wish V: The Face of Death (1994)\",\n    \"id\": 3434,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 38\n  },\n  {\n    \"title\": \"Quatermass II (1957)\",\n    \"id\": 3659,\n    \"genres\": [\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 38\n  },\n  {\n    \"title\": \"Official Story, The (La Historia Oficial) (1985)\",\n    \"id\": 3816,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 38\n  },\n  {\n    \"title\": \"Boys of St. Vincent, The (1993)\",\n    \"id\": 121,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 37\n  },\n  {\n    \"title\": \"What Happened Was... (1994)\",\n    \"id\": 496,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 37\n  },\n  {\n    \"title\": \"Scarlet Letter, The (1926)\",\n    \"id\": 957,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 37\n  },\n  {\n    \"title\": \"Beat the Devil (1954)\",\n    \"id\": 970,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 37\n  },\n  {\n    \"title\": \"Cats Don't Dance (1997)\",\n    \"id\": 1489,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 37\n  },\n  {\n    \"title\": \"Shiloh (1997)\",\n    \"id\": 1547,\n    \"genres\": [\n      \"Children's\",\n      \"Drama\"\n    ],\n    \"count\": 37\n  },\n  {\n    \"title\": \"Tango Lesson, The (1997)\",\n    \"id\": 1669,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 37\n  },\n  {\n    \"title\": \"Critical Care (1997)\",\n    \"id\": 1677,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 37\n  },\n  {\n    \"title\": \"Chinese Box (1997)\",\n    \"id\": 1829,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 37\n  },\n  {\n    \"title\": \"$1,000,000 Duck (1971)\",\n    \"id\": 2031,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 37\n  },\n  {\n    \"title\": \"Happiest Millionaire, The (1967)\",\n    \"id\": 2049,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 37\n  },\n  {\n    \"title\": \"Tall Tale (1994)\",\n    \"id\": 2103,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 37\n  },\n  {\n    \"title\": \"Concorde: Airport '79, The (1979)\",\n    \"id\": 2536,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 37\n  },\n  {\n    \"title\": \"My Boyfriend's Back (1993)\",\n    \"id\": 2552,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 37\n  },\n  {\n    \"title\": \"Body Shots (1999)\",\n    \"id\": 2979,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 37\n  },\n  {\n    \"title\": \"How I Won the War (1967)\",\n    \"id\": 3049,\n    \"genres\": [\n      \"Comedy\",\n      \"War\"\n    ],\n    \"count\": 37\n  },\n  {\n    \"title\": \"Isn't She Great? (2000)\",\n    \"id\": 3239,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 37\n  },\n  {\n    \"title\": \"Idiots, The (Idioterne) (1998)\",\n    \"id\": 3569,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 37\n  },\n  {\n    \"title\": \"Whipped (2000)\",\n    \"id\": 3597,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 37\n  },\n  {\n    \"title\": \"Thomas and the Magic Railroad (2000)\",\n    \"id\": 3820,\n    \"genres\": [\n      \"Children's\"\n    ],\n    \"count\": 37\n  },\n  {\n    \"title\": \"Cosi (1996)\",\n    \"id\": 705,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 36\n  },\n  {\n    \"title\": \"American Dream (1990)\",\n    \"id\": 1169,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 36\n  },\n  {\n    \"title\": \"Blood For Dracula (Andy Warhol's Dracula) (1974)\",\n    \"id\": 1329,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 36\n  },\n  {\n    \"title\": \"Thieves (Voleurs, Les) (1996)\",\n    \"id\": 1415,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 36\n  },\n  {\n    \"title\": \"Traveller (1997)\",\n    \"id\": 1508,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 36\n  },\n  {\n    \"title\": \"Buddy (1997)\",\n    \"id\": 1551,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Drama\"\n    ],\n    \"count\": 36\n  },\n  {\n    \"title\": \"Roseanna's Grave (For Roseanna) (1997)\",\n    \"id\": 1564,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 36\n  },\n  {\n    \"title\": \"Hana-bi (1997)\",\n    \"id\": 1809,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 36\n  },\n  {\n    \"title\": \"Great Ziegfeld, The (1936)\",\n    \"id\": 1932,\n    \"genres\": [\n      \"Musical\"\n    ],\n    \"count\": 36\n  },\n  {\n    \"title\": \"This Is My Father (1998)\",\n    \"id\": 2620,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 36\n  },\n  {\n    \"title\": \"Mummy's Tomb, The (1942)\",\n    \"id\": 2638,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 36\n  },\n  {\n    \"title\": \"Funhouse, The (1981)\",\n    \"id\": 3021,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 36\n  },\n  {\n    \"title\": \"Roommates (1995)\",\n    \"id\": 304,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 35\n  },\n  {\n    \"title\": \"Inkwell, The (1994)\",\n    \"id\": 476,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 35\n  },\n  {\n    \"title\": \"Bhaji on the Beach (1993)\",\n    \"id\": 568,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 35\n  },\n  {\n    \"title\": \"Underground (1995)\",\n    \"id\": 665,\n    \"genres\": [\n      \"War\"\n    ],\n    \"count\": 35\n  },\n  {\n    \"title\": \"Surviving Picasso (1996)\",\n    \"id\": 1044,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 35\n  },\n  {\n    \"title\": \"Normal Life (1996)\",\n    \"id\": 1053,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 35\n  },\n  {\n    \"title\": \"Crossfire (1947)\",\n    \"id\": 1068,\n    \"genres\": [\n      \"Crime\",\n      \"Film-Noir\"\n    ],\n    \"count\": 35\n  },\n  {\n    \"title\": \"Palookaville (1996)\",\n    \"id\": 1112,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 35\n  },\n  {\n    \"title\": \"Blood & Wine (1997)\",\n    \"id\": 1351,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 35\n  },\n  {\n    \"title\": \"Cruise, The (1998)\",\n    \"id\": 2323,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 35\n  },\n  {\n    \"title\": \"My Name Is Joe (1998)\",\n    \"id\": 2481,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 35\n  },\n  {\n    \"title\": \"Monster, The (Il Mostro) (1994)\",\n    \"id\": 2593,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 35\n  },\n  {\n    \"title\": \"Edge of Seventeen (1998)\",\n    \"id\": 2626,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 35\n  },\n  {\n    \"title\": \"My Best Fiend (Mein liebster Feind) (1999)\",\n    \"id\": 3002,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 35\n  },\n  {\n    \"title\": \"Rosetta (1999)\",\n    \"id\": 3010,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 35\n  },\n  {\n    \"title\": \"My Tutor (1983)\",\n    \"id\": 3331,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 35\n  },\n  {\n    \"title\": \"Bossa Nova (1999)\",\n    \"id\": 3567,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 35\n  },\n  {\n    \"title\": \"Rent-A-Cop (1988)\",\n    \"id\": 3667,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 35\n  },\n  {\n    \"title\": \"Fun and Fancy Free (1947)\",\n    \"id\": 3759,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 35\n  },\n  {\n    \"title\": \"Nico Icon (1995)\",\n    \"id\": 77,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 34\n  },\n  {\n    \"title\": \"Browning Version, The (1994)\",\n    \"id\": 211,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 34\n  },\n  {\n    \"title\": \"Wedding Gift, The (1994)\",\n    \"id\": 571,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 34\n  },\n  {\n    \"title\": \"Drunks (1997)\",\n    \"id\": 1119,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 34\n  },\n  {\n    \"title\": \"That Old Feeling (1997)\",\n    \"id\": 1463,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 34\n  },\n  {\n    \"title\": \"Beyond the Poseidon Adventure (1979)\",\n    \"id\": 2537,\n    \"genres\": [\n      \"Adventure\"\n    ],\n    \"count\": 34\n  },\n  {\n    \"title\": \"Hell Night (1981)\",\n    \"id\": 2878,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 34\n  },\n  {\n    \"title\": \"Agnes Browne (1999)\",\n    \"id\": 3124,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 34\n  },\n  {\n    \"title\": \"Good Mother, The (1988)\",\n    \"id\": 3449,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 34\n  },\n  {\n    \"title\": \"Where the Money Is (2000)\",\n    \"id\": 3537,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 34\n  },\n  {\n    \"title\": \"Roustabout (1964)\",\n    \"id\": 3610,\n    \"genres\": [\n      \"Musical\"\n    ],\n    \"count\": 34\n  },\n  {\n    \"title\": \"Knightriders (1981)\",\n    \"id\": 3805,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Drama\"\n    ],\n    \"count\": 34\n  },\n  {\n    \"title\": \"Once Upon a Time... When We Were Colored (1995)\",\n    \"id\": 83,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Unforgettable (1996)\",\n    \"id\": 103,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Three Wishes (1995)\",\n    \"id\": 201,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Sum of Us, The (1994)\",\n    \"id\": 324,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Visitors, The (Les Visiteurs) (1993)\",\n    \"id\": 718,\n    \"genres\": [\n      \"Comedy\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Stalingrad (1993)\",\n    \"id\": 760,\n    \"genres\": [\n      \"War\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Pompatus of Love, The (1996)\",\n    \"id\": 984,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Perfect Candidate, A (1996)\",\n    \"id\": 1123,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Player's Club, The (1998)\",\n    \"id\": 1825,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Mr. & Mrs. Smith (1941)\",\n    \"id\": 2205,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Mummy's Hand, The (1940)\",\n    \"id\": 2637,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Ghost of Frankenstein, The (1942)\",\n    \"id\": 2650,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Adventures of Sebastian Cole, The (1998)\",\n    \"id\": 2766,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Only Angels Have Wings (1939)\",\n    \"id\": 2847,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Adventures of Elmo in Grouchland, The (1999)\",\n    \"id\": 2886,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Sandpiper, The (1965)\",\n    \"id\": 3073,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Country (1984)\",\n    \"id\": 3110,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Hell in the Pacific (1968)\",\n    \"id\": 3143,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Big Tease, The (1999)\",\n    \"id\": 3240,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Man from Laramie, The (1955)\",\n    \"id\": 3311,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Shanghai Surprise (1986)\",\n    \"id\": 3390,\n    \"genres\": [\n      \"Adventure\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Sarafina! (1992)\",\n    \"id\": 3711,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Crime and Punishment in Suburbia (2000)\",\n    \"id\": 3900,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 33\n  },\n  {\n    \"title\": \"Far From Home: The Adventures of Yellow Dog (1995)\",\n    \"id\": 238,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 32\n  },\n  {\n    \"title\": \"Suture (1993)\",\n    \"id\": 320,\n    \"genres\": [\n      \"Film-Noir\",\n      \"Thriller\"\n    ],\n    \"count\": 32\n  },\n  {\n    \"title\": \"Ed (1996)\",\n    \"id\": 619,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 32\n  },\n  {\n    \"title\": \"Month by the Lake, A (1995)\",\n    \"id\": 753,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 32\n  },\n  {\n    \"title\": \"Amityville 1992: It's About Time (1992)\",\n    \"id\": 1322,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 32\n  },\n  {\n    \"title\": \"Career Girls (1997)\",\n    \"id\": 1596,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 32\n  },\n  {\n    \"title\": \"Artemisia (1997)\",\n    \"id\": 1695,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 32\n  },\n  {\n    \"title\": \"Taste of Cherry (1997)\",\n    \"id\": 1859,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 32\n  },\n  {\n    \"title\": \"Mr. Jealousy (1997)\",\n    \"id\": 1906,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 32\n  },\n  {\n    \"title\": \"Broadway Melody, The (1929)\",\n    \"id\": 1926,\n    \"genres\": [\n      \"Musical\"\n    ],\n    \"count\": 32\n  },\n  {\n    \"title\": \"Garbage Pail Kids Movie, The (1987)\",\n    \"id\": 2449,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 32\n  },\n  {\n    \"title\": \"My Life So Far (1999)\",\n    \"id\": 2711,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 32\n  },\n  {\n    \"title\": \"Killer's Kiss (1955)\",\n    \"id\": 2727,\n    \"genres\": [\n      \"Film-Noir\"\n    ],\n    \"count\": 32\n  },\n  {\n    \"title\": \"Wanted: Dead or Alive (1987)\",\n    \"id\": 2756,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 32\n  },\n  {\n    \"title\": \"Romance (1999)\",\n    \"id\": 2894,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 32\n  },\n  {\n    \"title\": \"Poison (1991)\",\n    \"id\": 3218,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 32\n  },\n  {\n    \"title\": \"Raise the Titanic (1980)\",\n    \"id\": 3403,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 32\n  },\n  {\n    \"title\": \"How to Stuff a Wild Bikini (1965)\",\n    \"id\": 3520,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 32\n  },\n  {\n    \"title\": \"Under Suspicion (2000)\",\n    \"id\": 3906,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 32\n  },\n  {\n    \"title\": \"Barenaked in America (1999)\",\n    \"id\": 3913,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 32\n  },\n  {\n    \"title\": \"Double Happiness (1994)\",\n    \"id\": 341,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 31\n  },\n  {\n    \"title\": \"Endless Summer 2, The (1994)\",\n    \"id\": 443,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 31\n  },\n  {\n    \"title\": \"Favor, The (1994)\",\n    \"id\": 447,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 31\n  },\n  {\n    \"title\": \"Force of Evil (1948)\",\n    \"id\": 746,\n    \"genres\": [\n      \"Film-Noir\"\n    ],\n    \"count\": 31\n  },\n  {\n    \"title\": \"Manny & Lo (1996)\",\n    \"id\": 862,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 31\n  },\n  {\n    \"title\": \"Funeral, The (1996)\",\n    \"id\": 1114,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 31\n  },\n  {\n    \"title\": \"Faust (1994)\",\n    \"id\": 1151,\n    \"genres\": [\n      \"Animation\",\n      \"Comedy\",\n      \"Thriller\"\n    ],\n    \"count\": 31\n  },\n  {\n    \"title\": \"Nowhere (1997)\",\n    \"id\": 1529,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 31\n  },\n  {\n    \"title\": \"Mummy's Curse, The (1944)\",\n    \"id\": 2635,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 31\n  },\n  {\n    \"title\": \"Light of Day (1987)\",\n    \"id\": 2755,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 31\n  },\n  {\n    \"title\": \"Plenty (1985)\",\n    \"id\": 2758,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 31\n  },\n  {\n    \"title\": \"Man Facing Southeast (Hombre Mirando al Sudeste) (1986)\",\n    \"id\": 2938,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 31\n  },\n  {\n    \"title\": \"Rawhead Rex (1986)\",\n    \"id\": 2992,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 31\n  },\n  {\n    \"title\": \"Benji the Hunted (1987)\",\n    \"id\": 3673,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 31\n  },\n  {\n    \"title\": \"Cry, the Beloved Country (1995)\",\n    \"id\": 40,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 30\n  },\n  {\n    \"title\": \"Total Eclipse (1995)\",\n    \"id\": 202,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 30\n  },\n  {\n    \"title\": \"Heavyweights (1994)\",\n    \"id\": 250,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 30\n  },\n  {\n    \"title\": \"S.F.W. (1994)\",\n    \"id\": 386,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 30\n  },\n  {\n    \"title\": \"Love and a .45 (1994)\",\n    \"id\": 600,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 30\n  },\n  {\n    \"title\": \"It's My Party (1995)\",\n    \"id\": 685,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 30\n  },\n  {\n    \"title\": \"Cyclo (1995)\",\n    \"id\": 850,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 30\n  },\n  {\n    \"title\": \"Star Maps (1997)\",\n    \"id\": 1613,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 30\n  },\n  {\n    \"title\": \"Shooting Fish (1997)\",\n    \"id\": 1755,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 30\n  },\n  {\n    \"title\": \"Dream for an Insomniac (1996)\",\n    \"id\": 1902,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 30\n  },\n  {\n    \"title\": \"Autumn Sonata (H\\u00f6stsonaten ) (1978)\",\n    \"id\": 2131,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 30\n  },\n  {\n    \"title\": \"Still Crazy (1998)\",\n    \"id\": 2482,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 30\n  },\n  {\n    \"title\": \"Queens Logic (1991)\",\n    \"id\": 2849,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 30\n  },\n  {\n    \"title\": \"Carmen (1984)\",\n    \"id\": 3222,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 30\n  },\n  {\n    \"title\": \"Limelight (1952)\",\n    \"id\": 3559,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 30\n  },\n  {\n    \"title\": \"Inferno (1980)\",\n    \"id\": 3587,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 30\n  },\n  {\n    \"title\": \"Slipper and the Rose, The (1976)\",\n    \"id\": 3612,\n    \"genres\": [\n      \"Adventure\",\n      \"Musical\",\n      \"Romance\"\n    ],\n    \"count\": 30\n  },\n  {\n    \"title\": \"Sleepaway Camp (1983)\",\n    \"id\": 3843,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 30\n  },\n  {\n    \"title\": \"Mute Witness (1994)\",\n    \"id\": 183,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 29\n  },\n  {\n    \"title\": \"Fluke (1995)\",\n    \"id\": 241,\n    \"genres\": [\n      \"Children's\",\n      \"Drama\"\n    ],\n    \"count\": 29\n  },\n  {\n    \"title\": \"Pyromaniac's Love Story, A (1995)\",\n    \"id\": 295,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 29\n  },\n  {\n    \"title\": \"Being Human (1993)\",\n    \"id\": 418,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 29\n  },\n  {\n    \"title\": \"Lassie (1994)\",\n    \"id\": 484,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 29\n  },\n  {\n    \"title\": \"He Walked by Night (1948)\",\n    \"id\": 1152,\n    \"genres\": [\n      \"Crime\",\n      \"Film-Noir\",\n      \"Thriller\"\n    ],\n    \"count\": 29\n  },\n  {\n    \"title\": \"Female Perversions (1996)\",\n    \"id\": 1312,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 29\n  },\n  {\n    \"title\": \"Live Flesh (1997)\",\n    \"id\": 1844,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 29\n  },\n  {\n    \"title\": \"Cimarron (1931)\",\n    \"id\": 1928,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 29\n  },\n  {\n    \"title\": \"One Magic Christmas (1985)\",\n    \"id\": 2086,\n    \"genres\": [\n      \"Drama\",\n      \"Fantasy\"\n    ],\n    \"count\": 29\n  },\n  {\n    \"title\": \"Following (1998)\",\n    \"id\": 2579,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 29\n  },\n  {\n    \"title\": \"Never Talk to Strangers (1995)\",\n    \"id\": 2778,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 29\n  },\n  {\n    \"title\": \"Source, The (1999)\",\n    \"id\": 2813,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 29\n  },\n  {\n    \"title\": \"Santa Fe Trail (1940)\",\n    \"id\": 3122,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\",\n      \"Western\"\n    ],\n    \"count\": 29\n  },\n  {\n    \"title\": \"Draughtsman's Contract, The (1982)\",\n    \"id\": 3221,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 29\n  },\n  {\n    \"title\": \"Zed & Two Noughts, A (1985)\",\n    \"id\": 3223,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 29\n  },\n  {\n    \"title\": \"We're Back! A Dinosaur's Story (1993)\",\n    \"id\": 3400,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 29\n  },\n  {\n    \"title\": \"Up at the Villa (2000)\",\n    \"id\": 3580,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 29\n  },\n  {\n    \"title\": \"Shaft in Africa (1973)\",\n    \"id\": 3781,\n    \"genres\": [\n      \"Action\",\n      \"Crime\"\n    ],\n    \"count\": 29\n  },\n  {\n    \"title\": \"In Crowd, The (2000)\",\n    \"id\": 3797,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 29\n  },\n  {\n    \"title\": \"It Takes Two (1995)\",\n    \"id\": 38,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Before the Rain (Pred dozhdot) (1994)\",\n    \"id\": 214,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Castle Freak (1995)\",\n    \"id\": 220,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Ladybird Ladybird (1994)\",\n    \"id\": 263,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Panther (1995)\",\n    \"id\": 297,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Good Man in Africa, A (1994)\",\n    \"id\": 462,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Dear Diary (Caro Diario) (1994)\",\n    \"id\": 583,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Switchblade Sisters (1975)\",\n    \"id\": 716,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Kaspar Hauser (1993)\",\n    \"id\": 824,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Caught (1996)\",\n    \"id\": 997,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Love Serenade (1996)\",\n    \"id\": 1585,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Bent (1997)\",\n    \"id\": 1696,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Harmonists, The (1997)\",\n    \"id\": 2493,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Metroland (1997)\",\n    \"id\": 2577,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"It Conquered the World (1956)\",\n    \"id\": 2666,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Communion (a.k.a. Alice, Sweet Alice/Holy Terror) (1977)\",\n    \"id\": 2853,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Splendor (1999)\",\n    \"id\": 2864,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Alvarez Kelly (1966)\",\n    \"id\": 2896,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Mike's Murder (1984)\",\n    \"id\": 2945,\n    \"genres\": [\n      \"Mystery\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Judy Berlin (1999)\",\n    \"id\": 3319,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Passion of Mind (1999)\",\n    \"id\": 3540,\n    \"genres\": [\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Idolmaker, The (1980)\",\n    \"id\": 3586,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Blood In, Blood Out (a.k.a. Bound by Honor) (1993)\",\n    \"id\": 3761,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"And God Created Woman (Et Dieu&#8230;Cr\\u00e9a la Femme) (1956)\",\n    \"id\": 3845,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Kronos (1957)\",\n    \"id\": 3934,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"Sorority House Massacre II (1990)\",\n    \"id\": 3942,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 28\n  },\n  {\n    \"title\": \"When Night Is Falling (1995)\",\n    \"id\": 49,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 27\n  },\n  {\n    \"title\": \"Faces (1968)\",\n    \"id\": 702,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 27\n  },\n  {\n    \"title\": \"Haunted World of Edward D. Wood Jr., The (1995)\",\n    \"id\": 722,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 27\n  },\n  {\n    \"title\": \"Love Jones (1997)\",\n    \"id\": 1477,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 27\n  },\n  {\n    \"title\": \"Simple Wish, A (1997)\",\n    \"id\": 1583,\n    \"genres\": [\n      \"Children's\",\n      \"Fantasy\"\n    ],\n    \"count\": 27\n  },\n  {\n    \"title\": \"Still Breathing (1997)\",\n    \"id\": 1874,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 27\n  },\n  {\n    \"title\": \"Knock Off (1998)\",\n    \"id\": 2196,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 27\n  },\n  {\n    \"title\": \"I'll Be Home For Christmas (1998)\",\n    \"id\": 2339,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 27\n  },\n  {\n    \"title\": \"Theory of Flight, The (1998)\",\n    \"id\": 2426,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 27\n  },\n  {\n    \"title\": \"Last Days, The (1998)\",\n    \"id\": 2494,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 27\n  },\n  {\n    \"title\": \"King and I, The (1999)\",\n    \"id\": 2559,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 27\n  },\n  {\n    \"title\": \"Year My Voice Broke, The (1987)\",\n    \"id\": 3329,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 27\n  },\n  {\n    \"title\": \"For All Mankind (1989)\",\n    \"id\": 3338,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 27\n  },\n  {\n    \"title\": \"All the Vermeers in New York (1990)\",\n    \"id\": 3531,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 27\n  },\n  {\n    \"title\": \"Giant Gila Monster, The (1959)\",\n    \"id\": 3931,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 27\n  },\n  {\n    \"title\": \"Slumber Party Massacre, The (1982)\",\n    \"id\": 3938,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 27\n  },\n  {\n    \"title\": \"Nadja (1994)\",\n    \"id\": 184,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 26\n  },\n  {\n    \"title\": \"Trial by Jury (1994)\",\n    \"id\": 554,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 26\n  },\n  {\n    \"title\": \"Johns (1996)\",\n    \"id\": 1063,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 26\n  },\n  {\n    \"title\": \"B*A*P*S (1997)\",\n    \"id\": 1490,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 26\n  },\n  {\n    \"title\": \"Contempt (Le M\\u00e9pris) (1963)\",\n    \"id\": 1572,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 26\n  },\n  {\n    \"title\": \"Music From Another Room (1998)\",\n    \"id\": 1767,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 26\n  },\n  {\n    \"title\": \"Lawn Dogs (1997)\",\n    \"id\": 1880,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 26\n  },\n  {\n    \"title\": \"Stage Fright (1950)\",\n    \"id\": 2187,\n    \"genres\": [\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 26\n  },\n  {\n    \"title\": \"Hi-Lo Country, The (1998)\",\n    \"id\": 2441,\n    \"genres\": [\n      \"Drama\",\n      \"Western\"\n    ],\n    \"count\": 26\n  },\n  {\n    \"title\": \"Loss of Sexual Innocence, The (1999)\",\n    \"id\": 2674,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 26\n  },\n  {\n    \"title\": \"Committed (2000)\",\n    \"id\": 3562,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 26\n  },\n  {\n    \"title\": \"Smiling Fish and Goat on Fire (1999)\",\n    \"id\": 3568,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 26\n  },\n  {\n    \"title\": \"Carnosaur 2 (1995)\",\n    \"id\": 3573,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 26\n  },\n  {\n    \"title\": \"Curse of the Puppet Master (1998)\",\n    \"id\": 3665,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 26\n  },\n  {\n    \"title\": \"Love & Sex (2000)\",\n    \"id\": 3885,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 26\n  },\n  {\n    \"title\": \"Pajama Party (1964)\",\n    \"id\": 3924,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 26\n  },\n  {\n    \"title\": \"Slumber Party Massacre II, The (1987)\",\n    \"id\": 3939,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 26\n  },\n  {\n    \"title\": \"Secret Adventures of Tom Thumb, The (1993)\",\n    \"id\": 392,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 25\n  },\n  {\n    \"title\": \"Purple Noon (1960)\",\n    \"id\": 659,\n    \"genres\": [\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 25\n  },\n  {\n    \"title\": \"Eyes Without a Face (1959)\",\n    \"id\": 841,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 25\n  },\n  {\n    \"title\": \"Amityville Curse, The (1990)\",\n    \"id\": 1328,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 25\n  },\n  {\n    \"title\": \"Anna Karenina (1997)\",\n    \"id\": 1496,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 25\n  },\n  {\n    \"title\": \"Price Above Rubies, A (1998)\",\n    \"id\": 1814,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 25\n  },\n  {\n    \"title\": \"Safe Men (1998)\",\n    \"id\": 2128,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 25\n  },\n  {\n    \"title\": \"Shadrach (1998)\",\n    \"id\": 2293,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 25\n  },\n  {\n    \"title\": \"Slam (1998)\",\n    \"id\": 2305,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 25\n  },\n  {\n    \"title\": \"Day of the Beast, The (El D\\u00eda de la bestia) (1995)\",\n    \"id\": 2483,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 25\n  },\n  {\n    \"title\": \"Goodbye, Lover (1999)\",\n    \"id\": 2586,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 25\n  },\n  {\n    \"title\": \"Desert Blue (1999)\",\n    \"id\": 2678,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 25\n  },\n  {\n    \"title\": \"Illuminata (1998)\",\n    \"id\": 2767,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 25\n  },\n  {\n    \"title\": \"Tales of Terror (1962)\",\n    \"id\": 2785,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 25\n  },\n  {\n    \"title\": \"Come See the Paradise (1990)\",\n    \"id\": 3106,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 25\n  },\n  {\n    \"title\": \"Room at the Top (1959)\",\n    \"id\": 3171,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 25\n  },\n  {\n    \"title\": \"Perils of Pauline, The (1947)\",\n    \"id\": 3349,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 25\n  },\n  {\n    \"title\": \"Psycho Beach Party (2000)\",\n    \"id\": 3830,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 25\n  },\n  {\n    \"title\": \"Ilsa, She Wolf of the SS (1974)\",\n    \"id\": 3847,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 25\n  },\n  {\n    \"title\": \"Man of the House (1995)\",\n    \"id\": 274,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Calendar Girl (1993)\",\n    \"id\": 430,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Jimmy Hollywood (1994)\",\n    \"id\": 478,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Grass Harp, The (1995)\",\n    \"id\": 767,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Carpool (1996)\",\n    \"id\": 867,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Girls Town (1996)\",\n    \"id\": 877,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"C\\u00e9r\\u00e9monie, La (1995)\",\n    \"id\": 1406,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Touch (1997)\",\n    \"id\": 1458,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"A Chef in Love (1996)\",\n    \"id\": 1511,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Promise, The (La Promesse) (1996)\",\n    \"id\": 1533,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Chairman of the Board (1998)\",\n    \"id\": 1679,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Real Blonde, The (1997)\",\n    \"id\": 1857,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Parasite (1982)\",\n    \"id\": 2256,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Native Son (1986)\",\n    \"id\": 2743,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Terrorist, The (Malli) (1998)\",\n    \"id\": 3192,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Circus, The (1928)\",\n    \"id\": 3306,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Dog's Life, A (1920)\",\n    \"id\": 3309,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Jonah Who Will Be 25 in the Year 2000 (1976)\",\n    \"id\": 3473,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Trixie (1999)\",\n    \"id\": 3721,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Golden Bowl, The (2000)\",\n    \"id\": 3756,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Melody Time (1948)\",\n    \"id\": 3776,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Couch in New York, A (1996)\",\n    \"id\": 3874,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Goya in Bordeaux (Goya en Bodeos) (1999)\",\n    \"id\": 3902,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 24\n  },\n  {\n    \"title\": \"Awfully Big Adventure, An (1995)\",\n    \"id\": 148,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 23\n  },\n  {\n    \"title\": \"Pushing Hands (1992)\",\n    \"id\": 298,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 23\n  },\n  {\n    \"title\": \"Slingshot, The (K\\u00e5disbellan ) (1993)\",\n    \"id\": 570,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 23\n  },\n  {\n    \"title\": \"Aparajito (1956)\",\n    \"id\": 669,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 23\n  },\n  {\n    \"title\": \"Little Lord Fauntleroy (1936)\",\n    \"id\": 961,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 23\n  },\n  {\n    \"title\": \"Outlaw, The (1943)\",\n    \"id\": 967,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 23\n  },\n  {\n    \"title\": \"Damsel in Distress, A (1937)\",\n    \"id\": 1067,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\",\n      \"Romance\"\n    ],\n    \"count\": 23\n  },\n  {\n    \"title\": \"Innocents, The (1961)\",\n    \"id\": 1076,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 23\n  },\n  {\n    \"title\": \"Amityville: A New Generation (1993)\",\n    \"id\": 1325,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 23\n  },\n  {\n    \"title\": \"Zeus and Roxanne (1997)\",\n    \"id\": 1426,\n    \"genres\": [\n      \"Children's\"\n    ],\n    \"count\": 23\n  },\n  {\n    \"title\": \"Tetsuo II: Body Hammer (1992)\",\n    \"id\": 1570,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 23\n  },\n  {\n    \"title\": \"Education of Little Tree, The (1997)\",\n    \"id\": 1725,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 23\n  },\n  {\n    \"title\": \"Mass Appeal (1984)\",\n    \"id\": 2397,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 23\n  },\n  {\n    \"title\": \"Assassination (1987)\",\n    \"id\": 2737,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 23\n  },\n  {\n    \"title\": \"Black Cat, White Cat (Crna macka, beli macor) (1998)\",\n    \"id\": 2843,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 23\n  },\n  {\n    \"title\": \"Charlie, the Lonesome Cougar (1967)\",\n    \"id\": 3345,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 23\n  },\n  {\n    \"title\": \"Turtle Diary (1985)\",\n    \"id\": 3402,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 23\n  },\n  {\n    \"title\": \"Mirror, The (Zerkalo) (1975)\",\n    \"id\": 3415,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 23\n  },\n  {\n    \"title\": \"Kronos (1973)\",\n    \"id\": 3935,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 23\n  },\n  {\n    \"title\": \"Cure, The (1995)\",\n    \"id\": 219,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 22\n  },\n  {\n    \"title\": \"Amityville: Dollhouse (1996)\",\n    \"id\": 1324,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 22\n  },\n  {\n    \"title\": \"Grateful Dead (1995)\",\n    \"id\": 1421,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 22\n  },\n  {\n    \"title\": \"Hotel de Love (1996)\",\n    \"id\": 1455,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 22\n  },\n  {\n    \"title\": \"Deceiver (1997)\",\n    \"id\": 1671,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 22\n  },\n  {\n    \"title\": \"Winter Guest, The (1997)\",\n    \"id\": 1728,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 22\n  },\n  {\n    \"title\": \"Hanging Garden, The (1997)\",\n    \"id\": 1879,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 22\n  },\n  {\n    \"title\": \"Gnome-Mobile, The (1967)\",\n    \"id\": 2047,\n    \"genres\": [\n      \"Children's\"\n    ],\n    \"count\": 22\n  },\n  {\n    \"title\": \"Velocity of Gary, The (1998)\",\n    \"id\": 2715,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 22\n  },\n  {\n    \"title\": \"Little Nemo: Adventures in Slumberland (1992)\",\n    \"id\": 2800,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 22\n  },\n  {\n    \"title\": \"Lulu on the Bridge (1998)\",\n    \"id\": 2873,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\",\n      \"Romance\"\n    ],\n    \"count\": 22\n  },\n  {\n    \"title\": \"Hideous Sun Demon, The (1959)\",\n    \"id\": 3488,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 22\n  },\n  {\n    \"title\": \"Time Regained (Le Temps Retrouv\\u00e9) (1999)\",\n    \"id\": 3749,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 22\n  },\n  {\n    \"title\": \"Sorority House Massacre (1986)\",\n    \"id\": 3941,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 22\n  },\n  {\n    \"title\": \"Last Summer in the Hamptons (1995)\",\n    \"id\": 84,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"Nina Takes a Lover (1994)\",\n    \"id\": 287,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"Tom & Viv (1994)\",\n    \"id\": 331,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"Saint of Fort Washington, The (1993)\",\n    \"id\": 525,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"Ciao, Professore! (Io speriamo che me la cavo ) (1993)\",\n    \"id\": 573,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"Police Story 4: Project S (Chao ji ji hua) (1993)\",\n    \"id\": 876,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"Blood Beach (1981)\",\n    \"id\": 1335,\n    \"genres\": [\n      \"Action\",\n      \"Horror\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"Cement Garden, The (1993)\",\n    \"id\": 1437,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"All Over Me (1997)\",\n    \"id\": 1509,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"Meet the Deedles (1998)\",\n    \"id\": 1822,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"Beyond Silence (1996)\",\n    \"id\": 1893,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"Life of \\u00c9mile Zola, The (1937)\",\n    \"id\": 1933,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"Night Porter, The (Il Portiere di notte) (1974)\",\n    \"id\": 2074,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"I Married A Strange Person (1997)\",\n    \"id\": 2189,\n    \"genres\": [\n      \"Animation\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"Lodger, The (1926)\",\n    \"id\": 2227,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"Bedrooms & Hallways (1998)\",\n    \"id\": 2837,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"Simpatico (1999)\",\n    \"id\": 3162,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"Freedom for Us (\\u00c0 nous la libert\\u00e9 ) (1931)\",\n    \"id\": 3532,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"Story of G.I. Joe, The (1945)\",\n    \"id\": 3670,\n    \"genres\": [\n      \"War\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"Black Sabbath (Tre Volti Della Paura, I) (1963)\",\n    \"id\": 3832,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"Solas (1999)\",\n    \"id\": 3894,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 21\n  },\n  {\n    \"title\": \"Colonel Chabert, Le (1994)\",\n    \"id\": 389,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\",\n      \"War\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Denise Calls Up (1995)\",\n    \"id\": 633,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Nelly & Monsieur Arnaud (1995)\",\n    \"id\": 645,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Stonewall (1995)\",\n    \"id\": 831,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Angel on My Shoulder (1946)\",\n    \"id\": 960,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Rich Man's Wife, The (1996)\",\n    \"id\": 992,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Curdled (1996)\",\n    \"id\": 1000,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Some Mother's Son (1996)\",\n    \"id\": 1412,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Smile Like Yours, A (1997)\",\n    \"id\": 1632,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Wonderland (1997)\",\n    \"id\": 1657,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Gang Related (1997)\",\n    \"id\": 1662,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Hugo Pool (1997)\",\n    \"id\": 1666,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Alien Escape (1995)\",\n    \"id\": 1692,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Nil By Mouth (1997)\",\n    \"id\": 1846,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Chambermaid on the Titanic, The (1998)\",\n    \"id\": 2157,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Henry: Portrait of a Serial Killer, Part 2 (1996)\",\n    \"id\": 2158,\n    \"genres\": [\n      \"Crime\",\n      \"Horror\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Surf Nazis Must Die (1987)\",\n    \"id\": 2164,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Paradine Case, The (1947)\",\n    \"id\": 2201,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Overnight Delivery (1996)\",\n    \"id\": 2292,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Legend of 1900, The (Leggenda del pianista sull'oceano) (1998)\",\n    \"id\": 2691,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Love Stinks (1999)\",\n    \"id\": 2809,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Sugar Town (1999)\",\n    \"id\": 2865,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"And the Ship Sails On (E la nave va) (1984)\",\n    \"id\": 2897,\n    \"genres\": [\n      \"Comedy\",\n      \"War\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Chushingura (1962)\",\n    \"id\": 3092,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Class Reunion (1982)\",\n    \"id\": 3313,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Blood Feast (1963)\",\n    \"id\": 3344,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Solar Crisis (1993)\",\n    \"id\": 3464,\n    \"genres\": [\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Vagabond (Sans toit ni loi) (1985)\",\n    \"id\": 3637,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Make Mine Music (1946)\",\n    \"id\": 3775,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Musical\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Silent Fall (1994)\",\n    \"id\": 3848,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Aim\\u00e9e & Jaguar (1999)\",\n    \"id\": 3854,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"About Adam (2000)\",\n    \"id\": 3862,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 20\n  },\n  {\n    \"title\": \"Love & Human Remains (1993)\",\n    \"id\": 178,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 19\n  },\n  {\n    \"title\": \"Walking Dead, The (1995)\",\n    \"id\": 336,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 19\n  },\n  {\n    \"title\": \"I Like It Like That (1994)\",\n    \"id\": 359,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 19\n  },\n  {\n    \"title\": \"Thin Line Between Love and Hate, A (1996)\",\n    \"id\": 626,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 19\n  },\n  {\n    \"title\": \"Jack and Sarah (1995)\",\n    \"id\": 638,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 19\n  },\n  {\n    \"title\": \"Shadow Conspiracy (1997)\",\n    \"id\": 1055,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 19\n  },\n  {\n    \"title\": \"Mina Tannenbaum (1994)\",\n    \"id\": 1163,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 19\n  },\n  {\n    \"title\": \"Sixth Man, The (1997)\",\n    \"id\": 1494,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 19\n  },\n  {\n    \"title\": \"Rough Magic (1995)\",\n    \"id\": 1549,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 19\n  },\n  {\n    \"title\": \"B. Monkey (1998)\",\n    \"id\": 1770,\n    \"genres\": [\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 19\n  },\n  {\n    \"title\": \"Secret Agent (1936)\",\n    \"id\": 2211,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 19\n  },\n  {\n    \"title\": \"Heart Condition (1990)\",\n    \"id\": 2350,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 19\n  },\n  {\n    \"title\": \"Gate II: Trespassers, The (1990)\",\n    \"id\": 2452,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 19\n  },\n  {\n    \"title\": \"Ballad of Narayama, The (Narayama Bushiko) (1982)\",\n    \"id\": 2512,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 19\n  },\n  {\n    \"title\": \"Onegin (1999)\",\n    \"id\": 3161,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 19\n  },\n  {\n    \"title\": \"Great Locomotive Chase, The (1956)\",\n    \"id\": 3585,\n    \"genres\": [\n      \"Adventure\",\n      \"War\"\n    ],\n    \"count\": 19\n  },\n  {\n    \"title\": \"Amazing Panda Adventure, The (1995)\",\n    \"id\": 146,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 18\n  },\n  {\n    \"title\": \"Hunted, The (1995)\",\n    \"id\": 251,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 18\n  },\n  {\n    \"title\": \"New Jersey Drive (1995)\",\n    \"id\": 283,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 18\n  },\n  {\n    \"title\": \"Coldblooded (1995)\",\n    \"id\": 394,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 18\n  },\n  {\n    \"title\": \"Vie est belle, La (Life is Rosey) (1987)\",\n    \"id\": 771,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 18\n  },\n  {\n    \"title\": \"Madame Butterfly (1995)\",\n    \"id\": 1087,\n    \"genres\": [\n      \"Musical\"\n    ],\n    \"count\": 18\n  },\n  {\n    \"title\": \"Bad Moon (1996)\",\n    \"id\": 1168,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 18\n  },\n  {\n    \"title\": \"Keys to Tulsa (1997)\",\n    \"id\": 1501,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 18\n  },\n  {\n    \"title\": \"In Search of the Castaways (1962)\",\n    \"id\": 2056,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 18\n  },\n  {\n    \"title\": \"D\\u00e9j\\u00e0 Vu (1997)\",\n    \"id\": 2175,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 18\n  },\n  {\n    \"title\": \"Eight Days a Week (1997)\",\n    \"id\": 2509,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 18\n  },\n  {\n    \"title\": \"School of Flesh, The (L' \\u00c9cole de la chair) (1998)\",\n    \"id\": 2544,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 18\n  },\n  {\n    \"title\": \"Very Thought of You, The (1998)\",\n    \"id\": 2834,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 18\n  },\n  {\n    \"title\": \"Experience Preferred... But Not Essential (1982)\",\n    \"id\": 3047,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 18\n  },\n  {\n    \"title\": \"Hitch-Hiker, The (1953)\",\n    \"id\": 3121,\n    \"genres\": [\n      \"Film-Noir\"\n    ],\n    \"count\": 18\n  },\n  {\n    \"title\": \"That's Life! (1986)\",\n    \"id\": 3465,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 18\n  },\n  {\n    \"title\": \"Cleo From 5 to 7 (Cl\\u00e9o de 5 \\u00e0 7) (1962)\",\n    \"id\": 3645,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 18\n  },\n  {\n    \"title\": \"Ballad of Ramblin' Jack, The (2000)\",\n    \"id\": 3880,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 18\n  },\n  {\n    \"title\": \"Stars Fell on Henrietta, The (1995)\",\n    \"id\": 197,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Safe Passage (1994)\",\n    \"id\": 375,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Germinal (1993)\",\n    \"id\": 563,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Flower of My Secret, The (La Flor de Mi Secreto) (1995)\",\n    \"id\": 617,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Single Girl, A (La Fille Seule) (1995)\",\n    \"id\": 1116,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Best of the Best 3: No Turning Back (1995)\",\n    \"id\": 1170,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Love and Other Catastrophes (1996)\",\n    \"id\": 1493,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Head Above Water (1996)\",\n    \"id\": 1565,\n    \"genres\": [\n      \"Comedy\",\n      \"Thriller\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Telling Lies in America (1997)\",\n    \"id\": 1651,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Cool Dry Place, A (1998)\",\n    \"id\": 1807,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Alan Smithee Film: Burn Hollywood Burn, An (1997)\",\n    \"id\": 1853,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"I Confess (1953)\",\n    \"id\": 2185,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"No Small Affair (1984)\",\n    \"id\": 2257,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Avalanche (1978)\",\n    \"id\": 2534,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Kindred, The (1986)\",\n    \"id\": 2740,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Rain (1932)\",\n    \"id\": 2904,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Mating Habits of the Earthbound Human, The (1998)\",\n    \"id\": 2913,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Citizen's Band (a.k.a. Handle with Care) (1977)\",\n    \"id\": 2923,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Joe the King (1999)\",\n    \"id\": 2963,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Tarzan the Fearless (1933)\",\n    \"id\": 3139,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Horror Express (1972)\",\n    \"id\": 3490,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Carnosaur 3: Primal Species (1996)\",\n    \"id\": 3574,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Dark Command (1940)\",\n    \"id\": 3644,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Asylum (1972)\",\n    \"id\": 3757,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 17\n  },\n  {\n    \"title\": \"Frankie Starlight (1995)\",\n    \"id\": 131,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 16\n  },\n  {\n    \"title\": \"Cold Fever (\\u00c1 k\\u00f6ldum klaka) (1994)\",\n    \"id\": 649,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 16\n  },\n  {\n    \"title\": \"Algiers (1938)\",\n    \"id\": 974,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 16\n  },\n  {\n    \"title\": \"Line King: Al Hirschfeld, The (1996)\",\n    \"id\": 1144,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 16\n  },\n  {\n    \"title\": \"Hollow Reed (1996)\",\n    \"id\": 1504,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 16\n  },\n  {\n    \"title\": \"Prom Night IV: Deliver Us From Evil (1992)\",\n    \"id\": 1990,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 16\n  },\n  {\n    \"title\": \"Light It Up (1999)\",\n    \"id\": 3050,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 16\n  },\n  {\n    \"title\": \"Sea Wolves, The (1980)\",\n    \"id\": 3137,\n    \"genres\": [\n      \"Action\",\n      \"War\"\n    ],\n    \"count\": 16\n  },\n  {\n    \"title\": \"Killing of Sister George, The (1968)\",\n    \"id\": 3333,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 16\n  },\n  {\n    \"title\": \"Retroactive (1997)\",\n    \"id\": 3474,\n    \"genres\": [\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 16\n  },\n  {\n    \"title\": \"Better Living Through Circuitry (1999)\",\n    \"id\": 3625,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 16\n  },\n  {\n    \"title\": \"Other Side of Sunday, The (S\\u00f8ndagsengler) (1996)\",\n    \"id\": 3817,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 16\n  },\n  {\n    \"title\": \"Opportunists, The (1999)\",\n    \"id\": 3860,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 16\n  },\n  {\n    \"title\": \"Killer Shrews, The (1959)\",\n    \"id\": 3933,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 16\n  },\n  {\n    \"title\": \"Reckless (1995)\",\n    \"id\": 189,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 15\n  },\n  {\n    \"title\": \"Clean Slate (Coup de Torchon) (1981)\",\n    \"id\": 681,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 15\n  },\n  {\n    \"title\": \"Careful (1992)\",\n    \"id\": 751,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 15\n  },\n  {\n    \"title\": \"T-Men (1947)\",\n    \"id\": 1154,\n    \"genres\": [\n      \"Film-Noir\"\n    ],\n    \"count\": 15\n  },\n  {\n    \"title\": \"Commandments (1997)\",\n    \"id\": 1520,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 15\n  },\n  {\n    \"title\": \"Fall (1997)\",\n    \"id\": 1574,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 15\n  },\n  {\n    \"title\": \"Prom Night III: The Last Kiss (1989)\",\n    \"id\": 1989,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 15\n  },\n  {\n    \"title\": \"This World, Then the Fireworks (1996)\",\n    \"id\": 2008,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Film-Noir\"\n    ],\n    \"count\": 15\n  },\n  {\n    \"title\": \"Squanto: A Warrior's Tale (1994)\",\n    \"id\": 2101,\n    \"genres\": [\n      \"Adventure\",\n      \"Drama\"\n    ],\n    \"count\": 15\n  },\n  {\n    \"title\": \"Strangeland (1998)\",\n    \"id\": 2298,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 15\n  },\n  {\n    \"title\": \"Steam: The Turkish Bath (Hamam) (1997)\",\n    \"id\": 2388,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 15\n  },\n  {\n    \"title\": \"Violets Are Blue... (1986)\",\n    \"id\": 2415,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 15\n  },\n  {\n    \"title\": \"Brother, Can You Spare a Dime? (1975)\",\n    \"id\": 2981,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 15\n  },\n  {\n    \"title\": \"Minnie and Moskowitz (1971)\",\n    \"id\": 3283,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 15\n  },\n  {\n    \"title\": \"Two Thousand Maniacs! (1964)\",\n    \"id\": 3351,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 15\n  },\n  {\n    \"title\": \"Daughters of the Dust (1992)\",\n    \"id\": 3374,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 15\n  },\n  {\n    \"title\": \"Saludos Amigos (1943)\",\n    \"id\": 3611,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 15\n  },\n  {\n    \"title\": \"Catfish in Black Bean Sauce (2000)\",\n    \"id\": 3883,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 15\n  },\n  {\n    \"title\": \"Slumber Party Massacre III, The (1990)\",\n    \"id\": 3940,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 15\n  },\n  {\n    \"title\": \"Star Maker, The (Uomo delle stelle, L') (1995)\",\n    \"id\": 124,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 14\n  },\n  {\n    \"title\": \"Bushwhacked (1995)\",\n    \"id\": 212,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 14\n  },\n  {\n    \"title\": \"Kika (1993)\",\n    \"id\": 567,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 14\n  },\n  {\n    \"title\": \"Rendezvous in Paris (Rendez-vous de Paris, Les) (1995)\",\n    \"id\": 807,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 14\n  },\n  {\n    \"title\": \"Raw Deal (1948)\",\n    \"id\": 1153,\n    \"genres\": [\n      \"Film-Noir\"\n    ],\n    \"count\": 14\n  },\n  {\n    \"title\": \"Nosferatu a Venezia (1986)\",\n    \"id\": 1349,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 14\n  },\n  {\n    \"title\": \"Hearts and Minds (1996)\",\n    \"id\": 1423,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 14\n  },\n  {\n    \"title\": \"Prisoner of the Mountains (Kavkazsky Plennik) (1996)\",\n    \"id\": 1450,\n    \"genres\": [\n      \"War\"\n    ],\n    \"count\": 14\n  },\n  {\n    \"title\": \"Gabbeh (1996)\",\n    \"id\": 1575,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 14\n  },\n  {\n    \"title\": \"Swept from the Sea (1997)\",\n    \"id\": 1656,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 14\n  },\n  {\n    \"title\": \"Land Girls, The (1998)\",\n    \"id\": 1898,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 14\n  },\n  {\n    \"title\": \"Kidnapped (1960)\",\n    \"id\": 2079,\n    \"genres\": [\n      \"Children's\",\n      \"Drama\"\n    ],\n    \"count\": 14\n  },\n  {\n    \"title\": \"Stars and Bars (1988)\",\n    \"id\": 2246,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 14\n  },\n  {\n    \"title\": \"Endurance (1998)\",\n    \"id\": 2627,\n    \"genres\": [\n      \"Documentary\",\n      \"Drama\"\n    ],\n    \"count\": 14\n  },\n  {\n    \"title\": \"Alice and Martin (Alice et Martin) (1998)\",\n    \"id\": 2773,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 14\n  },\n  {\n    \"title\": \"Head On (1998)\",\n    \"id\": 2775,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 14\n  },\n  {\n    \"title\": \"On the Ropes (1999)\",\n    \"id\": 2824,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 14\n  },\n  {\n    \"title\": \"Rough Night in Jericho (1967)\",\n    \"id\": 3025,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 14\n  },\n  {\n    \"title\": \"Son of the Sheik, The (1926)\",\n    \"id\": 3492,\n    \"genres\": [\n      \"Adventure\"\n    ],\n    \"count\": 14\n  },\n  {\n    \"title\": \"Human Traffic (1999)\",\n    \"id\": 3581,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 14\n  },\n  {\n    \"title\": \"In the Bleak Midwinter (1995)\",\n    \"id\": 96,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Steal Big, Steal Little (1995)\",\n    \"id\": 119,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Pie in the Sky (1995)\",\n    \"id\": 129,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Man of the Year (1995)\",\n    \"id\": 137,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Destiny Turns on the Radio (1995)\",\n    \"id\": 228,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"You So Crazy (1994)\",\n    \"id\": 411,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"New Age, The (1994)\",\n    \"id\": 503,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Killer (Bulletproof Heart) (1994)\",\n    \"id\": 561,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Naked in New York (1994)\",\n    \"id\": 566,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Land and Freedom (Tierra y libertad) (1995)\",\n    \"id\": 632,\n    \"genres\": [\n      \"War\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Gold Diggers: The Secret of Bear Mountain (1995)\",\n    \"id\": 754,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Lady of Burlesque (1943)\",\n    \"id\": 958,\n    \"genres\": [\n      \"Comedy\",\n      \"Mystery\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Glory Daze (1996)\",\n    \"id\": 1121,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Mad Dog Time (1996)\",\n    \"id\": 1313,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Kissed (1996)\",\n    \"id\": 1502,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Office Killer (1997)\",\n    \"id\": 1715,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Welcome to Woop-Woop (1997)\",\n    \"id\": 1793,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Niagara, Niagara (1997)\",\n    \"id\": 1811,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"I Love You, Don't Touch Me! (1998)\",\n    \"id\": 1850,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Belizaire the Cajun (1986)\",\n    \"id\": 2466,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Mummy's Ghost, The (1944)\",\n    \"id\": 2636,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Stiff Upper Lips (1998)\",\n    \"id\": 2768,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Marcello Mastroianni: I Remember Yes, I Remember (1997)\",\n    \"id\": 2776,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Dreaming of Joseph Lees (1998)\",\n    \"id\": 2998,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Something for Everyone (1970)\",\n    \"id\": 3419,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Crow: Salvation, The (2000)\",\n    \"id\": 3563,\n    \"genres\": [\n      \"Action\",\n      \"Horror\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"In Old California (1942)\",\n    \"id\": 3642,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Wisdom of Crocodiles, The (a.k.a. Immortality) (2000)\",\n    \"id\": 3796,\n    \"genres\": [\n      \"Romance\",\n      \"Thriller\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Whatever Happened to Aunt Alice? (1969)\",\n    \"id\": 3850,\n    \"genres\": [\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Tic Code, The (1998)\",\n    \"id\": 3853,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 13\n  },\n  {\n    \"title\": \"Big Bully (1996)\",\n    \"id\": 75,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"Nobody Loves Me (Keiner liebt mich) (1994)\",\n    \"id\": 106,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"Jupiter's Wife (1994)\",\n    \"id\": 128,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"Tie That Binds, The (1995)\",\n    \"id\": 200,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"Fear, The (1995)\",\n    \"id\": 397,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"Marlene Dietrich: Shadow and Light (1996)\",\n    \"id\": 769,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"Wife, The (1995)\",\n    \"id\": 864,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"Golden Earrings (1947)\",\n    \"id\": 925,\n    \"genres\": [\n      \"Adventure\",\n      \"Romance\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"Eighth Day, The (Le Huiti\\u00e8me jour ) (1996)\",\n    \"id\": 1117,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"No Looking Back (1998)\",\n    \"id\": 1817,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"Rich and Strange (1932)\",\n    \"id\": 2215,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"Shattered Image (1998)\",\n    \"id\": 2326,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"Relax... It's Just Sex (1998)\",\n    \"id\": 2545,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"Deadtime Stories (1987)\",\n    \"id\": 2754,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"New Rose Hotel (1998)\",\n    \"id\": 2892,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"Julien Donkey-Boy (1999)\",\n    \"id\": 2964,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"Ape, The (1940)\",\n    \"id\": 3058,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"Jail Bait (1954)\",\n    \"id\": 3335,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"Cool as Ice (1991)\",\n    \"id\": 3437,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"Roadside Prophets (1992)\",\n    \"id\": 3495,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"King in New York, A (1957)\",\n    \"id\": 3640,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"All the Rage (a.k.a. It's the Rage) (1999)\",\n    \"id\": 3867,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"X: The Unknown (1956)\",\n    \"id\": 3878,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 12\n  },\n  {\n    \"title\": \"Margaret's Museum (1995)\",\n    \"id\": 114,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Glass Shield, The (1994)\",\n    \"id\": 245,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Reluctant Debutante, The (1958)\",\n    \"id\": 939,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"They Made Me a Criminal (1939)\",\n    \"id\": 962,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Unhook the Stars (1996)\",\n    \"id\": 1038,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Bitter Sugar (Azucar Amargo) (1996)\",\n    \"id\": 1058,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Adrenalin: Fear the Rush (1996)\",\n    \"id\": 1383,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Losing Chase (1996)\",\n    \"id\": 1531,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Twin Town (1997)\",\n    \"id\": 1539,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Incognito (1997)\",\n    \"id\": 1675,\n    \"genres\": [\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Arguing the World (1996)\",\n    \"id\": 1743,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Sour Grapes (1998)\",\n    \"id\": 1864,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Woo (1998)\",\n    \"id\": 1878,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Firelight (1997)\",\n    \"id\": 2197,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Blackmail (1929)\",\n    \"id\": 2221,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Monument Ave. (1998)\",\n    \"id\": 2281,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Hard Core Logo (1996)\",\n    \"id\": 2342,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Saragossa Manuscript, The (Rekopis znaleziony w Saragossie) (1965)\",\n    \"id\": 2632,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Flying Saucer, The (1950)\",\n    \"id\": 2658,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Boys, The (1997)\",\n    \"id\": 2695,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Cabaret Balkan (Bure Baruta) (1998)\",\n    \"id\": 2830,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Don't Look in the Basement! (1973)\",\n    \"id\": 2854,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Molly (1999)\",\n    \"id\": 2914,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Miss Julie (1999)\",\n    \"id\": 3116,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Voyage of the Damned (1976)\",\n    \"id\": 3215,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Nekromantik (1987)\",\n    \"id\": 3777,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 11\n  },\n  {\n    \"title\": \"Gordy (1995)\",\n    \"id\": 243,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Gumby: The Movie (1995)\",\n    \"id\": 244,\n    \"genres\": [\n      \"Animation\",\n      \"Children's\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Man of No Importance, A (1994)\",\n    \"id\": 385,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Boys (1996)\",\n    \"id\": 703,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"My Life and Times With Antonin Artaud (En compagnie d'Antonin Artaud) (1993)\",\n    \"id\": 793,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Maybe, Maybe Not (Bewegte Mann, Der) (1994)\",\n    \"id\": 860,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Celestial Clockwork (1994)\",\n    \"id\": 863,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Shooter, The (1995)\",\n    \"id\": 1181,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Leading Man, The (1996)\",\n    \"id\": 1749,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Go Now (1995)\",\n    \"id\": 1872,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Passion in the Desert (1998)\",\n    \"id\": 1899,\n    \"genres\": [\n      \"Adventure\",\n      \"Drama\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Merry War, A (1997)\",\n    \"id\": 2191,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Young and Innocent (1937)\",\n    \"id\": 2209,\n    \"genres\": [\n      \"Crime\",\n      \"Thriller\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Murder! (1930)\",\n    \"id\": 2219,\n    \"genres\": [\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Twice Upon a Yesterday (1998)\",\n    \"id\": 2675,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Tomb of Ligeia, The (1965)\",\n    \"id\": 2783,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"West Beirut (West Beyrouth) (1998)\",\n    \"id\": 2839,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Nightmares (1983)\",\n    \"id\": 2855,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Time of the Gypsies (Dom za vesanje) (1989)\",\n    \"id\": 2931,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Fever Pitch (1997)\",\n    \"id\": 2962,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"City, The (1998)\",\n    \"id\": 2994,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Battling Butler (1926)\",\n    \"id\": 3012,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Different for Girls (1996)\",\n    \"id\": 3282,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Last Resort (1994)\",\n    \"id\": 3463,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Devil Girl From Mars (1954)\",\n    \"id\": 3486,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Last September, The (1999)\",\n    \"id\": 3570,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Anatomy (Anatomie) (2000)\",\n    \"id\": 3892,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 10\n  },\n  {\n    \"title\": \"Kids of the Round Table (1995)\",\n    \"id\": 56,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\",\n      \"Fantasy\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Loaded (1994)\",\n    \"id\": 614,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Two Much (1996)\",\n    \"id\": 618,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"301, 302 (1995)\",\n    \"id\": 652,\n    \"genres\": [\n      \"Mystery\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Last Dance (1996)\",\n    \"id\": 726,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Kim (1950)\",\n    \"id\": 755,\n    \"genres\": [\n      \"Children's\",\n      \"Drama\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Spirits of the Dead (Tre Passi nel Delirio) (1968)\",\n    \"id\": 775,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Bewegte Mann, Der (1994)\",\n    \"id\": 811,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Infinity (1996)\",\n    \"id\": 993,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Falling in Love Again (1980)\",\n    \"id\": 1436,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Last Time I Committed Suicide, The (1997)\",\n    \"id\": 1567,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Ugly, The (1997)\",\n    \"id\": 1891,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Best Man, The (Il Testimone dello sposo) (1997)\",\n    \"id\": 2156,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"24-hour Woman (1998)\",\n    \"id\": 2486,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Apple, The (Sib) (1998)\",\n    \"id\": 2503,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Bandits (1997)\",\n    \"id\": 2562,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Among Giants (1998)\",\n    \"id\": 2569,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Fire Within, The (Le Feu Follet) (1963)\",\n    \"id\": 2933,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"On Any Sunday (1971)\",\n    \"id\": 2984,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Three Ages, The (1923)\",\n    \"id\": 3140,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Diamonds (1999)\",\n    \"id\": 3149,\n    \"genres\": [\n      \"Mystery\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Soft Fruit (1999)\",\n    \"id\": 3410,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Those Who Love Me Can Take the Train (Ceux qui m'aiment prendront le train) (1998)\",\n    \"id\": 3636,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Abominable Snowman, The (1957)\",\n    \"id\": 3648,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Greaser's Palace (1972)\",\n    \"id\": 3803,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Bootmen (2000)\",\n    \"id\": 3944,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 9\n  },\n  {\n    \"title\": \"Across the Sea of Time (1995)\",\n    \"id\": 37,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Lamerica (1994)\",\n    \"id\": 53,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Confessional, The (Le Confessionnal) (1995)\",\n    \"id\": 59,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"From the Journals of Jean Seberg (1995)\",\n    \"id\": 136,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Federal Hill (1994)\",\n    \"id\": 406,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Savage Nights (Nuits fauves, Les) (1992)\",\n    \"id\": 526,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Beans of Egypt, Maine, The (1994)\",\n    \"id\": 560,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Window to Paris (1994)\",\n    \"id\": 598,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Faithful (1996)\",\n    \"id\": 664,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Ballad of Narayama, The (Narayama Bushiko) (1958)\",\n    \"id\": 854,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Killer: A Journal of Murder (1995)\",\n    \"id\": 874,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Ed's Next Move (1996)\",\n    \"id\": 1002,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"So Dear to My Heart (1949)\",\n    \"id\": 1026,\n    \"genres\": [\n      \"Children's\",\n      \"Drama\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Guantanamera (1994)\",\n    \"id\": 1444,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Ayn Rand: A Sense of Life (1997)\",\n    \"id\": 1780,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Little City (1998)\",\n    \"id\": 1782,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Seventh Heaven (Le Septi\\u00e8me ciel) (1997)\",\n    \"id\": 2063,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Saltmen of Tibet, The (1997)\",\n    \"id\": 2129,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Under Capricorn (1949)\",\n    \"id\": 2200,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Jamaica Inn (1939)\",\n    \"id\": 2207,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Let's Talk About Sex (1998)\",\n    \"id\": 2234,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Alarmist, The (1997)\",\n    \"id\": 2317,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Belly (1998)\",\n    \"id\": 2332,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Trick or Treat (1986)\",\n    \"id\": 2464,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Bat, The (1959)\",\n    \"id\": 2814,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Dog of Flanders, A (1999)\",\n    \"id\": 2831,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Train of Life (Train De Vie) (1998)\",\n    \"id\": 3003,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Slaughterhouse (1987)\",\n    \"id\": 3026,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Blood on the Sun (1945)\",\n    \"id\": 3154,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Taffin (1988)\",\n    \"id\": 3523,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Time Masters (Les Ma\\u00eetres du Temps) (1982)\",\n    \"id\": 3592,\n    \"genres\": [\n      \"Animation\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Woman of Paris, A (1923)\",\n    \"id\": 3641,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Specials, The (2000)\",\n    \"id\": 3905,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 8\n  },\n  {\n    \"title\": \"Catwalk (1995)\",\n    \"id\": 108,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Carried Away (1996)\",\n    \"id\": 630,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Story of Xinghua, The (1993)\",\n    \"id\": 844,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Big Squeeze, The (1996)\",\n    \"id\": 847,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Dadetown (1995)\",\n    \"id\": 1138,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Santa with Muscles (1996)\",\n    \"id\": 1311,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Message to Love: The Isle of Wight Festival (1996)\",\n    \"id\": 1420,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Quiet Room, The (1996)\",\n    \"id\": 1486,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Temptress Moon (Feng Yue) (1996)\",\n    \"id\": 1514,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Ripe (1996)\",\n    \"id\": 1522,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Designated Mourner, The (1997)\",\n    \"id\": 1543,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Hurricane Streets (1998)\",\n    \"id\": 1659,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Stranger in the House (1997)\",\n    \"id\": 1718,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Time Tracers (1995)\",\n    \"id\": 1720,\n    \"genres\": [\n      \"Action\",\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Prince Valiant (1997)\",\n    \"id\": 1849,\n    \"genres\": [\n      \"Adventure\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Little Boy Blue (1997)\",\n    \"id\": 1890,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Full Tilt Boogie (1997)\",\n    \"id\": 2061,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"See the Sea (Regarde la mer) (1997)\",\n    \"id\": 2192,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Allnighter, The (1987)\",\n    \"id\": 2244,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Love Is the Devil (1998)\",\n    \"id\": 2304,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Blood, Guts, Bullets and Octane (1998)\",\n    \"id\": 2487,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Eternity and a Day (Mia eoniotita ke mia mera ) (1998)\",\n    \"id\": 2673,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Late August, Early September (Fin ao\\u00fbt, d\\u00e9but septembre) (1998)\",\n    \"id\": 2705,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Gambler, The (A J\\u00e1t\\u00e9kos) (1997)\",\n    \"id\": 2760,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Acid House, The (1998)\",\n    \"id\": 2765,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Public Access (1993)\",\n    \"id\": 2850,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Grandfather, The (El Abuelo) (1998)\",\n    \"id\": 2911,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Slaughterhouse 2 (1988)\",\n    \"id\": 3027,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Actor's Revenge, An (Yukinojo Henge) (1963)\",\n    \"id\": 3533,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Pot O' Gold (1941)\",\n    \"id\": 3818,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Turn It Up (2000)\",\n    \"id\": 3891,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 7\n  },\n  {\n    \"title\": \"Journey of August King, The (1995)\",\n    \"id\": 90,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Shopping (1994)\",\n    \"id\": 98,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Rent-a-Kid (1995)\",\n    \"id\": 310,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Original Gangstas (1996)\",\n    \"id\": 732,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Power 98 (1995)\",\n    \"id\": 815,\n    \"genres\": [\n      \"Action\",\n      \"Mystery\",\n      \"Thriller\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Walk in the Sun, A (1945)\",\n    \"id\": 966,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Last Time I Saw Paris, The (1954)\",\n    \"id\": 972,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Six of a Kind (1934)\",\n    \"id\": 1160,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Substance of Fire, The (1996)\",\n    \"id\": 1384,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Broken English (1996)\",\n    \"id\": 1519,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Sprung (1997)\",\n    \"id\": 1532,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Bonheur, Le (1965)\",\n    \"id\": 1534,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Second Jungle Book: Mowgli & Baloo, The (1997)\",\n    \"id\": 1538,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"I Love You, I Love You Not (1996)\",\n    \"id\": 1685,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Hav Plenty (1997)\",\n    \"id\": 1903,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Grandview, U.S.A. (1984)\",\n    \"id\": 2242,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Mighty Peking Man (Hsing hsing wang) (1977)\",\n    \"id\": 2602,\n    \"genres\": [\n      \"Adventure\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Lucie Aubrac (1997)\",\n    \"id\": 2833,\n    \"genres\": [\n      \"Romance\",\n      \"War\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Suburbans, The (1999)\",\n    \"id\": 3001,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Oxygen (1999)\",\n    \"id\": 3056,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Brenda Starr (1989)\",\n    \"id\": 3166,\n    \"genres\": [\n      \"Adventure\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Big Combo, The (1955)\",\n    \"id\": 3292,\n    \"genres\": [\n      \"Film-Noir\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Black Tar Heroin: The Dark End of the Street (1999)\",\n    \"id\": 3303,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Pandora and the Flying Dutchman (1951)\",\n    \"id\": 3657,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Retro Puppetmaster (1999)\",\n    \"id\": 3666,\n    \"genres\": [\n      \"Horror\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"On Our Merry Way (1948)\",\n    \"id\": 3778,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Sunset Strip (2000)\",\n    \"id\": 3866,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 6\n  },\n  {\n    \"title\": \"Feast of July (1995)\",\n    \"id\": 167,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Enfer, L' (1994)\",\n    \"id\": 264,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Paris, France (1993)\",\n    \"id\": 559,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Delta of Venus (1994)\",\n    \"id\": 698,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"In the Line of Duty 2 (1987)\",\n    \"id\": 980,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Secret Agent, The (1996)\",\n    \"id\": 1040,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"For the Moment (1994)\",\n    \"id\": 1071,\n    \"genres\": [\n      \"Romance\",\n      \"War\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Search for One-eye Jimmy, The (1996)\",\n    \"id\": 1098,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Two or Three Things I Know About Her (1966)\",\n    \"id\": 1164,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Grosse Fatigue (1994)\",\n    \"id\": 1174,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"I Can't Sleep (J'ai pas sommeil) (1994)\",\n    \"id\": 1369,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Machine, The (1994)\",\n    \"id\": 1433,\n    \"genres\": [\n      \"Comedy\",\n      \"Horror\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Boys Life 2 (1997)\",\n    \"id\": 1471,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Kicked in the Head (1997)\",\n    \"id\": 1622,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Stag (1997)\",\n    \"id\": 1636,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"N\\u00e9nette et Boni (1996)\",\n    \"id\": 1664,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Midaq Alley (Callej\\u00f3n de los milagros, El) (1995)\",\n    \"id\": 1741,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"In God's Hands (1998)\",\n    \"id\": 1796,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Dear Jesse (1997)\",\n    \"id\": 1901,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Cavalcade (1933)\",\n    \"id\": 1930,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Digging to China (1998)\",\n    \"id\": 2233,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"24 7: Twenty Four Seven (1997)\",\n    \"id\": 2444,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Dancemaker (1998)\",\n    \"id\": 2538,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"I Stand Alone (Seul contre tous) (1998)\",\n    \"id\": 2557,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Heaven (1998)\",\n    \"id\": 2608,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Frogs for Snakes (1998)\",\n    \"id\": 2631,\n    \"genres\": [\n      \"Comedy\",\n      \"Film-Noir\",\n      \"Thriller\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Finding North (1999)\",\n    \"id\": 2679,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Cobra (1925)\",\n    \"id\": 2777,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Return with Honor (1998)\",\n    \"id\": 2930,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Love Bewitched, A (El Amor Brujo) (1986)\",\n    \"id\": 2934,\n    \"genres\": [\n      \"Musical\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Montana (1998)\",\n    \"id\": 3184,\n    \"genres\": [\n      \"Action\",\n      \"Comedy\",\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Snows of Kilimanjaro, The (1952)\",\n    \"id\": 3207,\n    \"genres\": [\n      \"Adventure\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Seven Chances (1925)\",\n    \"id\": 3232,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"I Am Cuba (Soy Cuba/Ya Kuba) (1964)\",\n    \"id\": 3245,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Conceiving Ada (1997)\",\n    \"id\": 3293,\n    \"genres\": [\n      \"Drama\",\n      \"Sci-Fi\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Color Me Blood Red (1965)\",\n    \"id\": 3346,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Born American (1986)\",\n    \"id\": 3443,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"City of the Living Dead (Paura nella citt\\u00e0 dei morti viventi) (1980)\",\n    \"id\": 3652,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Devil Rides Out, The (1968)\",\n    \"id\": 3875,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 5\n  },\n  {\n    \"title\": \"Wings of Courage (1995)\",\n    \"id\": 33,\n    \"genres\": [\n      \"Adventure\",\n      \"Romance\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Two Bits (1995)\",\n    \"id\": 67,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Brother Minister: The Assassination of Malcolm X (1994)\",\n    \"id\": 404,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Tigrero: A Film That Was Never Made (1994)\",\n    \"id\": 682,\n    \"genres\": [\n      \"Documentary\",\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Butterfly Kiss (1995)\",\n    \"id\": 696,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Man from Down Under, The (1943)\",\n    \"id\": 749,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Carmen Miranda: Bananas Is My Business (1994)\",\n    \"id\": 756,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Ashes of Time (1994)\",\n    \"id\": 757,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Babyfever (1994)\",\n    \"id\": 776,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Death in the Garden (Mort en ce jardin, La) (1956)\",\n    \"id\": 820,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Collectionneuse, La (1967)\",\n    \"id\": 823,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Phat Beach (1996)\",\n    \"id\": 834,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Dangerous Ground (1997)\",\n    \"id\": 981,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Small Wonders (1996)\",\n    \"id\": 985,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"American Strays (1996)\",\n    \"id\": 1102,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"JLG/JLG - autoportrait de d\\u00e9cembre (1994)\",\n    \"id\": 1149,\n    \"genres\": [\n      \"Documentary\",\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Late Bloomers (1996)\",\n    \"id\": 1553,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Wedding Bell Blues (1996)\",\n    \"id\": 1561,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Year of the Horse (1997)\",\n    \"id\": 1652,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Friend of the Deceased, A (1997)\",\n    \"id\": 1871,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Little Men (1998)\",\n    \"id\": 1877,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"I Got the Hook Up (1998)\",\n    \"id\": 1886,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"First Love, Last Rites (1997)\",\n    \"id\": 2127,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Strike! (a.k.a. All I Wanna Do, The Hairy Bird) (1998)\",\n    \"id\": 2172,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Battle of the Sexes, The (1959)\",\n    \"id\": 2299,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Savior (1998)\",\n    \"id\": 2358,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Sticky Fingers of Time, The (1997)\",\n    \"id\": 2578,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Trippin' (1999)\",\n    \"id\": 2623,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Red Dwarf, The (Le Nain rouge) (1998)\",\n    \"id\": 2685,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Spiders, The (Die Spinnen, 1. Teil: Der Goldene See) (1919)\",\n    \"id\": 2823,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Rosie (1998)\",\n    \"id\": 2825,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Simon Sez (1999)\",\n    \"id\": 2887,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Penitentiary II (1982)\",\n    \"id\": 2955,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Beefcake (1999)\",\n    \"id\": 2960,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Man of the Century (1999)\",\n    \"id\": 2999,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Black Sunday (La Maschera Del Demonio) (1960)\",\n    \"id\": 3205,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Cotton Mary (1999)\",\n    \"id\": 3288,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Big Trees, The (1952)\",\n    \"id\": 3314,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"3 Strikes (2000)\",\n    \"id\": 3322,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Ogre, The (Der Unhold) (1996)\",\n    \"id\": 3378,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Horror Hotel (a.k.a. The City of the Dead) (1960)\",\n    \"id\": 3472,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Held Up (2000)\",\n    \"id\": 3595,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Regret to Inform (1998)\",\n    \"id\": 3609,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Jerry & Tom (1998)\",\n    \"id\": 3876,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Went to Coney Island on a Mission From God... Be Back by Five (1998)\",\n    \"id\": 3887,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 4\n  },\n  {\n    \"title\": \"Race the Sun (1996)\",\n    \"id\": 120,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Modern Affair, A (1995)\",\n    \"id\": 623,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Theodore Rex (1995)\",\n    \"id\": 634,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Peanuts - Die Bank zahlt alles (1996)\",\n    \"id\": 643,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"All Things Fair (1996)\",\n    \"id\": 666,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Country Life (1994)\",\n    \"id\": 687,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Institute Benjamenta, or This Dream People Call Human Life (1995)\",\n    \"id\": 729,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Getting Away With Murder (1996)\",\n    \"id\": 734,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Brothers in Trouble (1995)\",\n    \"id\": 744,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Gate of Heavenly Peace, The (1995)\",\n    \"id\": 787,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Very Natural Thing, A (1974)\",\n    \"id\": 796,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Dingo (1992)\",\n    \"id\": 853,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Synthetic Pleasures (1995)\",\n    \"id\": 1039,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Loser (1991)\",\n    \"id\": 1107,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Paris Was a Woman (1995)\",\n    \"id\": 1315,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Junk Mail (1997)\",\n    \"id\": 1861,\n    \"genres\": [\n      \"Comedy\",\n      \"Thriller\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Farmer's Wife, The (1928)\",\n    \"id\": 2223,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Just the Ticket (1999)\",\n    \"id\": 2510,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Love, etc. (1996)\",\n    \"id\": 2576,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Male and Female (1919)\",\n    \"id\": 2821,\n    \"genres\": [\n      \"Adventure\",\n      \"Drama\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Separation, The (La S\\u00e9paration) (1994)\",\n    \"id\": 2869,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Bay of Blood (Reazione a catena) (1971)\",\n    \"id\": 3119,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Spring Fever USA (a.k.a. Lauderdale) (1989)\",\n    \"id\": 3123,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"End of the Affair, The (1955)\",\n    \"id\": 3126,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"James Dean Story, The (1957)\",\n    \"id\": 3136,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Trans (1998)\",\n    \"id\": 3187,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Vampyros Lesbos (Las Vampiras) (1970)\",\n    \"id\": 3216,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Santitos (1997)\",\n    \"id\": 3242,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Brown's Requiem (1998)\",\n    \"id\": 3352,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Buck and the Preacher (1972)\",\n    \"id\": 3373,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Railroaded! (1947)\",\n    \"id\": 3380,\n    \"genres\": [\n      \"Film-Noir\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Carriers Are Waiting, The (Les Convoyeurs Attendent) (1999)\",\n    \"id\": 3407,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Price of Glory (2000)\",\n    \"id\": 3482,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Sacco and Vanzetti (Sacco e Vanzetti) (1971)\",\n    \"id\": 3522,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Defying Gravity (1997)\",\n    \"id\": 3575,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Gay Deceivers, The (1969)\",\n    \"id\": 3603,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Possession (1981)\",\n    \"id\": 3621,\n    \"genres\": [\n      \"Drama\",\n      \"Horror\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"It's in the Water (1998)\",\n    \"id\": 3631,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Daughter of Dr. Jeckyll (1957)\",\n    \"id\": 3762,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Hatchet For the Honeymoon (Rosso Segno Della Follia) (1969)\",\n    \"id\": 3772,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Criminal Lovers (Les Amants Criminels) (1999)\",\n    \"id\": 3800,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Circus (2000)\",\n    \"id\": 3899,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 3\n  },\n  {\n    \"title\": \"Angela (1995)\",\n    \"id\": 130,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Sonic Outlaws (1995)\",\n    \"id\": 134,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Neon Bible, The (1995)\",\n    \"id\": 138,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Show, The (1995)\",\n    \"id\": 192,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Mirage (1995)\",\n    \"id\": 401,\n    \"genres\": [\n      \"Action\",\n      \"Thriller\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Dangerous Game (1993)\",\n    \"id\": 439,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Second Best (1994)\",\n    \"id\": 530,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Mamma Roma (1962)\",\n    \"id\": 557,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Foreign Student (1994)\",\n    \"id\": 572,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Hour of the Pig, The (1993)\",\n    \"id\": 578,\n    \"genres\": [\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Metisse (Caf\\u00e9 au Lait) (1993)\",\n    \"id\": 582,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Tough and Deadly (1995)\",\n    \"id\": 591,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Century (1993)\",\n    \"id\": 607,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Yankee Zulu (1994)\",\n    \"id\": 657,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"August (1996)\",\n    \"id\": 660,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Tarantella (1995)\",\n    \"id\": 672,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Run of the Country, The (1995)\",\n    \"id\": 679,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Promise, The (Versprechen, Das) (1994)\",\n    \"id\": 690,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Sunset Park (1996)\",\n    \"id\": 706,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Captives (1994)\",\n    \"id\": 712,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"I, Worst of All (Yo, la peor de todas) (1990)\",\n    \"id\": 789,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Last Klezmer: Leopold Kozlowski, His Life and Music, The (1995)\",\n    \"id\": 791,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Crude Oasis, The (1995)\",\n    \"id\": 821,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Convent, The (Convento, O) (1995)\",\n    \"id\": 827,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Sweet Nothing (1995)\",\n    \"id\": 884,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Talk of Angels (1998)\",\n    \"id\": 887,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"1-900 (1994)\",\n    \"id\": 889,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Venice/Venice (1992)\",\n    \"id\": 895,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Something to Sing About (1937)\",\n    \"id\": 975,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Moonlight Murder (1936)\",\n    \"id\": 977,\n    \"genres\": [\n      \"Mystery\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Sunchaser, The (1996)\",\n    \"id\": 1062,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Macao (1952)\",\n    \"id\": 1070,\n    \"genres\": [\n      \"Adventure\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Talking About Sex (1994)\",\n    \"id\": 1133,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Johnny 100 Pesos (1993)\",\n    \"id\": 1134,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Everything Relative (1996)\",\n    \"id\": 1139,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Get Over It (1996)\",\n    \"id\": 1142,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Snowriders (1996)\",\n    \"id\": 1145,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Identification of a Woman (Identificazione di una donna) (1982)\",\n    \"id\": 1360,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Zero Kelvin (Kj\\u00e6rlighetens kj\\u00f8tere) (1995)\",\n    \"id\": 1364,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Stranger, The (1994)\",\n    \"id\": 1434,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Brother's Kiss, A (1997)\",\n    \"id\": 1510,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Intimate Relations (1996)\",\n    \"id\": 1528,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"War at Home, The (1996)\",\n    \"id\": 1548,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"To Have, or Not (1995)\",\n    \"id\": 1555,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Sudden Manhattan (1996)\",\n    \"id\": 1558,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Tokyo Fist (1995)\",\n    \"id\": 1773,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Paralyzing Fear: The Story of Polio in America, A (1998)\",\n    \"id\": 1787,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Callej\\u00f3n de los milagros, El (1995)\",\n    \"id\": 1795,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Illtown (1996)\",\n    \"id\": 1842,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Leather Jacket Love Story (1997)\",\n    \"id\": 1851,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Truce, The (1996)\",\n    \"id\": 1868,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Marie Baie Des Anges (1997)\",\n    \"id\": 1905,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Modulations (1998)\",\n    \"id\": 2198,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Cabinet of Dr. Ramirez, The (1991)\",\n    \"id\": 2251,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Master Ninja I (1984)\",\n    \"id\": 2258,\n    \"genres\": [\n      \"Action\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Inheritors, The (Die Siebtelbauern) (1998)\",\n    \"id\": 2309,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Naked Man, The (1998)\",\n    \"id\": 2343,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Dry Cleaning (Nettoyage \\u00e0 sec) (1997)\",\n    \"id\": 2480,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Six Ways to Sunday (1997)\",\n    \"id\": 2543,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Jeanne and the Perfect Guy (Jeanne et le gar\\u00e7on formidable) (1998)\",\n    \"id\": 2591,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"With Friends Like These... (1998)\",\n    \"id\": 2811,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Napoleon and Samantha (1972)\",\n    \"id\": 2895,\n    \"genres\": [\n      \"Adventure\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Where's Marlowe? (1999)\",\n    \"id\": 3057,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Home Page (1999)\",\n    \"id\": 3084,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Living Dead Girl, The (La Morte Vivante) (1982)\",\n    \"id\": 3085,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Broadway Damage (1997)\",\n    \"id\": 3131,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Born to Win (1971)\",\n    \"id\": 3212,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Wirey Spindell (1999)\",\n    \"id\": 3228,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Smashing Time (1967)\",\n    \"id\": 3233,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Zachariah (1971)\",\n    \"id\": 3236,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Eaten Alive (1976)\",\n    \"id\": 3294,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Raining Stones (1993)\",\n    \"id\": 3295,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Bluebeard (1944)\",\n    \"id\": 3305,\n    \"genres\": [\n      \"Film-Noir\",\n      \"Horror\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"It Happened Here (1961)\",\n    \"id\": 3336,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"I'll Never Forget What's 'is Name (1967)\",\n    \"id\": 3337,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Closer You Get, The (2000)\",\n    \"id\": 3353,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Hangmen Also Die (1943)\",\n    \"id\": 3377,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Slaves to the Underground (1997)\",\n    \"id\": 3381,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Impact (1949)\",\n    \"id\": 3413,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Autopsy (Macchie Solari) (1975)\",\n    \"id\": 3485,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Torso (Corpi Presentano Tracce di Violenza Carnale) (1973)\",\n    \"id\": 3493,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Bells, The (1926)\",\n    \"id\": 3517,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Coming Apart (1969)\",\n    \"id\": 3542,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Castaway Cowboy, The (1974)\",\n    \"id\": 3601,\n    \"genres\": [\n      \"Comedy\",\n      \"Western\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Blood Spattered Bride, The (La Novia Ensangrentada) (1972)\",\n    \"id\": 3651,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Light Years (1988)\",\n    \"id\": 3687,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Match, The (1999)\",\n    \"id\": 3748,\n    \"genres\": [\n      \"Comedy\",\n      \"Romance\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Better Living (1998)\",\n    \"id\": 3828,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Skipped Parts (2000)\",\n    \"id\": 3888,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 2\n  },\n  {\n    \"title\": \"Silence of the Palace, The (Saimt el Qusur) (1994)\",\n    \"id\": 127,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Nueba Yol (1995)\",\n    \"id\": 133,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Target (1995)\",\n    \"id\": 139,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Shadows (Cienie) (1988)\",\n    \"id\": 142,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Dream Man (1995)\",\n    \"id\": 226,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Nemesis 2: Nebula (1995)\",\n    \"id\": 286,\n    \"genres\": [\n      \"Action\",\n      \"Sci-Fi\",\n      \"Thriller\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Relative Fear (1994)\",\n    \"id\": 311,\n    \"genres\": [\n      \"Horror\",\n      \"Thriller\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Fall Time (1995)\",\n    \"id\": 396,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Frank and Ollie (1995)\",\n    \"id\": 398,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Open Season (1996)\",\n    \"id\": 402,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Harlem (1993)\",\n    \"id\": 545,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Fausto (1993)\",\n    \"id\": 576,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Scorta, La (1993)\",\n    \"id\": 579,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"I Don't Want to Talk About It (De eso no se habla) (1993)\",\n    \"id\": 584,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Wooden Man's Bride, The (Wu Kui) (1994)\",\n    \"id\": 601,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Condition Red (1995)\",\n    \"id\": 624,\n    \"genres\": [\n      \"Action\",\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Little Indian, Big City (Un indien dans la ville) (1994)\",\n    \"id\": 641,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Roula (1995)\",\n    \"id\": 642,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Happy Weekend (1996)\",\n    \"id\": 644,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Superweib, Das (1996)\",\n    \"id\": 651,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Mutters Courage (1995)\",\n    \"id\": 655,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Billy's Holiday (1995)\",\n    \"id\": 658,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Windows (1980)\",\n    \"id\": 684,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Daens (1992)\",\n    \"id\": 701,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Mouth to Mouth (Boca a boca) (1995)\",\n    \"id\": 717,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Low Life, The (1994)\",\n    \"id\": 730,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Jar, The (Khomreh) (1992)\",\n    \"id\": 758,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Last of the High Kings, The (a.k.a. Summer Fling) (1996)\",\n    \"id\": 763,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Wend Kuuni (God's Gift) (1982)\",\n    \"id\": 774,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"An Unforgettable Summer (1994)\",\n    \"id\": 790,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Hungarian Fairy Tale, A (1987)\",\n    \"id\": 792,\n    \"genres\": [\n      \"Fantasy\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Boy Called Hate, A (1995)\",\n    \"id\": 814,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Diebinnen (1995)\",\n    \"id\": 826,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Lotto Land (1995)\",\n    \"id\": 843,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Hippie Revolution, The (1996)\",\n    \"id\": 859,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Small Faces (1995)\",\n    \"id\": 865,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Death in Brunswick (1991)\",\n    \"id\": 868,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Aiqing wansui (1994)\",\n    \"id\": 872,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Bye-Bye (1995)\",\n    \"id\": 878,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Schlafes Bruder (Brother of Sleep) (1995)\",\n    \"id\": 989,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Sleepover (1995)\",\n    \"id\": 1115,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Tashunga (1995)\",\n    \"id\": 1118,\n    \"genres\": [\n      \"Adventure\",\n      \"Western\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Bloody Child, The (1996)\",\n    \"id\": 1165,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Anna (1996)\",\n    \"id\": 1316,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Terror in a Texas Town (1958)\",\n    \"id\": 1386,\n    \"genres\": [\n      \"Western\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Underworld (1997)\",\n    \"id\": 1430,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Rhyme & Reason (1997)\",\n    \"id\": 1470,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"For Ever Mozart (1996)\",\n    \"id\": 1579,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Lay of the Land, The (1997)\",\n    \"id\": 1630,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Legal Deceit (1997)\",\n    \"id\": 1709,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Never Met Picasso (1996)\",\n    \"id\": 1714,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Full Speed (1996)\",\n    \"id\": 1724,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Tainted (1998)\",\n    \"id\": 1764,\n    \"genres\": [\n      \"Comedy\",\n      \"Thriller\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Eden (1997)\",\n    \"id\": 1815,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Proposition, The (1998)\",\n    \"id\": 1820,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Follow the Bitch (1998)\",\n    \"id\": 1830,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Heaven's Burning (1997)\",\n    \"id\": 1832,\n    \"genres\": [\n      \"Action\",\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Slappy and the Stinkers (1998)\",\n    \"id\": 1843,\n    \"genres\": [\n      \"Children's\",\n      \"Comedy\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Love Walked In (1998)\",\n    \"id\": 1852,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Resurrection Man (1998)\",\n    \"id\": 1908,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Voyage to the Beginning of the World (1997)\",\n    \"id\": 1915,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Cheetah (1989)\",\n    \"id\": 2039,\n    \"genres\": [\n      \"Adventure\",\n      \"Children's\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Waltzes from Vienna (1933)\",\n    \"id\": 2213,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Number Seventeen (1932)\",\n    \"id\": 2214,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Elstree Calling (1930)\",\n    \"id\": 2217,\n    \"genres\": [\n      \"Comedy\",\n      \"Musical\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Juno and Paycock (1930)\",\n    \"id\": 2218,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Ring, The (1927)\",\n    \"id\": 2226,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"One Man's Hero (1999)\",\n    \"id\": 2235,\n    \"genres\": [\n      \"Drama\",\n      \"War\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Choices (1981)\",\n    \"id\": 2254,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Somewhere in the City (1997)\",\n    \"id\": 2277,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Detroit 9000 (1973)\",\n    \"id\": 2308,\n    \"genres\": [\n      \"Action\",\n      \"Crime\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Outside Ozona (1998)\",\n    \"id\": 2438,\n    \"genres\": [\n      \"Drama\",\n      \"Thriller\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Tinseltown (1998)\",\n    \"id\": 2484,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Telling You (1998)\",\n    \"id\": 2556,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Beauty (1998)\",\n    \"id\": 2563,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Foolish (1999)\",\n    \"id\": 2584,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Joyriders, The (1999)\",\n    \"id\": 2592,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Mascara (1999)\",\n    \"id\": 2619,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Broken Vessels (1998)\",\n    \"id\": 2703,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"M\\u00e9nage (Tenue de soir\\u00e9e) (1986)\",\n    \"id\": 2742,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"White Boys (1999)\",\n    \"id\": 2845,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Five Wives, Three Secretaries and Me (1998)\",\n    \"id\": 2909,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Ten Benny (1997)\",\n    \"id\": 3065,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Bat Whispers, The (1930)\",\n    \"id\": 3151,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\",\n      \"Mystery\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Alley Cats, The (1968)\",\n    \"id\": 3164,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Ulysses (Ulisse) (1954)\",\n    \"id\": 3172,\n    \"genres\": [\n      \"Adventure\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Even Dwarfs Started Small (Auch Zwerge haben klein angefangen) (1971)\",\n    \"id\": 3202,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Loves of Carmen, The (1948)\",\n    \"id\": 3209,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Night Tide (1961)\",\n    \"id\": 3220,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Another Man's Poison (1952)\",\n    \"id\": 3229,\n    \"genres\": [\n      \"Crime\",\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Kestrel's Eye (Falkens \\u00f6ga) (1998)\",\n    \"id\": 3237,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Beloved/Friend (Amigo/Amado) (1999)\",\n    \"id\": 3277,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Baby, The (1973)\",\n    \"id\": 3280,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Soft Toilet Seats (1999)\",\n    \"id\": 3290,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Trois (2000)\",\n    \"id\": 3291,\n    \"genres\": [\n      \"Thriller\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"With Byrd at the South Pole (1930)\",\n    \"id\": 3297,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"McCullochs, The (1975)\",\n    \"id\": 3312,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Happy Go Lovely (1951)\",\n    \"id\": 3315,\n    \"genres\": [\n      \"Musical\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Waiting Game, The (2000)\",\n    \"id\": 3321,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Chain of Fools (2000)\",\n    \"id\": 3323,\n    \"genres\": [\n      \"Comedy\",\n      \"Crime\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Fantastic Night, The (La Nuit Fantastique) (1949)\",\n    \"id\": 3376,\n    \"genres\": [\n      \"Romance\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Song of Freedom (1936)\",\n    \"id\": 3382,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Blood and Sand (Sangre y Arena) (1989)\",\n    \"id\": 3458,\n    \"genres\": [\n      \"Drama\",\n      \"Romance\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Hillbillys in a Haunted House (1967)\",\n    \"id\": 3460,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Smoking/No Smoking (1993)\",\n    \"id\": 3530,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"One Little Indian (1973)\",\n    \"id\": 3607,\n    \"genres\": [\n      \"Comedy\",\n      \"Drama\",\n      \"Western\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Running Free (2000)\",\n    \"id\": 3647,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Lured (1947)\",\n    \"id\": 3656,\n    \"genres\": [\n      \"Crime\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Live Virgin (1999)\",\n    \"id\": 3722,\n    \"genres\": [\n      \"Comedy\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Project Moon Base (1953)\",\n    \"id\": 3779,\n    \"genres\": [\n      \"Sci-Fi\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Make Them Die Slowly (Cannibal Ferox) (1980)\",\n    \"id\": 3842,\n    \"genres\": [\n      \"Horror\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Bittersweet Motel (2000)\",\n    \"id\": 3881,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Back Stage (2000)\",\n    \"id\": 3890,\n    \"genres\": [\n      \"Documentary\"\n    ],\n    \"count\": 1\n  },\n  {\n    \"title\": \"Uninvited Guest, An (2000)\",\n    \"id\": 3904,\n    \"genres\": [\n      \"Drama\"\n    ],\n    \"count\": 1\n  }\n]"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/java/org/tensorflow/lite/examples/recommendation/Config.java",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.recommendation;\n\nimport android.util.Log;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/** Config for recommendation app. */\npublic final class Config {\n  private static final String TAG = \"Config\";\n  private static final String DEFAULT_MODEL_PATH = \"recommendation_rnn_i10o100.tflite\";\n  private static final String DEFAULT_MOVIE_LIST_PATH = \"sorted_movie_vocab.json\";\n  private static final int DEFAULT_OUTPUT_LENGTH = 100;\n  private static final int DEFAULT_TOP_K = 10;\n  private static final int PAD_ID = 0;\n  private static final int UNKNOWN_GENRE = 0;\n  private static final int DEFAULT_OUTPUT_IDS_INDEX = 0;\n  private static final int DEFAULT_OUTPUT_SCORES_INDEX = 1;\n  private static final int DEFAULT_FAVORITE_LIST_SIZE = 100;\n\n  public static final String FEATURE_MOVIE = \"movieFeature\";\n  public static final String FEATURE_GENRE = \"genreFeature\";\n\n  /** Feature of the model. */\n  public static class Feature {\n    /** Input feature name. */\n    public String name;\n    /** Input feature index. */\n    public int index;\n    /** Input feature length. */\n    public int inputLength;\n  }\n\n  /** TF Lite model path. */\n  public String model = DEFAULT_MODEL_PATH;\n\n  /** List of input features */\n  public List<Feature> inputs = new ArrayList<>();\n  /** Number of output length from the model. */\n  public int outputLength = DEFAULT_OUTPUT_LENGTH;\n  /** Number of max results to show in the UI. */\n  public int topK = DEFAULT_TOP_K;\n  /** Path to the movie list. */\n  public String movieList = DEFAULT_MOVIE_LIST_PATH;\n  /** Path to the genre list. Use genre feature if it is not null. */\n  public String genreList = null;\n\n  /** Id for padding. */\n  public int pad = PAD_ID;\n\n  /** Movie genre for unknown. */\n  public int unknownGenre = UNKNOWN_GENRE;\n\n  /** Output index for ID. */\n  public int outputIdsIndex = DEFAULT_OUTPUT_IDS_INDEX;\n  /** Output index for score. */\n  public int outputScoresIndex = DEFAULT_OUTPUT_SCORES_INDEX;\n\n  /** The number of favorite movies for users to choose from. */\n  public int favoriteListSize = DEFAULT_FAVORITE_LIST_SIZE;\n\n  public Config() {}\n\n  public boolean validate() {\n    if (inputs.isEmpty()) {\n      Log.e(TAG, \"config inputs should not be empty\");\n      return false;\n    }\n\n    boolean hasGenreFeature = false;\n    for (Config.Feature feature : inputs) {\n      if (FEATURE_GENRE.equals(feature.name)) {\n        hasGenreFeature = true;\n        break;\n      }\n    }\n    if (useGenres() || hasGenreFeature) {\n      if (!useGenres() || !hasGenreFeature) {\n        String msg =\n            \"If uses genre, must set both `genreFeature` in inputs and `genreList` as vocab.\";\n        if (!useGenres()) {\n          msg += \"`genreList` is missing.\";\n        }\n        if (!hasGenreFeature) {\n          msg += \"`genreFeature` is missing.\";\n        }\n        Log.e(TAG, msg);\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n  public boolean useGenres() {\n    return genreList != null;\n  }\n}\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/java/org/tensorflow/lite/examples/recommendation/MainActivity.java",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.recommendation;\n\nimport android.os.Bundle;\nimport android.os.Handler;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.util.Log;\nimport android.widget.Toast;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport org.tensorflow.lite.examples.recommendation.RecommendationClient.Result;\nimport org.tensorflow.lite.examples.recommendation.data.FileUtil;\nimport org.tensorflow.lite.examples.recommendation.data.MovieItem;\n\n/** The main activity to provide interactions with users. */\npublic class MainActivity extends AppCompatActivity\n    implements MovieFragment.OnListFragmentInteractionListener,\n        RecommendationFragment.OnListFragmentInteractionListener {\n  private static final String TAG = \"OnDeviceRecommendationDemo\";\n  private static final String CONFIG_PATH = \"config.json\"; // Default config path in assets.\n\n  private Config config;\n  private RecommendationClient client;\n  private final List<MovieItem> allMovies = new ArrayList<>();\n  private final List<MovieItem> selectedMovies = new ArrayList<>();\n\n  private Handler handler;\n  private MovieFragment movieFragment;\n  private RecommendationFragment recommendationFragment;\n\n  @Override\n  protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    setContentView(R.layout.tfe_re_activity_main);\n    Log.v(TAG, \"onCreate\");\n\n    // Load config file.\n    try {\n      config = FileUtil.loadConfig(getAssets(), CONFIG_PATH);\n    } catch (IOException ex) {\n      Log.e(TAG, String.format(\"Error occurs when loading config %s: %s.\", CONFIG_PATH, ex));\n    }\n\n    // Load movies list.\n    try {\n      allMovies.clear();\n      allMovies.addAll(FileUtil.loadMovieList(getAssets(), config.movieList));\n    } catch (IOException ex) {\n      Log.e(TAG, String.format(\"Error occurs when loading movies %s: %s.\", config.movieList, ex));\n    }\n\n    client = new RecommendationClient(this, config);\n    handler = new Handler();\n    movieFragment =\n        (MovieFragment) getSupportFragmentManager().findFragmentById(R.id.movie_fragment);\n    recommendationFragment =\n        (RecommendationFragment)\n            getSupportFragmentManager().findFragmentById(R.id.recommendation_fragment);\n  }\n\n  @Override\n  protected void onStart() {\n    super.onStart();\n    Log.v(TAG, \"onStart\");\n\n    // Add favorite movies to the fragment.\n    List<MovieItem> favoriteMovies =\n        allMovies.stream().limit(config.favoriteListSize).collect(Collectors.toList());\n    movieFragment.setMovies(favoriteMovies);\n\n    handler.post(\n        () -> {\n          client.load();\n        });\n  }\n\n  @Override\n  protected void onStop() {\n    super.onStop();\n    Log.v(TAG, \"onStop\");\n    handler.post(\n        () -> {\n          client.unload();\n        });\n  }\n\n  /** Sends selected movie list and get recommendations. */\n  private void recommend(final List<MovieItem> movies) {\n    handler.post(\n        () -> {\n          // Run inference with TF Lite.\n          Log.d(TAG, \"Run inference with TFLite model.\");\n          List<Result> recommendations = client.recommend(movies);\n\n          // Show result on screen\n          showResult(recommendations);\n        });\n  }\n\n  /** Shows result on the screen. */\n  private void showResult(final List<Result> recommendations) {\n    // Run on UI thread as we'll updating our app UI\n    runOnUiThread(() -> recommendationFragment.setRecommendations(recommendations));\n  }\n\n  @Override\n  public void onItemSelectionChange(MovieItem item) {\n    if (item.selected) {\n      if (!selectedMovies.contains(item)) {\n        selectedMovies.add(item);\n      }\n    } else {\n      selectedMovies.remove(item);\n    }\n\n    if (!selectedMovies.isEmpty()) {\n      // Log selected movies.\n      StringBuilder sb = new StringBuilder();\n      sb.append(\"Select movies in the following order:\\n\");\n      for (MovieItem movie : selectedMovies) {\n        sb.append(String.format(\"  movie: %s\\n\", movie));\n      }\n      Log.d(TAG, sb.toString());\n\n      // Recommend based on selected movies.\n      recommend(selectedMovies);\n    } else {\n      // Clear result list.\n      showResult(new ArrayList<Result>());\n    }\n  }\n\n  /** Handles click event of recommended movie. */\n  @Override\n  public void onClickRecommendedMovie(MovieItem item) {\n    // Show message for the clicked movie.\n    String message = String.format(\"Clicked recommended movie: %s.\", item.title);\n    Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();\n  }\n}\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/java/org/tensorflow/lite/examples/recommendation/MovieFragment.java",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.recommendation;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport androidx.fragment.app.Fragment;\nimport androidx.recyclerview.widget.GridLayoutManager;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.tensorflow.lite.examples.recommendation.data.MovieItem;\n\n/**\n * A fragment representing a list of items for user to select from.\n *\n * <p>Activities containing this fragment MUST implement the {@link\n * OnListFragmentInteractionListener} interface.\n */\npublic class MovieFragment extends Fragment {\n\n  private static final String ARG_COLUMN_COUNT = \"movie-fragment-column-count\";\n  private int columnCount = 1;\n  private OnListFragmentInteractionListener listener;\n  private RecyclerView recyclerView;\n  private List<MovieItem> items = new ArrayList<>();\n\n  /**\n   * Mandatory empty constructor for the fragment manager to instantiate the fragment (e.g. upon\n   * screen orientation changes).\n   */\n  public MovieFragment() {}\n\n  @SuppressWarnings(\"unused\")\n  public static MovieFragment newInstance(int columnCount) {\n    MovieFragment fragment = new MovieFragment();\n    Bundle args = new Bundle();\n    args.putInt(ARG_COLUMN_COUNT, columnCount);\n    fragment.setArguments(args);\n    return fragment;\n  }\n\n  @Override\n  public void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n\n    if (getArguments() != null) {\n      columnCount = getArguments().getInt(ARG_COLUMN_COUNT);\n    }\n  }\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n    View view = inflater.inflate(R.layout.tfe_re_fragment_selection_list, container, false);\n\n    // Set the adapter\n    if (view instanceof RecyclerView) {\n      Context context = view.getContext();\n      recyclerView = (RecyclerView) view;\n      if (columnCount <= 1) {\n        recyclerView.setLayoutManager(new LinearLayoutManager(context));\n      } else {\n        recyclerView.setLayoutManager(new GridLayoutManager(context, columnCount));\n      }\n      recyclerView.setAdapter(new MovieRecyclerViewAdapter(items, listener));\n    }\n    return view;\n  }\n\n  public void setMovies(List<MovieItem> movies) {\n    this.items = movies;\n    recyclerView.setAdapter(new MovieRecyclerViewAdapter(this.items, listener));\n  }\n\n  @Override\n  public void onAttach(Context context) {\n    super.onAttach(context);\n    if (context instanceof OnListFragmentInteractionListener) {\n      listener = (OnListFragmentInteractionListener) context;\n    } else {\n      throw new IllegalStateException(\n          context + \" must implement OnListFragmentInteractionListener\");\n    }\n  }\n\n  @Override\n  public void onDetach() {\n    super.onDetach();\n    listener = null;\n  }\n\n  /**\n   * This interface must be implemented by activities that contain this fragment to allow an\n   * interaction in this fragment to be communicated to the activity and potentially other fragments\n   * contained in that activity.\n   *\n   * <p>See the Android Training lesson <a href=\n   * \"http://developer.android.com/training/basics/fragments/communicating.html\" >Communicating with\n   * Other Fragments</a> for more information.\n   */\n  public interface OnListFragmentInteractionListener {\n    void onItemSelectionChange(MovieItem item);\n  }\n}\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/java/org/tensorflow/lite/examples/recommendation/MovieRecyclerViewAdapter.java",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.recommendation;\n\nimport androidx.recyclerview.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Switch;\nimport android.widget.TextView;\nimport java.util.List;\nimport org.tensorflow.lite.examples.recommendation.MovieFragment.OnListFragmentInteractionListener;\nimport org.tensorflow.lite.examples.recommendation.data.MovieItem;\n\n/**\n * MovieRecyclerViewAdapter: a {@link RecyclerView.Adapter} that can display a {@link MovieItem} for\n * users to select from, and makes a call to the specified {@link\n * OnListFragmentInteractionListener}.\n */\npublic class MovieRecyclerViewAdapter\n    extends RecyclerView.Adapter<MovieRecyclerViewAdapter.ViewHolder> {\n  private final List<MovieItem> values;\n  private final OnListFragmentInteractionListener listener;\n\n  public MovieRecyclerViewAdapter(\n      List<MovieItem> items, OnListFragmentInteractionListener listener) {\n    values = items;\n    this.listener = listener;\n  }\n\n  @Override\n  public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n    View view =\n        LayoutInflater.from(parent.getContext())\n            .inflate(R.layout.tfe_re_fragment_selection, parent, false);\n    return new ViewHolder(view, listener);\n  }\n\n  @Override\n  public void onBindViewHolder(final ViewHolder holder, int position) {\n    holder.item = values.get(position);\n    holder.movieSwitch.setOnClickListener(\n        new View.OnClickListener() {\n          @Override\n          public void onClick(View v) {\n            Switch sw = ((Switch) v);\n            // Use checked status.\n            boolean selected = sw.isChecked();\n            holder.setSelected(selected);\n            notifyItemChanged(holder.getAdapterPosition());\n          }\n        });\n    holder.movieTitle.setText(values.get(position).title);\n    holder.movieSwitch.setChecked(holder.item.selected);\n\n    holder.view.setOnClickListener(\n        new View.OnClickListener() {\n          @Override\n          public void onClick(View v) {\n            // Toggle checked status.\n            boolean selected = !holder.movieSwitch.isChecked();\n            holder.setSelected(selected);\n            notifyItemChanged(holder.getAdapterPosition());\n          }\n        });\n  }\n\n  @Override\n  public int getItemCount() {\n    return values.size();\n  }\n\n  /** ViewHolder to display one movie in list view of the selection. */\n  public static class ViewHolder extends RecyclerView.ViewHolder {\n\n    public final View view;\n    public final Switch movieSwitch;\n    public final TextView movieTitle;\n    public final OnListFragmentInteractionListener listener;\n    public MovieItem item;\n\n    public ViewHolder(View view, OnListFragmentInteractionListener listener) {\n      super(view);\n      this.view = view;\n      this.movieSwitch = (Switch) view.findViewById(R.id.movie_switch);\n      this.movieTitle = (TextView) view.findViewById(R.id.movie_title);\n      this.listener = listener;\n    }\n\n    public void setSelected(boolean selected) {\n      item.selected = selected;\n\n      if (null != listener) {\n        // Notify the active callbacks interface (the activity, if the\n        // fragment is attached to one) that an item has been selected.\n        listener.onItemSelectionChange(item);\n      }\n    }\n\n    @Override\n    public String toString() {\n      return super.toString() + \" '\" + movieTitle.getText() + \"'\";\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/java/org/tensorflow/lite/examples/recommendation/RecommendationClient.java",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.recommendation;\n\nimport android.content.Context;\nimport android.util.Log;\nimport androidx.annotation.WorkerThread;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport org.tensorflow.lite.Interpreter;\nimport org.tensorflow.lite.examples.recommendation.Config.Feature;\nimport org.tensorflow.lite.examples.recommendation.data.FileUtil;\nimport org.tensorflow.lite.examples.recommendation.data.MovieItem;\n\n/** Interface to load TfLite model and provide recommendations. */\npublic class RecommendationClient {\n  private static final String TAG = \"RecommendationClient\";\n\n  private final Context context;\n  private final Config config;\n  private Interpreter tflite;\n\n  final Map<Integer, MovieItem> candidates = new HashMap<>();\n  final Map<String, Integer> genres = new HashMap<>();\n\n  /** An immutable result returned by a RecommendationClient. */\n  public static class Result {\n\n    /** Predicted id. */\n    public final int id;\n\n    /** Recommended item. */\n    public final MovieItem item;\n\n    /** A sortable score for how good the result is relative to others. Higher should be better. */\n    public final float confidence;\n\n    public Result(final int id, final MovieItem item, final float confidence) {\n      this.id = id;\n      this.item = item;\n      this.confidence = confidence;\n    }\n\n    @Override\n    public String toString() {\n      return String.format(\"[%d] confidence: %.3f, item: %s\", id, confidence, item);\n    }\n  }\n\n  public RecommendationClient(Context context, Config config) {\n    this.context = context;\n    this.config = config;\n\n    if (!config.validate()) {\n      Log.e(TAG, \"Config is not valid.\");\n    }\n  }\n\n  /** Load the TF Lite model and dictionary. */\n  @WorkerThread\n  public void load() {\n    loadModel();\n    loadCandidateList();\n    if (config.useGenres()) {\n      loadGenreList();\n    }\n  }\n\n  /** Load TF Lite model. */\n  @WorkerThread\n  private synchronized void loadModel() {\n    try {\n      ByteBuffer buffer = FileUtil.loadModelFile(this.context.getAssets(), config.model);\n      tflite = new Interpreter(buffer);\n      Log.v(TAG, \"TFLite model loaded.\");\n    } catch (IOException ex) {\n      Log.e(TAG, ex.getMessage());\n    }\n  }\n\n  /** Load recommendation candidate list. */\n  @WorkerThread\n  private synchronized void loadCandidateList() {\n    try {\n      Collection<MovieItem> collection =\n          FileUtil.loadMovieList(this.context.getAssets(), config.movieList);\n      candidates.clear();\n      for (MovieItem item : collection) {\n        Log.d(TAG, String.format(\"Load candidate: %s\", item));\n        candidates.put(item.id, item);\n      }\n      Log.v(TAG, \"Candidate list loaded.\");\n    } catch (IOException ex) {\n      Log.e(TAG, ex.getMessage());\n    }\n  }\n\n  /** Load movie genre list. */\n  @WorkerThread\n  private synchronized void loadGenreList() {\n    try {\n      List<String> genreList = FileUtil.loadGenreList(this.context.getAssets(), config.genreList);\n      genres.clear();\n      for (String genre : genreList) {\n        Log.d(TAG, String.format(\"Load genre: \\\"%s\\\"\", genre));\n        genres.put(genre, genres.size());\n      }\n      Log.v(TAG, \"Candidate list loaded.\");\n    } catch (IOException ex) {\n      Log.e(TAG, ex.getMessage());\n    }\n  }\n\n  /** Free up resources as the client is no longer needed. */\n  @WorkerThread\n  public synchronized void unload() {\n    tflite.close();\n    candidates.clear();\n  }\n\n  int[] preprocessIds(List<MovieItem> selectedMovies, int length) {\n    int[] inputIds = new int[length];\n    Arrays.fill(inputIds, config.pad); // Fill inputIds with the default.\n    int i = 0;\n    for (MovieItem item : selectedMovies) {\n      if (i >= inputIds.length) {\n        break;\n      }\n      inputIds[i] = item.id;\n      ++i;\n    }\n    return inputIds;\n  }\n\n  int[] preprocessGenres(List<MovieItem> selectedMovies, int length) {\n    // Fill inputGenres.\n    int[] inputGenres = new int[length];\n    Arrays.fill(inputGenres, config.unknownGenre); // Fill inputGenres with the default.\n    int i = 0;\n    for (MovieItem item : selectedMovies) {\n      if (i >= inputGenres.length) {\n        break;\n      }\n      for (String genre : item.genres) {\n        if (i >= inputGenres.length) {\n          break;\n        }\n        inputGenres[i] = genres.containsKey(genre) ? genres.get(genre) : config.unknownGenre;\n        ++i;\n      }\n    }\n    return inputGenres;\n  }\n\n  /** Given a list of selected items, preprocess to get tflite input. */\n  @WorkerThread\n  synchronized Object[] preprocess(List<MovieItem> selectedMovies) {\n    List<Object> inputs = new ArrayList<>();\n\n    // Sort features.\n    List<Feature> sortedFeatures = new ArrayList<>(config.inputs);\n    Collections.sort(sortedFeatures, (Feature a, Feature b) -> Integer.compare(a.index, b.index));\n\n    for (Feature feature : sortedFeatures) {\n      if (Config.FEATURE_MOVIE.equals(feature.name)) {\n        inputs.add(preprocessIds(selectedMovies, feature.inputLength));\n      } else if (Config.FEATURE_GENRE.equals(feature.name)) {\n        inputs.add(preprocessGenres(selectedMovies, feature.inputLength));\n      } else {\n        Log.e(TAG, String.format(\"Invalid feature: %s\", feature.name));\n      }\n    }\n    return inputs.toArray();\n  }\n\n  /** Postprocess to gets results from tflite inference. */\n  @WorkerThread\n  synchronized List<Result> postprocess(\n      int[] outputIds, float[] confidences, List<MovieItem> selectedMovies) {\n    final ArrayList<Result> results = new ArrayList<>();\n\n    // Add recommendation results. Filter null or contained items.\n    for (int i = 0; i < outputIds.length; i++) {\n      if (results.size() >= config.topK) {\n        Log.v(TAG, String.format(\"Selected top K: %d. Ignore the rest.\", config.topK));\n        break;\n      }\n\n      int id = outputIds[i];\n      MovieItem item = candidates.get(id);\n      if (item == null) {\n        Log.v(TAG, String.format(\"Inference output[%d]. Id: %s is null\", i, id));\n        continue;\n      }\n      if (selectedMovies.contains(item)) {\n        Log.v(TAG, String.format(\"Inference output[%d]. Id: %s is contained\", i, id));\n        continue;\n      }\n      Result result = new Result(id, item, confidences[i]);\n      results.add(result);\n      Log.v(TAG, String.format(\"Inference output[%d]. Result: %s\", i, result));\n    }\n\n    return results;\n  }\n\n  /** Given a list of selected items, and returns the recommendation results. */\n  @WorkerThread\n  public synchronized List<Result> recommend(List<MovieItem> selectedMovies) {\n    Object[] inputs = preprocess(selectedMovies);\n\n    // Run inference.\n    int[] outputIds = new int[config.outputLength];\n    float[] confidences = new float[config.outputLength];\n    Map<Integer, Object> outputs = new HashMap<>();\n    outputs.put(config.outputIdsIndex, outputIds);\n    outputs.put(config.outputScoresIndex, confidences);\n    tflite.runForMultipleInputsOutputs(inputs, outputs);\n\n    return postprocess(outputIds, confidences, selectedMovies);\n  }\n\n  Interpreter getTflite() {\n    return this.tflite;\n  }\n}\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/java/org/tensorflow/lite/examples/recommendation/RecommendationFragment.java",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.recommendation;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport androidx.fragment.app.Fragment;\nimport androidx.recyclerview.widget.GridLayoutManager;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.tensorflow.lite.examples.recommendation.RecommendationClient.Result;\nimport org.tensorflow.lite.examples.recommendation.data.MovieItem;\n\n/**\n * A fragment representing a list of recommended movie items.\n *\n * <p>Activities containing this fragment MUST implement the {@link\n * OnListFragmentInteractionListener} interface.\n */\npublic class RecommendationFragment extends Fragment {\n  private static final String ARG_COLUMN_COUNT = \"recommendation-fragment-column-count\";\n  private int columnCount = 1;\n  private OnListFragmentInteractionListener listener;\n\n  private RecyclerView recyclerView;\n  private List<Result> recommendedMovies = new ArrayList<>();\n\n  /**\n   * Mandatory empty constructor for the fragment manager to instantiate the fragment (e.g. upon\n   * screen orientation changes).\n   */\n  public RecommendationFragment() {}\n\n  // TODO: Customize parameter initialization\n  @SuppressWarnings(\"unused\")\n  public static RecommendationFragment newInstance(int columnCount) {\n    RecommendationFragment fragment = new RecommendationFragment();\n    Bundle args = new Bundle();\n    args.putInt(ARG_COLUMN_COUNT, columnCount);\n    fragment.setArguments(args);\n    return fragment;\n  }\n\n  @Override\n  public void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n\n    if (getArguments() != null) {\n      columnCount = getArguments().getInt(ARG_COLUMN_COUNT);\n    }\n  }\n\n  @Override\n  public View onCreateView(\n      LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n    View view = inflater.inflate(R.layout.tfe_re_fragment_recommendation_list, container, false);\n\n    // Set the adapter\n    if (view instanceof RecyclerView) {\n      Context context = view.getContext();\n      recyclerView = (RecyclerView) view;\n      if (columnCount <= 1) {\n        recyclerView.setLayoutManager(new LinearLayoutManager(context));\n      } else {\n        recyclerView.setLayoutManager(new GridLayoutManager(context, columnCount));\n      }\n      setRecommendations(recommendedMovies);\n    }\n    return view;\n  }\n\n  public void setRecommendations(List<Result> recommendedMovies) {\n    this.recommendedMovies = recommendedMovies;\n    recyclerView.setAdapter(\n        new RecommendationRecyclerViewAdapter(this.recommendedMovies, listener));\n  }\n\n  @Override\n  public void onAttach(Context context) {\n    super.onAttach(context);\n    if (context instanceof OnListFragmentInteractionListener) {\n      listener = (OnListFragmentInteractionListener) context;\n    } else {\n      throw new IllegalStateException(\n          context + \" must implement OnListFragmentInteractionListener\");\n    }\n  }\n\n  @Override\n  public void onDetach() {\n    super.onDetach();\n    listener = null;\n  }\n\n  /**\n   * This interface must be implemented by activities that contain this fragment to allow an\n   * interaction in this fragment to be communicated to the activity and potentially other fragments\n   * contained in that activity.\n   *\n   * <p>See the Android Training lesson <a href=\n   * \"http://developer.android.com/training/basics/fragments/communicating.html\" >Communicating with\n   * Other Fragments</a> for more information.\n   */\n  public interface OnListFragmentInteractionListener {\n    void onClickRecommendedMovie(MovieItem item);\n  }\n}\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/java/org/tensorflow/lite/examples/recommendation/RecommendationRecyclerViewAdapter.java",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.recommendation;\n\nimport androidx.recyclerview.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport java.util.List;\nimport org.tensorflow.lite.examples.recommendation.RecommendationClient.Result;\nimport org.tensorflow.lite.examples.recommendation.RecommendationFragment.OnListFragmentInteractionListener;\nimport org.tensorflow.lite.examples.recommendation.data.MovieItem;\n\n/**\n * RecommendationRecyclerViewAdapter: a {@link RecyclerView.Adapter} that can display a recommended\n * {@link MovieItem} and makes a call to the specified {@link OnListFragmentInteractionListener}.\n */\npublic class RecommendationRecyclerViewAdapter\n    extends RecyclerView.Adapter<RecommendationRecyclerViewAdapter.ViewHolder> {\n\n  private final List<Result> results;\n  private final OnListFragmentInteractionListener listener;\n\n  public RecommendationRecyclerViewAdapter(\n      List<Result> results, OnListFragmentInteractionListener listener) {\n    this.results = results;\n    this.listener = listener;\n  }\n\n  @Override\n  public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n    View view =\n        LayoutInflater.from(parent.getContext())\n            .inflate(R.layout.tfe_re_fragment_recommendation, parent, false);\n    return new ViewHolder(view);\n  }\n\n  @Override\n  public void onBindViewHolder(final ViewHolder holder, int position) {\n    MovieItem item = results.get(position).item;\n    holder.result = results.get(position);\n    holder.recommendationMovieTitleView.setText(item.title + \" - \" + item.genres);\n    holder.scoreView.setText(String.format(\"[%d]\", item.id));\n\n    holder.view.setOnClickListener(\n        new View.OnClickListener() {\n          @Override\n          public void onClick(View v) {\n            if (null != listener) {\n              // Notify the active callbacks interface (the activity, if the\n              // fragment is attached to one) that an item has been selected.\n              // listener.onListFragmentInteraction(holder.mItem);\n              listener.onClickRecommendedMovie(item);\n            }\n          }\n        });\n  }\n\n  @Override\n  public int getItemCount() {\n    return results.size();\n  }\n\n  /** ViewHolder to display one movie in list view of recommendation result. */\n  public static class ViewHolder extends RecyclerView.ViewHolder {\n\n    public final View view;\n    public final TextView scoreView;\n    public final TextView recommendationMovieTitleView;\n    public Result result;\n\n    public ViewHolder(View view) {\n      super(view);\n      this.view = view;\n      this.scoreView = (TextView) view.findViewById(R.id.recommendation_score);\n      this.recommendationMovieTitleView =\n          (TextView) view.findViewById(R.id.recommendation_movie_title);\n    }\n\n    @Override\n    public String toString() {\n      return super.toString() + \" '\" + recommendationMovieTitleView.getText() + \"'\";\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/java/org/tensorflow/lite/examples/recommendation/data/FileUtil.java",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.recommendation.data;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static java.util.stream.Collectors.joining;\n\nimport android.content.res.AssetFileDescriptor;\nimport android.content.res.AssetManager;\nimport com.google.gson.Gson;\nimport com.google.gson.reflect.TypeToken;\nimport java.io.BufferedReader;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.lang.reflect.Type;\nimport java.nio.MappedByteBuffer;\nimport java.nio.channels.FileChannel;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport org.tensorflow.lite.examples.recommendation.Config;\n\n/** FileUtil class to load data from asset files. */\npublic class FileUtil {\n\n  private FileUtil() {}\n\n  /** Load TF Lite model from asset file. */\n  public static MappedByteBuffer loadModelFile(AssetManager assetManager, String modelPath)\n      throws IOException {\n    try (AssetFileDescriptor fileDescriptor = assetManager.openFd(modelPath);\n        FileInputStream inputStream = new FileInputStream(fileDescriptor.getFileDescriptor())) {\n      FileChannel fileChannel = inputStream.getChannel();\n      long startOffset = fileDescriptor.getStartOffset();\n      long declaredLength = fileDescriptor.getDeclaredLength();\n      return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength);\n    }\n  }\n\n  /** Load candidates from asset file. */\n  public static Collection<MovieItem> loadMovieList(\n      AssetManager assetManager, String candidateListPath) throws IOException {\n    String content = loadFileContent(assetManager, candidateListPath);\n    Gson gson = new Gson();\n    Type type = new TypeToken<Collection<MovieItem>>() {}.getType();\n    return gson.fromJson(content, type);\n  }\n\n  public static List<String> loadGenreList(AssetManager assetManager, String genreListPath)\n      throws IOException {\n    String content = loadFileContent(assetManager, genreListPath);\n    String[] lines = content.split(System.lineSeparator());\n    return Arrays.asList(lines);\n  }\n\n  /** Load config from asset file. */\n  public static Config loadConfig(AssetManager assetManager, String configPath) throws IOException {\n    String content = loadFileContent(assetManager, configPath);\n    Gson gson = new Gson();\n    Type type = new TypeToken<Config>() {}.getType();\n    return gson.fromJson(content, type);\n  }\n\n  /** Load file content from asset file. */\n  private static String loadFileContent(AssetManager assetManager, String path) throws IOException {\n    try (InputStream ins = assetManager.open(path);\n        BufferedReader reader = new BufferedReader(new InputStreamReader(ins, UTF_8))) {\n      return reader.lines().collect(joining(System.lineSeparator()));\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/java/org/tensorflow/lite/examples/recommendation/data/MovieItem.java",
    "content": "/*\n * Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.recommendation.data;\n\nimport android.text.TextUtils;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/** A movie item representing recommended content. */\npublic class MovieItem {\n\n  public static final String JOINER = \" | \";\n  public static final String DELIMITER_REGEX = \"[|]\";\n\n  public final int id;\n  public final String title;\n  public final List<String> genres;\n  public final int count;\n\n  public boolean selected = false; // For UI selection. Default item is not selected.\n\n  private MovieItem() {\n    this(0, \"\", new ArrayList<>(), 0);\n  }\n\n  public MovieItem(int id, String title, List<String> genres, int count) {\n    this.id = id;\n    this.title = title;\n    this.genres = genres;\n    this.count = count;\n  }\n\n  @Override\n  public String toString() {\n    return String.format(\n        \"Id: %d, title: %s, genres: %s, count: %d, selected: %s\",\n        id, title, TextUtils.join(JOINER, genres), count, selected);\n  }\n\n}\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillColor=\"#26A69A\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n</vector>\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n  <path\n      android:fillType=\"evenOdd\"\n      android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\">\n    <aapt:attr name=\"android:fillColor\">\n      <gradient\n          android:endX=\"78.5885\"\n          android:endY=\"90.9159\"\n          android:startX=\"48.7653\"\n          android:startY=\"61.0927\"\n          android:type=\"linear\">\n        <item\n            android:color=\"#44000000\"\n            android:offset=\"0.0\"/>\n        <item\n            android:color=\"#00000000\"\n            android:offset=\"1.0\"/>\n      </gradient>\n    </aapt:attr>\n  </path>\n  <path\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"nonZero\"\n      android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/res/layout/tfe_re_activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:layout_margin=\"@dimen/tfe_re_activity_margin\"\n    tools:context=\".MainActivity\">\n\n  <TextView\n      android:id=\"@+id/instruction_text_view\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:text=\"@string/tfe_re_instruction_text\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:layout_constraintTop_toTopOf=\"parent\" />\n  <ScrollView\n      android:id=\"@+id/movie_scroll_view\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"370dp\"\n      app:layout_constraintEnd_toEndOf=\"parent\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:layout_constraintTop_toBottomOf=\"@+id/instruction_text_view\">\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\">\n      <fragment\n          android:id=\"@+id/movie_fragment\"\n          android:name=\"org.tensorflow.lite.examples.recommendation.MovieFragment\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:baselineAligned=\"false\" />\n    </LinearLayout>\n  </ScrollView>\n  <TextView\n      android:id=\"@+id/recommendation_text_view\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_marginTop=\"16dp\"\n      android:text=\"@string/tfe_re_recommendation_text\"\n      app:layout_constraintBottom_toTopOf=\"@+id/result_scroll_view\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:layout_constraintTop_toBottomOf=\"@+id/movie_scroll_view\" />\n  <ScrollView\n      android:id=\"@+id/result_scroll_view\"\n      android:layout_width=\"0dp\"\n      android:layout_height=\"0dp\"\n      app:layout_constraintBottom_toBottomOf=\"parent\"\n      app:layout_constraintEnd_toEndOf=\"parent\"\n      app:layout_constraintStart_toStartOf=\"parent\"\n      app:layout_constraintTop_toBottomOf=\"@+id/recommendation_text_view\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\">\n      <TextView\n          android:id=\"@+id/result_text_view\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"wrap_content\"\n          android:visibility=\"invisible\" />\n      <fragment\n          android:id=\"@+id/recommendation_fragment\"\n          android:name=\"org.tensorflow.lite.examples.recommendation.RecommendationFragment\"\n          android:layout_width=\"match_parent\"\n          android:layout_height=\"match_parent\" />\n    </LinearLayout>\n  </ScrollView>\n\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/res/layout/tfe_re_fragment_recommendation.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"40dp\"\n    android:orientation=\"horizontal\">\n\n  <TextView\n      android:id=\"@+id/recommendation_score\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_margin=\"@dimen/movie_item_margin\"\n      android:textAppearance=\"?attr/textAppearanceListItem\"\n      android:textSize=\"12sp\"\n      android:visibility=\"visible\" />\n  <TextView\n      android:id=\"@+id/recommendation_movie_title\"\n      android:layout_width=\"wrap_content\"\n      android:layout_height=\"wrap_content\"\n      android:layout_margin=\"@dimen/movie_item_margin\"\n      android:textAppearance=\"?attr/textAppearanceListItem\"\n      android:textSize=\"12sp\" />\n</LinearLayout>\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/res/layout/tfe_re_fragment_recommendation_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.recyclerview.widget.RecyclerView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/list\"\n    android:name=\"org.tensorflow.lite.examples.recommendation.RecommendationFragment\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:layout_marginLeft=\"16dp\"\n    android:layout_marginRight=\"16dp\"\n    app:layoutManager=\"LinearLayoutManager\"\n    tools:context=\".RecommendationFragment\"\n    tools:listitem=\"@layout/tfe_re_fragment_recommendation\" />"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/res/layout/tfe_re_fragment_selection.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_alignParentLeft=\"true\"\n    android:orientation=\"horizontal\"\n    android:layout_alignParentStart=\"true\"\n    android:baselineAligned=\"false\">\n\n  <androidx.constraintlayout.widget.ConstraintLayout\n      android:layout_width=\"0dp\"\n      android:layout_height=\"match_parent\"\n      android:layout_weight=\"1\"\n      android:layout_margin=\"@dimen/movie_item_margin\">\n\n    <Switch\n        android:id=\"@+id/movie_switch\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_weight=\"1\"\n        android:switchMinWidth=\"60dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n    <TextView\n        android:id=\"@+id/movie_title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"40dp\"\n        android:layout_marginStart=\"80dp\"\n        android:layout_marginLeft=\"35dp\"\n        android:gravity=\"start|center_vertical\"\n        android:textAppearance=\"?attr/textAppearanceListItem\"\n        android:textSize=\"14sp\"\n        android:visibility=\"visible\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintHorizontal_weight=\"0\"\n        app:layout_constraintStart_toEndOf=\"@+id/movie_switch\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_constraintVertical_weight=\"0\" />\n  </androidx.constraintlayout.widget.ConstraintLayout>\n</LinearLayout>\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/res/layout/tfe_re_fragment_selection_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.recyclerview.widget.RecyclerView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/list\"\n    android:name=\"org.tensorflow.lite.examples.recommendation.MovieItemFragment\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:layout_marginLeft=\"16dp\"\n    android:layout_marginRight=\"16dp\"\n    android:clipToPadding=\"true\"\n    app:layoutManager=\"LinearLayoutManager\"\n    tools:context=\".MovieFragment\"\n    tools:listitem=\"@layout/tfe_re_fragment_selection\" />"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <color name=\"tfe_color_primary\">#ffa800</color>\n  <color name=\"tfe_color_primary_dark\">#ff6f00</color>\n  <color name=\"tfe_color_accent\">#425066</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <dimen name=\"tfe_re_activity_margin\">16dp</dimen>\n  <dimen name=\"text_margin\">16dp</dimen>\n  <dimen name=\"movie_item_margin\">4dp</dimen>\n  <dimen name=\"fab_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/res/values/strings.xml",
    "content": "<resources>\n  <string name=\"tfe_re_app_name\" translatable=\"false\">TFL Recommendation</string>\n  <string name=\"tfe_re_instruction_text\" translatable=\"false\">Please select your favorite movies.</string>\n  <string name=\"tfe_re_recommendation_text\" translatable=\"false\">Our recommendation for you:</string>\n</resources>\n"
  },
  {
    "path": "lite/examples/recommendation/android/app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n  <!-- Base application theme. -->\n  <style name=\"AppTheme.Recommendation\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n    <!-- Customize your theme here. -->\n    <item name=\"colorPrimary\">@color/tfe_color_primary</item>\n    <item name=\"colorPrimaryDark\">@color/tfe_color_primary_dark</item>\n    <item name=\"colorAccent\">@color/tfe_color_accent</item>\n  </style>\n\n  <style name=\"AppTheme.Recommendation.NoActionBar\">\n    <item name=\"windowActionBar\">false</item>\n    <item name=\"windowNoTitle\">true</item>\n  </style>\n\n  <style name=\"AppTheme.Recommendation.AppBarOverlay\" parent=\"ThemeOverlay.AppCompat.Dark.ActionBar\" />\n\n  <style name=\"AppTheme.Recommendation.PopupOverlay\" parent=\"ThemeOverlay.AppCompat.Light\" />\n\n</resources>\n"
  },
  {
    "path": "lite/examples/recommendation/android/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:4.2.0'\n        classpath 'de.undercouch:gradle-download-task:4.0.2'\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n        maven {\n            name 'ossrh-snapshot'\n            url 'https://oss.sonatype.org/content/repositories/snapshots'\n        }\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "lite/examples/recommendation/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/recommendation/android/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\nandroid.useAndroidX=true\n\n\n"
  },
  {
    "path": "lite/examples/recommendation/android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/recommendation/android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/recommendation/android/settings.gradle",
    "content": "rootProject.name = 'TFLite Recommendation Demo App'\ninclude ':app'\n"
  },
  {
    "path": "lite/examples/recommendation/ml/configs/__init__.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n"
  },
  {
    "path": "lite/examples/recommendation/ml/configs/input-config.proto",
    "content": "// For re-generating `input_config_generated_pb2.py`, please refer to\n// https://developers.google.com/protocol-buffers/docs/overview#generating.\nsyntax = \"proto3\";\n\npackage tensorflow_examples.lite.examples.recommendation.ml_v2.configs;\n\n\n// Different input feature types.\nenum FeatureType {\n  STRING = 0;\n  INT = 1;\n  FLOAT = 2;\n}\n\n// Supported context encoder types.\nenum EncoderType {\n  BOW = 0;\n  CNN = 1;\n  LSTM = 2;\n}\n\n// Input feature.\nmessage Feature {\n  // Name of the feature.\n  optional string feature_name = 1;\n\n  // Type of the feature.\n  optional FeatureType feature_type = 2;\n\n  // Name of the vocabulary (vocab file name).\n  // For INT type features, if the vocab_name is unset, the vocab of the feature\n  // will be considered to be [0, vocab_size). If this is not desired, the\n  // vocab_name should be set for INT type features.\n  // For STRING type features, the vocab_name field should be set.\n  // For FLOAT type feature, vocab_name is not needed.\n  optional string vocab_name = 3;\n\n  // Size of the vocabulary, if feature_type is STRING or INT.\n  optional int64 vocab_size = 4;\n\n  // Embedding dimension for STRING or INT feature.\n  optional int64 embedding_dim = 5;\n\n  // Sequence length of the feature.\n  optional int64 feature_length = 6;\n}\n\n// A Group of features that should be concatenated and encoded together.\nmessage FeatureGroup {\n  // Features in this feature group. STRING or INT features will be represented\n  // with embeddings. And FLOAT features will be directly used and concatenated\n  // with embeddings.\n  repeated Feature features = 1;\n\n  // Type of the encoder to generate encoding for this group. The supported\n  // encoder types are: 'bow', 'cnn', 'rnn'. Parameters for the selected encoder\n  // will be set in training config.\n  optional EncoderType encoder_type = 2;\n}\n\n// Config for input data.\nmessage InputConfig {\n  // Global feature groups, such as groups of user-level features\n  // (e.g. gender, age etc).\n  repeated FeatureGroup global_feature_groups = 1;\n\n  // Group of activity-level sequence features, that are mostly associated with\n  // each activity.\n  repeated FeatureGroup activity_feature_groups = 2;\n\n  // Label feature.\n  // Currently only support id-based label encoding, hence label feature expects\n  // to have FeatureType.INT.\n  optional Feature label_feature = 3;\n}\n"
  },
  {
    "path": "lite/examples/recommendation/ml/configs/input_config_generated_pb2.py",
    "content": "# THIS IS A GENERATED FILE. DO NOT EDIT!\n# pylint: skip-file\n# pyformat: disable\n# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n# -*- coding: utf-8 -*-\n# Generated by the protocol buffer compiler.  DO NOT EDIT!\n# source: input-config.proto\n\nfrom google.protobuf.internal import enum_type_wrapper\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import message as _message\nfrom google.protobuf import reflection as _reflection\nfrom google.protobuf import symbol_database as _symbol_database\n# @@protoc_insertion_point(imports)\n\n_sym_db = _symbol_database.Default()\n\n\n\n\nDESCRIPTOR = _descriptor.FileDescriptor(\n  name='input-config.proto',\n  package='tensorflow_examples.lite.examples.recommendation.ml_v2.configs',\n  syntax='proto2',\n  serialized_options=None,\n  serialized_pb=b'\\n\\x12input-config.proto\\x12>tensorflow_examples.lite.examples.recommendation.ml_v2.configs\\\"\\xd9\\x01\\n\\x07\\x46\\x65\\x61ture\\x12\\x14\\n\\x0c\\x66\\x65\\x61ture_name\\x18\\x01 \\x01(\\t\\x12\\x61\\n\\x0c\\x66\\x65\\x61ture_type\\x18\\x02 \\x01(\\x0e\\x32K.tensorflow_examples.lite.examples.recommendation.ml_v2.configs.FeatureType\\x12\\x12\\n\\nvocab_name\\x18\\x03 \\x01(\\t\\x12\\x12\\n\\nvocab_size\\x18\\x04 \\x01(\\x03\\x12\\x15\\n\\rembedding_dim\\x18\\x05 \\x01(\\x03\\x12\\x16\\n\\x0e\\x66\\x65\\x61ture_length\\x18\\x06 \\x01(\\x03\\\"\\xcc\\x01\\n\\x0c\\x46\\x65\\x61tureGroup\\x12Y\\n\\x08\\x66\\x65\\x61tures\\x18\\x01 \\x03(\\x0b\\x32G.tensorflow_examples.lite.examples.recommendation.ml_v2.configs.Feature\\x12\\x61\\n\\x0c\\x65ncoder_type\\x18\\x02 \\x01(\\x0e\\x32K.tensorflow_examples.lite.examples.recommendation.ml_v2.configs.EncoderType\\\"\\xc9\\x02\\n\\x0bInputConfig\\x12k\\n\\x15global_feature_groups\\x18\\x01 \\x03(\\x0b\\x32L.tensorflow_examples.lite.examples.recommendation.ml_v2.configs.FeatureGroup\\x12m\\n\\x17\\x61\\x63tivity_feature_groups\\x18\\x02 \\x03(\\x0b\\x32L.tensorflow_examples.lite.examples.recommendation.ml_v2.configs.FeatureGroup\\x12^\\n\\rlabel_feature\\x18\\x03 \\x01(\\x0b\\x32G.tensorflow_examples.lite.examples.recommendation.ml_v2.configs.Feature*-\\n\\x0b\\x46\\x65\\x61tureType\\x12\\n\\n\\x06STRING\\x10\\x00\\x12\\x07\\n\\x03INT\\x10\\x01\\x12\\t\\n\\x05\\x46LOAT\\x10\\x02*)\\n\\x0b\\x45ncoderType\\x12\\x07\\n\\x03\\x42OW\\x10\\x00\\x12\\x07\\n\\x03\\x43NN\\x10\\x01\\x12\\x08\\n\\x04LSTM\\x10\\x02'\n)\n\n_FEATURETYPE = _descriptor.EnumDescriptor(\n  name='FeatureType',\n  full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.FeatureType',\n  filename=None,\n  file=DESCRIPTOR,\n  values=[\n    _descriptor.EnumValueDescriptor(\n      name='STRING', index=0, number=0,\n      serialized_options=None,\n      type=None),\n    _descriptor.EnumValueDescriptor(\n      name='INT', index=1, number=1,\n      serialized_options=None,\n      type=None),\n    _descriptor.EnumValueDescriptor(\n      name='FLOAT', index=2, number=2,\n      serialized_options=None,\n      type=None),\n  ],\n  containing_type=None,\n  serialized_options=None,\n  serialized_start=845,\n  serialized_end=890,\n)\n_sym_db.RegisterEnumDescriptor(_FEATURETYPE)\n\nFeatureType = enum_type_wrapper.EnumTypeWrapper(_FEATURETYPE)\n_ENCODERTYPE = _descriptor.EnumDescriptor(\n  name='EncoderType',\n  full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.EncoderType',\n  filename=None,\n  file=DESCRIPTOR,\n  values=[\n    _descriptor.EnumValueDescriptor(\n      name='BOW', index=0, number=0,\n      serialized_options=None,\n      type=None),\n    _descriptor.EnumValueDescriptor(\n      name='CNN', index=1, number=1,\n      serialized_options=None,\n      type=None),\n    _descriptor.EnumValueDescriptor(\n      name='LSTM', index=2, number=2,\n      serialized_options=None,\n      type=None),\n  ],\n  containing_type=None,\n  serialized_options=None,\n  serialized_start=892,\n  serialized_end=933,\n)\n_sym_db.RegisterEnumDescriptor(_ENCODERTYPE)\n\nEncoderType = enum_type_wrapper.EnumTypeWrapper(_ENCODERTYPE)\nSTRING = 0\nINT = 1\nFLOAT = 2\nBOW = 0\nCNN = 1\nLSTM = 2\n\n\n\n_FEATURE = _descriptor.Descriptor(\n  name='Feature',\n  full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.Feature',\n  filename=None,\n  file=DESCRIPTOR,\n  containing_type=None,\n  fields=[\n    _descriptor.FieldDescriptor(\n      name='feature_name', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.Feature.feature_name', index=0,\n      number=1, type=9, cpp_type=9, label=1,\n      has_default_value=False, default_value=b\"\".decode('utf-8'),\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='feature_type', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.Feature.feature_type', index=1,\n      number=2, type=14, cpp_type=8, label=1,\n      has_default_value=False, default_value=0,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='vocab_name', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.Feature.vocab_name', index=2,\n      number=3, type=9, cpp_type=9, label=1,\n      has_default_value=False, default_value=b\"\".decode('utf-8'),\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='vocab_size', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.Feature.vocab_size', index=3,\n      number=4, type=3, cpp_type=2, label=1,\n      has_default_value=False, default_value=0,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='embedding_dim', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.Feature.embedding_dim', index=4,\n      number=5, type=3, cpp_type=2, label=1,\n      has_default_value=False, default_value=0,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='feature_length', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.Feature.feature_length', index=5,\n      number=6, type=3, cpp_type=2, label=1,\n      has_default_value=False, default_value=0,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n  ],\n  extensions=[\n  ],\n  nested_types=[],\n  enum_types=[\n  ],\n  serialized_options=None,\n  is_extendable=False,\n  syntax='proto2',\n  extension_ranges=[],\n  oneofs=[\n  ],\n  serialized_start=87,\n  serialized_end=304,\n)\n\n\n_FEATUREGROUP = _descriptor.Descriptor(\n  name='FeatureGroup',\n  full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.FeatureGroup',\n  filename=None,\n  file=DESCRIPTOR,\n  containing_type=None,\n  fields=[\n    _descriptor.FieldDescriptor(\n      name='features', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.FeatureGroup.features', index=0,\n      number=1, type=11, cpp_type=10, label=3,\n      has_default_value=False, default_value=[],\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='encoder_type', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.FeatureGroup.encoder_type', index=1,\n      number=2, type=14, cpp_type=8, label=1,\n      has_default_value=False, default_value=0,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n  ],\n  extensions=[\n  ],\n  nested_types=[],\n  enum_types=[\n  ],\n  serialized_options=None,\n  is_extendable=False,\n  syntax='proto2',\n  extension_ranges=[],\n  oneofs=[\n  ],\n  serialized_start=307,\n  serialized_end=511,\n)\n\n\n_INPUTCONFIG = _descriptor.Descriptor(\n  name='InputConfig',\n  full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.InputConfig',\n  filename=None,\n  file=DESCRIPTOR,\n  containing_type=None,\n  fields=[\n    _descriptor.FieldDescriptor(\n      name='global_feature_groups', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.InputConfig.global_feature_groups', index=0,\n      number=1, type=11, cpp_type=10, label=3,\n      has_default_value=False, default_value=[],\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='activity_feature_groups', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.InputConfig.activity_feature_groups', index=1,\n      number=2, type=11, cpp_type=10, label=3,\n      has_default_value=False, default_value=[],\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='label_feature', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.InputConfig.label_feature', index=2,\n      number=3, type=11, cpp_type=10, label=1,\n      has_default_value=False, default_value=None,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n  ],\n  extensions=[\n  ],\n  nested_types=[],\n  enum_types=[\n  ],\n  serialized_options=None,\n  is_extendable=False,\n  syntax='proto2',\n  extension_ranges=[],\n  oneofs=[\n  ],\n  serialized_start=514,\n  serialized_end=843,\n)\n\n_FEATURE.fields_by_name['feature_type'].enum_type = _FEATURETYPE\n_FEATUREGROUP.fields_by_name['features'].message_type = _FEATURE\n_FEATUREGROUP.fields_by_name['encoder_type'].enum_type = _ENCODERTYPE\n_INPUTCONFIG.fields_by_name['global_feature_groups'].message_type = _FEATUREGROUP\n_INPUTCONFIG.fields_by_name['activity_feature_groups'].message_type = _FEATUREGROUP\n_INPUTCONFIG.fields_by_name['label_feature'].message_type = _FEATURE\nDESCRIPTOR.message_types_by_name['Feature'] = _FEATURE\nDESCRIPTOR.message_types_by_name['FeatureGroup'] = _FEATUREGROUP\nDESCRIPTOR.message_types_by_name['InputConfig'] = _INPUTCONFIG\nDESCRIPTOR.enum_types_by_name['FeatureType'] = _FEATURETYPE\nDESCRIPTOR.enum_types_by_name['EncoderType'] = _ENCODERTYPE\n_sym_db.RegisterFileDescriptor(DESCRIPTOR)\n\nFeature = _reflection.GeneratedProtocolMessageType('Feature', (_message.Message,), {\n  'DESCRIPTOR' : _FEATURE,\n  '__module__' : 'input_config_pb2'\n  # @@protoc_insertion_point(class_scope:tensorflow_examples.lite.examples.recommendation.ml_v2.configs.Feature)\n  })\n_sym_db.RegisterMessage(Feature)\n\nFeatureGroup = _reflection.GeneratedProtocolMessageType('FeatureGroup', (_message.Message,), {\n  'DESCRIPTOR' : _FEATUREGROUP,\n  '__module__' : 'input_config_pb2'\n  # @@protoc_insertion_point(class_scope:tensorflow_examples.lite.examples.recommendation.ml_v2.configs.FeatureGroup)\n  })\n_sym_db.RegisterMessage(FeatureGroup)\n\nInputConfig = _reflection.GeneratedProtocolMessageType('InputConfig', (_message.Message,), {\n  'DESCRIPTOR' : _INPUTCONFIG,\n  '__module__' : 'input_config_pb2'\n  # @@protoc_insertion_point(class_scope:tensorflow_examples.lite.examples.recommendation.ml_v2.configs.InputConfig)\n  })\n_sym_db.RegisterMessage(InputConfig)\n\n\n# @@protoc_insertion_point(module_scope)\n# pyformat: enable\n"
  },
  {
    "path": "lite/examples/recommendation/ml/configs/model_config.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Class to hold model architecture configuration.\"\"\"\nfrom typing import List\nimport attr\n\n\n@attr.s(auto_attribs=True)\nclass ModelConfig(object):\n  \"\"\"Class to hold parameters for model architecture configuration.\n\n  Attributes:\n      hidden_layer_dims: List of hidden layer dimensions.\n      eval_top_k: Top k to evaluate.\n      conv_num_filter_ratios: Number of filter ratios for the Conv1D layer.\n      conv_kernel_size: Size of the Conv1D layer kernel size.\n      lstm_num_units: Number of units for the LSTM layer.\n      num_predictions: Number of predictions to return with serving mode, which\n      has default value 10.\n  \"\"\"\n  hidden_layer_dims: List[int]\n  eval_top_k: List[int]\n  conv_num_filter_ratios: List[int]\n  conv_kernel_size: int\n  lstm_num_units: int\n  num_predictions: int = 10\n"
  },
  {
    "path": "lite/examples/recommendation/ml/configs/sample_input_config.pbtxt",
    "content": "activity_feature_groups {\n  features {\n    feature_name: \"context_movie_id\"\n    feature_type: INT\n    vocab_size: 3953\n    embedding_dim: 8\n    feature_length: 10\n  }\n  features {\n    feature_name: \"context_movie_rating\"\n    feature_type: FLOAT\n    feature_length: 10\n  }\n  encoder_type: CNN\n}\nactivity_feature_groups {\n  features {\n    feature_name: \"context_movie_genre\"\n    feature_type: STRING\n    vocab_name: \"movie_genre_vocab.txt\"\n    vocab_size: 19\n    embedding_dim: 4\n    feature_length: 32\n  }\n  encoder_type: CNN\n}\nlabel_feature {\n  feature_name: \"label_movie_id\"\n  feature_type: INT\n  vocab_size: 3953\n  embedding_dim: 8\n  feature_length: 1\n}\n"
  },
  {
    "path": "lite/examples/recommendation/ml/data/__init__.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n"
  },
  {
    "path": "lite/examples/recommendation/ml/data/example_generation_movielens.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Prepare TF.Examples for on-device recommendation model.\n\nFollowing functions are included: 1) downloading raw data 2) processing to user\nactivity sequence and splitting to train/test data 3) convert to TF.Examples\nand write in output location.\n\nMore information about the movielens dataset can be found here:\nhttps://grouplens.org/datasets/movielens/\n\"\"\"\n\nimport collections\nimport json\nimport os\nimport random\nimport re\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\nimport pandas as pd\nimport tensorflow as tf\n\nFLAGS = flags.FLAGS\n\n# Permalinks to download movielens data.\nMOVIELENS_1M_URL = \"https://files.grouplens.org/datasets/movielens/ml-1m.zip\"\nMOVIELENS_ZIP_FILENAME = \"ml-1m.zip\"\nMOVIELENS_ZIP_HASH = \"a6898adb50b9ca05aa231689da44c217cb524e7ebd39d264c56e2832f2c54e20\"\nMOVIELENS_EXTRACTED_DIR = \"ml-1m\"\nRATINGS_FILE_NAME = \"ratings.dat\"\nMOVIES_FILE_NAME = \"movies.dat\"\nRATINGS_DATA_COLUMNS = [\"UserID\", \"MovieID\", \"Rating\", \"Timestamp\"]\nMOVIES_DATA_COLUMNS = [\"MovieID\", \"Title\", \"Genres\"]\nOUTPUT_TRAINING_DATA_FILENAME = \"train_movielens_1m.tfrecord\"\nOUTPUT_TESTING_DATA_FILENAME = \"test_movielens_1m.tfrecord\"\nOUTPUT_MOVIE_VOCAB_FILENAME = \"movie_vocab.json\"\nOUTPUT_MOVIE_YEAR_VOCAB_FILENAME = \"movie_year_vocab.txt\"\nOUTPUT_MOVIE_GENRE_VOCAB_FILENAME = \"movie_genre_vocab.txt\"\nOUTPUT_MOVIE_TITLE_UNIGRAM_VOCAB_FILENAME = \"movie_title_unigram_vocab.txt\"\nOUTPUT_MOVIE_TITLE_BIGRAM_VOCAB_FILENAME = \"movie_title_bigram_vocab.txt\"\nPAD_MOVIE_ID = 0\nPAD_RATING = 0.0\nPAD_MOVIE_YEAR = 0\nUNKNOWN_STR = \"UNK\"\nVOCAB_MOVIE_ID_INDEX = 0\nVOCAB_COUNT_INDEX = 3\n\n\ndef define_flags():\n  \"\"\"Define flags.\"\"\"\n  flags.DEFINE_string(\"data_dir\", \"/tmp\",\n                      \"Path to download and store movielens data.\")\n  flags.DEFINE_string(\"output_dir\", None,\n                      \"Path to the directory of output files.\")\n  flags.DEFINE_bool(\"build_vocabs\", True,\n                    \"If yes, generate movie feature vocabs.\")\n  flags.DEFINE_integer(\"min_timeline_length\", 3,\n                       \"The minimum timeline length to construct examples.\")\n  flags.DEFINE_integer(\"max_context_length\", 10,\n                       \"The maximum length of user context history.\")\n  flags.DEFINE_integer(\"max_context_movie_genre_length\", 10,\n                       \"The maximum length of user context history.\")\n  flags.DEFINE_integer(\n      \"min_rating\", None, \"Minimum rating of movie that will be used to in \"\n      \"training data\")\n  flags.DEFINE_float(\"train_data_fraction\", 0.9, \"Fraction of training data.\")\n\n\nclass MovieInfo(\n    collections.namedtuple(\n        \"MovieInfo\", [\"movie_id\", \"timestamp\", \"rating\", \"title\", \"genres\"])):\n  \"\"\"Data holder of basic information of a movie.\"\"\"\n  __slots__ = ()\n\n  def __new__(cls,\n              movie_id=PAD_MOVIE_ID,\n              timestamp=0,\n              rating=PAD_RATING,\n              title=\"\",\n              genres=\"\"):\n    return super(MovieInfo, cls).__new__(cls, movie_id, timestamp, rating,\n                                         title, genres)\n\n\ndef download_and_extract_data(data_directory,\n                              url=MOVIELENS_1M_URL,\n                              fname=MOVIELENS_ZIP_FILENAME,\n                              file_hash=MOVIELENS_ZIP_HASH,\n                              extracted_dir_name=MOVIELENS_EXTRACTED_DIR):\n  \"\"\"Download and extract zip containing MovieLens data to a given directory.\n\n  Args:\n    data_directory: Local path to extract dataset to.\n    url: Direct path to MovieLens dataset .zip file. See constants above for\n      examples.\n    fname: str, zip file name to download.\n    file_hash: str, SHA-256 file hash.\n    extracted_dir_name: str, extracted dir name under data_directory.\n\n  Returns:\n    Downloaded and extracted data file directory.\n  \"\"\"\n  if not tf.io.gfile.exists(data_directory):\n    tf.io.gfile.makedirs(data_directory)\n  path_to_zip = tf.keras.utils.get_file(\n      fname=fname,\n      origin=url,\n      file_hash=file_hash,\n      hash_algorithm=\"sha256\",\n      extract=True,\n      cache_dir=data_directory)\n  extracted_file_dir = os.path.join(\n      os.path.dirname(path_to_zip), extracted_dir_name)\n  return extracted_file_dir\n\n\ndef read_data(data_directory, min_rating=None):\n  \"\"\"Read movielens ratings.dat and movies.dat file into dataframe.\"\"\"\n  ratings_df = pd.read_csv(\n      os.path.join(data_directory, RATINGS_FILE_NAME),\n      sep=\"::\",\n      names=RATINGS_DATA_COLUMNS,\n      encoding=\"unicode_escape\")  # May contain unicode. Need to escape.\n  ratings_df[\"Timestamp\"] = ratings_df[\"Timestamp\"].apply(int)\n  if min_rating is not None:\n    ratings_df = ratings_df[ratings_df[\"Rating\"] >= min_rating]\n  movies_df = pd.read_csv(\n      os.path.join(data_directory, MOVIES_FILE_NAME),\n      sep=\"::\",\n      names=MOVIES_DATA_COLUMNS,\n      encoding=\"unicode_escape\")  # May contain unicode. Need to escape.\n  return ratings_df, movies_df\n\n\ndef convert_to_timelines(ratings_df):\n  \"\"\"Convert ratings data to user.\"\"\"\n  timelines = collections.defaultdict(list)\n  movie_counts = collections.Counter()\n  for user_id, movie_id, rating, timestamp in ratings_df.values:\n    timelines[user_id].append(\n        MovieInfo(movie_id=movie_id, timestamp=int(timestamp), rating=rating))\n    movie_counts[movie_id] += 1\n  # Sort per-user timeline by timestamp\n  for (user_id, context) in timelines.items():\n    context.sort(key=lambda x: x.timestamp)\n    timelines[user_id] = context\n  return timelines, movie_counts\n\n\ndef generate_movies_dict(movies_df):\n  \"\"\"Generates movies dictionary from movies dataframe.\"\"\"\n  movies_dict = {\n      movie_id: MovieInfo(movie_id=movie_id, title=title, genres=genres)\n      for movie_id, title, genres in movies_df.values\n  }\n  movies_dict[0] = MovieInfo()\n  return movies_dict\n\n\ndef extract_year_from_title(title):\n  year = re.search(r\"\\((\\d{4})\\)\", title)\n  if year:\n    return int(year.group(1))\n  return 0\n\n\ndef generate_feature_of_movie_years(movies_dict, movies):\n  \"\"\"Extracts year feature for movies from movie title.\"\"\"\n  return [\n      extract_year_from_title(movies_dict[movie.movie_id].title)\n      for movie in movies\n  ]\n\n\ndef generate_movie_genres(movies_dict, movies):\n  \"\"\"Create a feature of the genre of each movie.\n\n  Save genre as a feature for the movies.\n\n  Args:\n    movies_dict: Dict of movies, keyed by movie_id with value of (title, genre)\n    movies: list of movies to extract genres.\n\n  Returns:\n    movie_genres: list of genres of all input movies.\n  \"\"\"\n  movie_genres = []\n  for movie in movies:\n    if not movies_dict[movie.movie_id].genres:\n      continue\n    genres = [\n        tf.compat.as_bytes(genre)\n        for genre in movies_dict[movie.movie_id].genres.split(\"|\")\n    ]\n    movie_genres.extend(genres)\n\n  return movie_genres\n\n\ndef _pad_or_truncate_movie_feature(feature, max_len, pad_value):\n  feature.extend([pad_value for _ in range(max_len - len(feature))])\n  return feature[:max_len]\n\n\ndef generate_examples_from_single_timeline(timeline,\n                                           movies_dict,\n                                           max_context_len=100,\n                                           max_context_movie_genre_len=320):\n  \"\"\"Generate TF examples from a single user timeline.\n\n  Generate TF examples from a single user timeline. Timeline with length less\n  than minimum timeline length will be skipped. And if context user history\n  length is shorter than max_context_len, features will be padded with default\n  values.\n\n  Args:\n    timeline: The timeline to generate TF examples from.\n    movies_dict: Dictionary of all MovieInfos.\n    max_context_len: The maximum length of the context. If the context history\n      length is less than max_context_length, features will be padded with\n      default values.\n    max_context_movie_genre_len: The length of movie genre feature.\n\n  Returns:\n    examples: Generated examples from this single timeline.\n  \"\"\"\n  examples = []\n  for label_idx in range(1, len(timeline)):\n    start_idx = max(0, label_idx - max_context_len)\n    context = timeline[start_idx:label_idx]\n    # Pad context with out-of-vocab movie id 0.\n    while len(context) < max_context_len:\n      context.append(MovieInfo())\n    label_movie_id = int(timeline[label_idx].movie_id)\n    context_movie_id = [int(movie.movie_id) for movie in context]\n    context_movie_rating = [movie.rating for movie in context]\n    context_movie_year = generate_feature_of_movie_years(movies_dict, context)\n    context_movie_genres = generate_movie_genres(movies_dict, context)\n    context_movie_genres = _pad_or_truncate_movie_feature(\n        context_movie_genres, max_context_movie_genre_len,\n        tf.compat.as_bytes(UNKNOWN_STR))\n    feature = {\n        \"context_movie_id\":\n            tf.train.Feature(\n                int64_list=tf.train.Int64List(value=context_movie_id)),\n        \"context_movie_rating\":\n            tf.train.Feature(\n                float_list=tf.train.FloatList(value=context_movie_rating)),\n        \"context_movie_genre\":\n            tf.train.Feature(\n                bytes_list=tf.train.BytesList(value=context_movie_genres)),\n        \"context_movie_year\":\n            tf.train.Feature(\n                int64_list=tf.train.Int64List(value=context_movie_year)),\n        \"label_movie_id\":\n            tf.train.Feature(\n                int64_list=tf.train.Int64List(value=[label_movie_id]))\n    }\n    tf_example = tf.train.Example(features=tf.train.Features(feature=feature))\n    examples.append(tf_example)\n\n  return examples\n\n\ndef generate_examples_from_timelines(timelines,\n                                     movies_df,\n                                     min_timeline_len=3,\n                                     max_context_len=100,\n                                     max_context_movie_genre_len=320,\n                                     train_data_fraction=0.9,\n                                     random_seed=None,\n                                     shuffle=True):\n  \"\"\"Convert user timelines to tf examples.\n\n  Convert user timelines to tf examples by adding all possible context-label\n  pairs in the examples pool.\n\n  Args:\n    timelines: The user timelines to process.\n    movies_df: The dataframe of all movies.\n    min_timeline_len: The minimum length of timeline. If the timeline length is\n      less than min_timeline_len, empty examples list will be returned.\n    max_context_len: The maximum length of the context. If the context history\n      length is less than max_context_length, features will be padded with\n      default values.\n    max_context_movie_genre_len: The length of movie genre feature.\n    train_data_fraction: Fraction of training data.\n    random_seed: Seed for randomization.\n    shuffle: Whether to shuffle the examples before splitting train and test\n      data.\n\n  Returns:\n    train_examples: TF example list for training.\n    test_examples: TF example list for testing.\n  \"\"\"\n  examples = []\n  movies_dict = generate_movies_dict(movies_df)\n  progress_bar = tf.keras.utils.Progbar(len(timelines))\n  for timeline in timelines.values():\n    if len(timeline) < min_timeline_len:\n      progress_bar.add(1)\n      continue\n    single_timeline_examples = generate_examples_from_single_timeline(\n        timeline=timeline,\n        movies_dict=movies_dict,\n        max_context_len=max_context_len,\n        max_context_movie_genre_len=max_context_movie_genre_len)\n    examples.extend(single_timeline_examples)\n    progress_bar.add(1)\n  # Split the examples into train, test sets.\n  if shuffle:\n    random.seed(random_seed)\n    random.shuffle(examples)\n  last_train_index = round(len(examples) * train_data_fraction)\n\n  train_examples = examples[:last_train_index]\n  test_examples = examples[last_train_index:]\n  return train_examples, test_examples\n\n\ndef generate_movie_feature_vocabs(movies_df, movie_counts):\n  \"\"\"Generate vocabularies for movie features.\n\n  Generate vocabularies for movie features (movie_id, genre, year), sorted by\n  usage count. Vocab id 0 will be reserved for default padding value.\n\n  Args:\n    movies_df: Dataframe for movies.\n    movie_counts: Counts that each movie is rated.\n\n  Returns:\n    movie_id_vocab: List of all movie ids paired with movie usage count, and\n      sorted by counts.\n    movie_genre_vocab: List of all movie genres, sorted by genre usage counts.\n    movie_year_vocab: List of all movie years, sorted by year usage counts.\n  \"\"\"\n  movie_vocab = []\n  movie_genre_counter = collections.Counter()\n  movie_year_counter = collections.Counter()\n  for movie_id, title, genres in movies_df.values:\n    count = movie_counts.get(movie_id) or 0\n    movie_vocab.append([movie_id, title, genres, count])\n    year = extract_year_from_title(title)\n    movie_year_counter[year] += 1\n    for genre in genres.split(\"|\"):\n      movie_genre_counter[genre] += 1\n\n  movie_vocab.sort(key=lambda x: x[VOCAB_COUNT_INDEX], reverse=True)  # by count\n  movie_year_vocab = [0] + [x for x, _ in movie_year_counter.most_common()]\n  movie_genre_vocab = [UNKNOWN_STR\n                      ] + [x for x, _ in movie_genre_counter.most_common()]\n\n  return (movie_vocab, movie_year_vocab, movie_genre_vocab)\n\n\ndef write_tfrecords(tf_examples, filename):\n  \"\"\"Writes tf examples to tfrecord file, and returns the count.\"\"\"\n  with tf.io.TFRecordWriter(filename) as file_writer:\n    length = len(tf_examples)\n    progress_bar = tf.keras.utils.Progbar(length)\n    for example in tf_examples:\n      file_writer.write(example.SerializeToString())\n      progress_bar.add(1)\n    return length\n\n\ndef write_vocab_json(vocab, filename):\n  \"\"\"Write generated movie vocabulary to specified file.\"\"\"\n  with open(filename, \"w\", encoding=\"utf-8\") as jsonfile:\n    json.dump(vocab, jsonfile, indent=2)\n\n\ndef write_vocab_txt(vocab, filename):\n  with open(filename, \"w\", encoding=\"utf-8\") as f:\n    for item in vocab:\n      f.write(str(item) + \"\\n\")\n\n\ndef generate_datasets(extracted_data_dir,\n                      output_dir,\n                      min_timeline_length,\n                      max_context_length,\n                      max_context_movie_genre_length,\n                      min_rating=None,\n                      build_vocabs=True,\n                      train_data_fraction=0.9,\n                      train_filename=OUTPUT_TRAINING_DATA_FILENAME,\n                      test_filename=OUTPUT_TESTING_DATA_FILENAME,\n                      vocab_filename=OUTPUT_MOVIE_VOCAB_FILENAME,\n                      vocab_year_filename=OUTPUT_MOVIE_YEAR_VOCAB_FILENAME,\n                      vocab_genre_filename=OUTPUT_MOVIE_GENRE_VOCAB_FILENAME):\n  \"\"\"Generates train and test datasets as TFRecord, and returns stats.\"\"\"\n  logging.info(\"Reading data to dataframes.\")\n  ratings_df, movies_df = read_data(extracted_data_dir, min_rating=min_rating)\n  logging.info(\"Generating movie rating user timelines.\")\n  timelines, movie_counts = convert_to_timelines(ratings_df)\n  logging.info(\"Generating train and test examples.\")\n  train_examples, test_examples = generate_examples_from_timelines(\n      timelines=timelines,\n      movies_df=movies_df,\n      min_timeline_len=min_timeline_length,\n      max_context_len=max_context_length,\n      max_context_movie_genre_len=max_context_movie_genre_length,\n      train_data_fraction=train_data_fraction)\n\n  if not tf.io.gfile.exists(output_dir):\n    tf.io.gfile.makedirs(output_dir)\n  logging.info(\"Writing generated training examples.\")\n  train_file = os.path.join(output_dir, train_filename)\n  train_size = write_tfrecords(tf_examples=train_examples, filename=train_file)\n  logging.info(\"Writing generated testing examples.\")\n  test_file = os.path.join(output_dir, test_filename)\n  test_size = write_tfrecords(tf_examples=test_examples, filename=test_file)\n  stats = {\n      \"train_size\": train_size,\n      \"test_size\": test_size,\n      \"train_file\": train_file,\n      \"test_file\": test_file,\n  }\n\n  if build_vocabs:\n    (movie_vocab, movie_year_vocab, movie_genre_vocab) = (\n        generate_movie_feature_vocabs(\n            movies_df=movies_df, movie_counts=movie_counts))\n    vocab_file = os.path.join(output_dir, vocab_filename)\n    write_vocab_json(movie_vocab, filename=vocab_file)\n    stats.update({\n        \"vocab_size\": len(movie_vocab),\n        \"vocab_file\": vocab_file,\n        \"vocab_max_id\": max([arr[VOCAB_MOVIE_ID_INDEX] for arr in movie_vocab])\n    })\n\n    for vocab, filename, key in zip([movie_year_vocab, movie_genre_vocab],\n                                    [vocab_year_filename, vocab_genre_filename],\n                                    [\"year_vocab\", \"genre_vocab\"]):\n      vocab_file = os.path.join(output_dir, filename)\n      write_vocab_txt(vocab, filename=vocab_file)\n      stats.update({\n          key + \"_size\": len(vocab),\n          key + \"_file\": vocab_file,\n      })\n\n  return stats\n\n\ndef main(_):\n  logging.info(\"Downloading and extracting data.\")\n  extracted_data_dir = download_and_extract_data(data_directory=FLAGS.data_dir)\n\n  stats = generate_datasets(\n      extracted_data_dir=extracted_data_dir,\n      output_dir=FLAGS.output_dir,\n      min_timeline_length=FLAGS.min_timeline_length,\n      max_context_length=FLAGS.max_context_length,\n      max_context_movie_genre_length=FLAGS.max_context_movie_genre_length,\n      min_rating=FLAGS.min_rating,\n      build_vocabs=FLAGS.build_vocabs,\n      train_data_fraction=FLAGS.train_data_fraction,\n  )\n  logging.info(\"Generated dataset: %s\", stats)\n\n\nif __name__ == \"__main__\":\n  define_flags()\n  app.run(main)\n"
  },
  {
    "path": "lite/examples/recommendation/ml/data/example_generation_movielens_test.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Tests for example_generation_movielens.\"\"\"\n\nimport pandas as pd\nimport tensorflow as tf\nfrom data import example_generation_movielens as example_gen\n\nfrom google.protobuf import text_format\n\n\nMOVIES_DF = pd.DataFrame([\n    {\n        'MovieId': int(1),\n        'Title': 'Toy Story (1995)',\n        'Genres': 'Animation|Children|Comedy'\n    },\n    {\n        'MovieId': int(2),\n        'Title': 'Four Weddings and a Funeral (1994)',\n        'Genres': 'Comedy|Romance'\n    },\n    {\n        'MovieId': int(3),\n        'Title': 'Lion King, The (1994)',\n        'Genres': 'Adventure|Animation|Children'\n    },\n    {\n        'MovieId': int(4),\n        'Title': 'Forrest Gump (1994)',\n        'Genres': 'Comedy|Drama|Romance'\n    },\n    {\n        'MovieId': int(5),\n        'Title': 'Little Women (1994)',\n        'Genres': 'Drama'\n    },\n])\n\nRATINGS_DF = pd.DataFrame([\n    {\n        'UserID': int(1),\n        'MovieId': int(1),\n        'Rating': 3.5,\n        'Timestamp': 0\n    },\n    {\n        'UserID': int(1),\n        'MovieId': int(2),\n        'Rating': 4.0,\n        'Timestamp': 1\n    },\n    {\n        'UserID': int(1),\n        'MovieId': int(3),\n        'Rating': 4.5,\n        'Timestamp': 2\n    },\n    {\n        'UserID': int(1),\n        'MovieId': int(4),\n        'Rating': 4.7,\n        'Timestamp': 3\n    },\n    {\n        'UserID': int(2),\n        'MovieId': int(5),\n        'Rating': 4.0,\n        'Timestamp': 1\n    },\n])\n\nEXAMPLE1 = text_format.Parse(\n    \"\"\"\n    features {\n        feature {\n          key: \"context_movie_id\"\n          value {\n            int64_list {\n              value: [1, 0, 0, 0, 0]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_rating\"\n          value {\n            float_list {\n              value: [3.5, 0.0, 0.0, 0.0, 0.0]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_genre\"\n          value {\n            bytes_list {\n              value: [\"Animation\", \"Children\", \"Comedy\", \"UNK\", \"UNK\", \"UNK\", \"UNK\", \"UNK\", \"UNK\", \"UNK\"]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_year\"\n          value {\n            int64_list {\n              value: [1995, 0, 0, 0, 0]\n            }\n          }\n        }\n        feature {\n          key: \"label_movie_id\"\n          value {\n            int64_list {\n              value: [2]\n            }\n          }\n        }\n      }\n      \"\"\", tf.train.Example())\n\nEXAMPLE2 = text_format.Parse(\n    \"\"\"\n    features {\n        feature {\n          key: \"context_movie_id\"\n          value {\n            int64_list {\n              value: [1, 2, 0, 0, 0]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_rating\"\n          value {\n            float_list {\n              value: [3.5, 4.0, 0.0, 0.0, 0.0]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_genre\"\n          value {\n            bytes_list {\n              value: [\n                    \"Animation\", \"Children\", \"Comedy\", \"Comedy\", \"Romance\", \"UNK\", \"UNK\", \"UNK\", \"UNK\", \"UNK\"\n                ]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_year\"\n          value {\n            int64_list {\n              value: [1995, 1994, 0, 0, 0]\n            }\n          }\n        }\n        feature {\n          key: \"label_movie_id\"\n          value {\n            int64_list {\n              value: [3]\n            }\n          }\n        }\n      }\"\"\", tf.train.Example())\n\nEXAMPLE3 = text_format.Parse(\n    \"\"\"\n    features {\n        feature {\n          key: \"context_movie_id\"\n          value {\n            int64_list {\n              value: [1, 2, 3, 0, 0]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_rating\"\n          value {\n            float_list {\n              value: [3.5, 4.0, 4.5, 0.0, 0.0]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_genre\"\n          value {\n            bytes_list {\n              value: [\n                    \"Animation\", \"Children\", \"Comedy\", \"Comedy\", \"Romance\",\n                    \"Adventure\", \"Animation\", \"Children\", \"UNK\", \"UNK\"\n                ]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_year\"\n          value {\n            int64_list {\n              value: [1995, 1994, 1994, 0, 0]\n            }\n          }\n        }\n        feature {\n          key: \"label_movie_id\"\n          value {\n            int64_list {\n              value: [4]\n            }\n          }\n        }\n      }\"\"\", tf.train.Example())\n\n\nclass ExampleGenerationMovielensTest(tf.test.TestCase):\n\n  def test_example_generation(self):\n    timelines, _ = example_gen.convert_to_timelines(RATINGS_DF)\n    train_examples, test_examples = example_gen.generate_examples_from_timelines(\n        timelines=timelines,\n        movies_df=MOVIES_DF,\n        min_timeline_len=2,\n        max_context_len=5,\n        max_context_movie_genre_len=10,\n        train_data_fraction=0.66,\n        shuffle=False)\n    self.assertLen(train_examples, 2)\n    self.assertLen(test_examples, 1)\n    self.assertProtoEquals(train_examples[0], EXAMPLE1)\n    self.assertProtoEquals(train_examples[1], EXAMPLE2)\n    self.assertProtoEquals(test_examples[0], EXAMPLE3)\n\n  def test_vocabs_generation(self):\n    _, movie_counts = example_gen.convert_to_timelines(RATINGS_DF)\n    (_, movie_year_vocab, movie_genre_vocab) = (\n        example_gen.generate_movie_feature_vocabs(\n            movies_df=MOVIES_DF, movie_counts=movie_counts))\n    self.assertAllEqual(movie_year_vocab, [0, 1994, 1995])\n    self.assertAllEqual(movie_genre_vocab, [\n        'UNK', 'Comedy', 'Animation', 'Children', 'Romance', 'Drama',\n        'Adventure'\n    ])\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "lite/examples/recommendation/ml/model/__init__.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n"
  },
  {
    "path": "lite/examples/recommendation/ml/model/context_encoder.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Context encoder layer for on-device personalized recommendation model.\"\"\"\nimport math\nfrom typing import Dict\n\nimport tensorflow as tf\n\nfrom configs import input_config_generated_pb2 as input_config_pb2\nfrom configs import model_config as model_config_class\n\n\ndef safe_div(x, y):\n  return tf.where(tf.not_equal(y, 0), tf.divide(x, y), tf.zeros_like(x))\n\n\nclass ContextEncoder(tf.keras.layers.Layer):\n  \"\"\"Layer to encode context sequence.\n\n  This encoder layer supports three types: 1) bow: bag of words style averaging\n  sequence embeddings. 2) cnn: use convolutional neural network to encode\n  sequence. 3) rnn: use recurrent neural network to encode sequence.\n\n  This encoder should be initialized with a predefined embedding layer, encoder\n  type and necessary parameters corresponding to the encoder type.\n  \"\"\"\n\n  def __init__(self,\n               input_config: input_config_pb2.InputConfig,\n               model_config: model_config_class.ModelConfig):\n    \"\"\"Initialize ContextEncoder layer.\n\n    Initialize ContextEncoder layer according to input config and model config,\n    setting up feature group encoders and hidden layers.\n\n    Args:\n      input_config: The input config (input_config_pb2.InputConfig).\n      model_config: The model config (model_config_class.ModelConfig).\n    \"\"\"\n    super(ContextEncoder, self).__init__()\n    self._input_config = input_config\n    self._model_config = model_config\n    self._final_embedding_dim = self._input_config.label_feature.embedding_dim\n    self._feature_group_encoders = [\n        FeatureGroupEncoder(feature_group, self._model_config,\n                            self._final_embedding_dim)\n        for feature_group in input_config.global_feature_groups\n    ]\n    self._feature_group_encoders.extend([\n        FeatureGroupEncoder(feature_group, self._model_config,\n                            self._final_embedding_dim)\n        for feature_group in input_config.activity_feature_groups\n    ])\n    self._final_embedding_dim = self._input_config.label_feature.embedding_dim\n    # Set up hidden layers.\n    self._hidden_layers = []\n    for i, layer_dim in enumerate(self._model_config.hidden_layer_dims):\n      self._hidden_layers.append(\n          tf.keras.layers.Dense(\n              units=layer_dim,\n              name='hidden_layer{}'.format(i),\n              activation=tf.nn.relu))\n    # Append final top layer, dimension of which should equal final embedding\n    # dimension.\n    self._hidden_layers.append(\n        tf.keras.layers.Dense(\n            units=self._final_embedding_dim,\n            name='proj_layer',\n            activation=tf.nn.relu))\n\n  def call(self, input_context: Dict[str, tf.Tensor]) -> tf.Tensor:\n    \"\"\"Call function of the context encoder layer.\n\n    Takes in input context tensor dictionary, generates feature group\n    embeddings, concatenate them up and pass them through top hidden layers\n    to get final context embedding.\n\n    Args:\n      input_context: A dictionary mapping input context feature name to tenors.\n\n    Returns:\n      Context encoding.\n    \"\"\"\n    input_context = {\n        k: tf.expand_dims(v, 0) if v.shape.ndims == 1 else v\n        for k, v in input_context.items()\n    }\n    feature_group_embeddings = [\n        feature_group_encoder(input_context)\n        for feature_group_encoder in self._feature_group_encoders\n    ]\n    context_embedding = tf.concat(feature_group_embeddings, -1)\n    for hidden_layer in self._hidden_layers:\n      context_embedding = hidden_layer(context_embedding)\n    return context_embedding\n\n\nclass FeatureGroupEncoder(tf.keras.layers.Layer):\n  \"\"\"Layer to generate encoding for the feature group.\n\n  Layer to generate encoding for the group of features in the feature\n  group with encoder type (BOW/CNN/LSTM) specified in the feature group config.\n  Embeddings of INT or STRING type features are concatenated first, and FLOAT\n  type feature values are appended after. Embedding vector is properly masked.\n  \"\"\"\n\n  def __init__(self,\n               feature_group: input_config_pb2.FeatureGroup,\n               model_config: model_config_class.ModelConfig,\n               final_embedding_dim: int):\n    \"\"\"Initialize FeatureGroupEncoder layer.\n\n    Initialize ContextEncoder layer according to feature group.\n\n    Args:\n      feature_group: The feature group that needs to encode.\n      model_config: The model config (model_config_class.ModelConfig).\n      final_embedding_dim: Dimension of final context embedding.\n    \"\"\"\n    super(FeatureGroupEncoder, self).__init__()\n    self._feature_group = feature_group\n    self._model_config = model_config\n    self._final_embedding_dim = final_embedding_dim\n    # Prepare embedding layers.\n    self._embedding_layer_dict = {}\n    self._nonembedding_feature_names = []\n    for feature in self._feature_group.features:\n      if feature.feature_type in [\n          input_config_pb2.FeatureType.INT, input_config_pb2.FeatureType.STRING\n      ]:\n        assert feature.HasField('vocab_size')\n        assert feature.HasField('embedding_dim')\n        embedding_layer = tf.keras.layers.Embedding(\n            feature.vocab_size,\n            feature.embedding_dim,\n            embeddings_initializer=tf.keras.initializers.truncated_normal(\n                mean=0.0, stddev=1.0 / math.sqrt(feature.embedding_dim)),\n            mask_zero=True,\n            name=feature.feature_name+'embedding_layer')\n        self._embedding_layer_dict[feature.feature_name] = embedding_layer\n      else:\n        self._nonembedding_feature_names.append(feature.feature_name)\n  # Prepare CNN layers.\n    self._conv1d_layers = []\n    if self._feature_group.encoder_type == input_config_pb2.EncoderType.CNN:\n      assert self._model_config.conv_num_filter_ratios\n      assert self._model_config.conv_kernel_size\n      for ratio in self._model_config.conv_num_filter_ratios:\n        self._conv1d_layers.append(\n            tf.keras.layers.Conv1D(\n                filters=self._final_embedding_dim * ratio,\n                kernel_size=self._model_config.conv_kernel_size,\n                strides=self._model_config.conv_kernel_size,\n                padding='same',\n                activation='relu'))\n    # Prepare LSTM layer.\n    elif self._feature_group.encoder_type == input_config_pb2.EncoderType.LSTM:\n      assert self._model_config.lstm_num_units\n      self._lstm_layer = tf.keras.layers.LSTM(\n          self._model_config.lstm_num_units)\n\n  def call(self, input_context: Dict[str, tf.Tensor]) -> tf.Tensor:\n    \"\"\"Call function of the feature group encoder layer.\n\n    Takes in input context tensor dictionary, generates embedding for features\n    in the group, concatenate them up and encode them according to the\n    specified encoder type.\n\n    Feature embedding layers will have mask_zero enabled, and generated feature\n    embeddings will be masked if any of the feature value is 0. In most\n    cases, the mask of all features in the same group should be the same. This\n    function treats the input config generically.\n\n    Args:\n      input_context: A dictionary mapping input context feature name to tenors.\n\n    Returns:\n      Feature group encoding.\n    \"\"\"\n    embedding_feature_shape = []\n    embeddings = []\n    masks = []\n    for feature_name, embedding_layer in sorted(\n        self._embedding_layer_dict.items()):\n      input_feature = input_context[feature_name]\n      if isinstance(input_feature, tf.SparseTensor):\n        input_feature = tf.sparse.to_dense(input_feature)\n      if not embedding_feature_shape:\n        embedding_feature_shape = input_feature.shape.as_list()\n      assert input_feature.shape.as_list() == embedding_feature_shape\n\n      embeddings.append(embedding_layer(input_feature))\n      masks.append(embedding_layer.compute_mask(input_feature))\n    # Collect nonembedding feature values.\n    for feature_name in self._nonembedding_feature_names:\n      input_feature = input_context[feature_name]\n      if isinstance(input_feature, tf.SparseTensor):\n        input_feature = tf.sparse.to_dense(input_feature)\n      embeddings.append(tf.expand_dims(input_feature, -1))\n    embedding = tf.concat(embeddings, -1)\n\n    # Set mask_shape with batch dim = 1. Compatible to unknown batch size.\n    mask_shape = [1] + embedding_feature_shape[1:]\n    mask = tf.ones(shape=mask_shape)\n    for m in masks:\n      mask = mask * tf.cast(m, 'float32')\n    mask = tf.expand_dims(mask, -1)\n    embedding = embedding * mask\n\n    if self._feature_group.encoder_type == input_config_pb2.EncoderType.BOW:\n      weighted_summed_context_embedding = tf.keras.backend.sum(\n          embedding, axis=1)\n      total_weights = tf.ones_like(\n          weighted_summed_context_embedding) * tf.keras.backend.sum(\n              mask, axis=1)\n      embedding = safe_div(weighted_summed_context_embedding, total_weights)\n    elif self._feature_group.encoder_type == input_config_pb2.EncoderType.CNN:\n      for conv1d_layer in self._conv1d_layers:\n        embedding = conv1d_layer(embedding)\n      embedding = tf.math.reduce_max(embedding, axis=1)\n    elif self._feature_group.encoder_type == input_config_pb2.EncoderType.LSTM:\n      embedding = self._lstm_layer(embedding)\n    else:\n      raise ValueError('Unsupported encoder type %s.' %\n                       self._feature_group.encoder_type)\n\n    return embedding\n"
  },
  {
    "path": "lite/examples/recommendation/ml/model/context_encoder_test.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Tests for context_encoder.\"\"\"\nimport tensorflow as tf\n\nfrom configs import input_config_generated_pb2 as input_config_pb2\nfrom configs import model_config as model_config_class\nfrom model import context_encoder\n\n\nclass ContextEncoderTest(tf.test.TestCase):\n\n  def _create_test_feature_group(self,\n                                 encoder_type: input_config_pb2.EncoderType):\n    \"\"\"Prepare test feature group.\"\"\"\n    feature_context_movie_id = input_config_pb2.Feature(\n        feature_name='context_movie_id',\n        feature_type=input_config_pb2.FeatureType.INT,\n        vocab_size=3952,\n        embedding_dim=4)\n    feature_context_movie_rating = input_config_pb2.Feature(\n        feature_name='context_movie_rating',\n        feature_type=input_config_pb2.FeatureType.FLOAT)\n    return input_config_pb2.FeatureGroup(\n        features=[feature_context_movie_id, feature_context_movie_rating],\n        encoder_type=encoder_type)\n\n  def _create_test_input_config(self):\n    \"\"\"Generate test input_config_pb2.InputConfig proto.\"\"\"\n    feature_group_1 = self._create_test_feature_group(\n        encoder_type=input_config_pb2.EncoderType.BOW)\n\n    feature_context_movie_genre = input_config_pb2.Feature(\n        feature_name='context_movie_genre',\n        feature_type=input_config_pb2.FeatureType.STRING,\n        vocab_name='movie_genre_vocab.txt',\n        vocab_size=19,\n        embedding_dim=3)\n    feature_group_2 = input_config_pb2.FeatureGroup(\n        features=[feature_context_movie_genre],\n        encoder_type=input_config_pb2.EncoderType.BOW)\n\n    feature_label = input_config_pb2.Feature(\n        feature_name='label_movie_id',\n        feature_type=input_config_pb2.FeatureType.INT,\n        vocab_size=3952,\n        embedding_dim=4)\n\n    input_config = input_config_pb2.InputConfig(\n        activity_feature_groups=[feature_group_1, feature_group_2],\n        label_feature=feature_label)\n    return input_config\n\n  def _create_test_model_config(self):\n    return model_config_class.ModelConfig(\n        hidden_layer_dims=[8, 4],\n        eval_top_k=[1, 5],\n        conv_num_filter_ratios=[1, 2],\n        conv_kernel_size=2,\n        lstm_num_units=16)\n\n  def test_feature_group_encoder_bow(self):\n    feature_group = self._create_test_feature_group(\n        encoder_type=input_config_pb2.EncoderType.BOW)\n    model_config = self._create_test_model_config()\n    feature_group_encoder = context_encoder.FeatureGroupEncoder(\n        feature_group, model_config, final_embedding_dim=4)\n    input_context_movie_id = tf.constant([[1, 0, 0], [1, 2, 0]])\n    input_context_movie_rating = tf.constant([[1.0, 0.0, 0.0], [2.0, 3.0, 0.0]])\n    input_context = {\n        'context_movie_id': input_context_movie_id,\n        'context_movie_rating': input_context_movie_rating\n    }\n    feature_group_embedding = feature_group_encoder(input_context)\n    self.assertAllEqual([2, 5], list(feature_group_embedding.shape))\n\n  def test_feature_group_encoder_cnn(self):\n    feature_group = self._create_test_feature_group(\n        encoder_type=input_config_pb2.EncoderType.CNN)\n    model_config = self._create_test_model_config()\n    feature_group_encoder = context_encoder.FeatureGroupEncoder(\n        feature_group, model_config, final_embedding_dim=4)\n    input_context_movie_id = tf.constant([[1, 0, 0], [1, 2, 0]])\n    input_context_movie_rating = tf.constant([[1.0, 0.0, 0.0], [2.0, 3.0, 0.0]])\n    input_context = {\n        'context_movie_id': input_context_movie_id,\n        'context_movie_rating': input_context_movie_rating\n    }\n    feature_group_embedding = feature_group_encoder(input_context)\n    self.assertAllEqual([2, 8], list(feature_group_embedding.shape))\n\n  def test_feature_group_encoder_lstm(self):\n    feature_group = self._create_test_feature_group(\n        encoder_type=input_config_pb2.EncoderType.LSTM)\n    model_config = self._create_test_model_config()\n    feature_group_encoder = context_encoder.FeatureGroupEncoder(\n        feature_group, model_config, final_embedding_dim=4)\n    input_context_movie_id = tf.constant([[1, 0, 0], [1, 2, 0]])\n    input_context_movie_rating = tf.constant([[1.0, 0.0, 0.0], [2.0, 3.0, 0.0]])\n    input_context = {\n        'context_movie_id': input_context_movie_id,\n        'context_movie_rating': input_context_movie_rating\n    }\n    feature_group_embedding = feature_group_encoder(input_context)\n    self.assertAllEqual([2, 16], list(feature_group_embedding.shape))\n\n  def test_context_encoder(self):\n    input_config = self._create_test_input_config()\n    model_config = self._create_test_model_config()\n    input_context_encoder = context_encoder.ContextEncoder(\n        input_config=input_config, model_config=model_config)\n    input_context_movie_id = tf.constant([[1, 0, 0], [1, 2, 0]])\n    input_context_movie_rating = tf.constant([[1.0, 0.0, 0.0], [2.0, 3.0, 0.0]])\n    input_context_movie_genre = tf.constant([[1, 2, 2, 4, 3], [1, 1, 2, 2, 3]])\n    input_context = {\n        'context_movie_id': input_context_movie_id,\n        'context_movie_rating': input_context_movie_rating,\n        'context_movie_genre': input_context_movie_genre\n    }\n    context_embedding = input_context_encoder(input_context)\n    self.assertAllEqual([2, 4], list(context_embedding.shape))\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "lite/examples/recommendation/ml/model/dotproduct_similarity.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Dotproduct similarity layer.\"\"\"\nfrom typing import Optional\n\nimport tensorflow as tf\n\n\nclass DotProductSimilarity(tf.keras.layers.Layer):\n  \"\"\"Layer to comput dotproduct similarities for context/label embedding.\n\n    The top_k is an integer to represent top_k ids to compute among label ids.\n    if top_k is None, top_k computation will be ignored.\n  \"\"\"\n\n  def call(self,\n           context_embeddings: tf.Tensor,\n           label_embeddings: tf.Tensor,\n           top_k: Optional[int] = None):\n    \"\"\"Generate dotproduct similarity matrix and top values/indices.\n\n    Args:\n      context_embeddings: Context embeddings generated with input context\n        sequence. The shape of tensor should be [batch_size, embedding_dim] for\n        training mode, and [1, embedding_dim] for inference mode.\n      label_embeddings: Label embeddings generated for candidate label items.\n        The shape of tensor should be [num_items, embedding_dim].\n      top_k: The number of top values to compute. If it's not set, top values\n        and indices will not be computed.\n\n    Returns:\n      Dotproduct similarity matrix with shape [num_label_items, 1] and top\n      values/indices if top_k is set.\n    \"\"\"\n    if tf.keras.backend.ndim(label_embeddings) == 3:\n      label_embeddings = tf.squeeze(label_embeddings, 1)\n    dotproduct = tf.matmul(\n        context_embeddings, label_embeddings, transpose_b=True)\n    if top_k:\n      top_values, top_indices = tf.math.top_k(\n          tf.squeeze(dotproduct), top_k, sorted=True)\n      top_ids = tf.identity(top_indices, name='top_prediction_ids')\n      top_scores = tf.identity(\n          tf.math.sigmoid(top_values), name='top_prediction_scores')\n      return [dotproduct, top_ids, top_scores]\n    else:\n      return [dotproduct]\n"
  },
  {
    "path": "lite/examples/recommendation/ml/model/dotproduct_similarity_test.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Tests for dotproduct_similarity.\"\"\"\nimport tensorflow as tf\n\nfrom model import dotproduct_similarity\n\n\nclass DotproductSimilarityTest(tf.test.TestCase):\n\n  def test_dotproduct(self):\n    context_embeddings = tf.constant([[0.1, 0.1, 0.1, 0.1],\n                                      [0.2, 0.2, 0.2, 0.2]])\n    label_embeddings = tf.constant([[0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1]])\n    similarity_layer = dotproduct_similarity.DotProductSimilarity()\n    dotproduct = similarity_layer(context_embeddings, label_embeddings)\n    dotproduct, top_ids, top_scores = similarity_layer(context_embeddings,\n                                                       label_embeddings, 1)\n    self.assertAllClose(tf.constant([[0.04, 0.04], [0.08, 0.08]]), dotproduct)\n    self.assertAllEqual(tf.constant([[0], [0]]), top_ids)\n    self.assertAllEqual([2, 1], list(top_scores.shape))\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "lite/examples/recommendation/ml/model/input_pipeline.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Input pipeline for on-device recommendation model.\n\nProcess input TF example and prepare train/test datasets.\n\"\"\"\nimport collections\nimport functools\nimport os\nfrom typing import Dict, List, Tuple\n\nimport tensorflow as tf\nfrom configs import input_config_generated_pb2 as input_config_pb2\nfrom model import utils\n\n\nINT_DEFAULT_VALUE = 0\nSTRING_DEFAULT_VALUE = 'UNK'\nFLOAT_DEFAULT_VALUE = 0.0\n\n\nclass FeaturesAndVocabsByName(\n    collections.namedtuple(\n        'FeaturesAndVocabsByName', ['features_by_name', 'vocabs_by_name'])):\n  \"\"\"Holder for intermediate data in input processing pipeline.\"\"\"\n  __slots__ = ()\n\n  def __new__(cls, features_by_name=None, vocabs_by_name=None):\n    return super(FeaturesAndVocabsByName, cls).__new__(cls,\n                                                       features_by_name,\n                                                       vocabs_by_name)\n\n\ndef _prepare_feature_vocab_table(\n    feature: input_config_pb2.Feature,\n    vocab_file_dir: str) -> tf.lookup.StaticVocabularyTable:\n  \"\"\"Prepare vocabulary table for the feature if needed.\n\n  Prepare the vocabulary table with the specified vocab_name(vocab file name)\n  and vocab file directory. Feature type is required for the feature, as it\n  will be used to set up the vocabulary table.\n\n  Currently we assume the vocabulary file is a txt file and each line stores\n  single feature value.\n\n  Args:\n    feature: A feature config input_config_pb2.Feature proto.\n    vocab_file_dir: The directory storing vocabulary files.\n\n  Returns:\n    A vocabulary table (tf.lookup.StaticVocabularyTable) for the feature.\n  \"\"\"\n  assert feature.HasField('vocab_name')\n  assert feature.HasField('feature_type')\n  assert feature.feature_type in [input_config_pb2.FeatureType.INT,\n                                  input_config_pb2.FeatureType.STRING]\n  vocab_path = os.path.join(vocab_file_dir, feature.vocab_name)\n  num_oov_buckets = 1\n  key_type = tf.string if (\n      feature.feature_type == input_config_pb2.FeatureType.STRING) else tf.int64\n  return tf.lookup.StaticVocabularyTable(\n      tf.lookup.TextFileInitializer(\n          vocab_path,\n          key_dtype=key_type,\n          key_index=tf.lookup.TextFileIndex.WHOLE_LINE,\n          value_dtype=tf.int64,\n          value_index=tf.lookup.TextFileIndex.LINE_NUMBER,\n          delimiter='\\t'), num_oov_buckets)\n\n\ndef _get_features_vocabs_for_groups(\n    feature_groups: List[input_config_pb2.FeatureGroup],\n    vocab_file_dir: str = '') -> FeaturesAndVocabsByName:\n  \"\"\"Get feature and vocabulary dictionaries for feature groups.\n\n  Args:\n    feature_groups: A list of feature group configs(\n      input_config_pb2.FeatureGroup)s.\n    vocab_file_dir: The directory storing vocabulary files.\n\n  Returns:\n    A FeaturesAndVocabsByName object containing features and vocabs\n    dictionaries keyed feature name for features according to the feature\n    group list.\n  \"\"\"\n  features_by_name = {}\n  vocabs_by_name = {}\n  for feature_group in feature_groups:\n    for feature in feature_group.features:\n      features_by_name[feature.feature_name] = feature\n      if vocab_file_dir and feature.HasField('vocab_name'):\n        vocabs_by_name[feature.feature_name] = _prepare_feature_vocab_table(\n            feature, vocab_file_dir)\n  return FeaturesAndVocabsByName(\n      features_by_name=features_by_name, vocabs_by_name=vocabs_by_name)\n\n\ndef get_features_and_vocabs_by_name(\n    input_config: input_config_pb2.InputConfig,\n    vocab_file_dir: str = '') -> FeaturesAndVocabsByName:\n  \"\"\"Get feature and vocabulary dictionaries according to input config.\n\n  Args:\n    input_config: The input config input_config_pb2.InputConfig proto.\n    vocab_file_dir: The directory storing vocabulary files.\n\n  Returns:\n    A FeaturesAndVocabsByName object containing features and vocabs\n    dictionaries keyed feature name for all features according to input\n    config.\n  \"\"\"\n  global_features_and_vocabs_by_name = (\n      _get_features_vocabs_for_groups(list(input_config.global_feature_groups),\n                                      vocab_file_dir))\n  activity_features_and_vocabs_by_name = (\n      _get_features_vocabs_for_groups(\n          list(input_config.activity_feature_groups), vocab_file_dir))\n  features_by_name = {\n      **global_features_and_vocabs_by_name.features_by_name,\n      **activity_features_and_vocabs_by_name.features_by_name\n  }\n  vocabs_by_name = {\n      **global_features_and_vocabs_by_name.vocabs_by_name,\n      **activity_features_and_vocabs_by_name.vocabs_by_name\n  }\n  if input_config.label_feature.HasField('vocab_name'):\n    vocabs_by_name[\n        input_config.label_feature.feature_name] = _prepare_feature_vocab_table(\n            input_config.label_feature, vocab_file_dir)\n  return FeaturesAndVocabsByName(\n      features_by_name=features_by_name, vocabs_by_name=vocabs_by_name)\n\n\ndef _get_feature_spec(feature_type: input_config_pb2.FeatureType,\n                      feature_length: int) -> tf.io.FixedLenFeature:\n  \"\"\"Get feature spec based on feature type and length.\n\n  Args:\n    feature_type: The type of the feature (input_config_pb2.FeatureType).\n    feature_length: The length of the feature. The feature is expected to be\n      a sequence.\n\n  Returns:\n    The feature spec to parse serialized examples.\n\n  Raises:\n    An error occurring if the feature type is not supported.\n  \"\"\"\n  if feature_type == input_config_pb2.FeatureType.INT:\n    dtype = tf.int64\n    default_value = INT_DEFAULT_VALUE\n  elif feature_type == input_config_pb2.FeatureType.STRING:\n    dtype = tf.string\n    default_value = STRING_DEFAULT_VALUE\n  elif feature_type == input_config_pb2.FeatureType.FLOAT:\n    dtype = tf.float32\n    default_value = FLOAT_DEFAULT_VALUE\n  else:\n    raise ValueError('Unsupported feature type {}'.format(feature_type))\n\n  return tf.io.FixedLenFeature(\n      shape=[feature_length],\n      dtype=dtype,\n      default_value=[default_value] * feature_length)\n\n\ndef _get_serving_feature_spec(feature_name: str,\n                              feature_type: input_config_pb2.FeatureType,\n                              feature_length: int) -> tf.TensorSpec:\n  if feature_type == input_config_pb2.FeatureType.INT or feature_type == input_config_pb2.FeatureType.STRING:\n    dtype = tf.dtypes.int32\n  elif feature_type == input_config_pb2.FeatureType.FLOAT:\n    dtype = tf.dtypes.float32\n  else:\n    raise ValueError('Unsupported feature type {}'.format(feature_type))\n  return tf.TensorSpec(shape=[feature_length], dtype=dtype, name=feature_name)\n\n\ndef get_serving_input_specs(\n    input_config: input_config_pb2.InputConfig) -> Dict[str, tf.TensorSpec]:\n  features_by_name = get_features_and_vocabs_by_name(\n      input_config).features_by_name\n  input_specs = collections.OrderedDict()\n  for feature_name, feature in sorted(features_by_name.items()):\n    input_specs[feature_name] = _get_serving_feature_spec(\n        feature_name, feature.feature_type, feature.feature_length)\n  return input_specs\n\n\ndef decode_example(\n    serialized_proto: str, features_and_vocabs_by_name: FeaturesAndVocabsByName,\n    label_feature_name: str) -> Tuple[Dict[str, tf.Tensor], tf.Tensor]:\n  \"\"\"Decode single serialized example.\n\n  Decode single serialized example, accoring to specified features in input\n  config. Perform vocabulary lookup if vocabulary is specified.\n\n  Args:\n    serialized_proto: The serialized proto that needs to be decoded.\n    features_and_vocabs_by_name: A FeaturesAndVocabsByName object containing\n      features and vocabs dictionaries by feature names.\n    label_feature_name: Name of the label feature.\n\n  Returns:\n    features: The decoded features dictionary.\n    label_feature: The label feature.\n\n  \"\"\"\n  features_by_name = features_and_vocabs_by_name.features_by_name\n  vocabs_by_name = features_and_vocabs_by_name.vocabs_by_name\n  name_to_features = {}\n  name_to_features[label_feature_name] = tf.io.FixedLenFeature([1], tf.int64)\n  for feature_name, feature in features_by_name.items():\n    name_to_features[feature_name] = _get_feature_spec(\n        feature_type=feature.feature_type,\n        feature_length=feature.feature_length)\n  record_features = tf.io.parse_single_example(serialized_proto,\n                                               name_to_features)\n\n  features = {}\n  for feature_name, feature_value in record_features.items():\n    if feature_name in vocabs_by_name:\n      features[feature_name] = vocabs_by_name[feature_name].lookup(\n          feature_value)\n    else:\n      features[feature_name] = feature_value\n  features = {\n      k: v if v.dtype != tf.int64 else tf.cast(v, tf.int32)\n      for k, v in features.items()\n  }\n  label_feature = features[label_feature_name]\n  return features, label_feature\n\n\ndef get_input_dataset(data_filepattern: str,\n                      input_config: input_config_pb2.InputConfig,\n                      vocab_file_dir: str,\n                      batch_size: int) -> tf.data.Dataset:\n  \"\"\"An input_fn to create input datasets.\n\n  Args:\n    data_filepattern: The file pattern of the input data.\n    input_config: The input config input_config_pb2.InputConfig proto.\n    vocab_file_dir: The path to the directory storing the vocabulary files.\n    batch_size: Batch size of to-be generated dataset.\n\n  Returns:\n    A Dataset where each element is a batch of feature dicts.\n  \"\"\"\n  features_and_vocabs_by_name = get_features_and_vocabs_by_name(\n      input_config, vocab_file_dir)\n  if not input_config.HasField('label_feature'):\n    raise ValueError('Field label_feature is required.')\n  input_files = utils.GetShardFilenames(data_filepattern)\n  d = tf.data.TFRecordDataset(input_files)\n  d = d.shuffle(len(input_files))\n  d = d.repeat()\n  d = d.shuffle(buffer_size=10000)\n  d = d.map(\n      functools.partial(\n          decode_example,\n          features_and_vocabs_by_name=features_and_vocabs_by_name,\n          label_feature_name=input_config.label_feature.feature_name),\n      num_parallel_calls=8)\n  d = d.batch(batch_size, drop_remainder=True)\n  d = d.prefetch(1)\n  return d\n"
  },
  {
    "path": "lite/examples/recommendation/ml/model/input_pipeline_test.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Tests for input_pipeline.\"\"\"\nimport collections\nimport os\n\nimport tensorflow as tf\n\nfrom configs import input_config_generated_pb2 as input_config_pb2\nfrom model import input_pipeline\n\nfrom google.protobuf import text_format\n\nFAKE_MOVIE_GENRE_VOCAB = [\n    'UNK', 'Comedy', 'Drama', 'Romance', 'Animation', 'Children'\n]\n\nTEST_INPUT_CONFIG = text_format.Parse(\n    \"\"\"\n    activity_feature_groups {\n      features {\n        feature_name: \"context_movie_id\"\n        feature_type: INT\n        vocab_size: 3952\n        embedding_dim: 32\n        feature_length: 5\n      }\n      features {\n        feature_name: \"context_movie_rating\"\n        feature_type: FLOAT\n        feature_length: 5\n      }\n      encoder_type: BOW\n    }\n    activity_feature_groups {\n      features {\n        feature_name: \"context_movie_genre\"\n        feature_type: STRING\n        vocab_name: \"movie_genre_vocab.txt\"\n        vocab_size: 19\n        embedding_dim: 8\n        feature_length: 8\n      }\n      encoder_type: BOW\n    }\n    label_feature {\n      feature_name: \"label_movie_id\"\n      feature_type: INT\n      vocab_size: 3952\n      embedding_dim: 8\n      feature_length: 1\n    }\n    \"\"\", input_config_pb2.InputConfig())\n\nEXAMPLE1 = text_format.Parse(\n    \"\"\"\n    features {\n        feature {\n          key: \"context_movie_id\"\n          value {\n            int64_list {\n              value: [1, 2, 0, 0, 0]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_rating\"\n          value {\n            float_list {\n              value: [3.5, 4.0, 0.0, 0.0, 0.0]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_genre\"\n          value {\n            bytes_list {\n              value: [\n                    \"Animation\", \"Children\", \"Comedy\", \"Comedy\", \"Romance\", \"UNK\", \"UNK\", \"UNK\"\n                ]\n            }\n          }\n        }\n        feature {\n          key: \"label_movie_id\"\n          value {\n            int64_list {\n              value: [3]\n            }\n          }\n        }\n      }\"\"\", tf.train.Example())\n\n\nclass InputPipelineTest(tf.test.TestCase):\n\n  def _AssertSparseTensorValueEqual(self, a, b):\n    self.assertAllEqual(a.indices, b.indices)\n    self.assertAllEqual(a.values, b.values)\n    self.assertAllEqual(a.dense_shape, b.dense_shape)\n\n  def setUp(self):\n    super(InputPipelineTest, self).setUp()\n    self.tmp_dir = self.create_tempdir()\n    self.test_movie_genre_vocab_file = os.path.join(self.tmp_dir,\n                                                    'movie_genre_vocab.txt')\n    self.test_input_data_file = os.path.join(self.tmp_dir,\n                                             'test_input_data.tfrecord')\n    with open(self.test_movie_genre_vocab_file, 'w', encoding='utf-8') as f:\n      for item in FAKE_MOVIE_GENRE_VOCAB:\n        f.write(item + '\\n')\n    with tf.io.TFRecordWriter(self.test_input_data_file) as file_writer:\n      file_writer.write(EXAMPLE1.SerializeToString())\n\n  def test_features_vocabs_gen(self):\n    features_and_vocabs_by_name = (\n        input_pipeline.get_features_and_vocabs_by_name(TEST_INPUT_CONFIG,\n                                                       self.tmp_dir))\n    features_by_name = features_and_vocabs_by_name.features_by_name\n    vocabs_by_name = features_and_vocabs_by_name.vocabs_by_name\n    self.assertLen(features_by_name.keys(), 3)\n    self.assertLen(vocabs_by_name.keys(), 1)\n    self.assertAllInSet(\n        ['context_movie_id', 'context_movie_rating', 'context_movie_genre'],\n        features_by_name.keys())\n    self.assertAllInSet(['context_movie_genre'], vocabs_by_name.keys())\n\n  def test_decode_example(self):\n    features_and_vocabs_by_name = (\n        input_pipeline.get_features_and_vocabs_by_name(TEST_INPUT_CONFIG,\n                                                       self.tmp_dir))\n    features, label_feature = input_pipeline.decode_example(\n        EXAMPLE1.SerializeToString(),\n        features_and_vocabs_by_name,\n        'label_movie_id')\n    expected_context_movie_id = tf.constant([1, 2, 0, 0, 0],\n                                            dtype=tf.int32)\n    expected_context_movie_genre = tf.constant([4, 5, 1, 1, 3, 0, 0, 0],\n                                               dtype=tf.int32)\n    expected_context_movie_rating = tf.constant([3.5, 4.0, 0.0, 0.0, 0.0],\n                                                dtype=tf.float32)\n    self.assertAllEqual(features['context_movie_id'],\n                        expected_context_movie_id)\n    self.assertAllEqual(features['context_movie_genre'],\n                        expected_context_movie_genre)\n    self.assertAllEqual(features['context_movie_rating'],\n                        expected_context_movie_rating)\n    self.assertAllEqual(label_feature, tf.constant([3], dtype=tf.int32))\n\n  def test_get_input_dataset(self):\n    dataset = input_pipeline.get_input_dataset(\n        data_filepattern=self.test_input_data_file,\n        input_config=TEST_INPUT_CONFIG,\n        vocab_file_dir=self.tmp_dir,\n        batch_size=1)\n    dataset = dataset.take(1)\n    self.assertCountEqual([\n        'context_movie_id', 'context_movie_rating', 'context_movie_genre',\n        'label_movie_id'\n    ], dataset.element_spec[0].keys())\n\n  def test_get_serving_input_specs(self):\n    input_specs = input_pipeline.get_serving_input_specs(TEST_INPUT_CONFIG)\n    expected_input_specs = collections.OrderedDict()\n    expected_input_specs['context_movie_genre'] = tf.TensorSpec(\n        shape=[8], dtype=tf.dtypes.int32, name='context_movie_genre')\n    expected_input_specs['context_movie_id'] = tf.TensorSpec(\n        shape=[5], dtype=tf.dtypes.int32, name='context_movie_id')\n    expected_input_specs['context_movie_rating'] = tf.TensorSpec(\n        shape=[5], dtype=tf.dtypes.float32, name='context_movie_rating')\n    self.assertAllEqual(input_specs, expected_input_specs)\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "lite/examples/recommendation/ml/model/label_encoder.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Label encoder layer for on-device personalized recommendation model.\"\"\"\nimport math\nfrom typing import Dict\n\nimport tensorflow as tf\n\nfrom configs import input_config_generated_pb2 as input_config_pb2\n\n\nclass LabelEncoder(tf.keras.layers.Layer):\n  \"\"\"Layer to encode label feature.\n\n  Currently only id-based label encoder is supported. With this encoder, label\n  embedding layer will be created based on input config. And label embedding\n  will be generated by feeding input label feature to label embedding layer.\n  \"\"\"\n\n  def __init__(self,\n               input_config: input_config_pb2.InputConfig):\n    \"\"\"Initialize LabelEncoder layer based on input config.\n\n    Args:\n      input_config: The input config containing label feature configuation,\n        based on which label encoding could be generated.\n    \"\"\"\n    super(LabelEncoder, self).__init__()\n    self._input_config = input_config\n    self._label_feature = self._input_config.label_feature\n    if self._label_feature.feature_type != input_config_pb2.FeatureType.INT:\n      raise ValueError('Currently only INT type is supported for label.')\n    if not self.label_name:\n      raise ValueError('label_feature name is expected.')\n    self._label_embedding_layer = tf.keras.layers.Embedding(\n        self._label_feature.vocab_size,\n        self._label_feature.embedding_dim,\n        embeddings_initializer=tf.keras.initializers.truncated_normal(\n            mean=0.0,\n            stddev=1.0 / math.sqrt(self._label_feature.embedding_dim)),\n        mask_zero=True,\n        name=self.label_name + 'embedding_layer')\n\n  @property\n  def label_name(self):\n    return self._label_feature.feature_name\n\n  def encode(self, input_label: tf.Tensor) -> tf.Tensor:\n    \"\"\"Generates label encoding with input label tensor.\n\n    Args:\n      input_label: Input label tensor, the shape of tensor should\n        [num_label_items, 1].\n\n    Returns:\n      Label encoding.\n    \"\"\"\n    if isinstance(input_label, tf.SparseTensor):\n      input_label = tf.sparse.to_dense(input_label)\n    label_embedding = self._label_embedding_layer(input_label)\n    if tf.keras.backend.ndim(label_embedding) == 3:\n      label_embedding = tf.squeeze(label_embedding)\n    return label_embedding\n\n  def call(self, inputs: Dict[str, tf.Tensor]):\n    \"\"\"Generate label encoding with input tentor dictionary.\n\n    Args:\n      inputs: Dictionary mapping feature name to input tensors.\n\n    Returns:\n      Label encoding.\n    \"\"\"\n    input_label = inputs[self.label_name]\n    return self.encode(input_label)\n"
  },
  {
    "path": "lite/examples/recommendation/ml/model/label_encoder_test.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Tests for label_encoder.\"\"\"\nimport tensorflow as tf\n\nfrom configs import input_config_generated_pb2 as input_config_pb2\nfrom model import label_encoder\n\n\nclass LabelEncoderTest(tf.test.TestCase):\n\n  def _create_test_input_config(self):\n    \"\"\"Generate test input_config_pb2.InputConfig proto.\"\"\"\n    feature_context_movie_id = input_config_pb2.Feature(\n        feature_name='context_movie_id',\n        feature_type=input_config_pb2.FeatureType.INT,\n        vocab_size=3952,\n        embedding_dim=4)\n    feature_context_movie_rating = input_config_pb2.Feature(\n        feature_name='context_movie_rating',\n        feature_type=input_config_pb2.FeatureType.FLOAT)\n    feature_group_1 = input_config_pb2.FeatureGroup(\n        features=[feature_context_movie_id, feature_context_movie_rating],\n        encoder_type=input_config_pb2.EncoderType.BOW)\n\n    feature_label = input_config_pb2.Feature(\n        feature_name='label_movie_id',\n        feature_type=input_config_pb2.FeatureType.INT,\n        vocab_size=3952,\n        embedding_dim=4)\n\n    input_config = input_config_pb2.InputConfig(\n        activity_feature_groups=[feature_group_1],\n        label_feature=feature_label)\n    return input_config\n\n  def test_label_encoder(self):\n    input_config = self._create_test_input_config()\n    input_label_encoder = label_encoder.LabelEncoder(\n        input_config=input_config)\n    input_label_movie_id = tf.constant([[1], [2]])\n    inputs = {\n        'label_movie_id': input_label_movie_id\n    }\n    label_embedding = input_label_encoder(inputs)\n    print(label_embedding.shape)\n    self.assertAllEqual([2, 4], list(label_embedding.shape))\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "lite/examples/recommendation/ml/model/losses.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Customized losses.\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow as tf\n\n\nclass BatchSoftmax(tf.keras.losses.Loss):\n  \"\"\"Compute batch softmax over batch similarities.\n\n  This softmax loss takes in-batch negatives without considering\n  negatives out of batch.\n  \"\"\"\n\n  def __init__(self, name='batch_softmax', **kwargs):\n    super(BatchSoftmax, self).__init__(name=name, **kwargs)\n\n  @tf.function\n  def call(self, y_true: tf.Tensor, y_pred: tf.Tensor):\n    \"\"\"Compute in batch softmax loss.\n\n    Args:\n      y_true: the true labels with shape [batch_size, 1].\n      y_pred: pre-calculated similarities matrix between context embedding and\n        full vocab label embeddings, y_pred is expected to be the similarities\n        with shape [batch_size,label_embedding_vocab_size].\n\n    Returns:\n      The softmax loss with in-batch negatives.\n    \"\"\"\n    logits = tf.keras.backend.cast(y_pred, 'float32')\n    logits = tf.gather(y_pred, tf.transpose(y_true)[0], axis=1)\n    full_labels = tf.eye(\n        tf.shape(logits)[0], tf.shape(logits)[1], dtype=tf.dtypes.float32)\n    batch_loss = tf.nn.softmax_cross_entropy_with_logits(\n        labels=full_labels, logits=logits)\n    loss = tf.reduce_mean(batch_loss)\n    return loss\n\n\nclass GlobalSoftmax(tf.keras.losses.Loss):\n  \"\"\"Compute softmax over similarities.\n\n  This loss fuction computes softmax over similarities between context and\n  full vocab label embeddings, considering full label vocab non-label\n  predictions as negatives. This is currently the default loss in the model.\n  \"\"\"\n\n  def __init__(self, name='global_softmax', **kwargs):\n    super(GlobalSoftmax, self).__init__(name=name, **kwargs)\n\n  @tf.function\n  def call(self, y_true: tf.Tensor, y_pred: tf.Tensor):\n    \"\"\"Compute softmax loss with full vocab labels as negatives.\n\n    Args:\n      y_true: the true labels with shape [batch_size, 1].\n      y_pred: the pre-calculated similarities matrix with shape [batch_size,\n        label_embedding_vocab_size]\n\n    Returns:\n      The softmax loss with full vocab labels as negatives.\n    \"\"\"\n    logits = tf.keras.backend.cast(y_pred, 'float32')\n    # Compose a metric to tell which column represents the similarity between\n    # the example and the label. For each row, only the item at index of the\n    # label of that example will be set as 1.\n    full_labels = tf.one_hot(\n        tf.transpose(y_true)[0], tf.shape(logits)[1], dtype=tf.dtypes.float32)\n    batch_loss = tf.nn.softmax_cross_entropy_with_logits(\n        labels=full_labels, logits=logits)\n    loss = tf.reduce_mean(batch_loss)\n    return loss\n"
  },
  {
    "path": "lite/examples/recommendation/ml/model/losses_test.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Tests for the keras losses.\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow as tf\nfrom model import losses\n\n\nclass KerasLossesTest(tf.test.TestCase):\n\n  def test_batch_softmax_loss(self):\n    batch_softmax = losses.BatchSoftmax()\n    true_label = tf.constant([[2], [0], [1]], dtype=tf.int32)\n    logits = tf.constant([\n        [0.8, 0.1, 0.2, 0.3],\n        [0.2, 0.7, 0.1, 0.5],\n        [0.5, 0.4, 0.9, 0.2]\n    ], dtype=tf.float32)\n    self.assertBetween(\n        batch_softmax.call(y_true=true_label, y_pred=logits).numpy(),\n        1.3, 1.4)\n\n  def test_global_softmax_loss(self):\n    global_softmax = losses.GlobalSoftmax()\n    true_label = tf.constant([[2], [0], [1]], dtype=tf.int32)\n    logits = tf.constant([\n        [0.8, 0.1, 0.2, 0.3],\n        [0.2, 0.7, 0.1, 0.5],\n        [0.5, 0.4, 0.9, 0.2]\n    ], dtype=tf.float32)\n    self.assertBetween(\n        global_softmax.call(y_true=true_label, y_pred=logits).numpy(), 1.5, 1.6)\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "lite/examples/recommendation/ml/model/metrics.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Customized metrics.\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow as tf\n\n\ndef _get_batch_similarities(batch_label, full_vocab_similarities):\n  return tf.gather(\n      full_vocab_similarities, tf.transpose(batch_label)[0], axis=1)\n\n\nclass BatchRecall(tf.keras.metrics.Recall):\n  \"\"\"Compute batch recall for top_k.\"\"\"\n\n  def __init__(self, top_k=1, name='batch_recall'):\n    super().__init__(name=name, top_k=top_k)\n\n  def update_state(self, y_true, y_pred, sample_weight=None):\n    \"\"\"Update state of the metric.\n\n    Args:\n      y_true: the true labels with shape [batch_size, 1].\n      y_pred: model output, which is the similarity matrix with shape\n        [batch_size, label_embedding_vocab_size] between context and full vocab\n        label embeddings.\n      sample_weight: Optional weighting of each example. Defaults to 1.\n    \"\"\"\n    label_indices = tf.eye(\n        tf.shape(y_pred)[0], tf.shape(y_pred)[0], dtype=tf.dtypes.float32)\n    # Since tf.keras default recall metric only accept predicted values in\n    # range [0, 1], so use tf.nn.softmax to preprocess.\n    normalized_logits = tf.nn.softmax(_get_batch_similarities(y_true, y_pred))\n    super().update_state(label_indices, normalized_logits, sample_weight)\n\n\nclass GlobalRecall(tf.keras.metrics.Recall):\n  \"\"\"Compute global recall for top_k.\"\"\"\n\n  def __init__(self, top_k=1, name='global_recall'):\n    super().__init__(name=name, top_k=top_k)\n\n  def update_state(self, y_true, y_pred, sample_weight=None):\n    \"\"\"Update state of the metric.\n\n    Args:\n      y_true: the true labels with shape [batch_size, 1].\n      y_pred: model output, which is the similarity matrix with shape\n        [batch_size, label_embedding_vocab_size] between context and full vocab\n        label embeddings.\n      sample_weight: Optional weighting of each example. Defaults to 1.\n    \"\"\"\n    label_indices = tf.one_hot(\n        tf.transpose(y_true)[0], tf.shape(y_pred)[1], dtype=tf.dtypes.float32)\n    # Since tf.keras default recall metric only accept predicted values in\n    # range [0, 1], so use tf.nn.softmax to preprocess.\n    normalized_logits = tf.nn.softmax(y_pred)\n    super().update_state(label_indices, normalized_logits, sample_weight)\n\n\nclass BatchMeanRank(tf.keras.metrics.Mean):\n  \"\"\"Keras metric computing mean rank of correct label within batch.\"\"\"\n\n  def __init__(self, name='batch_mean_rank', **kwargs):\n    super().__init__(name=name, **kwargs)\n\n  def update_state(self, y_true, y_pred, sample_weight=None):  # pytype: disable=signature-mismatch  # overriding-parameter-count-checks\n    \"\"\"Update state of the metric.\n\n    Args:\n      y_true: the true labels with shape [batch_size, 1].\n      y_pred: model output, which is the similarity matrix with shape\n        [batch_size, label_embedding_vocab_size] between context and full vocab\n        label embeddings. Hence to compute batch mean rank, the default global\n        similarities will be coverted to batch similarities first with shape\n        [batch_size, batch_size].\n      sample_weight: Optional weighting of each example. Defaults to 1.\n    \"\"\"\n    similarities = _get_batch_similarities(y_true, y_pred)\n    # Get the ranks of the correct label for each context.\n    batch_size = tf.shape(similarities)[0]\n    label_indices = tf.expand_dims(tf.range(batch_size), -1)\n\n    # Get the indices of similar labels in sorted order for each context.\n    sorted_similarities = tf.argsort(\n        similarities, axis=-1, direction='DESCENDING')\n\n    ranks = tf.where(tf.equal(sorted_similarities, label_indices))[:, -1]\n    ranks = tf.keras.backend.cast(ranks, 'float32')\n    super().update_state(ranks, sample_weight=sample_weight)\n\n\nclass GlobalMeanRank(tf.keras.metrics.Mean):\n  \"\"\"Keras metric computing mean rank of correct label globally.\"\"\"\n\n  def __init__(self, name='global_mean_rank', **kwargs):\n    super().__init__(name=name, **kwargs)\n\n  def update_state(self, y_true, y_pred, sample_weight=None):  # pytype: disable=signature-mismatch  # overriding-parameter-count-checks\n    \"\"\"Update state of the metric.\n\n    Args:\n      y_true: the true labels with shape [batch_size, 1].\n      y_pred: model output, which is the similarity matrix with shape\n        [batch_size, label_embedding_vocab_size] between context and full vocab\n        label embeddings.\n      sample_weight: Optional weighting of each example. Defaults to 1.\n    \"\"\"\n    similarities = y_pred\n    label_indices = tf.expand_dims(tf.cast(y_true, dtype=tf.int32), -1)\n\n    # Get the indices of similar labels in sorted order for each context.\n    sorted_similarities = tf.argsort(\n        similarities, axis=-1, direction='DESCENDING')\n\n    ranks = tf.where(tf.equal(sorted_similarities, label_indices))[:, -1]\n    ranks = tf.keras.backend.cast(ranks, 'float32')\n    super().update_state(ranks, sample_weight=sample_weight)\n"
  },
  {
    "path": "lite/examples/recommendation/ml/model/metrics_test.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Tests for the keras_metrics.\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow as tf\nfrom model import metrics\n\n\nclass KerasMetricsTest(tf.test.TestCase):\n\n  def test_batch_recall_and_mean_rank(self):\n    batch_recall = metrics.BatchRecall(top_k=2)\n    batch_mean_rank = metrics.BatchMeanRank()\n    true_label = tf.constant([[2], [0], [1]], dtype=tf.int32)\n    logits = tf.constant([\n        [0.8, 0.1, 1.1, 0.3],\n        [0.2, 0.7, 0.1, 0.5],\n        [0.7, 0.4, 0.9, 0.2]\n    ], dtype=tf.float32)\n    batch_recall.update_state(y_true=true_label, y_pred=logits)\n    batch_mean_rank.update_state(y_true=true_label, y_pred=logits)\n    self.assertBetween(batch_recall.result().numpy(), 0.6, 0.7)\n    self.assertEqual(batch_mean_rank.result().numpy(), 1.0)\n\n  def test_global_recall_and_mean_rank(self):\n    global_recall = metrics.GlobalRecall(top_k=2)\n    global_mean_rank = metrics.GlobalMeanRank()\n    true_label = tf.constant([[2], [0], [1]], dtype=tf.int32)\n    logits = tf.constant([\n        [0.8, 0.1, 1.1, 0.3],\n        [0.2, 0.7, 0.1, 0.5],\n        [0.7, 0.4, 0.9, 0.2]\n    ], dtype=tf.float32)\n    global_recall.update_state(y_true=true_label, y_pred=logits)\n    global_mean_rank.update_state(y_true=true_label, y_pred=logits)\n    self.assertBetween(global_recall.result().numpy(), 0.3, 0.4)\n    self.assertBetween(global_mean_rank.result().numpy(), 1.3, 1.4)\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "lite/examples/recommendation/ml/model/recommendation_model.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"On-device personalized recommendation model.\"\"\"\nimport tensorflow as tf\n\nfrom configs import input_config_generated_pb2 as input_config_pb2\nfrom configs import model_config as model_config_class\nfrom model import context_encoder\nfrom model import dotproduct_similarity\nfrom model import label_encoder\n\n\nclass RecommendationModel(tf.keras.Model):\n  \"\"\"Personalized dual-encoder style recommendation model.\"\"\"\n\n  def __init__(self,\n               input_config: input_config_pb2.InputConfig,\n               model_config: model_config_class.ModelConfig):\n    \"\"\"Initializes RecommendationModel according to input and model configs.\n\n    Takes in input and model configs to initialize the recommendation model.\n    Context encoder layer, label encoder layer and dotproduct similarity layer\n    will be prepared.\n\n    Args:\n      input_config: The input config (input_config_pb2.InputConfig).\n      model_config: The model config (model_config_class.ModelConfig).\n    \"\"\"\n    super(RecommendationModel, self).__init__()\n    self._input_config = input_config\n    self._model_config = model_config\n    self._context_encoder = context_encoder.ContextEncoder(\n        input_config=self._input_config, model_config=self._model_config)\n    self._label_encoder = label_encoder.LabelEncoder(\n        input_config=self._input_config)\n    self._dotproduct_layer = dotproduct_similarity.DotProductSimilarity()\n\n  def call(self, inputs):  # pytype: disable=signature-mismatch  # overriding-parameter-count-checks\n    \"\"\"Compute outputs by passing inputs through the model.\n\n    Computes the dotproduct similarity between the context embeddings with\n    label embeddings of all items in the vocabulary, hence the output could be\n    used to compute loss with all non-label items as negatives.\n\n    Args:\n      inputs: The inputs to the model, which should be a dictionary having\n        feature names as keys and parsed inputs generated with input pipeline.\n\n    Returns:\n      Dotproduct similarity for training mode.\n    \"\"\"\n    context_embeddings = self._context_encoder(inputs)\n    # Compute the similarities between the context embedding and embeddings of\n    # all items in the vocabulary.\n    full_vocab_input_label = tf.range(\n        self._input_config.label_feature.vocab_size)\n    label = {self._label_encoder.label_name: full_vocab_input_label}\n    full_vocab_label_embeddings = self._label_encoder(label)\n    full_vocab_dotproduct = self._dotproduct_layer(\n        context_embeddings=context_embeddings,\n        label_embeddings=full_vocab_label_embeddings,\n        top_k=None)[0]\n    return full_vocab_dotproduct\n\n  @tf.function\n  def serve(self, **kwargs):\n    inputs = kwargs\n    context_embeddings = self._context_encoder(inputs)\n    full_vocab_input_label = tf.range(\n        self._input_config.label_feature.vocab_size)\n    full_vocab_label_embeddings = self._label_encoder.encode(\n        full_vocab_input_label)\n    assert self._model_config.num_predictions\n    (_, top_ids, top_scores) = self._dotproduct_layer(\n        context_embeddings=context_embeddings,\n        label_embeddings=full_vocab_label_embeddings,\n        top_k=self._model_config.num_predictions)\n    return {'top_prediction_ids': top_ids, 'top_prediction_scores': top_scores}\n"
  },
  {
    "path": "lite/examples/recommendation/ml/model/recommendation_model_launcher.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Personalized recommendation model runner based on Tensorflow keras API.\"\"\"\nimport os\nimport time\nfrom typing import List\n\nfrom absl import app\nfrom absl import flags\nimport tensorflow as tf\nfrom configs import input_config_generated_pb2 as input_config_pb2\nfrom configs import model_config as model_config_class\nfrom model import input_pipeline\nfrom model import losses\nfrom model import metrics\nfrom model import recommendation_model\n\nfrom google.protobuf import text_format\n\n\nFLAGS = flags.FLAGS\n\n\ndef define_flags():\n  \"\"\"Define flags.\"\"\"\n  flags.DEFINE_string('training_data_filepattern', None,\n                      'File pattern of the training data.')\n  flags.DEFINE_string('testing_data_filepattern', None,\n                      'File pattern of the training data.')\n  flags.DEFINE_string('model_dir', None, 'Directory to store checkpoints.')\n  flags.DEFINE_string('export_dir', None, 'Directory for the exported model.')\n  flags.DEFINE_integer('batch_size', 1, 'Training batch size.')\n  flags.DEFINE_float('learning_rate', 0.1, 'Learning rate.')\n  flags.DEFINE_integer('steps_per_epoch', 10,\n                       'Number of steps to run in each epoch.')\n  flags.DEFINE_integer('num_epochs', 10000, 'Number of training epochs.')\n  flags.DEFINE_integer('num_eval_steps', 1000, 'Number of eval steps.')\n  flags.DEFINE_enum('run_mode', 'train_and_eval',\n                    ['train_and_eval', 'export', 'export_tflite'],\n                    'Mode of the launcher, default value is: train_and_eval')\n  flags.DEFINE_float('gradient_clip_norm', 1.0,\n                     'gradient_clip_norm <= 0 meaning no clip.')\n  flags.DEFINE_string('vocab_dir', None,\n                      'Path of the directory storing vocabulary files.')\n  flags.DEFINE_string('input_config_file', None,\n                      'Path to the input config pbtxt'\n                      'file.')\n  flags.DEFINE_list('hidden_layer_dims', None, 'Hidden layer dimensions.')\n  flags.DEFINE_list('eval_top_k', None, 'Top k to evaluate.')\n  flags.DEFINE_list(\n      'conv_num_filter_ratios', None,\n      'Number of filter ratios for the Conv1D layer, this'\n      'flag is only required if CNN encoder type is used.')\n  flags.DEFINE_integer(\n      'conv_kernel_size', 4,\n      'Size of the Conv1D layer kernel size, this flag is only'\n      'required if CNN encoder type is used.')\n  flags.DEFINE_integer(\n      'lstm_num_units', 4, 'Number of units for the LSTM layer,'\n      'this flag is only required if LSTM encoder type is used.')\n  flags.DEFINE_integer('num_predictions', 5,\n                       'Num of top predictions to output.')\n  flags.DEFINE_string('checkpoint_path', '', 'Path to the checkpoint.')\n\n\nclass SimpleCheckpoint(tf.keras.callbacks.Callback):\n  \"\"\"Keras callback to save tf.train.Checkpoints.\"\"\"\n\n  def __init__(self, checkpoint_manager):\n    super(SimpleCheckpoint, self).__init__()\n    self.checkpoint_manager = checkpoint_manager\n\n  def on_epoch_end(self, epoch, logs=None):\n    step_counter = self.checkpoint_manager._step_counter.numpy()  # pylint: disable=protected-access\n    self.checkpoint_manager.save(checkpoint_number=step_counter)\n\n\ndef _get_optimizer(learning_rate: float, gradient_clip_norm: float):\n  \"\"\"Gets model optimizer.\"\"\"\n  kwargs = {'clipnorm': gradient_clip_norm} if gradient_clip_norm > 0 else {}\n  return tf.keras.optimizers.Adagrad(learning_rate, **kwargs)\n\n\ndef _get_metrics(eval_top_k: List[int]):\n  \"\"\"Gets model evaluation metrics of both batch samples and full vocabulary.\"\"\"\n  metrics_list = [\n      metrics.GlobalRecall(name=f'Global_Recall/Recall_{k}', top_k=k)\n      for k in eval_top_k\n  ]\n  metrics_list.append(metrics.GlobalMeanRank(name='global_mean_rank'))\n  metrics_list.extend(\n      metrics.BatchRecall(name=f'Batch_Recall/Recall_{k}', top_k=k)\n      for k in eval_top_k)\n  metrics_list.append(metrics.BatchMeanRank(name='batch_mean_rank'))\n  return metrics_list\n\n\ndef compile_model(model, eval_top_k, learning_rate, gradient_clip_norm):\n  \"\"\"Compile keras model.\"\"\"\n  model.compile(\n      optimizer=_get_optimizer(\n          learning_rate=learning_rate, gradient_clip_norm=gradient_clip_norm),\n      loss=losses.GlobalSoftmax(),\n      metrics=_get_metrics(eval_top_k))\n\n\ndef build_keras_model(input_config: input_config_pb2.InputConfig,\n                      model_config: model_config_class.ModelConfig):\n  \"\"\"Construct and compile recommendation keras model.\n\n  Construct recommendation model according to input config and model config.\n  Compile the model with optimizer, loss function and eval metrics.\n\n  Args:\n    input_config: The configuration object(input_config_pb2.InputConfig) that\n      holds parameters for model input feature processing.\n    model_config: A ModelConfig object that holds parameters to set up the\n      model architecture.\n\n  Returns:\n    The compiled keras model.\n  \"\"\"\n  model = recommendation_model.RecommendationModel(\n      input_config=input_config, model_config=model_config)\n  compile_model(model, model_config.eval_top_k, FLAGS.learning_rate,\n                FLAGS.gradient_clip_norm)\n  return model\n\n\ndef get_callbacks(keras_model: tf.keras.Model,\n                  model_dir: str):\n  \"\"\"Sets up callbacks for training and evaluation.\"\"\"\n  summary_dir = os.path.join(model_dir, 'summaries')\n  summary_callback = tf.keras.callbacks.TensorBoard(summary_dir)\n  checkpoint = tf.train.Checkpoint(\n      model=keras_model, optimizer=keras_model.optimizer)\n  checkpoint_manager = tf.train.CheckpointManager(\n      checkpoint,\n      directory=model_dir,\n      max_to_keep=None,\n      step_counter=keras_model.optimizer.iterations,\n      checkpoint_interval=0)\n  checkpoint_callback = SimpleCheckpoint(checkpoint_manager)\n  return [summary_callback, checkpoint_callback]\n\n\ndef train_and_eval(model: tf.keras.Model,\n                   model_dir: str,\n                   train_input_dataset: tf.data.Dataset,\n                   eval_input_dataset: tf.data.Dataset,\n                   steps_per_epoch: int,\n                   epochs: int,\n                   eval_steps: int):\n  \"\"\"Train and evaluate.\"\"\"\n  callbacks = get_callbacks(model, model_dir)\n  history = model.fit(\n      x=train_input_dataset,\n      validation_data=eval_input_dataset,\n      steps_per_epoch=steps_per_epoch,\n      epochs=epochs,\n      validation_steps=eval_steps,\n      callbacks=callbacks)\n  tf.get_logger().info(history)\n  return model\n\n\ndef save_model(checkpoint_path: str, export_dir: str,\n               input_config: input_config_pb2.InputConfig,\n               model_config: model_config_class.ModelConfig):\n  \"\"\"Export to savedmodel.\n\n  Args:\n    checkpoint_path: The path to the checkpoint that the model will be exported\n      based on.\n    export_dir: The directory to export models to.\n    input_config: The input config of the model.\n    model_config: The configuration to set up the model.\n  \"\"\"\n  model = recommendation_model.RecommendationModel(\n      input_config=input_config,\n      model_config=model_config)\n  checkpoint = tf.train.Checkpoint(model=model)\n  checkpoint.restore(checkpoint_path).run_restore_ops()\n  input_specs = input_pipeline.get_serving_input_specs(input_config)\n  signatures = {\n      tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY:\n          model.serve.get_concrete_function(**input_specs)\n  }\n  tf.saved_model.save(model, export_dir=export_dir, signatures=signatures)\n\n\ndef export_tflite(export_dir):\n  \"\"\"Export to TFLite model.\n\n  Args:\n    export_dir: the model exportation dir, where saved_model is located.\n  \"\"\"\n  converter = tf.lite.TFLiteConverter.from_saved_model(export_dir)\n  tflite_model = converter.convert()\n  tflite_model_path = os.path.join(export_dir, 'model.tflite')\n  with tf.io.gfile.GFile(tflite_model_path, 'wb') as f:\n    f.write(tflite_model)\n\n\ndef export(checkpoint_path: str, input_config: input_config_pb2.InputConfig,\n           model_config: model_config_class.ModelConfig, export_dir: str):\n  \"\"\"Export to tensorflow saved model and TFLite model.\n\n  Args:\n    checkpoint_path: The path to the checkpoint that the model will be exported\n      based on.\n    input_config: The input config of the model.\n    model_config: The configuration to set up the model.\n    export_dir: The directory to store the exported model, If not set, model is\n      exported to the model_dir with timestamp.\n  \"\"\"\n  logger = tf.get_logger()\n  if not export_dir:\n    export_dir = os.path.join(FLAGS.model_dir, 'export', str(int(time.time())))\n  logger.info('Exporting model to dir: {}'.format(export_dir))\n  save_model(\n      checkpoint_path=checkpoint_path,\n      export_dir=export_dir,\n      input_config=input_config,\n      model_config=model_config)\n  logger.info('Converting model to tflite model.')\n  export_tflite(export_dir)\n\n\ndef load_input_config():\n  \"\"\"Load input config.\"\"\"\n  assert FLAGS.input_config_file, 'input_config_file cannot be empty.'\n  with tf.io.gfile.GFile(FLAGS.input_config_file, 'rb') as reader:\n    return text_format.Parse(reader.read(), input_config_pb2.InputConfig())\n\n\ndef prepare_model_config():\n  \"\"\"Prepare model config.\"\"\"\n  return model_config_class.ModelConfig(\n      hidden_layer_dims=[int(x) for x in FLAGS.hidden_layer_dims],\n      eval_top_k=[int(x) for x in FLAGS.eval_top_k],\n      conv_num_filter_ratios=[int(x) for x in FLAGS.conv_num_filter_ratios],\n      conv_kernel_size=FLAGS.conv_kernel_size,\n      lstm_num_units=FLAGS.lstm_num_units,\n      num_predictions=FLAGS.num_predictions)\n\n\ndef main(_):\n  logger = tf.get_logger()\n  if not tf.io.gfile.exists(FLAGS.model_dir):\n    tf.io.gfile.mkdir(FLAGS.model_dir)\n\n  if not tf.io.gfile.exists(FLAGS.export_dir):\n    tf.io.gfile.mkdir(FLAGS.export_dir)\n\n  input_config = load_input_config()\n  model_config = prepare_model_config()\n\n  logger.info('Setting up train and eval input datasets.')\n  train_input_dataset = input_pipeline.get_input_dataset(\n      data_filepattern=FLAGS.training_data_filepattern,\n      input_config=input_config,\n      vocab_file_dir=FLAGS.vocab_dir,\n      batch_size=FLAGS.batch_size)\n  eval_input_dataset = input_pipeline.get_input_dataset(\n      data_filepattern=FLAGS.testing_data_filepattern,\n      input_config=input_config,\n      vocab_file_dir=FLAGS.vocab_dir,\n      batch_size=FLAGS.batch_size)\n\n  logger.info('Build keras model for mode: {}.'.format(FLAGS.run_mode))\n  model = build_keras_model(\n      input_config=input_config, model_config=model_config)\n\n  if FLAGS.run_mode == 'train_and_eval':\n    train_and_eval(\n        model=model,\n        model_dir=FLAGS.model_dir,\n        train_input_dataset=train_input_dataset,\n        eval_input_dataset=eval_input_dataset,\n        steps_per_epoch=FLAGS.steps_per_epoch,\n        epochs=FLAGS.num_epochs,\n        eval_steps=FLAGS.num_eval_steps)\n    latest_checkpoint_path = tf.train.latest_checkpoint(FLAGS.model_dir)\n    if latest_checkpoint_path:\n      export(\n          checkpoint_path=latest_checkpoint_path,\n          input_config=input_config,\n          model_config=model_config,\n          export_dir=FLAGS.export_dir)\n  elif FLAGS.run_mode == 'export':\n    checkpoint_path = (\n        FLAGS.checkpoint_path if FLAGS.checkpoint_path else\n        tf.train.latest_checkpoint(FLAGS.model_dir))\n    export(\n        checkpoint_path=checkpoint_path,\n        input_config=input_config,\n        model_config=model_config,\n        export_dir=FLAGS.export_dir)\n  else:\n    logger.error('Unsupported launcher run model {}.'.format(FLAGS.run_mode))\n\n\nif __name__ == '__main__':\n  define_flags()\n  app.run(main)\n"
  },
  {
    "path": "lite/examples/recommendation/ml/model/recommendation_model_launcher_test.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Tests for recommendation_model_launcher.\"\"\"\nimport os\nfrom absl import flags\nimport tensorflow as tf\nfrom model import input_pipeline\nfrom model import recommendation_model_launcher as launcher\nfrom google.protobuf import text_format\n# pylint: disable=g-direct-tensorflow-import\nfrom ai_edge_litert import interpreter as tfl_interpreter\n# pylint: enable=g-direct-tensorflow-import\n\nFLAGS = flags.FLAGS\n\nFAKE_MOVIE_GENRE_VOCAB = [\n    'UNK',\n    'Comedy',\n    'Drama',\n    'Romance',\n    'Animation',\n    'Children'\n]\n\nTEST_INPUT_CONFIG = \"\"\"\n    activity_feature_groups {\n      features {\n        feature_name: \"context_movie_id\"\n        feature_type: INT\n        vocab_size: 3952\n        embedding_dim: 8\n        feature_length: 5\n      }\n      features {\n        feature_name: \"context_movie_rating\"\n        feature_type: FLOAT\n        feature_length: 5\n      }\n      encoder_type: CNN\n    }\n    activity_feature_groups {\n      features {\n        feature_name: \"context_movie_genre\"\n        feature_type: STRING\n        vocab_name: \"movie_genre_vocab.txt\"\n        vocab_size: 19\n        embedding_dim: 8\n        feature_length: 8\n      }\n      encoder_type: BOW\n    }\n    label_feature {\n      feature_name: \"label_movie_id\"\n      feature_type: INT\n      vocab_size: 3952\n      embedding_dim: 8\n      feature_length: 1\n    }\n\"\"\"\n\nEXAMPLE1 = text_format.Parse(\n    \"\"\"\n    features {\n        feature {\n          key: \"context_movie_id\"\n          value {\n            int64_list {\n              value: [1, 2, 0, 0, 0]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_rating\"\n          value {\n            float_list {\n              value: [3.5, 4.0, 0.0, 0.0, 0.0]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_genre\"\n          value {\n            bytes_list {\n              value: [\n                    \"Animation\", \"Children\", \"Comedy\", \"Comedy\", \"Romance\", \"UNK\", \"UNK\", \"UNK\"\n                ]\n            }\n          }\n        }\n        feature {\n          key: \"label_movie_id\"\n          value {\n            int64_list {\n              value: [3]\n            }\n          }\n        }\n      }\"\"\", tf.train.Example())\n\n\nclass RecommendationModelLauncherTest(tf.test.TestCase):\n\n  def _AssertSparseTensorValueEqual(self, a, b):\n    self.assertAllEqual(a.indices, b.indices)\n    self.assertAllEqual(a.values, b.values)\n    self.assertAllEqual(a.dense_shape, b.dense_shape)\n\n  def _assertInputDetail(self, input_details, index, name, shape):\n    self.assertEqual(name, input_details[index]['name'])\n    self.assertEqual(shape, input_details[index]['shape'])\n\n  def setUp(self):\n    super().setUp()\n    self.tmp_dir = self.create_tempdir()\n    self.test_input_config_file = os.path.join(self.tmp_dir,\n                                               'input_config.pbtxt')\n    self.test_movie_genre_vocab_file = os.path.join(self.tmp_dir,\n                                                    'movie_genre_vocab.txt')\n    self.test_input_data_file = os.path.join(self.tmp_dir,\n                                             'test_input_data.tfrecord')\n    with open(self.test_input_config_file, 'w', encoding='utf-8') as f:\n      f.write(TEST_INPUT_CONFIG)\n    with open(self.test_movie_genre_vocab_file, 'w', encoding='utf-8') as f:\n      for item in FAKE_MOVIE_GENRE_VOCAB:\n        f.write(item + '\\n')\n    with tf.io.TFRecordWriter(self.test_input_data_file) as file_writer:\n      file_writer.write(EXAMPLE1.SerializeToString())\n\n    self.test_model_dir = os.path.join(self.tmp_dir, 'test_model_dir')\n\n    FLAGS.training_data_filepattern = self.test_input_data_file\n    FLAGS.testing_data_filepattern = self.test_input_data_file\n    FLAGS.input_config_file = self.test_input_config_file\n    FLAGS.model_dir = self.test_model_dir\n    FLAGS.hidden_layer_dims = [8, 4]\n    FLAGS.eval_top_k = [1, 5]\n    FLAGS.num_predictions = 5\n    FLAGS.conv_num_filter_ratios = [2, 4]\n    FLAGS.conv_kernel_size = 4\n    FLAGS.lstm_num_units = 16\n\n  def testModelTrainEvalExport(self):\n    \"\"\"Verifies that model can be trained and evaluated.\"\"\"\n    tf.io.gfile.mkdir(FLAGS.model_dir)\n    input_config = launcher.load_input_config()\n    model_config = launcher.prepare_model_config()\n    dataset = input_pipeline.get_input_dataset(\n        data_filepattern=self.test_input_data_file,\n        input_config=input_config,\n        vocab_file_dir=self.tmp_dir,\n        batch_size=8)\n    model = launcher.build_keras_model(input_config, model_config)\n    launcher.train_and_eval(\n        model=model,\n        model_dir=FLAGS.model_dir,\n        train_input_dataset=dataset,\n        eval_input_dataset=dataset,\n        steps_per_epoch=2,\n        epochs=2,\n        eval_steps=1)\n    self.assertTrue(os.path.exists(self.test_model_dir))\n    summaries_dir = os.path.join(self.test_model_dir, 'summaries')\n    self.assertTrue(os.path.exists(summaries_dir))\n    export_dir = os.path.join(FLAGS.model_dir, 'export')\n    latest_checkpoint = tf.train.latest_checkpoint(FLAGS.model_dir)\n    launcher.save_model(\n        checkpoint_path=latest_checkpoint,\n        export_dir=export_dir,\n        input_config=input_config,\n        model_config=model_config)\n    savedmodel_path = os.path.join(export_dir, 'saved_model.pb')\n    self.assertTrue(os.path.exists(savedmodel_path))\n    imported = tf.saved_model.load(export_dir, tags=None)\n    infer = imported.signatures['serving_default']\n    context_movie_id = tf.range(5, dtype=tf.int32)\n    context_movie_rating = tf.range(5, dtype=tf.float32)\n    context_movie_genre = tf.range(8, dtype=tf.int32)\n    predictions = infer(context_movie_id=context_movie_id,\n                        context_movie_rating=context_movie_rating,\n                        context_movie_genre=context_movie_genre)\n    self.assertAllEqual([5], predictions['top_prediction_ids'].shape)\n    self.assertAllEqual([5], predictions['top_prediction_scores'].shape)\n    launcher.export_tflite(export_dir)\n    tflite_model_path = os.path.join(export_dir, 'model.tflite')\n    self.assertTrue(os.path.exists(tflite_model_path))\n    f = open(tflite_model_path, 'rb')\n    interpreter = tfl_interpreter.Interpreter(model_content=f.read())\n    interpreter.allocate_tensors()\n    inference_signature = interpreter.get_signature_list()['serving_default']\n    self.assertAllEqual(\n        ['context_movie_genre', 'context_movie_id', 'context_movie_rating'],\n        inference_signature['inputs'])\n    self.assertAllEqual(['top_prediction_ids', 'top_prediction_scores'],\n                        inference_signature['outputs'])\n    serving_name_to_tenors = {\n        'serving_default_context_movie_id:0': context_movie_id,\n        'serving_default_context_movie_rating:0': context_movie_rating,\n        'serving_default_context_movie_genre:0': context_movie_genre\n    }\n    input_details = interpreter.get_input_details()\n    output_details = interpreter.get_output_details()\n    indice_to_tensors = {}\n    for input_detail in input_details:\n      indice_to_tensors[input_detail['index']] = serving_name_to_tenors[\n          input_detail['name']]\n    for index, tensor in indice_to_tensors.items():\n      interpreter.set_tensor(index, tensor)\n    interpreter.invoke()\n    tflite_top_predictions_ids = interpreter.get_tensor(\n        output_details[0]['index'])\n    tflite_top_prediction_scores = interpreter.get_tensor(\n        output_details[1]['index'])\n    self.assertAllEqual([5], tflite_top_predictions_ids.shape)\n    self.assertAllEqual([5], tflite_top_prediction_scores.shape)\n\nif __name__ == '__main__':\n  launcher.define_flags()\n  tf.test.main()\n"
  },
  {
    "path": "lite/examples/recommendation/ml/model/recommendation_model_test.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Tests for personalized recommendation model.\"\"\"\nimport tensorflow as tf\n\nfrom configs import input_config_generated_pb2 as input_config_pb2\nfrom configs import model_config as model_config_class\nfrom model import recommendation_model\n\n\nclass RecommendationModelTest(tf.test.TestCase):\n\n  def _create_test_input_config(self,\n                                encoder_type: input_config_pb2.EncoderType):\n    \"\"\"Generate test input_config_pb2.InputConfig proto.\"\"\"\n    feature_context_movie_id = input_config_pb2.Feature(\n        feature_name='context_movie_id',\n        feature_type=input_config_pb2.FeatureType.INT,\n        vocab_size=20,\n        embedding_dim=4)\n    feature_context_movie_rating = input_config_pb2.Feature(\n        feature_name='context_movie_rating',\n        feature_type=input_config_pb2.FeatureType.FLOAT)\n    feature_group_1 = input_config_pb2.FeatureGroup(\n        features=[feature_context_movie_id, feature_context_movie_rating],\n        encoder_type=encoder_type)\n\n    feature_label = input_config_pb2.Feature(\n        feature_name='label_movie_id',\n        feature_type=input_config_pb2.FeatureType.INT,\n        vocab_size=20,\n        embedding_dim=4)\n\n    input_config = input_config_pb2.InputConfig(\n        activity_feature_groups=[feature_group_1],\n        label_feature=feature_label)\n    return input_config\n\n  def _create_test_model_config(self):\n    return model_config_class.ModelConfig(\n        hidden_layer_dims=[8, 4],\n        eval_top_k=[1, 5],\n        conv_num_filter_ratios=[1, 2],\n        conv_kernel_size=2,\n        lstm_num_units=16)\n\n  def test_model_train_bow(self):\n    input_config = self._create_test_input_config(\n        input_config_pb2.EncoderType.BOW)\n    model_config = self._create_test_model_config()\n    test_model = recommendation_model.RecommendationModel(\n        input_config=input_config, model_config=model_config)\n    batch_size = 4\n    input_context_movie_id = tf.keras.layers.Input(\n        shape=(10,),\n        dtype=tf.int32,\n        batch_size=batch_size,\n        name='context_movie_id')\n    input_context_movie_rating = tf.keras.layers.Input(\n        shape=(10,),\n        dtype=tf.float32,\n        batch_size=batch_size,\n        name='context_movie_rating')\n    input_label_movie_id = tf.keras.layers.Input(\n        shape=(1,),\n        dtype=tf.int32,\n        batch_size=batch_size,\n        name='label_movie_id')\n    inputs = {\n        'context_movie_id': input_context_movie_id,\n        'context_movie_rating': input_context_movie_rating,\n        'label_movie_id': input_label_movie_id\n    }\n    logits = test_model(inputs)\n    self.assertAllEqual([batch_size, 20], logits.shape.as_list())\n\n  def test_model_train_cnn(self):\n    input_config = self._create_test_input_config(\n        input_config_pb2.EncoderType.CNN)\n    model_config = self._create_test_model_config()\n    test_model = recommendation_model.RecommendationModel(\n        input_config=input_config, model_config=model_config)\n    batch_size = 4\n    input_context_movie_id = tf.keras.layers.Input(\n        shape=(10,),\n        dtype=tf.int32,\n        batch_size=batch_size,\n        name='context_movie_id')\n    input_context_movie_rating = tf.keras.layers.Input(\n        shape=(10,),\n        dtype=tf.float32,\n        batch_size=batch_size,\n        name='context_movie_rating')\n    input_label_movie_id = tf.keras.layers.Input(\n        shape=(1,),\n        dtype=tf.int32,\n        batch_size=batch_size,\n        name='label_movie_id')\n    inputs = {\n        'context_movie_id': input_context_movie_id,\n        'context_movie_rating': input_context_movie_rating,\n        'label_movie_id': input_label_movie_id\n    }\n    logits = test_model(inputs)\n    self.assertAllEqual([batch_size, 20], logits.shape.as_list())\n\n  def test_model_train_lstm(self):\n    input_config = self._create_test_input_config(\n        input_config_pb2.EncoderType.LSTM)\n    model_config = self._create_test_model_config()\n    test_model = recommendation_model.RecommendationModel(\n        input_config=input_config, model_config=model_config)\n    batch_size = 4\n    input_context_movie_id = tf.keras.layers.Input(\n        shape=(10,),\n        dtype=tf.int32,\n        batch_size=batch_size,\n        name='context_movie_id')\n    input_context_movie_rating = tf.keras.layers.Input(\n        shape=(10,),\n        dtype=tf.float32,\n        batch_size=batch_size,\n        name='context_movie_rating')\n    input_label_movie_id = tf.keras.layers.Input(\n        shape=(1,),\n        dtype=tf.int32,\n        batch_size=batch_size,\n        name='label_movie_id')\n    inputs = {\n        'context_movie_id': input_context_movie_id,\n        'context_movie_rating': input_context_movie_rating,\n        'label_movie_id': input_label_movie_id\n    }\n    logits = test_model(inputs)\n    self.assertAllEqual([batch_size, 20], logits.shape.as_list())\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "lite/examples/recommendation/ml/model/utils.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Utilities for ondevice personalized recommendations model.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nfrom typing import Iterable, Tuple, TypeVar\n\nimport tensorflow as tf\n\nTFGradient = TypeVar('TFGradient', tf.Tensor, tf.IndexedSlices)\n\nScalar = TypeVar('Scalar', tf.Variable, tf.Tensor, float, int)\n\n\ndef GetShardFilenames(filepattern):\n  \"\"\"Get a list of filenames given a pattern.\n\n  This will also check whether the files exist on the filesystem. The pattern\n  can be either of the glob form, or the 'basename@num_shards' form.\n\n  Args:\n    filepattern: File pattern.\n\n  Returns:\n    A list of shard patterns.\n\n  Raises:\n    ValueError: if using the shard pattern, if some shards don't exist.\n  \"\"\"\n  filenames = tf.io.gfile.glob(filepattern)\n  for filename in filenames:\n    if not tf.io.gfile.exists(filename):\n      raise ValueError('File not found: %s' % filename)\n  return filenames\n\n\ndef ClipGradient(\n    grads_and_vars: Iterable[Tuple[TFGradient, tf.Variable]],\n    clip_val: Scalar = 1.0,\n    include_histogram_summary: bool = False\n) -> Tuple[Tuple[TFGradient, tf.Variable], ...]:\n  \"\"\"Clips all gradients by global norm, reducing norm to clip_val.\n\n  Args:\n    grads_and_vars: Gradients and vars list input.\n    clip_val: A 0-D (scalar) `Tensor` > 0. Value to clip to.\n    include_histogram_summary: Flag indicates adding clipped gradients to\n      histogram summary.\n\n  Returns:\n    clipped_grads_and_vars: Return gradients and vars list after operation.\n  \"\"\"\n\n  grads, variables = list(zip(*grads_and_vars))\n  with tf.name_scope('gradient_clip'):\n    clipped_grads, global_norm = tf.clip_by_global_norm(grads, clip_val)\n    if include_histogram_summary:\n      tf.summary.scalar('clipped_global_norm', global_norm)\n\n  clipped_grads_and_vars = tuple(zip(clipped_grads, variables))\n\n  if include_histogram_summary:\n    with tf.name_scope('Gradient_info_clip'):\n      for g, v in clipped_grads_and_vars:\n        if not isinstance(g, tf.IndexedSlices):\n          tf.summary.scalar('%s_clip_l2_norm' % v.name.replace(':', '_'),\n                            tf.sqrt(tf.reduce_sum(tf.square(g))))\n  return clipped_grads_and_vars\n"
  },
  {
    "path": "lite/examples/recommendation/ml/ondevice_recommendation.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"h2q27gKz1H20\"\n      },\n      \"source\": [\n        \"##### Copyright 2020 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"TUfAcER1oUS6\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Gb7qyhNL1yWt\"\n      },\n      \"source\": [\n        \"# On-device recommendation with TensorFlow Lite\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Fw5Y7snSuG51\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/lite/examples/recommendation/ml/ondevice_recommendation.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/lite/examples/recommendation/ml/ondevice_recommendation.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"fyYiyNxVp6mS\"\n      },\n      \"source\": [\n        \"# Overview\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"CShg7PXmqGUJ\"\n      },\n      \"source\": [\n        \"This code base provides an adaptive framework to train and serve on-device recommendation\\n\",\n        \"model. This approach personalizes recommendations by leveraging on-device data,\\n\",\n        \"and protects user privacy without having user data leave device.\\n\",\n        \"\\n\",\n        \"This Notebook shows an end-to-end example that\\n\",\n        \"\\n\",\n        \"*   prepares sequential training data \\n\",\n        \"*   trains neural-network model with various encoding techniques\\n\",\n        \"*   exports the model to TensorFlow Lite\\n\",\n        \"*   integrates in on-device ML applications to generate personalized recommendations.\\n\",\n        \"\\n\",\n        \"With this example, we demonstrate the approach with public\\n\",\n        \"[movielens](https://grouplens.org/datasets/movielens/) dataset, but you could\\n\",\n        \"adapt the data processing script for your dataset and train your own\\n\",\n        \"recommendation model.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"bcLF2PKkSbV3\"\n      },\n      \"source\": [\n        \"# Prerequisites\\n\",\n        \"\\n\",\n        \"To run this example, please clone the source code from github [repo](https://github.com/tensorflow/examples/tree/master/lite/examples/recommendation/ml), install required packages.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"6cv3K3oaksJv\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!git clone https://github.com/tensorflow/examples\\n\",\n        \"%cd examples/lite/examples/recommendation/ml/\\n\",\n        \"!pip install -r requirements.txt\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"qRBdzEu3qGFP\"\n      },\n      \"source\": [\n        \"# Model\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"m86-Nh4pMHqY\"\n      },\n      \"source\": [\n        \"We leverage a dual-encoder model architecture, with context-encoder to encode\\n\",\n        \"sequential user history and label-encoder to encode predicted recommendation\\n\",\n        \"candidate. Similarity between context and label encodings is used to represent\\n\",\n        \"the likeliness predicted candidate meets user's needs.\\n\",\n        \"\\n\",\n        \"Three different sequential user history encoding techniques are provided with\\n\",\n        \"this code base:\\n\",\n        \"\\n\",\n        \"* **Bag of words encoder (BOW)**: averaging user activities' embeddings without\\n\",\n        \"considering context order.\\n\",\n        \"* **Convolutional neural-network encoder (CNN)**: applying multiple layers of\\n\",\n        \"convolutional neural-network to generate context encoding.\\n\",\n        \"* **Recurrent neural-network encoder (RNN)**: applying recurrent neural network\\n\",\n        \"(LSTM in this example) to understand context sequence.\\n\",\n        \"\\n\",\n        \"In terms of user sequence modeling, we consider there are two approaches in general:\\n\",\n        \"* **Id-based**: putting all recommendation candidates in an embedding space to understand similarities of items. The embeddings are keyed with item Ids in the item vocabulary. Hence we call it id-based approach.\\n\",\n        \"* **Feature-based**: use more features of user activities, not only the item ids. For instance, movie genre and movie rating for movie recommendation. The model will understand the features and learns the way to understand user.\\n\",\n        \"\\n\",\n        \"This framework code base supports both id-based and feature-based recommendation models, with a configurable way.\\n\",\n        \"\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Oii_hG1843k_\"\n      },\n      \"source\": [\n        \"![Untitled drawing (2).jpg](data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAJBBdoDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3+iiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACimTSx28Ek0rBI41Lux6AAZJrgPAHiuTXNf1qGdiBM/2mBD/AAqMLj8tn60AehUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQBj+J9LvNa0KfTrO4S3efCvI4J+TuBj16fTNee+CPCN9Y+LLi4jvYcabP5Mq7T+9DLzj8/0r1quW8K/8h/xR/wBfw/8AQaAOpooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooqhqGqw2BSII891L/qrePG5vf0CjuTxQBfrLm1+ySRobbzb2dTgx2q78H0LfdU/UiqbWNzqXzatNujPSzhJEQ9mPV/xwPatCKKOCJYoo1jjUYVUGAB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eBxlXRsg1MQCCCMg9Qa5a88Kz6fdPqPhi4WxuWO6S0cZt5/qv8J9x+lAHVUVz2j+Kor27/s3Urd9N1ZRzbzHiT3RujCuhoAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACuN1OWbxfrEuh2crR6TaMBqE6HBlb/nkp/n/AJzq+LtWm0rQnNpzfXTrbWoHXzH4B/AZP4Vb0DR4dC0a3sIsEouZH7yOfvMfqaALttbQ2ltHb28SxQxqFRFGAoqWiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigDE1bwhoOt3f2rUNOSWcoI3cOyeYg/hfaRvHXhsir8OlWNvfC8htY47gW62wdBjESklUA6AAk1cooAzYdA0q3tNOtYrKNYNNcPaICcQsFKgjn0Zhz61RtPBHhqxvory20mGOaJzLH8zFVf+/tJ27ufvYz710FFAGFe+DPDuoai9/daXFJcSMryZZgkrL0LoDtcjA5YHpT9U8I6DrV59r1DTYp5ygjZiWXzFHRXAIDgdg2cVtUUAYl54P0DUILOG502Nks4hBBtZkKRgAbMqQSvA4OQalg8MaLbRWsUGnQxx2sUsMCrkCNJSDIo56EgVrUUAUF0TTVg06AWiCLTSps1yf3JVCgx9FJHPrXCH4d315q8El7aaHGkd+t5Lf2wcTz7HDgeVt2RsxChmDHPPrXpVFABRRRQAUUUUAFFFFABRRRQBnaxodhrtp9nvod+OY5F4eM+qnsa55dT1bwi4h1ovqGkZwmoouZIR2Eo7j/a/n0rsqR0WRGR1DKwwVIyCKAGW9xDdW6T28qSwyDcjochh7GpK4+4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/gtuP/iKP7Ui/wCfXUv/AAW3H/xFdVRRyIORHK/2pF/z66l/4Lbj/wCIo/tSL/n11L/wW3H/AMRXVUUciDkRxt1dfbPssMNpqBc3ls3z2MyABZkYklkAAABPJrsqKKpKw0rBRRRTGFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABXI+ILSfQdT/4SjTYy6YC6lbJ/y1jH8YH95f5fjnrqQgMCCAQeCD3oAitLuC+tIrq2kEkEqh0cdwamrjtLJ8J+IjokhI0q/YyWDHpFJ/FF/Uf/AF67GgAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigDybU/AME3jeGzk1GdjfRy3UkuwZDBs8fnXq0KNHDGjuZGVQC5GCxx1rmb/AP5KVpH/AF4zfzFdTQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAGT4j0Zdd0WW0DeXOMSW8veOReVP9PoTUfhfWW1rR1knXy72BjBdxHgpKvB49+v41tVyN3/xT3jqC8Hy2OsgQTeizr9w/iOPzoA66iiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAOWv/APkpWkf9eM38xXU1y1//AMlK0j/rxm/mK6mgAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACsbxVpR1nw5eWqD9+F8yAjqJF5XH4jH41s0UAZfh3VRrXh+y1DI3SxjzAOzjhh+YNalcp4WH9neIPEGi9I0nW7gX/AGJBkgewIxXV0AFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUVzuoa7qyeIZtK0rS7K58i1huZJbq+eD/WPKoACxPnHlEk5HUUm0ldglc6Kiua/tPxb/0AdE/8HMv/AMi0f2n4t/6AOif+DmX/AORaj2sO5XKzpaK5r+0/Fv8A0AdE/wDBzL/8i0f2n4t/6AOif+DmX/5Fo9rDuHKyO/8A+SlaR/14zfzFdTXDzweK5/EdprB0rRVa3heEQ/2rKQ27vu+z8Y9MGtP+0/Fv/QB0T/wcy/8AyLR7WHcOVnS0VzX9p+Lf+gDon/g5l/8AkWj+0/Fv/QB0T/wcy/8AyLR7WHcOVnS0VzX9p+Lf+gDon/g5l/8AkWmx6/rkGr6ZZ6no+nQw387W6y22ovMyMIZJeVaBBjEZHXuKaqRbsmLlZ09FFFWIKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAo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KKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigDlvH/wDyALf/AK/oP/Qq6muW8f8A/IAt/wDr+g/9CrqaACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKy/ENvcXWkFLaFppVnglEasoLBJUY4LEDOFPU1qUUAcx52pf9AC//wC/tv8A/HaPO1L/AKAF/wD9/bf/AOO109FRyInlRzHnal/0AL//AL+2/wD8do87Uv8AoAX/AP39t/8A47XT0UciDlRzHnal/wBAC/8A+/tv/wDHaPO1L/oAX/8A39t//jtdPRRyIOVHMedqX/QAv/8Av7b/APx2jztS/wCgBf8A/f23/wDjtdPRRyIOVHMedqX/AEAL/wD7+2//AMdo87Uv+gBf/wDf23/+O109FHIg5Ucx52pf9AC//wC/tv8A/HaPO1L/AKAF/wD9/bf/AOO109FHIg5Ucx52pf8AQAv/APv7b/8Ax2jztS/6AF//AN/bf/47XT0UciDlRzHnal/0AL//AL+2/wD8do87Uv8AoAX/AP39t/8A47XT0UciDlRzHnal/wBAC/8A+/tv/wDHaPO1L/oAX/8A39t//jtdPRRyIOVHMedqX/QAv/8Av7b/APx2jztS/wCgBf8A/f23/wDjtdPRRyIOVHJXOoXdnEJLjRL6NC6xgmW3+8zBQP8AW9yRU3nal/0AL/8A7+2//wAdrmvi3rDQQafpkLlXZ/tD4PIC8L+ufyru9C1NdY0Ky1Bcfv4gzAdm6MPwIIo5EHKjJ87Uv+gBf/8Af23/APjtHnal/wBAC/8A+/tv/wDHa6eijkQcqOY87Uv+gBf/APf23/8AjtHnal/0AL//AL+2/wD8drp6KORByo5jztS/6AF//wB/bf8A+O0edqX/AEAL/wD7+2//AMdrp6KORByo5jztS/6AF/8A9/bf/wCO0edqX/QAv/8Av7b/APx2unoo5EHKjmPO1L/oAX//AH9t/wD47R52pf8AQAv/APv7b/8Ax2unoo5EHKjmPO1L/oAX/wD39t//AI7R52pf9AC//wC/tv8A/Ha6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dBV4z+X6161XLeKv+Q/4X/6/j/6DQBo+GNLvNF0KDTry4S4eDKpIgI+TsDn06fTFbFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQBi+K7bUr7w7c2elKhubgeWSz7QqH7x/Lj8a838BeHta07xdJMqRGOzkNvdgSdmHb1xwfwr2KuW8K/8h/xR/wBfw/8AQaAOpooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAMDxv/AMiXq3/XA/zFaumf8gmz/wCuCf8AoIrK8b/8iXq3/XA/zFaumf8AIJs/+uCf+gigC1RRRQAUUUUAFFFFABRRRQAUUUUAFYHjf/kS9W/64H+YrfrA8b/8iXq3/XA/zFAGrpn/ACCbP/rgn/oIq1VXTP8AkE2f/XBP/QRVqgAooooAKKKKACiiigAooooAKKKKACiiigAooooAK5a//wCSlaR/14zfzFdTXLX/APyUrSP+vGb+YoA6miiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA5bQ/l8e+Kl7MLRh/37NdTXLaV8vxE8QD+9b2zfkpFdTQAUUUUAFFFFABRRRQAUUUUAFFFFAFLWLBdU0a8sWx+/haMZ7Ejg/nis7wZfNqHhDTpn/1ixeU+euUO3n8s1vVyvgz/AEa41/Tugt9SkdB6I4BA/nQB1VFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABVTU9Ot9W024sLpd0M6FG9vQj3B5/CrdFAHN+Dr+4ksbjSb9t1/pcn2eQ/30/gf8R/Kukrk9UH9kePdL1Fflh1JGsp/TeOYz9T0/CusoAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA5bxV/yH/C/wD1/H/0GuprlvFX/If8L/8AX8f/AEGupoAKKKKACiiigAooooAKKKKACiiigArlvFX/ACH/AAv/ANfx/wDQa6muW8Vf8h/wv/1/H/0GgDqaKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArlvCv/ACH/ABR/1/D/ANBrqa5bwr/yH/FH/X8P/QaAOpoo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7LVJIZLpJS8IZUaKeSIgNjIyjDIO0dfSqi7O4mrqxj0HpV3/AIRXS/8Ap+/8GNx/8XR/wiul/wDT9/4Mbj/4ut/brsZ+zZ5fbeCr2XUtOa502wtFs7wXclxb3crpIVJYCKBvli3HGcH16101x4O0S5vZLmW2kPmyiaWBbiQQyyDB3NEG2MeB1HPeuq/4RXS/+n7/AMGNx/8AF0f8Irpf/T9/4Mbj/wCLqVViug+R9zDOjWBGojyP+Ql/x9/O37z5Anrx8oA4xS2ej2Fhcm4toNkpgjti29j+7jzsXk9tx56881t/8Irpf/T9/wCDG4/+Lo/4RXS/+n7/AMGNx/8AF0/bR7C9m+5ysPg7RLe9juY7aQeVKZ4oDcSGCOTk71iLbAeT0HFaWpaZZ6vZNZ30IlhYhsZKlWByCCMEEHoQc1sf8Irpf/T9/wCDG4/+Lo/4RXS/+n7/AMGNx/8AF0e2j2D2b7mBpWhWGjGZ7VJWmnIM088zzSyY4GXckkDsOgrSq7/wiul/9P3/AIMbj/4uj/hFdL/6fv8AwY3H/wAXT9uuwezZSrS8M/8AIqaP/wBeMH/oAqL/AIRXS/8Ap+/8GNx/8XWrbW0Vnaw2sCbIYUWONck7VAwBz7Cs6k+exUY8pLRRRWRYUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB/9k=)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"jZpnkJ9X_RfA\"\n      },\n      \"source\": [\n        \"# Data Adaptivity\\n\",\n        \"\\n\",\n        \"The framework can support model training or customization with various kinds of data, for which we provide the way to configurate the input data and encoder architecture.\\n\",\n        \"\\n\",\n        \"With the input config, you can specify the input features' information, such as data type, shapde, vocab, embedding dimension. You can also freely group features in a feature group to encode together, for instance movie_id and movie rating in above diagram.  We support 3 feature types INT/STRING/FLOAT, for string and integer categorical features we will map them in embedding spaces, for float feature we suggest to concatenate directly together with other features' embeddings.\\n\",\n        \"\\n\",\n        \"Please check out the step-to-step example session below for more details about setting up the input config.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"5zCIjZgoSpNZ\"\n      },\n      \"source\": [\n        \"# Training Data Preparation\\n\",\n        \"\\n\",\n        \"This notebook makes use of public dataset [movielens](https://grouplens.org/datasets/movielens/) to demonstrate training of on-device recommendation model.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"KpQlunkiSwio\"\n      },\n      \"source\": [\n        \"## Examples Generation\\n\",\n        \"The examples generation process performs the following steps:\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"*   Downloads [movielens](https://grouplens.org/datasets/movielens/) dataset\\n\",\n        \"*   Groups movie rating records by user, and orders per-user movie rating records by timestamp.\\n\",\n        \"*   Generates TensorFlow examples with features: 1) `context_movie_id`: \\n\",\n        \"time-ordered sequential movie IDs 2) `context_movie_rating`: time-ordered sequential rating numbers 3) `context_movie_genre`: time-ordered sequential movie genres 4) `context_movie_year`: time-ordered sequential movie years. 5) `label_movie_id`: the next movie ID user rated.\\n\",\n        \"\\n\",\n        \"There's case that one user activity will have multiple values for a single feature. For example, the movie genre feature in movielens dataset, each movie can have multiple genres. For this case, we suggest to concatenate all movies' genres for the activity sequence. Let's look at one example, if the user activity sequence is\\n\",\n        \"```\\n\",\n        \"Star Wars: Episode IV - A New Hope (1977), Genres: Action|Adventure|Fantasy\\n\",\n        \"Terminator 2: Judgment Day (1991), Genres: Action|Sci-Fi|Thriller\\n\",\n        \"Jurassic Park (1993), Genres: Action|Adventure|Sci-Fi\\n\",\n        \"```\\n\",\n        \"The context_movie_genre feature will be\\n\",\n        \"```\\n\",\n        \"\\\"Action, Adventure, Fantasy, Action, Sci-Fi, Thriller, Action, Adventure, Sci-Fi\\\"\\n\",\n        \"```\\n\",\n        \"Since TFLite input tensors should be fixed length, so we suggest to pad features to fixed length.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Wff0kYZKS05g\"\n      },\n      \"source\": [\n        \"## Vocabularies Generation\\n\",\n        \"For String and Integer type features, we would suggest to create an embedding space for each of them, for which vocabularies will be needed. This framework supports txt file based vocabulary setup, for which you can puch vocab iten line-by-line in a txt file. And in the training input pipeline vocabularies will be formed as:\\n\",\n        \"\\n\",\n        \"```\\n\",\n        \"tf.lookup.StaticVocabularyTable(\\n\",\n        \"      tf.lookup.TextFileInitializer(\\n\",\n        \"          vocab_path,\\n\",\n        \"          key_dtype=key_type,\\n\",\n        \"          key_index=tf.lookup.TextFileIndex.WHOLE_LINE,\\n\",\n        \"          value_dtype=tf.int64,\\n\",\n        \"          value_index=tf.lookup.TextFileIndex.LINE_NUMBER,\\n\",\n        \"          delimiter='\\\\t'),\\n\",\n        \"      num_oov_buckets)\\n\",\n        \"```\\n\",\n        \"And vocab_path is the path to the genreated vocabulary txt file.\\n\",\n        \"\\n\",\n        \"The `data/example_generation_movielens.py` script, generates vocabularies with \\\"--build_vocabs\\\" set as true.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"eY0T_skNS4Uw\"\n      },\n      \"source\": [\n        \"## Try out data preparation\\n\",\n        \"Please try out the movielens training examples and vocabs generation script below.\\n\",\n        \"\\n\",\n        \"Note: If you would like to use your own data, please adapt the data processing script for your specific case.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"FQvryCfGtCQX\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!python -m data.example_generation_movielens \\\\\\n\",\n        \"  --data_dir=data/raw \\\\\\n\",\n        \"  --output_dir=data/examples \\\\\\n\",\n        \"  --min_timeline_length=3 \\\\\\n\",\n        \"  --max_context_length=10 \\\\\\n\",\n        \"  --max_context_movie_genre_length=32 \\\\\\n\",\n        \"  --min_rating=2 \\\\\\n\",\n        \"  --train_data_fraction=0.9 \\\\\\n\",\n        \"  --build_vocabs=True\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"8zcEXFkgCz8g\"\n      },\n      \"source\": [\n        \"Raw movielens ratings.dat data is in the following format:\\n\",\n        \"UserID::MovieID::Rating::Timestamp\\n\",\n        \"\\n\",\n        \"*   UserIDs range between 1 and 6040\\n\",\n        \"*   MovieIDs range between 1 and 3952\\n\",\n        \"*   Ratings are made on a 5-star scale (whole-star ratings only)\\n\",\n        \"*   Timestamp is represented in seconds since the epoch as returned by time(2)\\n\",\n        \"*   Each user has at least 20 ratings\\n\",\n        \"\\n\",\n        \"Ref:[movielens readme.txt](http://files.grouplens.org/datasets/movielens/ml-1m-README.txt)\\n\",\n        \"\\n\",\n        \"In this example, we consider each rating as a movie watch by the users, and construct user movie watch history with rated movie IDs ordering by time.\\n\",\n        \"\\n\",\n        \"Sample generated training example with max user history as 10:\\n\",\n        \"```\\n\",\n        \"0 : {   # (tensorflow.Example)\\n\",\n        \"  features: {   # (tensorflow.Features)\\n\",\n        \"    feature: {\\n\",\n        \"      key  : \\\"context_movie_id\\\"\\n\",\n        \"      value: {\\n\",\n        \"        int64_list: {\\n\",\n        \"          value: [ 3476, 3264, 2120, 1717, 382, 1644, 2328, 2461, 2064, 3679 ]\\n\",\n        \"        }\\n\",\n        \"      }\\n\",\n        \"    }\\n\",\n        \"    feature: {\\n\",\n        \"      key  : \\\"context_movie_year\\\"\\n\",\n        \"      value: {\\n\",\n        \"        int64_list: {\\n\",\n        \"          value: [ 1990, 1992, 1993, 1997, 1994, 1997, 1998, 1990, 1989, 1981 ]\\n\",\n        \"        }\\n\",\n        \"      }\\n\",\n        \"    }\\n\",\n        \"    feature: {\\n\",\n        \"      key  : \\\"context_movie_genre\\\"\\n\",\n        \"      value: {\\n\",\n        \"        bytes_list: {\\n\",\n        \"          value: [ \\\"Horror\\\", \\\"Mystery\\\", \\\"Thriller\\\", \\\"Comedy\\\", \\\"Horror\\\", \\\"Drama\\\", \\\"Horror\\\", \\\"Horror\\\", \\\"Thriller\\\", \\\"Drama\\\", \\\"Horror\\\", \\\"Horror\\\", \\\"Mystery\\\", \\\"Thriller\\\", \\\"Horror\\\", \\\"Horror\\\", \\\"Comedy\\\", \\\"Documentary\\\", \\\"Documentary\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\" ]\\n\",\n        \"        }\\n\",\n        \"      }\\n\",\n        \"    }\\n\",\n        \"    feature: {\\n\",\n        \"      key  : \\\"context_movie_rating\\\"\\n\",\n        \"      value: {\\n\",\n        \"        float_list: {\\n\",\n        \"          value: [ 4.0, 4.0, 3.0, 1.0, 3.0, 3.0, 1.0, 4.0, 3.0, 4.0 ]\\n\",\n        \"        }\\n\",\n        \"      }\\n\",\n        \"    }\\n\",\n        \"    feature: {\\n\",\n        \"      key  : \\\"label_movie_id\\\"\\n\",\n        \"      value: {\\n\",\n        \"        int64_list: {\\n\",\n        \"          value: [ 1361 ]\\n\",\n        \"        }\\n\",\n        \"      }\\n\",\n        \"    }\\n\",\n        \"  }\\n\",\n        \"}\\n\",\n        \"```\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"GApKDT6gRebW\"\n      },\n      \"source\": [\n        \"# Model Configuration\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ubHdVNyJXkVE\"\n      },\n      \"source\": [\n        \"##Input Configuration\\n\",\n        \"\\n\",\n        \"Trainer code will prepare tf datasets and set up model according to input config. You can configurate the following:\\n\",\n        \"\\n\",\n        \"*   Feature: name, data type, feature length, vocab name, vocab size, embedding dimension.\\n\",\n        \"*   Feature group: features to encode together, encoder type.\\n\",\n        \"*   Global feature groups: global features, e.g. user age, profession etc.\\n\",\n        \"*   Activity feature groups: features to represent activities.\\n\",\n        \"*   Label feature: the feature used as label.\\n\",\n        \"\\n\",\n        \"Both input data processing and model architecture setup will be based on the input configuration.\\n\",\n        \"\\n\",\n        \"Please check example input config with command below:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Mk7hpSlCta95\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!cat configs/sample_input_config.pbtxt\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"XgjrV1aRqaOp\"\n      },\n      \"source\": [\n        \"You can also see different model graph generated based on different input configs in the appendix section.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"XQcQ6AssuBN8\"\n      },\n      \"source\": [\n        \"# Train model\\n\",\n        \"\\n\",\n        \"The training launcher script uses TensorFlow keras compile/fit APIs and performs\\n\",\n        \"the following steps to kick off training and evaluation process:\\n\",\n        \"\\n\",\n        \"*   Set up both train and eval dataset input function.\\n\",\n        \"*   Construct keras model according to provided configs, please refer to sample.config file in the source code to config your model architecture, such as embedding dimension, convolutional neural network params, LSTM units etc.\\n\",\n        \"*   Setup loss function. In this code base, we leverages customized batch softmax loss function.\\n\",\n        \"*   Setup optimizer, with flag specified learning rate and gradient clip if needed.\\n\",\n        \"*   Setup evaluation metrics, we provided recall@k metrics by default.\\n\",\n        \"*   Compile model with loss function, optimizer and defined metrics.\\n\",\n        \"*   Setup callbacks for tensorboard and checkpoint manager.\\n\",\n        \"*   Run model.fit with compiled model, where you could specify number of epochs to train, number of train steps in each epoch and number of eval steps in each epoch.\\n\",\n        \"\\n\",\n        \"To start training please execute command:\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"3gPKz5InxEbF\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!python -m model.recommendation_model_launcher \\\\\\n\",\n        \"  --training_data_filepattern \\\"data/examples/train_movielens_1m.tfrecord\\\" \\\\\\n\",\n        \"  --testing_data_filepattern \\\"data/examples/test_movielens_1m.tfrecord\\\" \\\\\\n\",\n        \"  --model_dir \\\"model/model_dir\\\" \\\\\\n\",\n        \"  --export_dir \\\"model/model_dir/export_m1\\\" \\\\\\n\",\n        \"  --vocab_dir \\\"data/examples\\\" \\\\\\n\",\n        \"  --input_config_file \\\"configs/sample_input_config.pbtxt\\\" \\\\\\n\",\n        \"  --batch_size 32 \\\\\\n\",\n        \"  --learning_rate 0.01 \\\\\\n\",\n        \"  --steps_per_epoch 2 \\\\\\n\",\n        \"  --num_epochs 2 \\\\\\n\",\n        \"  --num_eval_steps 2 \\\\\\n\",\n        \"  --run_mode \\\"train_and_eval\\\" \\\\\\n\",\n        \"  --gradient_clip_norm 1.0 \\\\\\n\",\n        \"  --num_predictions 10 \\\\\\n\",\n        \"  --hidden_layer_dims \\\"32,32\\\" \\\\\\n\",\n        \"  --eval_top_k \\\"1,5\\\" \\\\\\n\",\n        \"  --conv_num_filter_ratios \\\"2,4\\\" \\\\\\n\",\n        \"  --conv_kernel_size 4 \\\\\\n\",\n        \"  --lstm_num_units 16\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ObH_mcGcxS96\"\n      },\n      \"source\": [\n        \"# Export model\\n\",\n        \"\\n\",\n        \"Inside launcher script we also provide model exportation functionality.\\n\",\n        \"\\n\",\n        \"In serve model, the model takes in user context history, for the example case the input is a vector of movie IDs you interacted with. With context encoder, model computes the context embedding vector, at the same time generate candidate embedding vector for all movie candidates in the vocab. By dotproduct and top-k ranking, top-k candidates will be served as the predicted candidates.\\n\",\n        \"\\n\",\n        \"At model exportation step, you could specify number of predictions you want to get from the output of the model.\\n\",\n        \"\\n\",\n        \"This step includes:\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"*   Export the model to saved_model with tf.saved_model.save.\\n\",\n        \"*   Convert the saved_model to TensorFlow lite with tf.lite.TFLiteConverter.from_saved_model, and save it to the export directory wanted.\\n\",\n        \"\\n\",\n        \"To export the model, please execute command:\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"SH5r6AxHzGrS\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!python -m model.recommendation_model_launcher \\\\\\n\",\n        \"  --training_data_filepattern \\\"data/examples/train_movielens_1m.tfrecord\\\" \\\\\\n\",\n        \"  --testing_data_filepattern \\\"data/examples/test_movielens_1m.tfrecord\\\" \\\\\\n\",\n        \"  --input_config_file \\\"configs/sample_input_config.pbtxt\\\" \\\\\\n\",\n        \"  --model_dir \\\"model/model_dir\\\" \\\\\\n\",\n        \"  --export_dir \\\"model/model_dir/export_m2\\\" \\\\\\n\",\n        \"  --vocab_dir \\\"data/examples\\\" \\\\\\n\",\n        \"  --run_mode \\\"export\\\" \\\\\\n\",\n        \"  --checkpoint_path \\\"model/model_dir/ckpt-4\\\" \\\\\\n\",\n        \"  --num_predictions 10 \\\\\\n\",\n        \"  --hidden_layer_dims \\\"32,32\\\" \\\\\\n\",\n        \"  --eval_top_k \\\"1,5\\\" \\\\\\n\",\n        \"  --conv_num_filter_ratios \\\"2,4\\\" \\\\\\n\",\n        \"  --conv_kernel_size 4 \\\\\\n\",\n        \"  --lstm_num_units 16\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"qXMQ5D5JzSgv\"\n      },\n      \"source\": [\n        \"# Model inference\\n\",\n        \"\\n\",\n        \"You could verify your model's performance by running inference with test examples.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"og0qkYavz3Nt\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import os\\n\",\n        \"import tensorflow as tf\\n\",\n        \"\\n\",\n        \"# Use [0, 1, ... 9] as example input to represent 10 movies that user interacted with.\\n\",\n        \"context = tf.range(10)\\n\",\n        \"# Path to exported TensorFlow Lite model.\\n\",\n        \"tflite_model_path = 'model/model_dir/export/export_m2/model.tflite'  #@param {type:\\\"string\\\"}\\n\",\n        \"\\n\",\n        \"# Create TFLite interpreter.\\n\",\n        \"interpreter = tf.lite.Interpreter(tflite_model_path)\\n\",\n        \"interpreter.allocate_tensors()\\n\",\n        \"input_details = interpreter.get_input_details()\\n\",\n        \"output_details = interpreter.get_output_details()\\n\",\n        \"print('Display inputs and outputs:')\\n\",\n        \"print(input_details)\\n\",\n        \"print(output_details)\\n\",\n        \"\\n\",\n        \"# Find indices.\\n\",\n        \"names = [\\n\",\n        \"  'serving_default_context_movie_id:0',\\n\",\n        \"  'serving_default_context_movie_genre:0',\\n\",\n        \"  'serving_default_context_movie_rating:0',\\n\",\n        \"]\\n\",\n        \"indices = {i['name']: i['index'] for i in input_details}\\n\",\n        \"\\n\",\n        \"# Fake inputs for illustration. Please change to the real data.\\n\",\n        \"# Use [0, 1, ... 9] to represent 10 movies that user interacted with.\\n\",\n        \"ids = tf.range(10)\\n\",\n        \"interpreter.set_tensor(indices[names[0]], ids)\\n\",\n        \"# Use [0, 1, ..., 31] to represent 32 movie genres.\\n\",\n        \"genres = tf.range(32)\\n\",\n        \"interpreter.set_tensor(indices[names[1]], genres)\\n\",\n        \"# Use [1.0, 1.0, ..., 1.0] to represent 10 movie ratings.\\n\",\n        \"ratings = tf.ones(10)\\n\",\n        \"interpreter.set_tensor(indices[names[2]], ratings)\\n\",\n        \"\\n\",\n        \"# Run inference.\\n\",\n        \"interpreter.invoke()\\n\",\n        \"\\n\",\n        \"# Get outputs.\\n\",\n        \"top_prediction_ids = interpreter.get_tensor(output_details[0]['index'])\\n\",\n        \"top_prediction_scores = interpreter.get_tensor(output_details[1]['index'])\\n\",\n        \"print('Predicted results:')\\n\",\n        \"print('Top ids: {}'.format(top_prediction_ids))\\n\",\n        \"print('Top scores: {}'.format(top_prediction_scores))\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"A_omMjoT035u\"\n      },\n      \"source\": [\n        \"# Integrate in your application\\n\",\n        \"\\n\",\n        \"We also open source an Android reference app to run inference with TF Lite.\\n\",\n        \"**Please follow [`android/app/README.md`](https://github.com/tensorflow/examples/blob/master/lite/examples/recommendation/android/README.md)** to install required developer tools and build Android app.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"N83Ev6nSwsUW\"\n      },\n      \"source\": [\n        \"The app uses one pretrained model to illustrate how to run TFLite. If you want to replace the existing model with the one you just trained above, please copy the respective TF Lite model to `assets` folder, and adapt its file name accordingly. If you directly train and export your model in this notebook, your\\n\",\n        \"exported model should be located at \\\"model/model_dir/export/model.tflite\\\".\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"hUaXjqGBvnFP\"\n      },\n      \"source\": [\n        \"```shell\\n\",\n        \"cp path/to/your/model.tflite ../android/app/src/main/assets/\\n\",\n        \"```\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"bwr7pigOB7pT\"\n      },\n      \"source\": [\n        \"The app uses the json file `config.json` to load one model and control how to consume IDs and scores predicted by the TF Lite recommendation model on device. `Config` definition can be found in [`android/app/src/main/java/org/tensorflow/lite/examples/recommendation/Config.java`](../android/app/src/main/java/org/tensorflow/lite/examples/recommendation/Config.java).\\n\",\n        \"\\n\",\n        \"A sample json is presented below for the built-in model, and you may need to *adapt* it and related code to handle your own trained model.\\n\",\n        \"\\n\",\n        \"``` json\\n\",\n        \"{\\n\",\n        \"  \\\"model\\\": \\\"<your_model>.tflite\\\",\\n\",\n        \"  \\\"inputs\\\": [\\n\",\n        \"    {\\\"name\\\": \\\"movieFeature\\\", \\\"index\\\": 0, \\\"inputLength\\\": 10},\\n\",\n        \"    {\\\"name\\\": \\\"genreFeature\\\", \\\"index\\\": 1, \\\"inputLength\\\": 32}\\n\",\n        \"  ],\\n\",\n        \"  \\\"movieList\\\": \\\"sorted_movie_vocab.json\\\",\\n\",\n        \"  \\\"genreList\\\": \\\"movie_genre_vocab.txt\\\",\\n\",\n        \"  \\\"topK\\\": 10,\\n\",\n        \"  \\\"outputLength\\\": 10,\\n\",\n        \"  \\\"outputIdsIndex\\\": 0,\\n\",\n        \"  \\\"outputScoresIndex\\\": 1\\n\",\n        \"}\\n\",\n        \"```\\n\",\n        \"# Further reading\\n\",\n        \"If you want to read more about the technology related to on-device recommendation, please check out our [blogpost](https://blog.tensorflow.org/2021/04/adaptive-framework-for-on-device-recommendation.html).\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"2egsu0GMsjyB\"\n      },\n      \"source\": [\n        \"# Appendix\\n\",\n        \"\\n\",\n        \"> Model A: ID-based bag-of-words\\n\",\n        \"\\n\",\n        \"```\\n\",\n        \"activity_feature_groups {\\n\",\n        \"  features {\\n\",\n        \"    feature_name: \\\"context_movie_id\\\"\\n\",\n        \"    feature_type: INT\\n\",\n        \"    vocab_size: 3953\\n\",\n        \"    embedding_dim: 8\\n\",\n        \"    feature_length: 10\\n\",\n        \"  }\\n\",\n        \"  encoder_type: BOW\\n\",\n        \"}\\n\",\n        \"label_feature {\\n\",\n        \"  feature_name: \\\"label_movie_id\\\"\\n\",\n        \"  feature_type: INT\\n\",\n        \"  vocab_size: 3953\\n\",\n        \"  embedding_dim: 8\\n\",\n        \"  feature_length: 1\\n\",\n        \"}\\n\",\n        \"```\\n\",\n        \"![Screen Shot 2021-03-15 at 4.12.22 PM.png](data:image/png;base64,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hg0r7mVfbVfwed9996WkTqn8fOh9escdd9j5559vEydOtFatWtl5552XM18QUgJKIQjkuACBbI53INVPn0BBQYF9//33tnDhwmIPojwacVy9enWRPD///LNpRE5p7ty57qrmtWvXunu1R2fW/j/99FN4s253qbKVdIp+5cqV7rFOq6pOxSXto1P53kidytU+yaTNmze7e8nH2+/XX391I6FePXUMjTzq6m0FcA0aNHCPo4O4VatW2ZdffmkbN24sVK2tW7e6/Dp+ZJKjDGIlmem1TZs2uX1VdqJJ9dZIrkZ0i0vy08h4rBHVyP7VeySy/1Se9lUeJZlEv67tsQzloP2ifdQ2lal6qzy9rveTHv/2228qrsQUWV+N5i5atCicX3302Wefhd9n4RciHuhiyVhTTOQX3ceRdVQRO++8s1WqVCmitN8fxmp/kUwxNkR+PryXE33fevk1leDaa6+1Sy65xAWyu+++u/3zn/+0Fi1a2I033uhl4zcCCPhcgEDW5x1E9bIjMHPmTOvUqZMdfvjhdsghh7hbUE6ZMqVQZXSv9d12282NuOmP39/+9jeLnGd3+umnu1PrBxxwgO2///727LPPuruKtWvXrkjwdNZZZ9ltt93mytdp+fbt24fL0ul5nZrVH13tq3odeeSRNnXq1EL1efPNN61t27Z26KGH2l/+8hfr06ePK/OKK64olK+kJ/fee6/7Q96tWzd3rJEjRxbJrgD32GOPdVMHunTpYm3atAnf1UgB91577eVO/T/++OPu8X/+8x9Xhm7nqXI15UC/GzdubI8++mi4/B9//NG1O3qawNlnn22qV6x05plnulFJfZnQcW+55ZZY2YpsU91U78MOO8y1U2bz588P51Mbe/To4Sz0HlD/qh8ik/r3oYcecm3RLUrVZ9rmBayqs0b6lPQeUnleKslQQaBGHXv16uVld1+m1Pea96mgMdr4tddeC+ct7oHq9uCDD5r67KCDDnLL0N1000327rvvutH/o446ylq2bFlkCoCmRug9p7VS99tvP9tjjz0KTTXRCOaAAQMKHXby5MmmwNCzOOecc+z//u//wnlKan84UzEPoj8fypbI+1af14cffjhcqoJ5BdJ6f3mpTJkydsYZZ5jWmyYhgEBuCBDI5kY/UcsMC+i2k5o3p9FP/Zxyyik2ZMiQ8MicTkNef/317o+zRrY0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G812p9Y+JLBkyRInFI4//vhE6e644w579dVXbebMmfbVV1/ZQQcd5CwzXoS1117bdt55Z5NllgCBahJ46aWX3HXYpEmTamaTkvall17qHuxSdub5MWLECDvllFOsadOf1/vRQ94+++xjw4YNs4ULF9p//vMfe+CBB+wf//iHS+mLL76w/fbbzy644AL7+uuv7eWXX7brrrvOnn322UROH3zwgZ188sn2yiuvJD7J1ul8/TSRUI6NI4880k488URbsGCB4zx+/Hi77777Emf89re/NT3oEiAAgVQCCNlUHvwKOAHdaPbaay/7xS9+YQcffLA9+uijDWqkG9dhhx1mvXr1cjes5Nd1Y8aMsaOOOsqdv/vuu6fczHSjUZq6we2www62yy672F133ZWSvm6SegX4y1/+0g444AB75plnUo7n+iHL0xFHHGEtWrRIRNt0003t9ttvt1atWjkrrcoti3Lyq8dzzjnHbr755sQ5bECgGgQk5AYOHOiSlnvOjjvuaHI3kGWye/fu7m3HO++8k8g6X3+RSFUan332mTtHfUe/db0rHHfccTZ37lzT634JzRdeeMHtz/VHr+DvueceO/XUUxPR5EOuMsqiqbDBBhu4/itxqqB0VX71e70JUZ+TaLzpppvccf3RilbdunVz5+p8fdZaa63E8UL66TfffJOIr43k3xLbH3/8sR1yyCEujtLeY489bNasWYlzZBFWPuPGjUvsYwMCEDBDyHIVhIaAblj65y+LxoUXXmjff/+9ybo5Z86clDqeffbZ1rhxY/v8889t6NChJvGq8NFHHzkhqZvnMcccY7rR7b///i49HdeNViJWlhgJ1RkzZjjLz9ixY3XY3Yj23ntvmzp1qrPe6GavG79eW+YLEqZ33nlnAx9EpdejRw93M9fgFfn+/frXv064HijdLbbYwvTqUZZbAgSqRUCv571rbPny5c4H9ZZbbnHCSq/r5X8uq6YX8vUXvWWQH6sGMil8++237rfXX9u0aWOy/kpctm3b1po1a+YlnfX7kUcecQ+yOtcLOlcifOXKle5/wv/+7//aa6+95h5mFUf/C1Sf5KC47733XmKXhKysuRLn6pPpltF8/VTuQHr49QagyWXiV7/6VeKBVPU87bTTnMuDBnA+/fTT7n+N8ksO5513XorATj7GNgQiSyB+AyVAwNcE5s2bF+vdu3cs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aptxGbl5AlbWZAIEIBBeAgjZ8LatL2v2m9/8xh577LGalG327Nm2xx572AknnFCT/MgEAmElIDGohQ023njjQFRRgltWZC3som8CBCAQXgII2fC2rS9r9uqrr7plK6tduEcffdR22WUXW2ONNdy8lNXOj/QhEGYCEoOXX365cy/o2bOnr90MJGK7du1qEt+sUBjmq5K6QeBnAghZroSSCCxdutRuv/12O+yww2zIkCFurkgvoUsuucQmTpzo/bQ//OEPNn78ePf7xhtvNM0t6QXt1xySgwYNsscff9zbXfb366+/7sowcODAstMiAQhAwJxl86mnnrLOnTtbt27dnO+pX1wN5Avbvn17VyZPxA4fPpxmgwAEIkCA6bci0MjVqOKll15qn376qclVQFbWXXfd1T7//HNbffXVbaeddrKTTjrJ3nnnHZs0aZLdd999dvbZZ7tiPP/8826+SM0tqeMSsXfffbetttpqduaZZ1q7du2sf//+DYo8Y8YMe//99xvs32233ax169YN9l933XVuX7KgbhCJHRCAQFEEZJmVQBw7dqyNGDGiqHNrEVl+sQMGDMASWwvY5AEBnxBAyPqkIYJWjOuvv96aNGniii0xOWrUKHvrrbds++23t3333df23ntv+5//+R+bMGGCPfDAA07gptdRwlcidOutt7Z11103xaqbHler/Tz55JPpu92yl5mEbIOI7IAABCpCwPM91Wt7rcCltx/lhCuuuML53uqtTKlBZWJQV6n0OA8CwSaAkA12+9Wt9FpuUuJVQvXHH3+0Tz75JGU1nhtuuMHdnI4++uisNxgNxNp///1t8803t+7du9uvf/1rO/bYYzPW6dBDDzV9CBCAgH8ISEDqU04466yznBVVllQCBCAAgWIJ4CNbLDHi26pVq+yII45wAnXKlCn2wQcfWJcuXVLIyIVAN7gxY8bYN998k3LM+9G4cWOT4J03b54bSHLTTTc5v1vvePL36NGjnciV0E3+SEATIAABCEAAAhCIJgEsstFs97JqvXjxYtPUVjvssIObFUCidebMmYk0NU+srCzjxo1zVltty082PWjgiAZ+XXnllbbnnnu6WQbmzp2bHs391kjpTCsLtWnTJmN8dkIAAhCAAAQgEH4CCNnwt3HFayifVE3FowFbGincr18/5xvrZXTaaafZKaec4qbAGTZsmG2x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Fu177LHHbPHixYljErGffvqpu3kmdsY3nnjiCRs6dGjyLrYhEFkC6jMtW7a09dZbL4XBH//4R9thhx0S+/R71KhRid/a2GWXXVwfk6BVuOiii+zee+91K7BJrL7zzjs2ceJE96bkrbfecgtazJ4925577jlbffXV3Tn8gQAEIBAkAgjZILWWD8t655132nHHHZfiPtC2bVs76qij7I477vBhiSkSBPxNQA+AetuhBRTkTrDVVltZo0aNSi70HnvsYdOnT3d9dOedd7btt9/eJk+ebOeff74TzIcccoj96le/sv3226/kPDgRAhCAQL0IIGTrRT4k+Y4bN84WLFhgGtjlhUGDBtmHH35oL7/8sreLbwhAoEACcrGRa46spPJBf/PNN92r/yuuuMJatGhRYCr/jab+efPNNzs3hVdeecWef/5554P77LPP2k8//eTeiMyaNcu5CP33LLYgAAEIBIMAQjYY7eTbUsZiMfvLX/5ip512WqKM2pallgABCJRGYMaMGe5NR7t27ax///42adIk5yZQyluOuXPnphRCrgtffvllyj79btqUIRMpUPgBAQgEggBCNhDN5O9C3nPPPdatWzfr27ev7b777tapUyfTFFrZgubGTA7pv5OPsQ2BKBPQoEj5tB599NFukNcRRxyRMq9sussBfSnKVwt1h0A0CaQqimgyoNZlEvjqq6/s4YcfdoO+NMjr/vvvdyOh05P95ptv3GhpTSfkBd2IN9tsM+8n3xCIPAEN4JIrQHrQzAUaxOVZTtXvttxyy5RomoWAAAEIQCBKBHiXFKXWrmJd9cpTPrGyCPXq1StjTkuWLHFT/0jsyidP03BdcMEF7pyMJ7ATAhEkoH4kMav+oRkHJFg1W8H1119v48ePt6+//tpRee2111z/0XzNo0ePdj6uZ555ZgSJUWUIQCDKBLDIRrn1K1h33VTffvttNxo6fU7Z5GyOP/54N62QbtYaST1//nwGhSUDYjvyBOSqc8YZZziRqunqli5dahqYpVkMNMOAFzS7gXzRr732Wvvkk0/shhtusMsvv9w7zDcEIACBSBBoFB+sE4tETalkQQS23XZbJy4/++yzguKXGkmLIGh0tqYZIgSXQO/evW3w4MHuE9xa1KbkAwYMsNdff90K7Vtyu+nSpYvziX3vvfds+fLlGQuqhRK0OIIsuITwEJALlizs6l8ECEAgOwFcC7Kz4UgVCSxcuLCKqZM0BIJPQDaGjz76KG9FJHARsXkxEQECEAgpAVwLQtqwVAsCEIAABCAAAQiEnQBCNuwtTP0gAAEIQAACEIBASAkgZEPasFQLAhCAAAQgAAEIhJ0AQjbsLUz9IAABCEAAAhCAQEgJIGRD2rBUCwIQgAAEIAABCISdALMWhL2FS6hfq1atSjiLUyAAgXwE6Fv5CHEcAhCAQHEEELLF8YpE7K5du0ainlSyfAKFzolafk7hSIG+FY52rEUt6Fu1oEweYSCAkA1DK1a4Dpq4nUm4Kww1pMlpAQ1C4QToW4WzinpMXSsECEAgPwF8ZPMzIgYEIAABCEAAAhCAgA8JIGR92CgUCQIQgAAEIAABCEAgPwGEbH5GxIAABCAAAQhAAAIQ8CEBhKwPG4UiQQACEIAABCAAAQjkJ4CQzc+IGBCAAAQgAAEIQAACPiSAkPVho0SpSJ9++ql98sknVa/yF198YR9++GHV8yEDCEAAAhCAAARqRwAhWzvW5JSBwNVXX22TJ0/OcKT0XT/88EODk99//30777zzGuxnBwTCQuCjjz6yadOmhaU61AMCEIBAQQQQsgVhIlI6gdNPP906d+6c+Gy55ZZ2yimn2JdffpkeNevvRYsW2YQJE+zQQw9NxFmyZImdeuqptummmyb2FbKxatUqu/jii61Dhw628cYb29Zbb21vvPFG4tRddtnFPv/8c/vggw8S+9iAQJgI3HbbbaYHw1qE2bNn2x577GEnnHBCLbIjDwhAAAJZCSBks6LhQC4CCxcudC4B++23nw0aNMjatm1rd911lx1//PG5Tks5NmLECCd+mzb9eV2Od955x3r37m0Ss3Pnzk2Jm+/H448/bvrIIrVgwQJnfT344INTTjv77LPtlltuSdnHDwiEhcCf/vQne/DBB6tenUcffdT0YLjGGmuYXHYIEIAABOpJACFbT/ohyPu0006zK6+80saNG2ctW7Y0idFCwooVK+yee+5x1lcvvlwM/vznP2e0KsldYNmyZV5U9/3tt98mfs+aNct23XVXW3vttd0+iVgt8bh06dJEnCOOOMKV85tvvknsYwMCfiOga/b222+3ww47zIYMGWJvv/12ooiXXHKJTZw4MfH7D3/4g40fP979Hjt2bIqQ1X49WOpBUw95lQqvv/66K8PAgQMrlSTpQAACECiZAEvUloyOE0VAvqcSmM8884wTjRdddFFBYB555BHba6+9rE2bNon43rK48vVLDw888IA9+eST9thjj1nz5s3t3HPPNbkTeBZWuSfoxqo4v/zlL50QOOmkk5y49tJabbXVTPtkCb7gggu83XxDwFcELr30UtMgyN/85jf26quvugc0ucWsvvrqttNOO7lrWA+MkyZNsvvuu8/0pkFB++bPn+9e92tbIvbuu+82XfdnnnmmtWvXzvr37+/iJv+ZMWOG68fJ+7S92267WevWrdN323XXXef2JQvqBpHYAQEIQKBGBBCyNQId1myOPPLIRNV22GEHJ04TO3Js/OMf/zD52RYa5DerG64E6yabbGISuxK1Xthwww1t//33t6OOOspZZRs1amR///vfvcOJb4ndk08+GSGbIMKG3whcf/311qRJE1csiclRo0bZW2+9Zdtvv73tu+++tvfee9v//M//OP9yPeBJ4KYHCV+JUPmKr7vuuilW3fS406dPdw+A6ft79eqVUcimx+M3BCAAgXoSQMjWk34I8p4yZYr17NnT3SiPOeYYZ/F57733bKONNspZOwnOhx56yHbfffec8ZIPygdwm222cbMcfPzxx9asWbPE4fPPP98N5pLbgCy2U6dONfnvymq0+eabJ+Ipz6OPPjrxmw0I+I2A/LwlXjUQ8scff3S+6MluNTfccIMb0KjrWD7lmYIGYunBTtd+9+7d7de//rUde+yxmaK6h8PkAZcZI7ETAhCAgE8J4CPr04YJSrFkDZJvrKyxO++8s3MvkA9dviDL6CuvvGIaNFZokFjt1KmTe2Uq0bx8+fLEqXJtkD+hRKzCtttu60SvxIAXJAokZOVeQICAHwnIXUa+3BKoekjULBtdunRJKerzzz/v+sGYMWMsm79348aNTYJ33rx5dvnll9tNN93k3G1SEvq/H6NHj3YiV0I3+VOL+Z0zlYd9EIAABIohgEW2GFrEbUBALgKyfsonTzdEzUAgUZsv6NWp3AXuvPNOu+yyy/JFt5EjRzo/Pg1akSVWc8Lq9aoGhylIuMqVQJYo3cS1+MFrr71mV111VSJtjeg+6KCDbM0110zsYwMCfiKwePFi09RW6kOaFUCidebMmYki6sHvrLPOcoMWZbXVtvxk08NTTz3lrn8NxNxzzz3dLAPZZgLRGxXvATA5nWT/9eT9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ciuQdVEfQmkExC55IJissyyiUBpLzspNwBOy9NfcnDgKAQjkJoCQzc2HowEiIMFVbetOLBazpUuXBohK8UWVsEi2ztZjEYXly5fbihUrii88ZwSGgOfPXo8CL1myxNSXCRCAQPAJIGSD34bUIE6gkNkKHnroIevWrZtbGKBYaLrxnXDCCdamTRtba621rEePHqYbcZhDJutsvpkNJD6HDh1qm2yyib377rtF45k1a5btuuuu1rp1a/fZc8897dNPPy06HU7wPwE9eBZrjV24cKEdcsghtvXWW5dUwWeeecY222wz14/btWtnQ4YMsWXLlpWUFidBAAL+IICQ9Uc7UIoKEchkkf3++++dCJXA0va3335bdG5nn322ffDBB/bOO++4NM444wz71a9+ZV9++WXRaQXphGTrrDfvbLbBYOKz4447OoH/0UcfmayqxYa9997bPSR88cUXNn/+fFt//fXtwAMPLDYZ4geAgFwLill979lnn7Utt9zSPv/8c/PcEoqpps4ZMGCAe9DSg+nUqVPt1Vdfdb+LSYe4EICAvwggZP3VHpSmRAKaPkpCK1N48MEHndXlrbfesg022CBTlLz7XnzxRfvtb39rG220ka2++uomIdu2bVubMmVK3nPDECFZ0Ko+meadlbV24MCB9txzz5VUZQlXWWQvueQSZ40V3wsvvND+85//2OLFi0tKk5P8SaCUgZnnn3++jRgxwl0fpdRq0qRJbkYOzWrSrFkz99Zg8ODBpr5NgAAEgksAIRvctqPkSQT0mjKbdefII4+0hx9+2LkEJJ2S2NSN7bXXXkv8fu+99+ywww6zVatWJfbtvPPONn78+MRvWWa//vpr22GHHRL7orAhQZs+GMxzN/jzn/9sF198sTVu3PDfilgddNBBKRbsRx99NGXO2k6dOjmhkcxZ27LCrbnmmlHAG5k6zpkzx9W1GNeCl156yVlUM0HSA+Wpp56a0mclfJ9++ulE9J122snmzp3rHpa0Uz6y+r+h/QQIQCC4BBrecYJbF0oeUQL5rDuy7OUKEq3777+/E7MSsfLL1OvsZEEmkfbdd9/ZFltsYbvvvrvtt99+TtCtvfbauZIO5bFk62yyu4H4ZAtqA7HbY489nJiViJWFW/6OyeH555+3P/7xj05c6CFBlvZ//vOfyVHYDgEBvebP9gYlW/Vy9eOtttrK5M6i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Zvyst956KXFr8WOTTTax0aNH21prrVWL7MgDAiUT8PxowzLTgSzOqgsBAhCAQD0INK1HpuRZPQK6Se6+++42ceJEGzVqVEpGa6+9tm200UYp+wr5cccdd9ibb75p559/fiK6xOqKFSvsuuuuS+zzNphdwSPBNwSyE/AErXxMNYWVrJr6SBhqXzbrpvxSsx3LnlvljqjcXpB7ASubeTT4hgAE6kEAIVsP6lXO89NPP7WbbrrJveqbNWtW3ty0Rvrmm29uX3zxhX355ZeJ+NrfqVMn97peS6Cuv/769vXXX9vSpUtdHA1Cy2b9TSQS32jWrJn16NHDVJZvv/3WJIIXL15sS5YssSZNmliHDh1c3hLGXlhnnXVs2bJl9t1333m73HebNm1MN9L33nvPHU85yA8IBJCArmeJQYlXiVSJWvmbevslbJODXuXrNb6OZwqasWLs2LHukKZhq/QMFpq1oGfPnm455KlTp9qAAQMaFEP/N/TZZpttMh5vcAI7IAABCJRIACFbIjg/n/aXv/zF9tprLycyd9llF/vpp5+yFvfUU0+1a665xr2SX2211ew///mPHXzwwfbJJ5/YGmusYR999JFpf9++fd2NVjfVhx56KGt66QeGDh1qF110kROzK1eutLPPPtvOOeccu/POO+322293IlbCe+utt7a33norcfoTTzxhL730kul8hY4dO9pjjz1m2267rauPBLDSkbWYAIEwEJAwVf/SR5ZZCVqJVg2k0j7P8ql4ErqZXudrBUB9FDTTSfKDaaUZ5Up72rRprs9rDmyVZ/jw4U7YVroMpAcBCEAAIRvCa2DVqlV2wgkn2Ntvv+2E4NVXX52xlvvuu68TlKeffroTve3bt3fuCM8884z17t3bWU01eEp+t+muBUpQQlf+s8lBVlRv5PJBBx3kRPKQIUPsvvvucxZdfWsKomLDpZdeahK8KvOPP/5ov/3tb50Qls/v559/XmxyxIeArwlIuOqjviQhq1f4ErfaJyEry61ErgaPeUH9zLPA+mUpa72NkauRyibL7eDBg73i8g0BCECgIgQY7FURjP5LRK//zjzzTLvsssucKM1UwuOOO86efPJJGzlypBOH8+bNcwJ4s802s+222y7TKSn7dthhB5sxY0bKx3ulqYjHHHOM/eMf/3DpS+DKunvyySdby5YtU9Ip5McZZ5xhRxxx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)\\n\",\n        \"\\n\",\n        \"> Model B: Feature-based CNN\\n\",\n        \"\\n\",\n        \"```\\n\",\n        \"activity_feature_groups {\\n\",\n        \"  features {\\n\",\n        \"    feature_name: \\\"context_movie_id\\\"\\n\",\n        \"    feature_type: INT\\n\",\n        \"    vocab_size: 3953\\n\",\n        \"    embedding_dim: 8\\n\",\n        \"    feature_length: 10\\n\",\n        \"  }\\n\",\n        \"  features {\\n\",\n        \"    feature_name: \\\"context_movie_rating\\\"\\n\",\n        \"    feature_type: FLOAT\\n\",\n        \"    feature_length: 10\\n\",\n        \"  }\\n\",\n        \"  encoder_type: CNN\\n\",\n        \"}\\n\",\n        \"activity_feature_groups {\\n\",\n        \"  features {\\n\",\n        \"    feature_name: \\\"context_movie_genre\\\"\\n\",\n        \"    feature_type: STRING\\n\",\n        \"    vocab_name: \\\"movie_genre_vocab.txt\\\"\\n\",\n        \"    vocab_size: 19\\n\",\n        \"    embedding_dim: 4\\n\",\n        \"    feature_length: 32\\n\",\n        \"  }\\n\",\n        \"  encoder_type: CNN\\n\",\n        \"}\\n\",\n        \"label_feature {\\n\",\n        \"  feature_name: \\\"label_movie_id\\\"\\n\",\n        \"  feature_type: INT\\n\",\n        \"  vocab_size: 3953\\n\",\n        \"  embedding_dim: 8\\n\",\n        \"  feature_length: 1\\n\",\n        \"}\\n\",\n        \"```\\n\",\n        \"![Screen Shot 2021-03-15 at 4.23.46 PM.png](data:image/png;base64,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hTXs33rrLX/Zunfvbi+88IItWLDAdtllF3eedM8991y3s/Bee+1lJ598crRzs3jxYjvmmGPsgAMOsP3228+uueYat0t3NME4B1hw2F/iwAMPtP/85z9FOqrkhdFv9sagbLgJ4Hpw1llnORcQysNI/2+//WZHHnmk+9txxx3tnXfecXciLCy8fPvtty7f/juf06ZNczskMzpIesnczW699VaDGeWGle/U/fLLLy7/3IvrbECI4LJA/i+66CJr3769S/+7775z10444QTXYaDc3loSr54Y0d5tt91cPonIcp0PPPCA20yQz2HDhtmZZ57p0oz33+WXX+7qGIYoGHTKyTv5oU4pP0LHgqV8qTvK4Ec2H3roIVcvLlDkvyeeeMIpi3ynbL/++qu7BIODDz7YDj/8cFf3vpzuYsx/yZ4Tnw/y+49//MOeeuqpaGzyddttt7nnoW3btu7Z5CKcCXv66ac7xqSRKD/z58+3K6+8MqrAMbp79tlnu3tUr17dTjrppGh9RG+cowclaRN4Xvhr166ds0Sm0iYke85Bh/WU9HheeK4OOeSQpETTbRNI7OijjzaeK54/3n86u7wXtAs77bST9e3b190zlTaBgPHeT5dAzH/ZbhPitYGlbRNow8855xzXFtL2MvhDOXh/t9lmm2j7SVHhxnvH7wc8UQKxSNFOeJkyZYoLw3fafAYzkETvoLsY8x9ufRdeeGH0LMrP7rvv7p6ZoGtyIb2/URg6EAERCDUBKS0lqB4afX5w3n77bWcV4YcNoQP9zTffuFHIzz77zK14hNXECyOmbFBHhwIFhJ21EX6cPv30U9cZ4Lsf2WYElPB0JseMGWM1atSIdtJvuukm90PD/egM4qqQaLScNOns9OzZ0+X5yy+/dJ0LFCSEH6cbbrjBzXHB5YCOTq9evWzbbbd1G4PxY0tnH4sLHRGu8Z18+Q4K6fh8c4zE5qdp06ZF0gtabv6O8ff/7777rn344YeuTCg/zZo1i671jxLFjzXlxWXqsssuiyo/WIX4MaZ8uCANHTrUJUjHoWbNmvbRRx+5jnaietpss81c2ehIYlXBjeK8885zHY4LLrjAunTpEk0zmF9/THlhyi7QPBt02FF8qCM6KeQXYZSTMn311VeuHPiQU15cp4jnub366quuHMThnD9PJ6hPnz42YsQIu//++12niDDxJNlzgnsM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dEIEREAEREAEREAEREAERCBMBKS0hKk2lBcREAEREAEREAEREAEREIENCEhp2QCJToiACIiACIiACIiACIiACISJgJSWMNWG8iICIiACIiACIiACIiACIrABASktGyDRCREQAREQAREQAREQAREQgTARkNISptpQXkRABERABERABERABERABDYgIKVlAyQ6IQIiIAIiIAIiIAIiIAIiECYCUlrCVBvKiwiIgAiIgAiIgAiIgAiIwAYEpLRsgEQnREAEREAEREAEREAEREAEwkRASkuYakN5EQEREAEREAEREAEREAER2ICAlJYNkOiECIiACIiACIiACIiACIhAmAhIaQlTbSgvIiACIiACIiACIiACIiACGxCQ0rIBEp0QAREQAREQAREQAREQAREIEwEpLWGqDeVFBERABERABERABERABERgAwJSWjZAohMiIAIiIAIiIAIiIAIiIAJhIiClJUy1obyUOYGvvvrKHnzwQfv000+L3HvFihX26quv2pAhQ2zOnDlFrumLCIhA7hEYO3asTZkyJZrx9evX28iRI+2JJ56wP/74I3peByIgAiIgAuEkIKUlnPWiXJUBgXvvvdduu+02q169ut100012zz33RO/asWNHGz16tC1evNjat29vv//+e/SaDkRABHKHwOrVq+3aa6+1ww47zF588cVoxvv162d9+/a1uXPn2gEHHGDz5s2LXtOBCIiACIhA+AhUDl+WlCMRKBsCS5YssSeffNJq1apl7dq1swsuuMB69Ohhs2fPtlatWlmfPn1cRn766Sf76KOP7NRTTy2bjOkuIiACGSPw8ssvW6NGjeyaa66xVatWuXSXLl1qgwcPth9++MGqVatmderUsdtvv93uuOOOjN1XCYmACIiACGSWgCwtmeWp1HKIwPXXX29r1661CRMm2P333x9VSho2bGj9+/d3JcGFBLeS1q1b51DJlFUREAFPoHPnznbxxRf7r+5z8uTJ1qZNG6ewcGLvvfe2iRMnFgmjLyIgAiIgAuEiIKUlXPWh3JQxgVGjRlnPnj1t3LhxtuOOO25w96uvvtq5h9HBkYiACOQHAVzCNtlkk2hhOJYLaBSHDkRABEQglATkHhbKalGmyorAgQceaPwx2Z7R1p9//tkqVvxbl7/zzjttwYIF9uijj5ZVdnQfERCBMiDQrFkzmzlzZvROs2bNsqZNm0a/60AEREAERCB8BGRpCV+dKEdlRIAJ9n/++ae7G5PxmbC7bt069/2xxx5zbmGDBg0qo9zoNiIgAmVFoHnz5jZt2jRDWUFeeOEF23fffcvq9rqPCIiACIhACQjI0lICaIqSHwTwc6ej0rJlSzch9+6777bKlSvbL7/8Yt26dbMWLVrYrrvu6grbtWtXu+yyy/Kj4CqFCBQ4gSpVqtiAAQPsiCOOsAYNGliNGjXslltuKXAqKr4IiIAIhJtAhchE4/XhzqJyJwIlI9C7d283mspeK4mEifi4iWy11VZRt7BEYXVeBEQgnARw9xo+fHjaLl68/wsXLrRNN900nAVTrkRABERABKIEZGmJotBBIRKoVKmSbb311oVYdJVZBAqeAO+/FJaCfwwEQAREIEcIaE5LjlSUsikCIiACIiACIiACIiAChUpASkuh1rzKLQIiIAIiIAIiIAIiIAI5QkBKS45UlLIpAiIgAiIgAiIgAiIgAoVKQEpLoda8yi0CIiACIiACIiACIiACOUJASkuOVJSyKQIiIAIiIAIiIAIiIAKFSkBKS6HWvMotAiIgAiIgAiIgAiIgAjlCQEpLjlSUsikCIiACIiACIiACIiAChUpASkuh1rzKLQIiIAIiIAIiIAIiIAI5QkBKS45UlLIpAiIgAiIgAiIgAiIgAoVKQEpLoda8yi0CIiACOUxg2rRpcXOf6HzcwDopAiIgAiKQMwSktORMVSmjqRLo2rWrxXZchg4dmmp0hRMBEcgBAk2bNrXevXsXySnv/YgRI4qc0xcREAEREIH8ICClJT/qUaUIEOjZs6d16NDBKS50YjimgyMRARHILwK8315xGTZsmDVr1szOPPPM/CqkSiMCIiACIuAIVFgfEbEQgXwjgKLiR1zbt29vw4cPz7ciqjwiUPAEUFpQVLwMGTJESouHoU8REAERyDMCUlryrEJVnL8JBDszKCwoLhIREIH8I4A7qHf/1Bhc/tWvSiQCIiACnoCUFk9CnwkJzJo1K+G1MF/wc1ty1crSqFGjMONV3vKQQC6+69OnT3eDEr169bIuXbrkXK3oPc+5KlOGRUAEyomAlJZyAh/229J5GTRokL3xxhtWrZKmPpVHfW3SYHOjQ9OtWzdr27ZteWRB9ywAAo888oiNHj3a/eldL/sKX7l2nTVs2NA6duzo3vWyz4HuKAIiIAK5QUBKS27UU5nmEkWFya1b1a1pjSN/kvIhsHLNWlu8crWtrb2RHXLEkerQlE815PVdu3fvbhPGfmvbblLH6larktdlDXPheNfn/rXS1taqZ1iMNEgR5tpS3kRABMqLgJSW8iIf0vsy4kpHptVm9dSJCUkd0aGZOG+R3ffAg+rMhKRO8iEbDEx8/P677l3Ph/LkQxnmLVthy6rXcRbufCiPyiACIiACmSQgv59M0syDtHAV2Xbj2lJYQlSX1SpXciPhjMBKRCATBHD/xKLK4IQkPAQ2q1Xd1ixaYLTDEhEQAREQgaIEpLQU5VHw37C0yE0kfI8BdbJg7u+WixOlw0dTOeI936xmNYEIIYEGEcWF+pGIgAiIgAgUJSClpSiPgv7mO8SM7EvCR4B6mT17dvgyphzlHIExY8ZYPc1hCWW9sRiCb4tDmUFlSgREQATKiYCUlnICH9bbavWgsNbM3/lSZybc9aPciYAIiIAIiIAIZIeAlJbscM3LVCtUqGCVKlV2fxUrlv7R2XrbFtZgy63ykpUKJQK5TKBipUrRd533vjRCWrvte0BpklBcERABERABEbDKYiACqRI45MTOdvDxJ9myJUusarXqtmTRn/bCwwNsyg8TU02iSLjd9t3f/pgzx+bOnFHkvL6IgAiUL4Hzb+hjjZo0s1UrV1i1GjVs1rSp9nj/O2zxwgVpZ6xylSp20rnn25jPPk47riKIgAiIgAiIgCdQ+uFyn5I+C4LAp++8Zb3P62rXdT3V3nvhaTvnmhstdiS2StWqcVlUrZZ44m/CONWrx02Lk1UTXMMKVKmy9PGE4HRBBFIg8MS9d7h3/Yazz7BZv06zThHFIyi8s7HvPtd597CuxJNE7znpJLrmrLsJ3udEceLdW+dEQAREQARym4B6drldf+Wa+++++tL+eelVVqNWbftr6RLbac+97ajT/mnr1q1z1phHb7vZli9bao232dZOu6iHrVyxwlAonh5wj8357VeX9y22bmKX33aPbbRJfftpwnf2xL13uvOtdt/DjjzljP/vyFSwQbfeZPNmzbS2+x1gO+7WzjberIHV2WgjW7d2rQ2+tY/Nmz3LxTvqtH/Zznvt7Y5/njjenn94oDvWfyIgAiUjsHbNGhv3xWfWqftFLgEUhTOvuMY22Wxzq1y1in34yov21Yfvu2snd7/QmrZoaWvXrrEpEyfYq8MGu/MVrIId1+Uc22HX3ZxSw3v50/hx7tphnU61Xf6xr7Po/D7jNxtyZ9+IhWelXT9wsH3x/ju210GHWvWaNe3r4f+1N58a5uJs1rCRdbn8386FrUKkTXnyvjttxi9T3DX9JwIiIAIikJ8EpLTkZ71mrVS4eqCkVKtezf5x8OE2a/o0p7DUrlvPju96rt397x7ObezAY0+wY7ucZc8+cJ8deOyJ9sGLz9mYzz+xrZpvY3Q4vNLSoFEju+eay42Ox5W332vMc/n158kRJWZTe6Rvb1u04A876LiT7IAjj7EXBz8YKVcFa91uL7utxwW2YN5c2/2AA+30S66w/tdeYa133zPSYdrBbr3sAlu/fr2de23PiJLT3kZ/OiJrPJSwCOQrAdzCeNfrbryxdTjmBPvx27+X4T369C7225SfbVC/m9z1q++63yaN+zaiwFS17drsbLdc1M0hIU71SBrrIu9i7Xr1bPzXXzolZud/7GMHRdoElBYUoNWrVtkdV13iBiDOu/4ma7lrWxv31RfOilMvMpjR95LuLtyVd9zr4kz6bqx1vfJae+nRh23K9xMigyLbRZSoa+3mC8/J16pQuURABERABCIEpLToMUiLwB7tD4p0KnazWnXqRRSK+Taw13UufpMW27vOxz6HHem+00lptn1Ldzzhm5FGR2eLxltHOiOfRzovX0XvOXH0N07BWB+xmPz8/Xhr8v9Ky9cj/mvb77Sr7XXwobZV0+aRMNEoNvm7b53Cwhn85DtHRoBxIdm2dRtjVPjQk05xgXE5abZDSykt/0OnIxFImcDxXbvZqhXLI1bNzd179vqTQ1zcbVu1sWmTfrTDTz7NfV+9aqVtvV0LGz/yS1u9cpWdffV17h3/7N03XZuAGycWVz/3bfK4sdb5vIv/P+4qG/nRB7b7/h2s/uZbRJSbjSJK0ibRPH714XvRcN9+/qk1b9nKpv80yS3gsV3rnYw/ZOP6m0bapLoRC+9i913/iYAIiIAI5B8BKS35V6dZLdEXH7xrb0Q6L6z6dVnfOyPKxDp3vwoVKtpfkY7J9IiVxAujpQiWjikRhaTNHv+w0y++3MaN/MLee/4Zd23t6jXuk//WrVmLIcWNsF4Rsbp8F1FwSI/zjKYWJ+Rhwbzfo3kg7uIF6U8cLu4+ui4ChUDg2QfutR/HjolYVA9zFk1cMRHeM+a4YOlEeM/m/DrduYXeceXFtv3Ou0asoXvaUZGBitt6XGhr1qyO/P3vPcd1DHcxZKP69e2yfnfbR6+9FFGEfrDNI+1KIllv6/+OFxmMIC/Btmbw7Tfb6tWrEkXVeREQAREQgTwgoIn4eVCJ5VEEVvz6LqKUHNbp79FWRj83icwzmTn1F/thzChbvnSp+07e9jviaKtcuap9+s6bzqWj5S5tk2Z5o003s7qR+SrvvvCMTRz1tRuBDUZoEbHAMKcF2W2f/W3mtF/+34d+fMSa08QmRTpa5KFW7TrOPSUYV8ciIALpEcASUnejjZ1bJjGxiG5cfzP3jvGuNW6+rXv/GMjY+5AjnKLz4qAHbX5knhnuoMmEd/m3KT/ZJ2+9bsxB23yrxkWC73XQYe47bmS77r2/s9as+Osv+2Pu75HBjDUuDzOm/OzcQldH5sFIREAEREAE8peALC35W7dZL9nbzz5p193/iH369hs2//fZ9vJjj9jFfW51LiK4ZjE5Fvnzj/l2fs+b7c/586xWZO7Li4MeSJq3hZER3O8jSseNDzzq3D1wRQnKL5Ells+66j8RhaRmZHT374n4XP8u4jPfdPsd7Nr7HnIjsQvnz7dhd98WjKpjERCBNAmwsMbrTzwWmUh/duS9/MbefHJoZE7Jf+zf9wx07yAun4sXLoy4gC2LKDZ72D8OOdxZYBnYYDJ+pSqJf2YYlNj/yI527b0PRlzRVhoT8YvKertuwCOROXQ13ET8yZH5LMiQO/vZmZdfEzla7+bVvBHJE/PYJCIgAiIgAvlLoEKkoVdLn7/1m1bJ2G290/HH2W4N/+dTnlYC/x+Yybcrli/fIGqNmrVs+V/LNjif6AS+8LiP4U7ihYn1LXbaxZ4Z2N+tKMSoa6z8vTFeJedPH3stl79PnLfIelzzH+vYsWMuF0N5DwGB3r172w+fDbfNaiVeUry4bLKE+ZrVq51bWDAsVhEUHeaXpSqsDrYy0mYEf45uiAxa3H/D1bZ08WJ3Pl56idqaVO8bxnArI+6wsytUtzfeeCOM2VOeREAERKDcCCQeAiu3LOnGuU4gnsJCmdJRWAi/KrJEcjKJp7AQHn9373+fLL6uiYAIlJwAyxLHE1YDS1cSvcukg2KUSBK1NYnC67wIiIAIiEDuEpDSkrt1V5A5Z1K/ljAuyKpXoQuMQJ8Lzi6wEqu4IiACIiACyQhoIn4yOromAiIgAiIgAiIgAiIgAiJQ7gSktJR7FSgDIiACIiACIiACIiACIiACyQjIPSwZnQK9Nm9Z8rkkBYql3IvNBF2JCGSKwKKVieeKZOoeSid9AivWRva+KsUCCenfUTFEQAREIDcISGnJjXoqs1yujPxgtty3Q5ndTzdKncCyUaNSD6yQIlAMga1btrZGjRoVE0qXy4PAKL3r5YFd9xQBEQg5ASktIa+gss5ew4YNrWfPnmV9W90vBQLdu3dPIZSCiEBqBI4++mgtn50aqjINxdLzUlrKFLluJgIikCMENKclRypK2RQBERABERABERABERCBQiUgpaVQa17lFgEREAEREAEREAEREIEcISClJUcqStkUAREQAREQAREQAREQgUIlIKWlUGte5RYBERABERABERABERCBHCEgpSVHKkrZ3JDA+++/b9OnT9/wwv+f+fTTT+2HH35IeF0XREAEwk9g/fr1Nnjw4KQZHTp0qK1erSWck0LSRREQARHIcQJSWnK8Ass7+//9739t0003tXXrInsLlLE88sgjtuWWW7q7orxcdNFF1rVrV/v222/duebNm9uAAQPKOFe6nQjkJ4HevXuXy2pj7777rq1d+789ih577DE75ZRT7Pbbb4+er1mzpr3wwgv5CV6lEgEREAERcASktOhBSIsAo56LFi2Kxlm1apX98ccf7vvy5cuj5/0Bo59//fWX/2rE938rVmy4ieWyZcuiHZFopDgHX3zxhe2xxx5WufLfq3b/85//tPPPP9+uv/56O+OMM1waKDQrV660+fPnx0lBp0RABJIRWLp0aZF3kXfTv/vx3l1/zafp3/M1a9bEtYIsXrzYB036+fjjj9u//vUvF+bll192gxIMWCxcuDA6KHHCCScY1yQiIAIiIAL5S0BKS/7WbcZLNnz4cNt8881t5513tp122imqrHAjLBy1a9e2gw8+OGp1ueKKK2yLLbawjTbayLp16+byc+GFF9pee+1lLVu2tDp16hjfETo2nTt3tsaNG9tmm21mjz76qDuf6L9BgwZF06RzxKhrq1atbJtttnHpekXpvPPOs4ceeihRMjovAiIQQ4D36fjjj3fvEu/jXXfdFQ3x559/Wtu2ba1evXpugIALuGBuv/32zurJPk9jx4514XkfjzvuOGeJrV+/vqF8IOxDsuuuu1rTpk1t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165dbZtttokGI8XYaQAAQABJREFUoV35+eefo+0AqxdmQ9j76L777stG0kqznAho3lo5gddtRUAEyoWA9mlJE/sdd9xhn3zyif36668JY9I5WL16ta1bty4aZvfdd3fWjvfffz96buDAgfaf//wn+j32oFatWkaHJh2pUKGC60wtX748nWgKm8cEGIlV5yazFdy8eXPr27evXX755UkTrlGjhsW+i8cff7xRJ1OmTInG3W677aIbWEZP/v8Blh3alNh0YsPFfifOmjVrbO3atbGX9F0EQkUAdz8WV9C8xVBVizKTpwTYrJeB7LZt2+ZcCaW0pFll7BfCLsQHHnigrV+/vkhsOijsI7D99ts7t68nnnjCbrrpJuvUqZP16NHDuZS0atXKTjrppCLxYr/stNNO9txzz7n0WSnnr7/+ssGDB9s777zjNrojfb/KGZ/sGI2wEd6JJ57ovv/444/OFYS4ksIlIGUlO3X/0EMP2ZFHHmlvvvmmffTRRxvc5LjjjrNbb73VKQ0oGyeffLJNnTrVncPqusMOO9iQIUOsX79+G8QNnvjnP/9pN998sy1dutTeeust904Td+utt7Ynn3zS2rVr54IzKHL77be7dgk3sGeeecYpqvw4cZ8bbrghmKyORSA0BNhq4LXXXrMtttjC+O2TiIAIZJfAl19+ab///rvNnTvX8MTJJeVFSkuaz8agQYPsiCOOcCOsd911V5HYt9xyi3PzYCSVHZw/++wz+/rrr+2FF16wxo0bO0tLcDdnFB/cy7ygmKBs0Bm58sorXSdlxx13tG+++cYpLYTDkhIU/x2FacWKFbbHHnu4jhKdqcMOO8xeeeWVYHAdi4AIZIAAFtB//etf9uyzz9puu+1WZACjQYMGhhV1r732st9++81QYFAi+I5rGe/0PffcY8OHD4/m5LbbbotaRLDi4saFwoEiwjtNOmeeeaZTVnyk4Bw42gHfFuB69vLLL7t74sJGXO6nVbc8ufz6xGrXNLJnSy5K9+7d3e8Vv3kSERCBsiWA5w8DcOedd17OKC5SWkrwjJxzzjnG8shBVy+SYQQVf3WE1YewlnDu3Xffdedi/8MUjkLiZdGiRVa3bl1ntmNUFfn+++9t5MiRPkjCT0Zzhw4daqeddpo1a9bM6DjR6ZGIQC53asJcewxI8M498MADdv7550ezipLx1VdfOWWB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aapKTwTyk0D9+vXzs2AqVVYJaE5LVvEqcREQAREQAREQAREQAREQgdISkNJSWoKKLwIiIAIiIAIiIAIiIAIikFUCUlqyileJi4AIiIAIiIAIiIAIiIAIlJaAlJbSElR8ERABERABERABERABERCBrBKQ0pJVvEpcBERABERABERABERABESgtASktJSWoOKLgAiIgAiIgAiIgAiIgAhklYCUlqziVeIiIAIiIAIiIAIiIAIiIAKlJSClpbQEFV8EREAEREAEREAEREAERCCrBKS0ZBWvEhcBERABERABERABERABESgtASktpSWo+CIgAiIgAiIgAiJQIASmTZsWt6SJzscNrJMiUAICUlpKAE1RREAEREAERKC8CHTt2tWCHcShQ4cafxIRKCsCvXv3jt6KZ5FnsmnTptFzOhCBbBConI1ElaYIiIAIiIAIiEB2CHTp0sU6dOjgFBc6iiNGjLCpU6dm52ZKVQRiCPhnjucOadasmQ0ZMsQd6z8RyCYBKS3ZpKu0RUAEREAERCDDBNq3b+9GtRnhxsKiDmOGASu5Ygn07NnTKc4ERIk588wzi42jACJQWgJyDystQcUXAREQAREQgTImQKcRUYexjMHrdo4AijN/iH8W3Rf9JwJZJCBLSxbhKmkREAEREIHwEpg1a5bNnj3b+MxFadu2res4vvHGG7mYfWvUqJFRhkIWnr3Ro0fnJALqbvz48Va/fn3LxWeQ569hw4buOczJCijATEtpKcBKV5FFQAREoJAJ0FF88803bcCAAQ7DkiVLchbHSy+9ZPzlolStWtVatGhhHTt2tG7duuViEUqcZxSV7t27W7VKFa1utSolTqe8I7bbcXt74I5byzsbJbr/yrXrbPHK1e75O/fcc6W8lIhi2UaS0lK2vHU3ERABERCBciZAZ5FOI1YWSfkSYF7O9OnTXSYKRXHBKnHrzX2s1Wb1clphKd8nJzN3X7lmrX378YfWfdQoe/jhh6W4ZAZr1lLRnJasoVXCIiACIiACYSOAwjJhwgQpLCGpmFWrVtmkSZOctShX3aTSQYmVj+WCpbCkQy17YatVrmSN69a0SssWuXrJ3p2UciYISGnJBEWlIQIiIAIikBME6Bgzui8JDwEUF+rlkUceCU+mspSTQYMG2bYb1zY6y5LwEGhQs5r9NOG7nJ1fFB6S2c2JlJbs8i3z1BP9GCc6X+YZ1A1FQATKhIDfQyF4s0JvB3DL+eOPP4JIdBwSAswrytUFEdJBOCrihpTLc1jSKWsuhfVKZCE8g7lUL7F5ldISSyQPvrPpmBc6KXxnWUyJCIhAYREI7lqNEhP8Xlgk/i6t5rCEt9axthRC/RRCGcP7lCXPGcqk6ic5o/K+qon45V0DGb4/ygl/7FDrP7XxWIYhKzkRyAECvP9du3Z17QCDF2xCqF3Tc6DilEUREAEREIG4BGRpiYslt0/6jZ68K4h2qs3t+lTuRaAkBFBaaAuwsNAW0A5wTlI8gQoVKth2221ntWrVKj5wghC77767bb/99gmu6rQIbEiA565SpcrRvw1DpHdmo/qb2rat2qQXSaFFIMQEpLSEuHJKmjU6Jl5RkZWlpBQVTwRynwA7VtMeIH4ww33RfwkJ9OjRw3799Vd7/PHHbeLEifb0009b7dq1E4b3F/bff3879NBD/Vfr3LmzHXjggdHvOhCB4gg03b6l3fnsK3b9wEHW86HHrNcjw2z/IzsWFy3h9S2bNbf9ShE/YcK6IALlREDuYeUEvrjbMhkM38rS7CVAZ4X170vix77bbrtpt+LiKknXRaAMCNAW+HagJP7WtAP8DRs2rES5LaS24OSTT3Yb/u2yyy5uwn6lSpXsoYcesn79+tnFF18c5VetWjVbs2aNrV27NnoOy0q9evXs/fffj57zB9WrV7cVK1b4r9HPGjVq2PLly6PfgwektWjRouApHRcAgd9++dnu/ncPV9LNGjayq+8e6Fa1mv3r9Gjpq1Staqsjc4BipWrkuVy1cmXsafe9JHFKkl7cm+ukCGSIgJSWDIHMZDJ0UNhLgNVUSrtTM5sllUT4Ufa7FWvDpZIQVBwRKD0BloBl13bagZUJOiOp3oW9MEoitAV16tSxiy66KO93LWdzw8suuyy6whhKyZVXXmkHHXSQQ1e5cmV75plnnBLYsGFDw5J9ww03WKdOnQwLDUpOq1at7KSTTnLhOf7iiy9syy23dG56Xbp0cee33XZbe+qpp6xKlSouDpbxb7/91k455RSXFq5pkydPjqZTknpTnNwnMG/2LJv+0yRr0GhLQ2lBiely+b+d+1iFihXtyfvutBm/TLGatevYWVdfZ5UjzxPP1EevvWSjP/3YAageUYy7/aeXbbFVY1u0cIENvetWW7TgD9tkswb2r0haVatWs7obb2zPPXS/jf/6q8jxJnZBz5tt2qQfrXnLVlY1onC/8tjDNu6rL1x6O+25tx112j9t3bp1tizSLj162822fNnS3IetEuQEASktIasmFBYmz06L+KCXVmHJRNHIAwpUr169rG3btplIUmmIgAikQID3jvkodF7LWxjAoA1gyWD+8lGYT0Abx5K0CJ0/rB3IJ5984j632WYbe/nll53igvXkt99+s3vuucdeeOEFa9y4sQsfdMND+dh3332tYqSDOXLkSMMaQ/rPPvusU3I+/fRTd08UoR122MHIA1Yewi1cuNDdU/8VFgGelRq1artnZttWra3Jti3ssQnjHYSuV15rLz36sE35foI13mY7O/OKa+3mC8+xXfbe1+bOnGHPPzwgosDUtnbt/1ayibRVs22t36XdbenixXbSuefb7vt3sA9ffdEabLmVvTbsUZv64/e2deQep13UwyktxNmyaXN76+nH7dkH73MK02X97rJJ3421ypWr2PFdz3WWoCWL/rQDjz3Bju1ylj37wH1Ek4hA1glIack64vRuQEclLAoLOccd5csvv4x2WNIrjUKLgAiUhACDF7x3YVBYyL9fjpb8YP3BIpFvsn79euMPxQFp06ZNdLNDlBk6k1irsL5gddp8883d8RZbbGELFiyIi+Ptt992I9KMSqP4tGvXzqXBBP32kflG/CFbbbWV1a9f3x1//PHHUlgcicL8r+HWTa1HvzutSsQCUqtuXev/nyvsr6VLDIsJisZ2rXdyf9DZODLRvladuvbzxPF26ImdrVO3C23CN1/ZJ2+9HoU3bfIPTmHhxORxY63tAR3ctUnjvrUmLba3g4/vZHU22sjqRSwsXlBwsLogc2fNtNnTp0WUn22sWiQPuKXtc9iR7lrtiFLfLDIPRyICZUWgYlndSPcpnoDf+CwMFpZgbtmMjc4KHSmJCIhA9gm8+eabodxojwGVfLW0UKtjxoxxVo7g8QknnGA///yzU2hw+3ryySfdXJPhw4fbn3/+mfRhQNnzsnr1aqcQoRQxH+abb76J/pGun/MSnCfj4+qzcAjMnPaL9b3kvIgF5VxbFlEesLo4iTw36yIK8/SfJ0f/Bt9+s61evcpZWfpe0t1ZTfY57Ci7sFffKDCeNS9r166xCpF/yEnnXmAHHHWszZszyyZFlJlksv7/L1aoUNH+iriC+TxM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LglgtOQSGMUhAAEIQAACEIAABCAAgYIlgNFSsLxpDQIQgAAEIAABCEAAAhDIJQGMllwCozgEIAABCEAAAhCAAAQgULAEMFoKljetQQACEIAABCAAAQhAAAK5JIDRkktgFIcABCAAAQhAAAIQgAAECpYARkvB8qY1CEAAAhCAAAQgAAEIQCCXBDBacgmM4hCAAAQgAAEIQAACEIBAwRLAaClY3rQGAQhAAAIQgAAEIAABCOSSAEZLLoFRHAIQgAAEIAABCEAAAhAoWAIYLQXLm9YgAAEIQAACEIAABCAAgVwSwGjJJTCKQwACEIAABCAAAQhAAAIFSwCjpWB50xoEIAABCEAAAhCAAAQgkEsCGC25BEbx5CKwdOlS++6773LcqdmzZ9vChQtzVH7fvn02ZswYGzBggP300085ukeFtm3bZuPHj89QfsuWLfb+++/b8OHDbcOGDRmucQIBCEAg1gRiqSvXrVtnI0aMsGHDhtnatWtz3JVwMs2dO9fp3GnTpuW4HgpCAAKJQQCjJTHGCSljQOCtt96yli1b2kMPPZRt7bt377b77rvPWrdube+991625VWgY8eONnbsWCtevLh16tTJPv/882zvmzlzpp122mnWvn37YFkZLC1atLAff/zRfv/9dzvllFOcYRMswAEEIACBGBKIpa5cv369nXHGGbZ69WpbtWqV028bN27MtjfhZNIE0a233urufeCBB5zxkm1FFIAABBKGQHrCSIqgEIgigSVLltikSZPczN6jjz4arHnPnj2Wnv73/xbe+QcffGBVqlSxe++913bt2pVteRWYPHmyMzJ0XKpUKed1adWqlU7Nqzfz8XPPPWdvv/22NWvWzJXTn6efftoZQLfccovLa9y4sfO2lCxZMliGAwhAAAKxIBBrXan67777buvQoYMT/9tvv7UZM2a4yZtQPamL3nk4mbZu3WrSn7NmzTLpxosuusi++uqrWCChTghAIE4E8LTECTzNxpdAjRo1bNCgQe7hFirJ0KFD7cEHH3RZMlS6devmjvVA9YyG0PLXX3+986Yo784777SRI0cGL1944YX21FNPOeNIoQ96iCrJa6OZxUWLFpketGeffbY71rU333zTatasqcNg0kO4YsWKdsEFF7g6ihQp4gyoYAEOIAABCMSIQKx15fHHH+8MlsWLFztdKm9LkyZNXG8i6eNw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QQABuwqcPXtW0ms+xrVr12TChAlpRjd+/Pg0OxcnQgABBBAIHgGCluCpS64EAQRsKqBPvGrXrl28pR84cKA0b97c6z4bN26UkydPet2W0Mr58+dLdHS0e7cvvvhCnnjiCendu7f8/fff7vW+Fg4cOCB9+vSRtm3byrJly+Ls9t///le+++479/otW7bI/v373e9ZQAABBBBAIDECBC2JUWIfBBBAIBEC2muhP1aylmOvv3DhQoxAYdCgQTJ9+nTrMImKipLIyEiTl5WHHnPmzBmzz6VLl9z76kLdunVl3rx57nV6jLWve6WPhU8//dQdMK1bt06mTp1qel4aNmwoTz/9tI+jrq/WMrZv314eeOABGTp0qLz22mvy119/uY/57LPP5Mcff5Sff/7Zva5Hjx4yatQo93sWEEAAAQQQSIwAQUtilNgHAQQQSITAzJkzJUeOHCYg0Z6K3Llzm+VevXpJkyZNTDDSqlUrKVasmBQoUEAmTpxoctWeiscee8wsa/CSP39+ueGGG+TBBx+UChUquM98+vRpqVGjhsn39ddfN+s1YLh48aJ07tzZBBtLliyRQoUKSdWqVaVKlSrx9sBs3bpVSpcuLVmzZjV5aY9L8eLFJXv27FK+fHlTXt2gAdCcOXPMPho8zZo1yyz/8ssvUqlSJSlYsKDs27dPli5dasquG7Xn55NPPpGXX37Z7Gv90nwPHTok58+ft1bxigACCCCAQIICBC0JErEDAgggkDiBRo0aSUREhGzbtk2WL18uOXPmFB26tX79emncuLF8/vnnptdhz5495g967XXQ3grP1LNnT3nyySdFh13F7i3RP/a//vpr6du3r7z//vty5coVE0Bky5ZNRo8eLR06dDA9NhUrVpTNmzfL4MGDRQMdX2nSpEnSqVMn9+bbb7/dBBvVq1cX7WmxvqgxV65cMnfuXBNktW7dWooWLWqO2bFjh2jvzMcff2yCmnvuuUcuX75str3yyisyZMgQyZgxozt/a6Fly5by1VdfWW95RQABBBBAIEEBgpYEidgBAQQQSJyA9o5Uq1ZNtLfjxIkTJojQZQ1cNGj57bffTCDSoEEDExDccsstcvDgQXfm2vugx+kf/9pLU79+ffc2XShbtqzpCalXr57pqTh37pxkyZLF7JM5c2YJCwuTZ5991gRC2lszYsSIOEGRZ4YamCxevNi9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)\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"ondevice_recommendation.ipynb\",\n      \"provenance\": [],\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "lite/examples/recommendation/ml/requirements.txt",
    "content": "pandas>=1.0.5\ntensorflow>=2.2.0\nabsl-py>=0.1.6\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/README.md",
    "content": "# Reinforcement Learning Android sample\n\n\nThis reference app is a simple board game (called 'Plane Strike') in which you\nplay against an agent trained by reinforcement learning. The agent runs the\nreinforcement learning model on-device using TFLite.\n\nThe game rule for Plane Strike is very simple. It is a turn-based board game and\nis very similar to the\n[Battleship game](https://en.wikipedia.org/wiki/Battleship_\\(game\\)). The only\ndifference is that Battleship allows you to place battleships (2–5 cells in a\nrow or a column as 'battleships'); you can place multple ships. Plane Strike\ninstead allows you to place a ‘plane’ on the board at the beginning of the game.\nIn the animation we can see 2 boards (the top one is the agent's board and the\nbottom one is yours), each of which has a plane on the board. Of course you have\nno visibility on the agent’s plane location when the game starts. In a live\ngame, the agent’s plane is hidden at the beginning; you need to guess out all\nthe plane cells before the agent does to your plane cells. Whoever finds out all\nof the opponent's plane cells first wins. Then the game restarts.\n\nAt the beginning of the game, the app will randomly place the planes for the\nagent and the player. You can see the plane as 8 blue cells in your board. If\nyou are not happy with the placement, just reset the game so that the plane\nplacement will be changed.\n\nDuring the gameplay, if you, as the player, tap a cell in the agent's board at\nthe top, and that cell turns out to be a 'plane cell', that cell will turn red\n(think of this action as a hit); if it's not a 'plane cell', the cell will turn\nyellow as a miss. The app also tracks the number of hits of both boards so that\nyou can a quick idea of the game progress.\n\n![SCREENRECORD](reinforcementlearning.gif)\n\n## Requirements\n\n*   Android Studio 4.2 or above (installed on a Linux, Mac or Windows machine)\n*   An Android device, or an Android Emulator\n\n## Build and run\n\n### Step 1. Clone the TensorFlow examples source code\n\nClone the TensorFlow examples GitHub repository to your computer to get the demo\napplication.\n\n```\ngit clone https://github.com/tensorflow/examples\n```\n\n### Step 2. Import the sample app to Android Studio\n\nOpen the TensorFlow source code in Android Studio. To do this, open Android\nStudio and select `Import Projects (Gradle, Eclipse ADT, etc.)`, setting the\nfolder to `examples/lite/examples/reinforcement_learning/android`\n\n### Step 3. Run the Android app\n\nConnect the Android device to the computer and be sure to approve any ADB\npermission prompts that appear on your phone. Select `Run -> Run app.` Select\nthe deployment target in the connected devices to the device on which the app\nwill be installed. This will install the app on the device.\n\nTo test the app, open the app called `Reinforcement Learning` on your device.\nRe-installing the app may require you to uninstall the previous installations.\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/app/build.gradle",
    "content": "apply plugin: 'com.android.application'\napply plugin: 'de.undercouch.download'\n\nandroid {\n    compileSdkVersion 29\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.reinforcementlearning\"\n        minSdkVersion 21\n        targetSdkVersion 29\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    aaptOptions {\n        noCompress \"tflite\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation 'org.tensorflow:tensorflow-lite:0.0.0-nightly-SNAPSHOT'\n    implementation 'org.tensorflow:tensorflow-lite-select-tf-ops:0.0.0-nightly-SNAPSHOT'\n    implementation 'androidx.appcompat:appcompat:1.1.0'\n    implementation 'com.google.android.material:material:1.1.0'\n    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'\n}\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          xmlns:tools=\"http://schemas.android.com/tools\"\n          package=\"org.tensorflow.lite.examples.reinforcementlearning\">\n\n  <uses-sdk />\n\n  <application\n          android:allowBackup=\"true\"\n          android:icon=\"@mipmap/ic_launcher\"\n          android:label=\"@string/tfe_rl_app_name\"\n          android:roundIcon=\"@mipmap/ic_launcher_round\"\n          android:supportsRtl=\"true\"\n          android:theme=\"@style/AppTheme.ReinforcementLearning\"\n          tools:ignore=\"GoogleAppIndexingWarning\">\n\n    <activity android:name=\".MainActivity\"\n              android:exported=\"true\">\n      <intent-filter>\n        <action android:name=\"android.intent.action.MAIN\"/>\n        <category android:name=\"android.intent.category.LAUNCHER\"/>\n      </intent-filter>\n    </activity>\n  </application>\n\n</manifest>\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/app/src/main/java/org/tensorflow/lite/examples/reinforcementlearning/BoardCellAdapter.java",
    "content": "/*\n * Copyright 2020 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.reinforcementlearning;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\nimport android.widget.TextView;\n\n/** The gridview adapter for filling the board. */\npublic class BoardCellAdapter extends BaseAdapter {\n  // We always use square board, so only one size is needed\n  private static final int BOARD_SIZE = 8;\n\n  private final Context context;\n  private final BoardCellStatus[][] board;\n  private final HiddenBoardCellStatus[][] hiddenBoard;\n  private final boolean isAgentBoard;\n\n  public BoardCellAdapter(\n      Context context,\n      BoardCellStatus[][] board,\n      HiddenBoardCellStatus[][] hiddenBoard,\n      boolean isAgentBoard) {\n    this.context = context;\n    this.board = board;\n    this.hiddenBoard = hiddenBoard;\n    this.isAgentBoard = isAgentBoard;\n  }\n\n  @Override\n  public View getView(int position, View convertView, ViewGroup parent) {\n    TextView cellTextView = new TextView(context);\n\n    int x = position / BOARD_SIZE;\n    int y = position % BOARD_SIZE;\n\n    if (board[x][y] == BoardCellStatus.UNTRIED) {\n      // Untried cell\n      cellTextView.setBackgroundColor(Color.WHITE);\n      if (hiddenBoard[x][y] == HiddenBoardCellStatus.OCCUPIED_BY_PLANE && !isAgentBoard) {\n        cellTextView.setBackgroundColor(Color.BLUE);\n      }\n    } else if (board[x][y] == BoardCellStatus.HIT) {\n      // Hit\n      cellTextView.setBackgroundColor(Color.RED);\n    } else {\n      // Miss\n      cellTextView.setBackgroundColor(Color.YELLOW);\n    }\n\n    cellTextView.setHeight(80);\n\n    return cellTextView;\n  }\n\n  @Override\n  public int getCount() {\n    return BOARD_SIZE * BOARD_SIZE;\n  }\n\n  @Override\n  public Object getItem(int position) {\n    return null;\n  }\n\n  @Override\n  public long getItemId(int position) {\n    return 0;\n  }\n}\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/app/src/main/java/org/tensorflow/lite/examples/reinforcementlearning/BoardCellStatus.java",
    "content": "/*\n * Copyright 2020 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.reinforcementlearning;\n\n/** enum for recording visible board status */\nenum BoardCellStatus {\n  HIT,\n  UNTRIED,\n  MISS\n}\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/app/src/main/java/org/tensorflow/lite/examples/reinforcementlearning/Constants.java",
    "content": "/*\n * Copyright 2020 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.reinforcementlearning;\n\n/** The class that holds all the constants. */\npublic final class Constants {\n  // We always use square board, so only one size is needed\n  public static final int BOARD_SIZE = 8;\n  public static final int PLANE_CELL_COUNT = 8;\n  public static final boolean USE_MODEL_FROM_TF = true;\n  public static final String TF_TFLITE_MODEL = \"planestrike_tf.tflite\";\n  public static final String TF_AGENTS_TFLITE_MODEL = \"planestrike_tf_agents.tflite\";\n  public static final String TAG = \"TfLiteRLDemo\";\n\n  // Cell status values to feed into the model\n  public static final float CELL_STATUS_VALUE_HIT = 1;\n  public static final float CELL_STATUS_VALUE_UNTRIED = 0;\n  public static final float CELL_STATUS_VALUE_MISS = -1;\n\n  private Constants() {}\n}\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/app/src/main/java/org/tensorflow/lite/examples/reinforcementlearning/HiddenBoardCellStatus.java",
    "content": "/*\n * Copyright 2020 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.reinforcementlearning;\n\n/** enum for recording visible board status */\nenum HiddenBoardCellStatus {\n  OCCUPIED_BY_PLANE,\n  UNOCCUPIED\n}\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/app/src/main/java/org/tensorflow/lite/examples/reinforcementlearning/MainActivity.java",
    "content": "/*\n * Copyright 2020 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.reinforcementlearning;\n\nimport android.os.Bundle;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.Button;\nimport android.widget.GridView;\nimport android.widget.TextView;\nimport android.widget.Toast;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.Random;\nimport java.util.Timer;\nimport java.util.TimerTask;\n\n/** The main activity to provide interactions with users. */\npublic class MainActivity extends AppCompatActivity {\n\n  private int agentHits;\n  private int playerHits;\n  private final BoardCellStatus[][] playerBoard =\n      new BoardCellStatus[Constants.BOARD_SIZE][Constants.BOARD_SIZE];\n  private final HiddenBoardCellStatus[][] playerHiddenBoard =\n      new HiddenBoardCellStatus[Constants.BOARD_SIZE][Constants.BOARD_SIZE];\n  private final BoardCellStatus[][] agentBoard =\n      new BoardCellStatus[Constants.BOARD_SIZE][Constants.BOARD_SIZE];\n  private final HiddenBoardCellStatus[][] agentHiddenBoard =\n      new HiddenBoardCellStatus[Constants.BOARD_SIZE][Constants.BOARD_SIZE];\n\n  private GridView agentBoardGridView;\n  private GridView playerBoardGridView;\n  private TextView agentHitsTextView;\n  private TextView playerHitsTextView;\n  private Button resetButton;\n\n  private PlaneStrikeAgent agent;\n\n  @Override\n  protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    setContentView(R.layout.activity_main);\n    agentBoardGridView = (GridView) findViewById(R.id.agent_board_gridview);\n    playerBoardGridView = (GridView) findViewById(R.id.player_board_gridview);\n    agentHitsTextView = (TextView) findViewById(R.id.agent_hits_textview);\n    playerHitsTextView = (TextView) findViewById(R.id.player_hits_textview);\n    initGame();\n    try {\n      if (Constants.USE_MODEL_FROM_TF) {\n        agent = new RLAgent(this);\n      } else {\n        agent = new RLAgentFromTFAgents(this);\n      }\n    } catch (IOException e) {\n      Log.e(Constants.TAG, e.getMessage());\n      return;\n    }\n\n    playerBoardGridView.setAdapter(\n        new BoardCellAdapter(this, playerBoard, playerHiddenBoard, false));\n    agentBoardGridView.setAdapter(new BoardCellAdapter(this, agentBoard, agentHiddenBoard, true));\n    agentBoardGridView.setOnItemClickListener(\n        new AdapterView.OnItemClickListener() {\n          @Override\n          public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {\n            // Player action\n            int playerActionX = position / Constants.BOARD_SIZE;\n            int playerActionY = position % Constants.BOARD_SIZE;\n            if (agentBoard[playerActionX][playerActionY] == BoardCellStatus.UNTRIED) {\n              if (agentHiddenBoard[playerActionX][playerActionY]\n                  == HiddenBoardCellStatus.OCCUPIED_BY_PLANE) {\n                agentBoard[playerActionX][playerActionY] = BoardCellStatus.HIT;\n                playerHits++;\n                playerHitsTextView.setText(\"Agent board:\\n\" + playerHits + \" hits\");\n              } else {\n                agentBoard[playerActionX][playerActionY] = BoardCellStatus.MISS;\n              }\n            }\n\n            // Agent action\n            int agentStrikePosition = agent.predictNextMove(playerBoard);\n            if (agentStrikePosition == -1) {\n              Toast.makeText(\n                      MainActivity.this,\n                      \"Something went wrong with the RL agent! Please restart the app.\",\n                      Toast.LENGTH_LONG)\n                  .show();\n              return;\n            }\n            int agentStrikePositionX = agentStrikePosition / Constants.BOARD_SIZE;\n            int agentStrikePositionY = agentStrikePosition % Constants.BOARD_SIZE;\n\n            if (playerHiddenBoard[agentStrikePositionX][agentStrikePositionY]\n                == HiddenBoardCellStatus.OCCUPIED_BY_PLANE) {\n              // Hit\n              playerBoard[agentStrikePositionX][agentStrikePositionY] = BoardCellStatus.HIT;\n              agentHits++;\n              agentHitsTextView.setText(\"Player board:\\n\" + agentHits + \" hits\");\n            } else {\n              // Miss\n              playerBoard[agentStrikePositionX][agentStrikePositionY] = BoardCellStatus.MISS;\n            }\n\n            if (agentHits == Constants.PLANE_CELL_COUNT\n                || playerHits == Constants.PLANE_CELL_COUNT) {\n              // Game ends\n              String gameEndMessage;\n              if (agentHits == Constants.PLANE_CELL_COUNT\n                  && playerHits == Constants.PLANE_CELL_COUNT) {\n                gameEndMessage = \"Draw game!\";\n              } else if (agentHits == Constants.PLANE_CELL_COUNT) {\n                gameEndMessage = \"Agent wins!\";\n              } else {\n                gameEndMessage = \"You win!\";\n              }\n              Toast.makeText(MainActivity.this, gameEndMessage, Toast.LENGTH_LONG).show();\n              // Automatically reset game UI after 2 seconds\n              Timer resetGameTimer = new Timer();\n              resetGameTimer.schedule(\n                  new TimerTask() {\n                    @Override\n                    public void run() {\n                      runOnUiThread(() -> initGame());\n                    }\n                  },\n                  2000);\n            }\n\n            agentBoardGridView.invalidateViews();\n            playerBoardGridView.invalidateViews();\n          }\n        });\n\n    resetButton = (Button) findViewById(R.id.reset_button);\n    resetButton.setOnClickListener(\n        new View.OnClickListener() {\n          @Override\n          public void onClick(View view) {\n            initGame();\n          }\n        });\n  }\n\n  private void initGame() {\n    initBoard(playerBoard);\n    placePlaneOnHiddenBoard(playerHiddenBoard);\n    initBoard(agentBoard);\n    placePlaneOnHiddenBoard(agentHiddenBoard);\n    agentBoardGridView.invalidateViews();\n    playerBoardGridView.invalidateViews();\n    agentHits = 0;\n    playerHits = 0;\n    agentHitsTextView.setText(\"Player board:\\n0 hits\");\n    playerHitsTextView.setText(\"Agent board:\\n0 hits\");\n  }\n\n  private void initBoard(BoardCellStatus[][] board) {\n    for (int i = 0; i < Constants.BOARD_SIZE; i++) {\n      Arrays.fill(board[i], 0, Constants.BOARD_SIZE, BoardCellStatus.UNTRIED);\n    }\n  }\n\n  private void initHiddenBoard(HiddenBoardCellStatus[][] board) {\n    for (int i = 0; i < Constants.BOARD_SIZE; i++) {\n      Arrays.fill(board[i], 0, Constants.BOARD_SIZE, HiddenBoardCellStatus.UNOCCUPIED);\n    }\n  }\n\n  private void placePlaneOnHiddenBoard(HiddenBoardCellStatus[][] hiddenBoard) {\n    initHiddenBoard(hiddenBoard);\n\n    // Place the plane on the board\n    // First, decide the plane's orientation\n    //   0: heading right\n    //   1: heading up\n    //   2: heading left\n    //   3: heading down\n    Random rand = new Random();\n    int planeOrientation = rand.nextInt(4);\n\n    // Next, figure out the location of plane core as the '*' below\n    //   | |      |      | |    ---\n    //   |-*-    -*-    -*-|     |\n    //   | |      |      | |    -*-\n    //           ---             |\n    int planeCoreX;\n    int planeCoreY;\n    switch (planeOrientation) {\n      case 0:\n        planeCoreX = rand.nextInt(Constants.BOARD_SIZE - 2) + 1;\n        planeCoreY = rand.nextInt(Constants.BOARD_SIZE - 3) + 2;\n        // Populate the tail\n        hiddenBoard[planeCoreX][planeCoreY - 2] = HiddenBoardCellStatus.OCCUPIED_BY_PLANE;\n        hiddenBoard[planeCoreX - 1][planeCoreY - 2] = HiddenBoardCellStatus.OCCUPIED_BY_PLANE;\n        hiddenBoard[planeCoreX + 1][planeCoreY - 2] = HiddenBoardCellStatus.OCCUPIED_BY_PLANE;\n        break;\n      case 1:\n        planeCoreX = rand.nextInt(Constants.BOARD_SIZE - 3) + 1;\n        planeCoreY = rand.nextInt(Constants.BOARD_SIZE - 2) + 1;\n        // Populate the tail\n        hiddenBoard[planeCoreX + 2][planeCoreY] = HiddenBoardCellStatus.OCCUPIED_BY_PLANE;\n        hiddenBoard[planeCoreX + 2][planeCoreY + 1] = HiddenBoardCellStatus.OCCUPIED_BY_PLANE;\n        hiddenBoard[planeCoreX + 2][planeCoreY - 1] = HiddenBoardCellStatus.OCCUPIED_BY_PLANE;\n        break;\n      case 2:\n        planeCoreX = rand.nextInt(Constants.BOARD_SIZE - 2) + 1;\n        planeCoreY = rand.nextInt(Constants.BOARD_SIZE - 3) + 1;\n        // Populate the tail\n        hiddenBoard[planeCoreX][planeCoreY + 2] = HiddenBoardCellStatus.OCCUPIED_BY_PLANE;\n        hiddenBoard[planeCoreX - 1][planeCoreY + 2] = HiddenBoardCellStatus.OCCUPIED_BY_PLANE;\n        hiddenBoard[planeCoreX + 1][planeCoreY + 2] = HiddenBoardCellStatus.OCCUPIED_BY_PLANE;\n        break;\n      default:\n        planeCoreX = rand.nextInt(Constants.BOARD_SIZE - 3) + 2;\n        planeCoreY = rand.nextInt(Constants.BOARD_SIZE - 2) + 1;\n        // Populate the tail\n        hiddenBoard[planeCoreX - 2][planeCoreY] = HiddenBoardCellStatus.OCCUPIED_BY_PLANE;\n        hiddenBoard[planeCoreX - 2][planeCoreY + 1] = HiddenBoardCellStatus.OCCUPIED_BY_PLANE;\n        hiddenBoard[planeCoreX - 2][planeCoreY - 1] = HiddenBoardCellStatus.OCCUPIED_BY_PLANE;\n    }\n\n    // Finally, populate the 'cross' in the plane\n    hiddenBoard[planeCoreX][planeCoreY] = HiddenBoardCellStatus.OCCUPIED_BY_PLANE;\n    hiddenBoard[planeCoreX + 1][planeCoreY] = HiddenBoardCellStatus.OCCUPIED_BY_PLANE;\n    hiddenBoard[planeCoreX - 1][planeCoreY] = HiddenBoardCellStatus.OCCUPIED_BY_PLANE;\n    hiddenBoard[planeCoreX][planeCoreY + 1] = HiddenBoardCellStatus.OCCUPIED_BY_PLANE;\n    hiddenBoard[planeCoreX][planeCoreY - 1] = HiddenBoardCellStatus.OCCUPIED_BY_PLANE;\n  }\n}\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/app/src/main/java/org/tensorflow/lite/examples/reinforcementlearning/PlaneStrikeAgent.java",
    "content": "/*\n * Copyright 2020 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.reinforcementlearning;\n\nimport android.app.Activity;\nimport android.content.res.AssetFileDescriptor;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.nio.MappedByteBuffer;\nimport java.nio.channels.FileChannel;\nimport org.tensorflow.lite.Interpreter;\n\n/** The class that defines a policy gradient agent to play the game. */\npublic abstract class PlaneStrikeAgent {\n\n  protected Interpreter tflite;\n  protected Interpreter.Options tfliteOptions;\n\n  protected int agentStrikePosition;\n\n  public PlaneStrikeAgent(Activity activity) throws IOException {\n    tfliteOptions = new Interpreter.Options();\n    tflite = new Interpreter(loadModelFile(activity), tfliteOptions);\n  }\n\n  /** Predict the next move based on current board state. */\n  protected abstract int predictNextMove(BoardCellStatus[][] board);\n\n  /** Run model inference on current board state. */\n  protected abstract void runInference();\n\n  protected abstract void prepareModelInput(BoardCellStatus[][] board);\n\n  /** Memory-map the model file in Assets. */\n  protected MappedByteBuffer loadModelFile(Activity activity) throws IOException {\n\n    String model;\n    if (Constants.USE_MODEL_FROM_TF) {\n      model = Constants.TF_TFLITE_MODEL;\n    } else {\n      model = Constants.TF_AGENTS_TFLITE_MODEL;\n    }\n    AssetFileDescriptor fileDescriptor = activity.getAssets().openFd(model);\n    FileInputStream inputStream = new FileInputStream(fileDescriptor.getFileDescriptor());\n    FileChannel fileChannel = inputStream.getChannel();\n    long startOffset = fileDescriptor.getStartOffset();\n    long declaredLength = fileDescriptor.getDeclaredLength();\n    return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength);\n  }\n}\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/app/src/main/java/org/tensorflow/lite/examples/reinforcementlearning/RLAgent.java",
    "content": "/*\n * Copyright 2020 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.reinforcementlearning;\n\nimport android.app.Activity;\nimport android.util.Log;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\n\n/**\n * The class that implements a policy gradient agent to play the game, assuming model is trained\n * using TensorFlow or JAX.\n */\npublic class RLAgent extends PlaneStrikeAgent {\n\n  private ByteBuffer boardData = null;\n  private final float[][] outputProbArrays =\n      new float[1][Constants.BOARD_SIZE * Constants.BOARD_SIZE];\n\n  public RLAgent(Activity activity) throws IOException {\n    super(activity);\n\n    boardData = ByteBuffer.allocateDirect(Constants.BOARD_SIZE * Constants.BOARD_SIZE * 4);\n    boardData.order(ByteOrder.nativeOrder());\n  }\n\n  /** Predict the next move based on current board state. */\n  @Override\n  protected int predictNextMove(BoardCellStatus[][] board) {\n\n    if (tflite == null) {\n      Log.e(Constants.TAG, \"Game agent failed to initialize. Please restart the app.\");\n      return -1;\n    } else {\n      prepareModelInput(board);\n      runInference();\n    }\n\n    // Post-processing (non-repeat argmax)\n    float[] probArray = outputProbArrays[0]; // batch size is 1 so we use [0] here\n    int agentStrikePosition = -1;\n    float maxProb = 0;\n    for (int i = 0; i < probArray.length; i++) {\n      int x = i / Constants.BOARD_SIZE;\n      int y = i % Constants.BOARD_SIZE;\n      if (board[x][y] == BoardCellStatus.UNTRIED && probArray[i] > maxProb) {\n        agentStrikePosition = i;\n        maxProb = probArray[i];\n      }\n    }\n    return agentStrikePosition;\n  }\n\n  /** Run model inference on current board state. */\n  @Override\n  protected void runInference() {\n    tflite.run(boardData, outputProbArrays);\n    boardData.rewind();\n  }\n\n  @Override\n  protected void prepareModelInput(BoardCellStatus[][] board) {\n    if (board == null) {\n      return;\n    }\n    float boardCellStatusValue = 0;\n    for (int i = 0; i < Constants.BOARD_SIZE; ++i) {\n      for (int j = 0; j < Constants.BOARD_SIZE; ++j) {\n        switch (board[i][j]) {\n          case HIT:\n            boardCellStatusValue = Constants.CELL_STATUS_VALUE_HIT;\n            break;\n          case MISS:\n            boardCellStatusValue = Constants.CELL_STATUS_VALUE_MISS;\n            break;\n          default:\n            boardCellStatusValue = Constants.CELL_STATUS_VALUE_UNTRIED;\n            break;\n        }\n        boardData.putFloat(boardCellStatusValue);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/app/src/main/java/org/tensorflow/lite/examples/reinforcementlearning/RLAgentFromTFAgents.java",
    "content": "/*\n * Copyright 2020 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.reinforcementlearning;\n\nimport android.app.Activity;\nimport android.util.Log;\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * The class that implements an agent to play the game, assuming model is trained\n * using TensorFlow Agents REINFORCE agent.\n */\npublic class RLAgentFromTFAgents extends PlaneStrikeAgent {\n\n  private final Object[] inputs = new Object[4];\n\n  public RLAgentFromTFAgents(Activity activity) throws IOException {\n    super(activity);\n  }\n\n  /** Predict the next move based on current board state. */\n  @Override\n  protected int predictNextMove(BoardCellStatus[][] board) {\n\n    if (tflite == null) {\n      Log.e(\n          Constants.TAG, \"Game agent failed to initialize. Please restart the app.\");\n      return -1;\n    } else {\n      prepareModelInput(board);\n      runInference();\n    }\n\n    return agentStrikePosition;\n  }\n\n  /** Run model inference on current board state. */\n  @Override\n  protected void runInference() {\n    Map<Integer, Object> output = new HashMap<>();\n    // TF Agent directly returns the predicted action\n    int[] prediction = new int[1];\n    output.put(0, prediction);\n    tflite.runForMultipleInputsOutputs(inputs, output);\n    agentStrikePosition = prediction[0];\n  }\n\n  @Override\n  protected void prepareModelInput(BoardCellStatus[][] board) {\n    if (board == null) {\n      return;\n    }\n\n    // Model converted from TF Agents takes 4 tensors as input; only the 3rd one 'observation' is\n    // useful for inference\n    int stepType = 0;\n    float discount = 0;\n    float reward = 0;\n    inputs[0] = stepType;\n    inputs[1] = discount;\n    float[][][] boardState = new float[1][8][8];\n    for (int i = 0; i < Constants.BOARD_SIZE; ++i) {\n      for (int j = 0; j < Constants.BOARD_SIZE; ++j) {\n        switch (board[i][j]) {\n          case HIT:\n            boardState[0][i][j] = Constants.CELL_STATUS_VALUE_HIT;\n            break;\n          case MISS:\n            boardState[0][i][j] = Constants.CELL_STATUS_VALUE_MISS;\n            break;\n          default:\n            boardState[0][i][j] = Constants.CELL_STATUS_VALUE_UNTRIED;\n            break;\n        }\n      }\n    }\n    inputs[2] = boardState;\n    inputs[3] = reward;\n  }\n}\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillColor=\"#26A69A\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n</vector>\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n  <path\n      android:fillType=\"evenOdd\"\n      android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\">\n    <aapt:attr name=\"android:fillColor\">\n      <gradient\n          android:endX=\"78.5885\"\n          android:endY=\"90.9159\"\n          android:startX=\"48.7653\"\n          android:startY=\"61.0927\"\n          android:type=\"linear\">\n        <item\n            android:color=\"#44000000\"\n            android:offset=\"0.0\"/>\n        <item\n            android:color=\"#00000000\"\n            android:offset=\"1.0\"/>\n      </gradient>\n    </aapt:attr>\n  </path>\n  <path\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"nonZero\"\n      android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"#00000000\"\n    >\n\n    <RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        xmlns:tools=\"http://schemas.android.com/tools\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        tools:context=\".MainActivity\">\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:layout_alignParentTop=\"true\"\n            android:background=\"@android:color/white\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/tfl2_logo_dark\" />\n        </androidx.appcompat.widget.Toolbar>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:paddingTop=\"?attr/actionBarSize\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                    android:id=\"@+id/text_prompt\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_gravity=\"center\"\n                    android:paddingBottom=\"3dp\"\n                    android:textStyle=\"bold|italic\"\n                    android:text=\"@string/text_prompt\" />\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:paddingBottom=\"5dp\"\n                android:paddingLeft=\"5dp\"\n                android:orientation=\"horizontal\">\n\n                <GridView\n                    android:id=\"@+id/agent_board_gridview\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_weight=\"1\"\n                    android:numColumns=\"8\"\n                    android:gravity=\"center\"\n                    android:columnWidth=\"10dp\"\n                    android:stretchMode=\"columnWidth\"\n                    android:padding=\"2dp\"\n                    android:horizontalSpacing=\"1dp\"\n                    android:verticalSpacing=\"1dp\"\n                    android:background=\"#9ACD32\" />\n\n                <TextView\n                        android:id=\"@+id/player_hits_textview\"\n                        android:layout_width=\"200dp\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_gravity=\"center\"\n                        android:layout_weight=\"1\"\n                        android:layout_marginLeft=\"15dp\"/>\n            </LinearLayout>\n\n            <View\n                android:id=\"@+id/line\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"2dp\"\n                android:background=\"#c0c0c0\"/>\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:paddingTop=\"5dp\"\n                android:paddingBottom=\"5dp\"\n                android:paddingLeft=\"5dp\"\n                android:orientation=\"horizontal\">\n                <GridView\n                        android:id=\"@+id/player_board_gridview\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_weight=\"1\"\n                        android:numColumns=\"8\"\n                        android:gravity=\"center\"\n                        android:columnWidth=\"10dp\"\n                        android:stretchMode=\"columnWidth\"\n                        android:padding=\"2dp\"\n                        android:horizontalSpacing=\"1dp\"\n                        android:verticalSpacing=\"1dp\"\n                        android:background=\"#9ACD32\" />\n\n                <TextView\n                    android:id=\"@+id/agent_hits_textview\"\n                    android:layout_width=\"200dp\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_gravity=\"center\"\n                    android:layout_weight=\"1\"\n                    android:layout_marginLeft=\"15dp\"/>\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:paddingTop=\"20dp\"\n                android:orientation=\"horizontal\"\n                />\n\n                <Button\n                    android:id=\"@+id/reset_button\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_gravity=\"center\"\n                    android:text=\"Reset game\" />\n            </LinearLayout>\n\n  </RelativeLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <color name=\"tfe_color_primary\">#ffa800</color>\n  <color name=\"tfe_color_primary_dark\">#ff6f00</color>\n  <color name=\"tfe_color_accent\">#425066</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/app/src/main/res/values/strings.xml",
    "content": "<resources>\n  <string name=\"tfe_rl_app_name\" translatable=\"false\">TFL Reinforcement Learning</string>\n  <string name=\"text_prompt\" translatable=\"false\">Tap any cell in the agent board as your strike.\\nRed - hit \\t Yellow - miss</string>\n</resources>\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n  <!-- Base application theme. -->\n  <style name=\"AppTheme.ReinforcementLearning\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n    <!-- Customize your theme here. -->\n    <item name=\"colorPrimary\">@color/tfe_color_primary</item>\n    <item name=\"colorPrimaryDark\">@color/tfe_color_primary_dark</item>\n    <item name=\"colorAccent\">@color/tfe_color_accent</item>\n  </style>\n\n</resources>\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:4.2.0'\n        classpath 'de.undercouch:gradle-download-task:4.0.2'\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n        maven {\n            name 'ossrh-snapshot'\n            url 'https://oss.sonatype.org/content/repositories/snapshots'\n        }\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app\"s APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Automatically convert third-party libraries to use AndroidX\nandroid.enableJetifier=true"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/android/settings.gradle",
    "content": "rootProject.name = 'TFLite Reinforcement Learning Demo App'\ninclude ':app'\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/ml/README.md",
    "content": "# Training code for Plane Strike.\n\nThis folder contains the training code for the board game Plane Strike. This\nreference app is a simple board game (called 'Plane Strike') in which you play\nagainst an agent trained by reinforcement learning. The agent runs the\nreinforcement learning model on-device using TensorFlow Lite.\n\nThe game rule for Plane Strike is very simple. It is a turn-based board game and\nis very similar to the\n[Battleship game](https://en.wikipedia.org/wiki/Battleship_\\(game\\)) game. The\nonly difference is that Battleship allows you to place battleships (2–5 cells in\na row or a column as 'battleships'); you can place multple ships. Plane Strike\ninstead allows you to place a ‘plane’ on the board at the beginning of the game.\nIn the animation we can see 2 boards (the top one is the agent's board and the\nbottom one is yours), each of which has a plane on the board. Of course you have\nno visibility on the agent’s plane location when the game starts. In a live\ngame, the agent’s plane is hidden at the beginning; you need to guess out all\nthe plane cells before the agent does to your plane cells. Whoever finds out all\nof the opponent's plane cells first wins. Then the game restarts.\n\nAt the beginning of the game, the app will randomly place the planes for the\nagent and the player. You can see the plane as 8 blue cells in your board. If\nyou are not happy with the placement, just reset the game so that the plane\nplacements will be changed.\n\nDuring the gameplay, if you, as the player, tap a cell in the agent's board at\nthe top, and that cell turns out to be a 'plane cell', it will turn red (think\nof this action as a hit); if it's not a 'plane cell', the cell will turn yellow\nas a miss. The app also tracks the number of hits of both boards so that you can\nget a quick idea of the game progress. You can tap the same cell multiple times,\nbut it won't do anything other than wasting your strike opportunities.\n\nTo train the agent, we have implemented different ways:\n\n*   [TF Agents](https://www.tensorflow.org/agents) (along with a python\n    envronment for TF Agents)\n*   TensorFlow and\n    [JAX](https://github.com/google/jax)/[Flax](https://github.com/google/flax)\n    (along with an [OpenAI gym environment](https://gym.openai.com/))\n\n*Note that the JAX implementation is highly experimental\n\nYou can choose one of the 3 training paths to train the model by yourself. It's\npossible to make training faster or more effective by taking advantage of the\nboard symmetry, better reward shaping, parallel runs and etc.\n\n## Requirements\n\nPlease install the required libraries in requirements.txt first.\n\n```\npip install -r requirements.txt\n```\n\nAs of 7/2021, only the nightly version of TF/TF Agents can support converting a\npolicy into TFLite model.\n\n## Build and run\n\n### TF Agents:\n\nGo into the `tf_agents` folder and run the following:\n\n```\npython training_tf_agents.py\ntensorboard --logdir=./tf_agents_log\n```\n\nYou can also use TensorBoard to visualize the training process by looking at the\naverage reward and the average episode length.\n\nYou can see the agent become smarter (indicated by the increasing average reward\nand decreasing episode length), as training progresses.\n\nAfter training, you will get a `planestrike_tf_agents.tflite` file, which you\ncan then integrate into the Android app we provide in the `android` folder. Note\nthat the TFLite model converted from the TF Agents policy is a little from\nTF/JAX model in that it takes 4 tensors as input (only the 3rd tensor\n'observation' is useful for inferene though):\n\n![TFLITE_FROM_TF_AGENTS](tf_agents/tflite_from_tf_agents.png)\n\n### TensorFlow or JAX:\n\n#### Step 1. Install the OpenAI gym environment\n\nGo into the `tf_and_jax/gym_planestrike` folder and run the following to install\nthe gym environment:\n\n```\npython setup.py install\n```\n\n#### Step 2. Train the model\n\nChoose either training_tf.py or training_jax.py in the `tf_and_jax` folder to\ntrain the model. You can also use TensorBoard to visualize the training process\nby looking at the game_length (a smoothing factor of 0.9 is recommended):\n\n```\npython training_tf.py\ntensorboard --logdir=./tf_log\n```\n\nOr:\n\n```\npython training_jax.py\ntensorboard --logdir=./jax_log\n```\n\nYou can see the agent become smarter (indicated by the decreasing game length),\nas training progresses.\n\n![TRAINING_PROGRESS](tf_and_jax/tf_training.png)\n\nAfter training, you will get a `planestrike.tflite` file, which you can then\nintegrate into the Android app we provide in the `android` folder (rename it to\n`planestrike_tf.tflite` and replace the existing one in the `asset` folder of\nthe Android project).\n\n## References\n\n*   [OpenAI Spinning Up](https://spinningup.openai.com/en/latest/algorithms/vpg.html)\n*   [Deep reinforcement learning, battleship](https://www.efavdb.com/battleship)\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/ml/common.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Common utils for training.\"\"\"\n\nimport random\n\nimport numpy as np\n\n# We always use square board, so only one size is needed\nBOARD_SIZE = 8\nPLANE_SIZE = 8\n\n# Reward discount factor\nGAMMA = 0.5\n\n# Plane direction\nPLANE_HEADING_RIGHT = 0\nPLANE_HEADING_UP = 1\nPLANE_HEADING_LEFT = 2\nPLANE_HEADING_DOWN = 3\n\n# Hidden board cell status; 'occupied' means it's part of the plane\nHIDDEN_BOARD_CELL_OCCUPIED = 1\nHIDDEN_BOARD_CELL_UNOCCUPIED = 0\n\n# Visible board cell status\nBOARD_CELL_HIT = 1\nBOARD_CELL_MISS = -1\nBOARD_CELL_UNTRIED = 0\n\n\ndef play_game(predict_fn):\n  \"\"\"Play one round of game to gather logs for TF/JAX training.\"\"\"\n\n  # Only import gym-related libraries when absolutely needed\n  # pylint: disable=g-import-not-at-top\n  import gym\n  # pylint: disable=unused-import,g-import-not-at-top\n  import gym_planestrike\n  env = gym.make('PlaneStrike-v0', board_size=BOARD_SIZE)\n  observation = env.reset()\n\n  game_board_log = []\n  predicted_action_log = []\n  action_result_log = []\n  while True:\n    probs = predict_fn(np.expand_dims(observation, 0))[0]\n    probs = np.array(probs)\n    probs = [\n        p * (index not in predicted_action_log) for index, p in enumerate(probs)\n    ]\n    probs = [p / sum(probs) for p in probs]\n    assert sum(probs) > 0.999999\n    game_board_log.append(np.copy(observation))\n    strike_pos = np.random.choice(BOARD_SIZE**2, p=probs)\n    observation, reward, done, _ = env.step(strike_pos)\n    action_result_log.append(reward)\n    predicted_action_log.append(strike_pos)\n    if done:\n      env.close()\n      return np.asarray(game_board_log), np.asarray(\n          predicted_action_log), np.asarray(action_result_log)\n\n\ndef compute_rewards(game_result_log, gamma=GAMMA):\n  \"\"\"Compute discounted rewards for TF/JAX training.\"\"\"\n  discounted_rewards = []\n  discounted_sum = 0\n\n  for reward in game_result_log[::-1]:\n    discounted_sum = reward + gamma * discounted_sum\n    discounted_rewards.append(discounted_sum)\n  return np.asarray(discounted_rewards[::-1])\n\n\ndef initialize_random_hidden_board(board_size):\n  \"\"\"Initialize the hidden board.\"\"\"\n\n  hidden_board = np.ones(\n      (board_size, board_size)) * HIDDEN_BOARD_CELL_UNOCCUPIED\n\n  # Populate the plane's position\n  # First figure out the plane's orientation\n  #   0: heading right\n  #   1: heading up\n  #   2: heading left\n  #   3: heading down\n\n  plane_orientation = random.randint(0, 3)\n\n  # Figrue out the location of plane core as the '*' below\n  #   | |      |      | |    ---\n  #   |-*-    -*-    -*-|     |\n  #   | |      |      | |    -*-\n  #           ---             |\n  if plane_orientation == PLANE_HEADING_RIGHT:\n    plane_core_row = random.randint(1, board_size - 2)\n    plane_core_column = random.randint(2, board_size - 2)\n    # Populate the tail\n    hidden_board[plane_core_row][plane_core_column -\n                                 2] = HIDDEN_BOARD_CELL_OCCUPIED\n    hidden_board[plane_core_row - 1][plane_core_column -\n                                     2] = HIDDEN_BOARD_CELL_OCCUPIED\n    hidden_board[plane_core_row + 1][plane_core_column -\n                                     2] = HIDDEN_BOARD_CELL_OCCUPIED\n  elif plane_orientation == PLANE_HEADING_UP:\n    plane_core_row = random.randint(1, board_size - 3)\n    plane_core_column = random.randint(1, board_size - 3)\n    # Populate the tail\n    hidden_board[plane_core_row +\n                 2][plane_core_column] = HIDDEN_BOARD_CELL_OCCUPIED\n    hidden_board[plane_core_row + 2][plane_core_column +\n                                     1] = HIDDEN_BOARD_CELL_OCCUPIED\n    hidden_board[plane_core_row + 2][plane_core_column -\n                                     1] = HIDDEN_BOARD_CELL_OCCUPIED\n  elif plane_orientation == PLANE_HEADING_LEFT:\n    plane_core_row = random.randint(1, board_size - 2)\n    plane_core_column = random.randint(1, board_size - 3)\n    # Populate the tail\n    hidden_board[plane_core_row][plane_core_column +\n                                 2] = HIDDEN_BOARD_CELL_OCCUPIED\n    hidden_board[plane_core_row - 1][plane_core_column +\n                                     2] = HIDDEN_BOARD_CELL_OCCUPIED\n    hidden_board[plane_core_row + 1][plane_core_column +\n                                     2] = HIDDEN_BOARD_CELL_OCCUPIED\n  elif plane_orientation == PLANE_HEADING_DOWN:\n    plane_core_row = random.randint(2, board_size - 2)\n    plane_core_column = random.randint(1, board_size - 2)\n    # Populate the tail\n    hidden_board[plane_core_row -\n                 2][plane_core_column] = HIDDEN_BOARD_CELL_OCCUPIED\n    hidden_board[plane_core_row - 2][plane_core_column +\n                                     1] = HIDDEN_BOARD_CELL_OCCUPIED\n    hidden_board[plane_core_row - 2][plane_core_column -\n                                     1] = HIDDEN_BOARD_CELL_OCCUPIED\n\n  # Populate the cross\n  hidden_board[plane_core_row][plane_core_column] = HIDDEN_BOARD_CELL_OCCUPIED\n  hidden_board[plane_core_row +\n               1][plane_core_column] = HIDDEN_BOARD_CELL_OCCUPIED\n  hidden_board[plane_core_row -\n               1][plane_core_column] = HIDDEN_BOARD_CELL_OCCUPIED\n  hidden_board[plane_core_row][plane_core_column +\n                               1] = HIDDEN_BOARD_CELL_OCCUPIED\n  hidden_board[plane_core_row][plane_core_column -\n                               1] = HIDDEN_BOARD_CELL_OCCUPIED\n\n  return hidden_board\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/ml/tf_agents/planestrike_py_environment.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"TF Agents python environment for the PlaneStrike board game.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport numpy as np\nimport os\nimport sys\nsys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))\nimport common\nfrom tf_agents.environments import py_environment\nfrom tf_agents.specs import array_spec\nfrom tf_agents.trajectories import time_step as ts\n\n# We always use square board, so only one size is needed\nBOARD_SIZE = 8\n\nMAX_STEPS_PER_EPISODE = BOARD_SIZE**2\n\n# Plane direction\nPLANE_HEADING_RIGHT = 0\nPLANE_HEADING_UP = 1\nPLANE_HEADING_LEFT = 2\nPLANE_HEADING_DOWN = 3\n\n# Rewards for each strike\nHIT_REWARD = 1\nMISS_REWARD = 0\nREPEAT_STRIKE_REWARD = -1\n# Reward for finishing the game within MAX_STEPS_PER_EPISODE\nFINISHED_GAME_REWARD = 10\n# Reward for not finishing the game within MAX_STEPS_PER_EPISODE\nUNFINISHED_GAME_REWARD = -10\n\n# Hidden board cell status; 'occupied' means it's part of the plane\nHIDDEN_BOARD_CELL_OCCUPIED = 1\nHIDDEN_BOARD_CELL_UNOCCUPIED = 0\n\n# Visible board cell status\nVISIBLE_BOARD_CELL_HIT = 1\nVISIBLE_BOARD_CELL_MISS = -1\nVISIBLE_BOARD_CELL_UNTRIED = 0\n\n\nclass PlaneStrikePyEnvironment(py_environment.PyEnvironment):\n  \"\"\"PlaneStrike environment for TF Agents.\"\"\"\n\n  def __init__(self,\n               board_size=BOARD_SIZE,\n               discount=0.9,\n               max_steps=MAX_STEPS_PER_EPISODE) -> None:\n    super(PlaneStrikePyEnvironment, self).__init__()\n    assert board_size >= 4\n    self._board_size = board_size\n    self._strike_count = 0\n    self._discount = discount\n    self._max_steps = max_steps\n    self._episode_ended = False\n    self._action_spec = array_spec.BoundedArraySpec(\n        (), np.int32, minimum=0, maximum=self._board_size**2 - 1)\n    self._observation_spec = array_spec.BoundedArraySpec(\n        (self._board_size, self._board_size),\n        np.float32,\n        minimum=VISIBLE_BOARD_CELL_MISS,\n        maximum=VISIBLE_BOARD_CELL_HIT)\n    self._time_step_spec = ts.time_step_spec(self._observation_spec)\n    self.set_boards()\n\n  def set_boards(self):\n    self._plane_size = common.PLANE_SIZE\n    self._hit_count = 0\n    self._visible_board = np.zeros((self._board_size, self._board_size))\n    self._hidden_board = common.initialize_random_hidden_board(self._board_size)\n\n  def current_time_step(self):\n    return self._current_time_step\n\n  def observation_spec(self):\n    \"\"\"Return observation_spec.\"\"\"\n    return self._observation_spec\n\n  def action_spec(self):\n    \"\"\"Return action_spec.\"\"\"\n    return self._action_spec\n\n  def _reset(self):\n    \"\"\"Return initial_time_step.\"\"\"\n    self._episode_ended = False\n    self._strike_count = 0\n    self._hit_count = 0\n    self.set_boards()\n    return ts.restart(np.array(self._visible_board, dtype=np.float32))\n\n  def _step(self, action):\n    \"\"\"Apply action and return new time_step.\"\"\"\n    if self._hit_count == self._plane_size:\n      self._episode_ended = True\n      return self.reset()\n\n    if self._strike_count + 1 == self._max_steps:\n      self.reset()\n      return ts.termination(\n          np.array(self._visible_board, dtype=np.float32),\n          UNFINISHED_GAME_REWARD)\n\n    self._strike_count += 1\n    action_x = action // self._board_size\n    action_y = action % self._board_size\n    # Hit\n    if self._hidden_board[action_x][action_y] == HIDDEN_BOARD_CELL_OCCUPIED:\n      # Non-repeat move\n      if self._visible_board[action_x][action_y] == VISIBLE_BOARD_CELL_UNTRIED:\n        self._hit_count += 1\n        self._visible_board[action_x][action_y] = VISIBLE_BOARD_CELL_HIT\n        # Successful strike\n        if self._hit_count == self._plane_size:\n          # Game finished\n          self._episode_ended = True\n          return ts.termination(\n              np.array(self._visible_board, dtype=np.float32),\n              FINISHED_GAME_REWARD)\n        else:\n          self._episode_ended = False\n          return ts.transition(\n              np.array(self._visible_board, dtype=np.float32), HIT_REWARD,\n              self._discount)\n      # Repeat strike\n      else:\n        self._episode_ended = False\n        return ts.transition(\n            np.array(self._visible_board, dtype=np.float32),\n            REPEAT_STRIKE_REWARD, self._discount)\n    # Miss\n    else:\n      # Unsuccessful strike\n      self._episode_ended = False\n      self._visible_board[action_x][action_y] = VISIBLE_BOARD_CELL_MISS\n      return ts.transition(\n          np.array(self._visible_board, dtype=np.float32), MISS_REWARD,\n          self._discount)\n\n  def render(self, mode: \"human\") -> np.ndarray:\n    if mode != \"human\":\n      raise ValueError(\n          \"Only rendering mode supported is 'human', got {} instead.\".format(\n              mode))\n    return self._visible_board\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/ml/tf_agents/requirements.txt",
    "content": "tensorflow==2.7.2\ntf-agents==0.9.0\ndm-reverb==0.5.0\ntensorflow-probability==0.14.0\nPillow==10.0.1\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/ml/tf_agents/training_tf_agents.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"TF Agents training code for the Plane Strike board game.\"\"\"\nimport os\nfrom typing import Sequence\n\nfrom absl import app\nimport reverb\nimport tensorflow as tf\nimport planestrike_py_environment\nimport tensorflow_probability as tfp\nimport tf_agents as tfa\nfrom tf_agents.agents.reinforce import reinforce_agent\nfrom tf_agents.drivers import py_driver\nfrom tf_agents.environments import tf_py_environment\nfrom tf_agents.policies import policy_saver\nfrom tf_agents.policies import py_tf_eager_policy\nfrom tf_agents.replay_buffers import reverb_replay_buffer\nfrom tf_agents.replay_buffers import reverb_utils\nfrom tf_agents.specs import tensor_spec\nfrom tf_agents.utils import common\n\nBOARD_SIZE = 8\nITERATIONS = 250000\nCOLLECT_EPISODES_PER_ITERATION = 1\nREPLAY_BUFFER_CAPACITY = 2000\nREPLAY_BUFFER_TABLE_NAME = 'uniform_table'\nDISCOUNT = 0.5\n\nFC_LAYER_PARAMS = 100\n\nLEARNING_RATE = 1e-3\nNUM_EVAL_EPISODES = 20\nEVAL_INTERVAL = 500\n\nLOGDIR = './tf_agents_log'\nMODELDIR = './'\nPOLICYDIR = './policy'\n\n\ndef compute_avg_return_and_steps(environment, policy, num_episodes=10):\n  \"\"\"Compute average return and # of steps.\"\"\"\n  total_return = 0.0\n  total_steps = 0.0\n  for _ in range(num_episodes):\n\n    time_step = environment.reset()\n    episode_return = 0.0\n    episode_steps = 0.0\n\n    while not time_step.is_last():\n      action_step = policy.action(time_step)\n      time_step = environment.step(action_step.action)\n      episode_return += time_step.reward\n      episode_steps += 1\n    total_return += episode_return\n    total_steps += episode_steps\n\n  average_return = total_return / num_episodes\n  average_episode_steps = total_steps / num_episodes\n  return average_return.numpy()[0], average_episode_steps\n\n\ndef collect_episode(environment, policy, num_episodes, replay_buffer_observer):\n  \"\"\"Collect game episode trajectories.\"\"\"\n  initial_time_step = environment.reset()\n\n  driver = py_driver.PyDriver(\n      environment,\n      py_tf_eager_policy.PyTFEagerPolicy(policy, use_tf_function=True),\n      [replay_buffer_observer],\n      max_episodes=num_episodes)\n  initial_time_step = environment.reset()\n  driver.run(initial_time_step)\n\n\ndef train_agent(iterations, modeldir, logdir, policydir):\n  \"\"\"Train and convert the model using TF Agents.\"\"\"\n\n  train_py_env = planestrike_py_environment.PlaneStrikePyEnvironment(\n      board_size=BOARD_SIZE, discount=DISCOUNT, max_steps=BOARD_SIZE**2)\n  eval_py_env = planestrike_py_environment.PlaneStrikePyEnvironment(\n      board_size=BOARD_SIZE, discount=DISCOUNT, max_steps=BOARD_SIZE**2)\n\n  train_env = tf_py_environment.TFPyEnvironment(train_py_env)\n  eval_env = tf_py_environment.TFPyEnvironment(eval_py_env)\n\n  # Alternatively you could use ActorDistributionNetwork as actor_net\n  actor_net = tfa.networks.Sequential([\n      tfa.keras_layers.InnerReshape([BOARD_SIZE, BOARD_SIZE], [BOARD_SIZE**2]),\n      tf.keras.layers.Dense(FC_LAYER_PARAMS, activation='relu'),\n      tf.keras.layers.Dense(BOARD_SIZE**2),\n      tf.keras.layers.Lambda(lambda t: tfp.distributions.Categorical(logits=t)),\n  ],\n                                      input_spec=train_py_env.observation_spec(\n                                      ))\n\n  optimizer = tf.keras.optimizers.Adam(learning_rate=LEARNING_RATE)\n\n  train_step_counter = tf.Variable(0)\n\n  tf_agent = reinforce_agent.ReinforceAgent(\n      train_env.time_step_spec(),\n      train_env.action_spec(),\n      actor_network=actor_net,\n      optimizer=optimizer,\n      normalize_returns=True,\n      train_step_counter=train_step_counter)\n\n  tf_agent.initialize()\n\n  eval_policy = tf_agent.policy\n  collect_policy = tf_agent.collect_policy\n\n  tf_policy_saver = policy_saver.PolicySaver(collect_policy)\n\n  # Use reverb as replay buffer\n  replay_buffer_signature = tensor_spec.from_spec(tf_agent.collect_data_spec)\n  table = reverb.Table(\n      REPLAY_BUFFER_TABLE_NAME,\n      max_size=REPLAY_BUFFER_CAPACITY,\n      sampler=reverb.selectors.Uniform(),\n      remover=reverb.selectors.Fifo(),\n      rate_limiter=reverb.rate_limiters.MinSize(1),\n      signature=replay_buffer_signature\n  )  # specify signature here for validation at insertion time\n\n  reverb_server = reverb.Server([table])\n\n  replay_buffer = reverb_replay_buffer.ReverbReplayBuffer(\n      tf_agent.collect_data_spec,\n      sequence_length=None,\n      table_name=REPLAY_BUFFER_TABLE_NAME,\n      local_server=reverb_server)\n\n  replay_buffer_observer = reverb_utils.ReverbAddEpisodeObserver(\n      replay_buffer.py_client, REPLAY_BUFFER_TABLE_NAME, REPLAY_BUFFER_CAPACITY)\n\n  # Optimize by wrapping some of the code in a graph using TF function.\n  tf_agent.train = common.function(tf_agent.train)\n\n  # Evaluate the agent's policy once before training.\n  avg_return = compute_avg_return_and_steps(eval_env, tf_agent.policy,\n                                            NUM_EVAL_EPISODES)\n\n  summary_writer = tf.summary.create_file_writer(logdir)\n\n  for i in range(iterations):\n    # Collect a few episodes using collect_policy and save to the replay buffer.\n    collect_episode(train_py_env, collect_policy,\n                    COLLECT_EPISODES_PER_ITERATION, replay_buffer_observer)\n\n    # Use data from the buffer and update the agent's network.\n    iterator = iter(replay_buffer.as_dataset(sample_batch_size=1))\n    trajectories, _ = next(iterator)\n    tf_agent.train(experience=trajectories)\n    replay_buffer.clear()\n\n    logger = tf.get_logger()\n    if i % EVAL_INTERVAL == 0:\n      avg_return, avg_episode_length = compute_avg_return_and_steps(\n          eval_env, eval_policy, NUM_EVAL_EPISODES)\n      with summary_writer.as_default():\n        tf.summary.scalar('Average return', avg_return, step=i)\n        tf.summary.scalar('Average episode length', avg_episode_length, step=i)\n        summary_writer.flush()\n      logger.info(\n          'iteration = {0}: Average Return = {1}, Average Episode Length = {2}'\n          .format(i, avg_return, avg_episode_length))\n\n  summary_writer.close()\n\n  tf_policy_saver.save(policydir)\n  # Convert to tflite model\n  converter = tf.lite.TFLiteConverter.from_saved_model(\n      policydir, signature_keys=['action'])\n  converter.target_spec.supported_ops = [\n      tf.lite.OpsSet.TFLITE_BUILTINS,  # enable TensorFlow Lite ops.\n      tf.lite.OpsSet.SELECT_TF_OPS  # enable TensorFlow ops.\n  ]\n  tflite_policy = converter.convert()\n  with open(os.path.join(modeldir, 'planestrike_tf_agents.tflite'), 'wb') as f:\n    f.write(tflite_policy)\n\n\ndef main(argv: Sequence[str]) -> None:\n  if len(argv) > 1:\n    raise app.UsageError('Too many command-line arguments.')\n\n  train_agent(ITERATIONS, MODELDIR, LOGDIR, POLICYDIR)\n\n\nif __name__ == '__main__':\n  app.run(main)\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/ml/tf_and_jax/gym_planestrike/gym_planestrike/__init__.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"OpenAI gym environment for the Plane Strike game.\"\"\"\nfrom gym.envs.registration import register\n\nregister(\n    id='PlaneStrike-v0',\n    entry_point='gym_planestrike.envs:PlaneStrikeEnv',\n    kwargs={\n        'board_size': 8,\n    },\n    nondeterministic=True,\n)\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/ml/tf_and_jax/gym_planestrike/gym_planestrike/envs/__init__.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"OpenAI gym environment for the Plane Strike game.\"\"\"\nfrom gym_planestrike.envs.planestrike import PlaneStrikeEnv\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/ml/tf_and_jax/gym_planestrike/gym_planestrike/envs/planestrike.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"OpenAI gym environment for the Plane Strike game.\n\n   Refer to http://gym.openai.com/docs/ for more information.\n\"\"\"\nimport gym\nfrom gym import spaces\nimport numpy as np\nimport os\nimport sys\nsys.path.append(os.path.dirname(__file__))\nimport common\n\n\n# Rewards for each strike\nHIT_REWARD = 1\nMISS_REWARD = 0\nREPEAT_STRIKE_REWARD = -1\n\n\nclass PlaneStrikeEnv(gym.Env):\n  \"\"\"A class that defines the Plane Strike environement.\"\"\"\n\n  metadata = {'render.modes': ['human']}\n\n  def __init__(self, board_size) -> None:\n    super().__init__()\n    assert board_size >= 4\n    self.board_size = board_size\n    self.set_board()\n\n  def step(self, action):\n    if self.hit_count == self.plane_size:\n      return self.observation, 0, True, {}\n\n    action_x = action // self.board_size\n    action_y = action % self.board_size\n    # Hit\n    if self.hidden_board[action_x][\n        action_y] == common.HIDDEN_BOARD_CELL_OCCUPIED:\n      # Non-repeat move\n      if self.observation[action_x][action_y] == common.BOARD_CELL_UNTRIED:\n        self.hit_count = self.hit_count + 1\n        self.observation[action_x][action_y] = common.BOARD_CELL_HIT\n        # Successful strike\n        if self.hit_count == self.plane_size:\n          # Game finished\n          return self.observation, HIT_REWARD, True, {}\n        else:\n          return self.observation, HIT_REWARD, False, {}\n      # Repeat strike\n      else:\n        return self.observation, REPEAT_STRIKE_REWARD, False, {}\n    # Miss\n    else:\n      # Unsuccessful strike\n      self.observation[action_x][action_y] = common.BOARD_CELL_MISS\n      return self.observation, MISS_REWARD, False, {}\n\n  def reset(self):\n    self.set_board()\n    return self.observation\n\n  def render(self, mode='human'):\n    print(self.observation)\n    return\n\n  def close(self):\n    return\n\n  def set_board(self):\n    self.plane_size = common.PLANE_SIZE\n    self.hit_count = 0\n    self.observation = np.zeros((self.board_size, self.board_size))\n    self.hidden_board = common.initialize_random_hidden_board(self.board_size)\n    self.action_space = spaces.Discrete(self.board_size * self.board_size)\n    self.observation_space = spaces.MultiDiscrete(\n        [self.board_size, self.board_size])\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/ml/tf_and_jax/gym_planestrike/setup.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Setup file for the OpenAI gym environment.\"\"\"\nimport os\nimport shutil\n\nfrom setuptools import find_packages\nfrom setuptools import setup\n\nsource = os.path.abspath(\n    os.path.join(os.path.dirname(__file__), '../../common.py'))\ntarget = os.path.abspath(\n    os.path.join(os.path.dirname(__file__), 'gym_planestrike/envs/common.py'))\nshutil.copyfile(source, target)\n\nsetup(\n    name='gym_planestrike',\n    version='0.1',\n    description='Board game Plane Strike',\n    author='TensorFlow Authors',\n    license='Apache License 2.0',\n    packages=find_packages(),\n    install_requires=['gym', 'numpy'])\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/ml/tf_and_jax/requirements.txt",
    "content": "flax==0.6.0\njax==0.3.17\njaxlib==0.3.15\ntensorboard==2.9.1\ntensorflow==2.9.1\nnumpy==1.23.2\ngym==0.18.0\ntqdm==4.64.0"
  },
  {
    "path": "lite/examples/reinforcement_learning/ml/tf_and_jax/training_jax.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Experimental JAX/Flax training code for Plane Strike board game.\"\"\"\nimport functools\nimport os\nfrom typing import Sequence\nfrom absl import app\nfrom flax import linen as nn\nfrom flax.metrics import tensorboard\nimport jax\nfrom jax import random\nfrom jax.experimental import jax2tf\nimport jax.numpy as jnp\nimport optax\nimport tensorflow as tf\nimport os\nimport sys\nsys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))\nimport common\nfrom tqdm import tqdm\n\nITERATIONS = 500000\nLEARNING_RATE = 0.005\nMODELDIR = './'\nLOGDIR = './jax_log'\n\n\nclass PolicyGradient(nn.Module):\n  \"\"\"Neural network to predict the next strike position.\"\"\"\n\n  @nn.compact\n  def __call__(self, x):\n    dtype = jnp.float32\n    x = x.reshape((x.shape[0], -1))\n    x = nn.Dense(\n        features=2 * common.BOARD_SIZE**2, name='hidden1', dtype=dtype)(\n            x)\n    x = nn.relu(x)\n    x = nn.Dense(features=common.BOARD_SIZE**2, name='hidden2', dtype=dtype)(x)\n    x = nn.relu(x)\n    x = nn.Dense(features=common.BOARD_SIZE**2, name='logits', dtype=dtype)(x)\n    policy_probabilities = nn.softmax(x)\n    return policy_probabilities\n\n\ndef create_optimizer(learning_rate: float):\n  return optax.sgd(learning_rate=learning_rate)\n\n\ndef compute_loss(logits, labels, rewards):\n  one_hot_labels = jax.nn.one_hot(labels, num_classes=common.BOARD_SIZE**2)\n  loss = -jnp.mean(\n      jnp.sum(one_hot_labels * jnp.log(logits), axis=-1) * jnp.asarray(rewards))\n  return loss\n\n\ndef train_step(model_optimizer, params, opt_state, game_board_log,\n               predicted_action_log, action_result_log):\n  \"\"\"Run one training step.\"\"\"\n\n  def loss_fn(model_params):\n    logits = run_inference(model_params, game_board_log)\n    loss = compute_loss(logits, predicted_action_log, action_result_log)\n    return loss\n\n  def compute_grads(params):\n    return jax.grad(loss_fn)(params)\n\n  grads = compute_grads(params)\n  updates, opt_state = model_optimizer.update(grads, opt_state)\n  params = optax.apply_updates(params, updates)\n  return model_optimizer, params, opt_state\n\n\n@jax.jit\ndef run_inference(model_params, board):\n  logits = PolicyGradient().apply({'params': model_params}, board)\n  return logits\n\n\ndef train_agent(iterations, modeldir, logdir):\n  \"\"\"Train and convert the model.\"\"\"\n  summary_writer = tensorboard.SummaryWriter(logdir)\n\n  rng = random.PRNGKey(0)\n  rng, init_rng = random.split(rng)\n  policygradient = PolicyGradient()\n  params = policygradient.init(\n      init_rng, jnp.ones([1, common.BOARD_SIZE, common.BOARD_SIZE]))['params']\n  optimizer = create_optimizer(learning_rate=LEARNING_RATE)\n  opt_state = optimizer.init(params)\n\n  # Main training loop\n  for i in tqdm(range(iterations)):\n    predict_fn = functools.partial(run_inference, params)\n    board_log, action_log, result_log = common.play_game(predict_fn)\n    rewards = common.compute_rewards(result_log)\n    summary_writer.scalar('game_length', len(board_log), i)\n    optimizer, params, opt_state = train_step(optimizer, params, opt_state,\n                                              board_log, action_log, rewards)\n\n    summary_writer.flush()\n\n  summary_writer.close()\n\n  # Convert to tflite model\n  model = PolicyGradient()\n  jax_predict_fn = lambda input: model.apply({'params': params}, input)\n\n  tf_predict = tf.function(\n      jax2tf.convert(jax_predict_fn, enable_xla=False),\n      input_signature=[\n          tf.TensorSpec(\n              shape=[1, common.BOARD_SIZE, common.BOARD_SIZE],\n              dtype=tf.float32,\n              name='input')\n      ],\n      autograph=False,\n  )\n\n  converter = tf.lite.TFLiteConverter.from_concrete_functions(\n      [tf_predict.get_concrete_function()], tf_predict)\n\n  tflite_model = converter.convert()\n\n  # Save the model\n  with open(os.path.join(modeldir, 'planestrike.tflite'), 'wb') as f:\n    f.write(tflite_model)\n\n  print('TFLite model generated!')\n\n\ndef main(argv: Sequence[str]) -> None:\n  if len(argv) > 1:\n    raise app.UsageError('Too many command-line arguments.')\n\n  train_agent(ITERATIONS, MODELDIR, LOGDIR)\n\n\nif __name__ == '__main__':\n  app.run(main)\n"
  },
  {
    "path": "lite/examples/reinforcement_learning/ml/tf_and_jax/training_tf.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"TensorFlow training code for Plane Strike board game.\"\"\"\nimport os\nfrom typing import Sequence\nfrom absl import app\nimport tensorflow as tf\nimport os\nimport sys\nsys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))\nimport common\n\nITERATIONS = 80000\nLEARNING_RATE = 0.002\nMODELDIR = './'\nLOGDIR = './tf_log'\n\n\ndef train_agent(iterations, modeldir, logdir):\n  \"\"\"Train and convert the model.\"\"\"\n\n  model = tf.keras.models.Sequential([\n      tf.keras.layers.Flatten(\n          input_shape=(common.BOARD_SIZE, common.BOARD_SIZE)),\n      tf.keras.layers.Dense(2 * common.BOARD_SIZE**2, activation='relu'),\n      tf.keras.layers.Dense(common.BOARD_SIZE**2, activation='relu'),\n      tf.keras.layers.Dense(common.BOARD_SIZE**2, activation='softmax')\n  ])\n\n  sgd = tf.keras.optimizers.SGD(learning_rate=LEARNING_RATE)\n\n  model.compile(loss='sparse_categorical_crossentropy', optimizer=sgd)\n\n  summary_writer = tf.summary.create_file_writer(logdir)\n\n  def predict_fn(board):\n    return model.predict(board, verbose=0)\n\n  # Main training loop\n  progress_bar = tf.keras.utils.Progbar(iterations)\n  for i in range(iterations):\n    board_log, action_log, result_log = common.play_game(predict_fn)\n    with summary_writer.as_default():\n      tf.summary.scalar('game_length', len(action_log), step=i)\n    rewards = common.compute_rewards(result_log)\n\n    model.fit(\n        x=board_log,\n        y=action_log,\n        batch_size=1,\n        verbose=0,\n        epochs=1,\n        sample_weight=rewards)\n\n    summary_writer.flush()\n    progress_bar.add(1)\n\n  summary_writer.close()\n\n  # Convert to tflite model\n  converter = tf.lite.TFLiteConverter.from_keras_model(model)\n  tflite_model = converter.convert()\n\n  # Save the model\n  with open(os.path.join(modeldir, 'planestrike.tflite'), 'wb') as f:\n    f.write(tflite_model)\n\n  print('TFLite model generated!')\n\n\ndef main(argv: Sequence[str]) -> None:\n  if len(argv) > 1:\n    raise app.UsageError('Too many command-line arguments.')\n\n  train_agent(ITERATIONS, MODELDIR, LOGDIR)\n\n\nif __name__ == '__main__':\n  app.run(main)\n"
  },
  {
    "path": "lite/examples/smart_reply/android/README.md",
    "content": "# Smart Reply Model\n\n## What is On-Device Smart Reply Model?\n\nSmart Replies are contextually relevant, one-touch responses that help the user\nto reply to an incoming text message (or email) efficiently and effortlessly.\nSmart Replies have been highly successful across several Google products\nincluding\n[Gmail](https://www.blog.google/products/gmail/save-time-with-smart-reply-in-gmail/),\n[Inbox](https://www.blog.google/products/gmail/computer-respond-to-this-email/)\nand\n[Allo](https://blog.google/products/allo/google-allo-smarter-messaging-app/).\n\nThe On-device Smart Reply model is targeted towards text chat use cases. It has\na completely different architecture from its cloud-based counterparts, and is\nbuilt specifically for memory constraints devices such as phones & watches. It\nhas been successfully used to provide [Smart Replies on Android\nWear](https://research.googleblog.com/2017/02/on-device-machine-intelligence.html)\nto all first- & third-party apps.\n\nThe on-device model comes with several benefits. It is:\n\n*   **Faster**: The model resides on the device and does not require internet\n    connectivity. Thus, the inference is very fast and has an average latency of\n    only a few milliseconds.\n*   **Resource efficient**: The model has a small memory footprint on\n    the device.\n*   **Privacy-friendly**: The user data never leaves the device and this\n    eliminates any privacy restrictions.\n\nA caveat, though, is that the on-device model has lower triggering rate than its\ncloud counterparts (triggering rate is the percentage of times the model\nsuggests a response for an incoming message).\n\n## When to use this Model?\n\nThe On-Device Smart Reply model is aimed towards improving the messaging\nexperience for day-to-day conversational chat messages. We recommend using this\nmodel for similar use cases. Some sample messages on which the model does well\nare provided in this\n[tsv file](https://github.com/tensorflow/examples/tree/master/lite/examples/smartreply/android/testdata/smartreply_samples.tsv)\nfor reference. The file format is:\n\n```\n   {incoming_message  smart_reply1   [smart_reply2]   [smart_reply3]}\n```\n\nFor the current model, we see a triggering rate of about 30-40% for messages\nwhich are similar to those provided in the tsv file above.\n\nIn case the model does not trigger any response, the system falls back to\nsuggesting replies from a fixed back-off set that was compiled from popular\nresponse intents observed in chat conversations. Some of the fallback responses\nare `Ok, Yes, No, 👍, ☺`.\n\nThe model can only be used for inference at this time (i.e. it cannot be custom\ntrained). If you are interested to know how the model was trained, please refer\nto this [blog\npost](https://research.googleblog.com/2017/02/on-device-machine-intelligence.html)\nand [research paper](https://arxiv.org/pdf/1708.00630).\n\n## How to Use This Model?\n\nWe have provided a pre-built demo APK that you can download, install and test on\nyour phone\n([demo APK here](http://download.tensorflow.org/models/tflite/smartreply/SmartReplyDemo.apk)).\n\nThe On-Device Smart Reply demo App works in the following way:\n\n1.  Android app links to the JNI binary with a predictor library.\n\n2.  In the predictor library, `GetSegmentPredictions` is called with a list of input\n    strings.\n\n    2.1 The input string can be 1-3 most recent messages of the conversations in\n    form of string vector. The model will run on these input sentences and\n    provide Smart Replies corresponding to them.\n\n    2.2 The function performs some preprocessing on input data which includes:\n\n    *   Sentence splitting: The input message will be split into sentences if\n        message has more than one sentence. Eg: a message like “How are you?\n        Want to grab lunch?” will be broken down into 2 different sentences.\n    *   Normalization: The individual sentences will be normalized by converting\n        them into lower cases, removing unnecessary punctuations, etc. Eg: “how\n        are you????” will be converted to “how are you?” (refer for NORMALIZE op\n        for more details).\n\n        The input string content will be converted to tensors.\n\n    2.3 The function then runs the prediction model on the input tensors.\n\n    2.4 The function also performs some post-processing which includes\n    aggregating the model predictions for the input sentences from 2.2 and\n    returning the appropriate responses.\n\n3.  Finally, it gets response(s) from `std::vector<PredictorResponse>`, and\n    returns back to Android app. Responses are sorted in descending order of\n    confidence score.\n\n## Ops and Functionality Supported\n\nFollowing are the ops supported for using On-Device Smart Reply model:\n\n*   **NORMALIZE**\n\n    This is a custom op which normalizes the sentences by:\n\n    *   Converting all sentences into lower case.\n    *   Removing unnecessary punctuations (eg: “how are you????” → “how are\n        you?”).\n    *   Expanding sentences wherever necessary (eg: “ I’m home” → “I am home”).\n\n*   **SKIP_GRAM**\n\n    This is an op inside TensorFlow Lite that converts sentences into a list of\n    skip grams. The configurable parameters are `ngram_size` and\n    `max_skip_size`. For the model provided, the values for these parameters are\n    set to 3 & 2 respectively.\n\n*   **EXTRACT_FEATURES**\n\n    This is a custom op that hashes skip grams to features represented as\n    integers. Longer skip-grams are allocated higher weights.\n\n*   **LSH_PROJECTION**\n\n    This is an op inside TensorFlow Lite that projects input features to a\n    corresponding bit vector space using Locality Sensitive Hashing (LSH).\n\n*   **PREDICT**\n\n    This is a custom op that runs the input features through the projection\n    model (details [here](https://arxiv.org/pdf/1708.00630.pdf)), computes the\n    appropriate response labels along with weights for the projected features,\n    and aggregates the response labels and weights together.\n\n*   **HASHTABLE_LOOKUP**\n\n    This is an op inside TensorFlow Lite that uses label id from predict op and\n    looks up the response text from the given label id.\n\n## How to Run the App?\n\nWe release\n([pre-built demo APK here](http://download.tensorflow.org/models/tflite/smartreply/SmartReplyDemo.apk))\nfor you to download, install and test on your phone.\n\nIf you want to know how to build the app at own side, please refer to\n[how-to-build.md](https://github.com/tensorflow/examples/blob/master/lite/examples/smart_reply/android/how-to-build.md).\n\n## Further Information\n\n*   Open source code\n    [here](https://github.com/tensorflow/examples/blob/master/lite/examples/smart_reply/android).\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/build.gradle",
    "content": "apply plugin: 'com.android.application'\napply plugin: 'de.undercouch.download'\n\nandroid {\n    compileSdkVersion 28\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.smartreply.SmartReply\"\n        minSdkVersion 19\n        targetSdkVersion 28\n        versionCode 1\n        versionName \"1.0\"\n        ndk {\n            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'\n        }\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    aaptOptions {\n        noCompress \"tflite\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility '1.8'\n        targetCompatibility '1.8'\n    }\n\n    repositories {\n        mavenCentral()\n        maven {\n            name 'ossrh-snapshot'\n            url 'https://oss.sonatype.org/content/repositories/snapshots'\n        }\n        flatDir {\n            dirs 'libs'\n        }\n    }\n}\n\next {\n    LITE_MODEL_URL = 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/smartreply/smartreply.tflite'\n    LITE_MODEL_NAME = 'smartreply.tflite'\n    LITE_MODEL_DIRS = [\n            \"$projectDir/src/main/assets\",\n            \"$projectDir/libs/cc/testdata\",\n    ]\n\n    AAR_URL = 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/smartreply/smartreply_runtime_aar.aar'\n    AAR_PATH = \"$projectDir/libs/smartreply_runtime_aar.aar\"\n}\n\n// Download prebuilt AAR and TF Lite model.\napply from: 'download.gradle'\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])\n\n    assert new File(project.ext.AAR_PATH).exists(): 'Please run `./gradlew :app:downloadAAR()` to download prebuilt aar package, or build it from code. (See: `https://github.com/tensorflow/examples/blob/master/lite/examples/smart_reply/android/how-to-build.md`)'\n    implementation files(project.ext.AAR_PATH)\n\n    // Support Libraries\n    implementation 'com.google.guava:guava:28.1-android'\n    implementation 'androidx.appcompat:appcompat:1.1.0'\n    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'\n\n    // TF Lite\n    implementation 'org.tensorflow:tensorflow-lite:0.0.0-nightly-SNAPSHOT'\n\n    testImplementation 'junit:junit:4.12'\n    testImplementation 'androidx.test:core:1.2.0'\n    testImplementation 'org.robolectric:robolectric:4.3.1'\n    // TODO: Include AAR for testing.\n}\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/download.gradle",
    "content": "// Required to download TF Lite model for Smart Reply.\ntask downloadLiteModel {\n    def tempFile = new File(buildDir, project.ext.LITE_MODEL_NAME)\n    // Download and copy to targets, without overwriting.\n    download {\n        src project.ext.LITE_MODEL_URL\n        dest tempFile\n        overwrite false\n    }\n    for (dir in project.ext.LITE_MODEL_DIRS) {\n        if (!new File(dir, project.ext.LITE_MODEL_NAME).exists()) {\n            copy {\n                from tempFile\n                into dir\n            }\n        }\n    }\n}\n\n// Required to download Prebuilt AAR Package for custom ops.\ntask downloadAAR {\n    download {\n        src project.ext.AAR_URL\n        dest project.ext.AAR_PATH\n        overwrite false\n    }\n}\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/libs/.bazelrc",
    "content": "# Align with TensorFlow .bazelrc to build from the source.\n# Please refer to TensorFlow .bazelrc for more details:\n# https://github.com/tensorflow/tensorflow/blob/master/.bazelrc\n\n# Android configs. Bazel needs to have --cpu and --fat_apk_cpu both set to the\n# target CPU to build transient dependencies correctly. See\n# https://docs.bazel.build/versions/master/user-manual.html#flag--fat_apk_cpu\nbuild:android --crosstool_top=//external:android/crosstool\nbuild:android --host_crosstool_top=@bazel_tools//tools/cpp:toolchain\nbuild:android_arm --config=android\nbuild:android_arm --cpu=armeabi-v7a\nbuild:android_arm --fat_apk_cpu=armeabi-v7a\nbuild:android_arm64 --config=android\nbuild:android_arm64 --cpu=arm64-v8a\nbuild:android_arm64 --fat_apk_cpu=arm64-v8a\nbuild:android_x86 --config=android\nbuild:android_x86 --cpu=x86\nbuild:android_x86 --fat_apk_cpu=x86\nbuild:android_x86_64 --config=android\nbuild:android_x86_64 --cpu=x86_64\nbuild:android_x86_64 --fat_apk_cpu=x86_64\n\n# Sets the default Apple platform to macOS.\nbuild --apple_platform_type=macos\n\n# iOS configs for each architecture and the fat binary builds.\nbuild:ios --apple_platform_type=ios\nbuild:ios --apple_bitcode=embedded --copt=-fembed-bitcode\nbuild:ios_armv7 --config=ios\nbuild:ios_armv7 --cpu=ios_armv7\nbuild:ios_armv7 --cops -Wno-c++11-narrowing\nbuild:ios_arm64 --config=ios\nbuild:ios_arm64 --cpu=ios_arm64\nbuild:ios_x86_64 --config=ios\nbuild:ios_x86_64 --cpu=ios_x86_64\nbuild:ios_fat --config=ios\nbuild:ios_fat --ios_multi_cpus=armv7,arm64\nbuild:ios_fat --copt -Wno-c++11-narrowing\n\n# BEGIN OF REMOTE BUILD EXECUTION OPTIONS\n# Options when using remote execution\n# WARNING: THESE OPTIONS WONT WORK IF YOU DO NOT HAVE PROPER AUTHENTICATION AND PERMISSIONS\n# Flag to enable remote config\ncommon --experimental_repo_remote_exec\n\n# Config to use a mostly-static build and disable modular op registration\n# support (this will revert to loading TensorFlow with RTLD_GLOBAL in Python).\n# By default, TensorFlow will build with a dependence on\n# //tensorflow:libtensorflow_framework.so.\nbuild:monolithic --define framework_shared_object=false\n\n# Flags for open source build, always set to be true.\nbuild --define open_source_build=true\ntest --define open_source_build=true\n\n# Flags for fat_apk_cpu build, always set to multiple cpus.\nbuild --fat_apk_cpu=x86,x86_64,arm64-v8a,armeabi-v7a\ntest --fat_apk_cpu=x86,x86_64,arm64-v8a,armeabi-v7a\n\n# Other build flags.\nbuild --define=grpc_no_ares=true\n\n# See https://github.com/bazelbuild/bazel/issues/7362 for information on what\n# --incompatible_remove_legacy_whole_archive flag does.\n# This flag is set to true in Bazel 1.0 and newer versions. We tried to migrate\n# Tensorflow to the default, however test coverage wasn't enough to catch the\n# errors.\n# There is ongoing work on Bazel team's side to provide support for transitive\n# shared libraries. As part of migrating to transitive shared libraries, we\n# hope to provide a better mechanism for control over symbol exporting, and\n# then tackle this issue again.\n#\n# TODO: Remove this line once TF doesn't depend on Bazel wrapping all library\n# archives in -whole_archive -no_whole_archive.\nbuild --noincompatible_remove_legacy_whole_archive\n\n# These are bazel 2.0's incompatible flags. Tensorflow needs to use bazel 2.0.0\n# to use cc_shared_library, as part of the Tensorflow Build Improvements RFC:\n# https://github.com/tensorflow/community/pull/179\nbuild --noincompatible_prohibit_aapt1\n\n# Modular TF build options\nbuild:dynamic_kernels --define=dynamic_loaded_kernels=true\nbuild:dynamic_kernels --copt=-DAUTOLOAD_DYNAMIC_KERNELS\n\n# Build TF with C++ 17 features.\nbuild:c++17 --cxxopt=-std=c++1z\nbuild:c++17 --cxxopt=-stdlib=libc++\nbuild:c++1z --config=c++17\n\n# Enable using platform specific build settings\nbuild --enable_platform_specific_config\n\n# Suppress C++ compiler warnings, otherwise build logs become 10s of MBs.\nbuild:linux --copt=-w\nbuild:macos --copt=-w\nbuild:windows --copt=/w\n\n# Tensorflow uses M_* math constants that only get defined by MSVC headers if\n# _USE_MATH_DEFINES is defined.\nbuild:windows --copt=/D_USE_MATH_DEFINES\nbuild:windows --host_copt=/D_USE_MATH_DEFINES\n\n# Default paths for TF_SYSTEM_LIBS\nbuild:linux --define=PREFIX=/usr\nbuild:linux --define=LIBDIR=$(PREFIX)/lib\nbuild:linux --define=INCLUDEDIR=$(PREFIX)/include\nbuild:macos --define=PREFIX=/usr\nbuild:macos --define=LIBDIR=$(PREFIX)/lib\nbuild:macos --define=INCLUDEDIR=$(PREFIX)/include\n# TF_SYSTEM_LIBS do not work on windows.\n\n# By default, build TF in C++ 14 mode.\nbuild:linux --cxxopt=-std=c++14\nbuild:linux --host_cxxopt=-std=c++14\nbuild:macos --cxxopt=-std=c++14\nbuild:macos --host_cxxopt=-std=c++14\nbuild:windows --cxxopt=/std:c++14\nbuild:windows --host_cxxopt=/std:c++14\n\n# On windows, we still link everything into a single DLL.\nbuild:windows --config=monolithic\n\n# On linux, we dynamically link small amount of kernels\nbuild:linux --config=dynamic_kernels\n\n# Make sure to include as little of windows.h as possible\nbuild:windows --copt=-DWIN32_LEAN_AND_MEAN\nbuild:windows --host_copt=-DWIN32_LEAN_AND_MEAN\nbuild:windows --copt=-DNOGDI\nbuild:windows --host_copt=-DNOGDI\n\n# Misc build options we need for windows.\nbuild:windows --linkopt=/DEBUG\nbuild:windows --host_linkopt=/DEBUG\nbuild:windows --linkopt=/OPT:REF\nbuild:windows --host_linkopt=/OPT:REF\nbuild:windows --linkopt=/OPT:ICF\nbuild:windows --host_linkopt=/OPT:ICF\nbuild:windows --experimental_strict_action_env=true\n\n# Verbose failure logs when something goes wrong\nbuild:windows --verbose_failures\n\n# Suppress all warning messages.\nbuild:short_logs --output_filter=DONT_MATCH_ANYTHING\n\n# Instruction set optimizations\n# TODO(gunan): Create a feature in toolchains for avx/avx2 to\n#   avoid having to define linux/win separately.\nbuild:avx_linux --copt=-mavx\nbuild:avx2_linux --copt=-mavx2\nbuild:native_arch_linux --copt=-march=native\nbuild:avx_win --copt=/arch=AVX\nbuild:avx2_win --copt=/arch=AVX2\n\n# Options to build TensorFlow 1.x or 2.x.\nbuild:v1 --define=tf_api_version=1\nbuild:v2 --define=tf_api_version=2\nbuild:v1 --action_env=TF2_BEHAVIOR=0\nbuild:v2 --action_env=TF2_BEHAVIOR=1\nbuild --config=v2\ntest --config=v2\n\n# Enable XLA\nbuild:xla --action_env=TF_ENABLE_XLA=1\nbuild:xla --define=with_xla_support=true\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/libs/.bazelversion",
    "content": "3.7.2"
  },
  {
    "path": "lite/examples/smart_reply/android/app/libs/BUILD",
    "content": "exports_files([\"LICENSE\"])\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/libs/LICENSE",
    "content": "Copyright 2019 The TensorFlow Authors.  All rights reserved.\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/libs/WORKSPACE",
    "content": "\"\"\"SmartReply Workspace\"\"\"\n\nworkspace(name = \"org_tensorflow_lite_examples_smartreply\")\n\nload(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\n\n# START: Upstream TensorFlow dependencies\n# TensorFlow build depends on these dependencies.\n# Needs to be in-sync with TensorFlow sources.\nhttp_archive(\n    name = \"io_bazel_rules_closure\",\n    sha256 = \"5b00383d08dd71f28503736db0500b6fb4dda47489ff5fc6bed42557c07c6ba9\",\n    strip_prefix = \"rules_closure-308b05b2419edb5c8ee0471b67a40403df940149\",\n    urls = [\n        \"https://storage.googleapis.com/mirror.tensorflow.org/github.com/bazelbuild/rules_closure/archive/308b05b2419edb5c8ee0471b67a40403df940149.tar.gz\",\n        \"https://github.com/bazelbuild/rules_closure/archive/308b05b2419edb5c8ee0471b67a40403df940149.tar.gz\",  # 2019-06-13\n    ],\n)\n\n\n# https://github.com/bazelbuild/bazel-skylib/releases\nhttp_archive(\n    name = \"bazel_skylib\",\n    sha256 = \"1dde365491125a3db70731e25658dfdd3bc5dbdfd11b840b3e987ecf043c7ca0\",\n    urls = [\n        \"https://storage.googleapis.com/mirror.tensorflow.org/github.com/bazelbuild/bazel-skylib/releases/download/0.9.0/bazel_skylib-0.9.0.tar.gz\",\n        \"https://github.com/bazelbuild/bazel-skylib/releases/download/0.9.0/bazel_skylib-0.9.0.tar.gz\",\n    ],\n)\n# END: Upstream TensorFlow dependencies\n\nhttp_archive(\n    name = \"org_tensorflow\",\n    sha256 = \"1f313adfbc52d4810a784d27d6b3887fec9d66e1ad67c0142e938fa4cf46fd0f\",\n    strip_prefix = \"tensorflow-fabcd8f89cd5975331994049705e15cb75f32e0c\",\n    urls = [\n        \"https://github.com/tensorflow/tensorflow/archive/fabcd8f89cd5975331994049705e15cb75f32e0c.tar.gz\",   # 2020-06-18\n    ],\n)\n\nload(\"@bazel_skylib//lib:versions.bzl\", \"versions\")\nversions.check(\"1.0.0\")\nload(\"@org_tensorflow//tensorflow:workspace.bzl\", \"tf_repositories\")\ntf_repositories(path_prefix=\"\", tf_repo_name=\"org_tensorflow\")\n\nregister_toolchains(\"@local_config_python//:py_toolchain\")\n\nload(\"@io_bazel_rules_closure//closure:defs.bzl\", \"closure_repositories\")\nclosure_repositories()\n\nload(\"@org_tensorflow//third_party/toolchains/preconfig/generate:archives.bzl\",\n     \"bazel_toolchains_archive\")\nbazel_toolchains_archive()\n\nload(\n    \"@bazel_toolchains//repositories:repositories.bzl\",\n    bazel_toolchains_repositories = \"repositories\",\n)\nbazel_toolchains_repositories()\n\nload(\"@org_tensorflow//third_party/android:android_configure.bzl\", \"android_configure\")\nandroid_configure(name=\"local_config_android\")\nload(\"@local_config_android//:android.bzl\", \"android_workspace\")\nandroid_workspace()\n\n# Need to export environment variable ANDROID_HOME.\nandroid_sdk_repository(\n    name = \"androidsdk\",\n)\n\n# Need to export environment variable ANDROID_NDK_HOME.\nandroid_ndk_repository(\n    name = \"androidndk\",\n)\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/libs/cc/BUILD",
    "content": "load(\"//third_party/bazel_rules/rules_cc/cc:cc_library.bzl\", \"cc_library\")\nload(\"//third_party/bazel_rules/rules_cc/cc:cc_test.bzl\", \"cc_test\")\nload(\"@org_tensorflow//tensorflow:tensorflow.bzl\", \"tf_cc_test\")\nload(\n    \"@org_tensorflow//tensorflow/lite:build_def.bzl\",\n    \"gen_selected_ops\",\n    \"tflite_copts\",\n    \"tflite_jni_binary\",\n)\nload(\"@org_tensorflow//tensorflow/lite/java:aar_with_jni.bzl\", \"aar_with_jni\")\nload(\"@build_bazel_rules_android//android:rules.bzl\", \"android_library\")\n\npackage(\n    default_applicable_licenses = [\"//third_party/py/tensorflow_examples:license\"],\n    default_visibility = [\"//visibility:public\"],\n)\n\nlicenses([\"notice\"])\n\ngen_selected_ops(\n    name = \"smartreply_ops\",\n    model = \"//cc/testdata:smartreply.tflite\",\n)\n\ncc_library(\n    name = \"custom_ops\",\n    srcs = [\n        \"ops/extract_feature.cc\",\n        \"ops/normalize.cc\",\n        \"ops/predict.cc\",\n        \":smartreply_ops\",\n    ],\n    copts = tflite_copts(),\n    deps = [\n        \"@org_tensorflow//tensorflow/lite:framework\",\n        \"@org_tensorflow//tensorflow/lite:string_util\",\n        \"@org_tensorflow//tensorflow/lite/c:c_api_types\",\n        \"@org_tensorflow//tensorflow/lite/c:common\",\n        \"@org_tensorflow//tensorflow/lite/kernels:builtin_ops\",\n        \"@org_tensorflow//tensorflow/lite/kernels:kernel_util\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_googlesource_code_re2//:re2\",\n        \"@farmhash_archive//:farmhash\",\n    ],\n    alwayslink = 1,\n)\n\ncc_library(\n    name = \"predictor_lib\",\n    srcs = [\"predictor.cc\"],\n    hdrs = [\"predictor.h\"],\n    copts = tflite_copts(),\n    deps = [\n        \":custom_ops\",\n        \"@org_tensorflow//tensorflow/lite:framework\",\n        \"@org_tensorflow//tensorflow/lite:string_util\",\n        \"@org_tensorflow//tensorflow/lite/kernels:builtin_ops\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_googlesource_code_re2//:re2\",\n    ],\n)\n\n# TODO(b/118895218): Make this test compatible with oss.\ntf_cc_test(\n    name = \"predictor_test\",\n    srcs = [\"predictor_test.cc\"],\n    data = [\n        \"//cc/testdata:smartreply.tflite\",\n        \"//cc/testdata:smartreply_samples.tsv\",\n    ],\n    deps = [\n        \":predictor_lib\",\n        \"@org_tensorflow//tensorflow/core:test\",\n        \"@org_tensorflow//tensorflow/lite:string_util\",\n        \"@org_tensorflow//tensorflow/lite/testing:util\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_google_googletest//:gtest\",\n    ],\n)\n\ncc_test(\n    name = \"extract_feature_op_test\",\n    size = \"small\",\n    srcs = [\"ops/extract_feature_test.cc\"],\n    deps = [\n        \":custom_ops\",\n        \"@org_tensorflow//tensorflow/lite:framework\",\n        \"@org_tensorflow//tensorflow/lite/kernels:builtin_ops\",\n        \"@org_tensorflow//tensorflow/lite/kernels:test_util\",\n        \"@com_google_googletest//:gtest\",\n        \"@farmhash_archive//:farmhash\",\n    ],\n)\n\ncc_test(\n    name = \"normalize_op_test\",\n    size = \"small\",\n    srcs = [\"ops/normalize_test.cc\"],\n    deps = [\n        \":custom_ops\",\n        \"@org_tensorflow//tensorflow/lite:framework\",\n        \"@org_tensorflow//tensorflow/lite:string_util\",\n        \"@org_tensorflow//tensorflow/lite/kernels:builtin_ops\",\n        \"@org_tensorflow//tensorflow/lite/kernels:test_util\",\n        \"@com_google_googletest//:gtest\",\n    ],\n)\n\ncc_test(\n    name = \"predict_op_test\",\n    size = \"small\",\n    srcs = [\"ops/predict_test.cc\"],\n    deps = [\n        \":custom_ops\",\n        \"@org_tensorflow//tensorflow/lite:framework\",\n        \"@org_tensorflow//tensorflow/lite:string_util\",\n        \"@org_tensorflow//tensorflow/lite/kernels:builtin_ops\",\n        \"@org_tensorflow//tensorflow/lite/kernels:test_util\",\n        \"@com_google_googletest//:gtest\",\n    ],\n)\n\ncc_library(\n    name = \"smartreply_jni_lib\",\n    srcs = [\n        \"smartreply_jni.cc\",\n    ],\n    copts = tflite_copts(),\n    linkopts = [\n        \"-lm\",\n        \"-ldl\",\n    ],\n    deps = [\n        \":predictor_lib\",\n        \"@org_tensorflow//tensorflow/lite:framework\",\n        \"@org_tensorflow//tensorflow/lite/java/jni\",\n    ],\n    alwayslink = 1,\n)\n\ncc_library(\n    name = \"smartreply_runtime\",\n    srcs = [\"libsmartreply_jni.so\"],\n    alwayslink = 1,\n)\n\ntflite_jni_binary(\n    name = \"libsmartreply_jni.so\",\n    deps = [\n        \":smartreply_jni_lib\",\n    ],\n)\n\nandroid_library(\n    name = \"smartreply_jni\",\n    custom_package = \"org.tensorflow.lite.examples.smartreply\",\n    manifest = \"DummyManifest.xml\",\n    resource_files = [],\n    deps = [\n        \":smartreply_runtime\",  # build_cleaner: skip\n    ],\n)\n\naar_with_jni(\n    name = \"smartreply_runtime_aar\",\n    android_library = \":smartreply_jni\",\n)\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/libs/cc/DummyManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.tensorflow.lite.examples.smartreply\">\n</manifest>\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/libs/cc/misc/BUILD",
    "content": "# This empty BUILD file is required to make Bazel treat this directory as a package.\n\npackage(\n    default_applicable_licenses = [\"//third_party/py/tensorflow_examples:license\"],\n    default_visibility = [\"//visibility:public\"],\n)\n\nlicenses([\"notice\"])\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/libs/cc/ops/extract_feature.cc",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n// Convert a list of strings to integers via hashing.\n// Input:\n//     Input[0]: A list of ngrams. string[num of input]\n//\n// Output:\n//     Output[0]: Hashed features. int32[num of input]\n//     Output[1]: Weights. float[num of input]\n\n#include <algorithm>\n#include <map>\n\n#include \"tensorflow/lite/context.h\"\n#include \"tensorflow/lite/kernels/kernel_util.h\"\n#include \"tensorflow/lite/string_util.h\"\n#include <farmhash.h>\n\nnamespace tflite {\nnamespace ops {\nnamespace custom {\n\nnamespace extract {\n\nstatic const int kMaxDimension = 1000000;\nstatic const std::vector<string> kBlacklistNgram = {\"<S>\", \"<E>\", \"<S> <E>\"};\n\nbool Equals(const string& x, const tflite::StringRef& strref) {\n  if (strref.len != x.length()) {\n    return false;\n  }\n  if (strref.len > 0) {\n    int r = memcmp(strref.str, x.data(), strref.len);\n    return r == 0;\n  }\n  return true;\n}\n\nbool IsValidNgram(const tflite::StringRef& strref) {\n  for (const auto& s : kBlacklistNgram) {\n    if (Equals(s, strref)) {\n      return false;\n    }\n  }\n  return true;\n}\n\nTfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {\n  const TfLiteTensor* input = GetInput(context, node, 0);\n  TF_LITE_ENSURE(context, input != nullptr);\n  int dim = input->dims->data[0];\n  if (dim == 0) {\n    // TFLite non-string output should have size greater than 0.\n    dim = 1;\n  }\n  TF_LITE_ENSURE_EQ(context, input->type, kTfLiteString);\n  TfLiteIntArray* outputSize1 = TfLiteIntArrayCreate(1);\n  TfLiteIntArray* outputSize2 = TfLiteIntArrayCreate(1);\n  outputSize1->data[0] = dim;\n  outputSize2->data[0] = dim;\n  context->ResizeTensor(context, GetOutput(context, node, 0), outputSize1);\n  context->ResizeTensor(context, GetOutput(context, node, 1), outputSize2);\n  return kTfLiteOk;\n}\n\nTfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {\n  const TfLiteTensor* input = GetInput(context, node, 0);\n  TF_LITE_ENSURE(context, input != nullptr);\n  int num_strings = tflite::GetStringCount(input);\n  TfLiteTensor* label = GetOutput(context, node, 0);\n  TF_LITE_ENSURE(context, label != nullptr);\n  TfLiteTensor* weight = GetOutput(context, node, 1);\n  TF_LITE_ENSURE(context, weight != nullptr);\n\n  std::map<int64_t, int> feature_id_counts;\n  for (int i = 0; i < num_strings; i++) {\n    // Use fingerprint of feature name as id.\n    auto strref = tflite::GetString(input, i);\n    if (!IsValidNgram(strref)) {\n      label->data.i32[i] = 0;\n      weight->data.i32[i] = 0;\n      continue;\n    }\n\n    int64_t feature_id =\n        ::util::Fingerprint64(strref.str, strref.len) % kMaxDimension;\n    label->data.i32[i] = static_cast<int32_t>(feature_id);\n    weight->data.f[i] =\n        std::count(strref.str, strref.str + strref.len, ' ') + 1;\n  }\n  // Explicitly set an empty result to make preceding ops run.\n  if (num_strings == 0) {\n    label->data.i32[0] = 0;\n    weight->data.i32[0] = 0;\n  }\n  return kTfLiteOk;\n}\n\n}  // namespace extract\n\nTfLiteRegistration* Register_EXTRACT_FEATURES() {\n  static TfLiteRegistration r = {nullptr, nullptr, extract::Prepare,\n                                 extract::Eval};\n  return &r;\n}\n\n}  // namespace custom\n}  // namespace ops\n}  // namespace tflite\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/libs/cc/ops/extract_feature_test.cc",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#include <vector>\n\n#include <gtest/gtest.h>\n#include \"tensorflow/lite/interpreter.h\"\n#include \"tensorflow/lite/kernels/register.h\"\n#include \"tensorflow/lite/kernels/test_util.h\"\n#include \"tensorflow/lite/model.h\"\n#include <farmhash.h>\n\nnamespace tflite {\n\nnamespace ops {\nnamespace custom {\nTfLiteRegistration* Register_EXTRACT_FEATURES();\n\nnamespace {\n\nusing ::testing::ElementsAre;\n\nclass ExtractFeatureOpModel : public SingleOpModel {\n public:\n  explicit ExtractFeatureOpModel(const std::vector<string>& input) {\n    input_ = AddInput(TensorType_STRING);\n    signature_ = AddOutput(TensorType_INT32);\n    weight_ = AddOutput(TensorType_FLOAT32);\n\n    SetCustomOp(\"ExtractFeatures\", {}, Register_EXTRACT_FEATURES);\n    BuildInterpreter({{static_cast<int>(input.size())}});\n    PopulateStringTensor(input_, input);\n  }\n\n  std::vector<int> GetSignature() { return ExtractVector<int>(signature_); }\n  std::vector<float> GetWeight() { return ExtractVector<float>(weight_); }\n\n private:\n  int input_;\n  int signature_;\n  int weight_;\n};\n\nint CalcFeature(const string& str) {\n  return ::util::Fingerprint64(str) % 1000000;\n}\n\nTEST(ExtractFeatureOpTest, RegularInput) {\n  ExtractFeatureOpModel m({\"<S>\", \"<S> Hi\", \"Hi\", \"Hi !\", \"!\", \"! <E>\", \"<E>\"});\n  ASSERT_EQ(m.Invoke(), kTfLiteOk);\n  EXPECT_THAT(m.GetSignature(),\n              ElementsAre(0, CalcFeature(\"<S> Hi\"), CalcFeature(\"Hi\"),\n                          CalcFeature(\"Hi !\"), CalcFeature(\"!\"),\n                          CalcFeature(\"! <E>\"), 0));\n  EXPECT_THAT(m.GetWeight(), ElementsAre(0, 2, 1, 2, 1, 2, 0));\n}\n\nTEST(ExtractFeatureOpTest, OneInput) {\n  ExtractFeatureOpModel m({\"Hi\"});\n  ASSERT_EQ(m.Invoke(), kTfLiteOk);\n  EXPECT_THAT(m.GetSignature(), ElementsAre(CalcFeature(\"Hi\")));\n  EXPECT_THAT(m.GetWeight(), ElementsAre(1));\n}\n\nTEST(ExtractFeatureOpTest, ZeroInput) {\n  ExtractFeatureOpModel m({});\n  ASSERT_EQ(m.Invoke(), kTfLiteOk);\n  EXPECT_THAT(m.GetSignature(), ElementsAre(0));\n  EXPECT_THAT(m.GetWeight(), ElementsAre(0));\n}\n\nTEST(ExtractFeatureOpTest, AllBlacklistInput) {\n  ExtractFeatureOpModel m({\"<S>\", \"<E>\"});\n  ASSERT_EQ(m.Invoke(), kTfLiteOk);\n  EXPECT_THAT(m.GetSignature(), ElementsAre(0, 0));\n  EXPECT_THAT(m.GetWeight(), ElementsAre(0, 0));\n}\n\n}  // namespace\n}  // namespace custom\n}  // namespace ops\n}  // namespace tflite\n\nint main(int argc, char** argv) {\n  ::tflite::LogToStderr();\n  ::testing::InitGoogleTest(&argc, argv);\n  return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/libs/cc/ops/normalize.cc",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n// Normalize the string input.\n//\n// Input:\n//     Input[0]: One sentence. string[1]\n//\n// Output:\n//     Output[0]: Normalized sentence. string[1]\n//\n\n#include <algorithm>\n#include <string>\n\n#include \"absl/strings/str_cat.h\"\n#include \"absl/strings/strip.h\"\n#include \"re2/re2.h\"\n#include \"tensorflow/lite/context.h\"\n#include \"tensorflow/lite/kernels/kernel_util.h\"\n#include \"tensorflow/lite/string_util.h\"\n\nnamespace tflite {\nnamespace ops {\nnamespace custom {\n\nnamespace normalize {\n\n// Predictor transforms.\nconst char kPunctuationsRegex[] = \"[.*()\\\"]\";\n\nconst std::map<string, string>* kRegexTransforms =\n    new std::map<string, string>({\n        {\"([^\\\\s]+)n't\", \"\\\\1 not\"},\n        {\"([^\\\\s]+)'nt\", \"\\\\1 not\"},\n        {\"([^\\\\s]+)'ll\", \"\\\\1 will\"},\n        {\"([^\\\\s]+)'re\", \"\\\\1 are\"},\n        {\"([^\\\\s]+)'ve\", \"\\\\1 have\"},\n        {\"i'm\", \"i am\"},\n    });\n\nstatic const char kStartToken[] = \"<S>\";\nstatic const char kEndToken[] = \"<E>\";\nstatic const int32_t kMaxInputChars = 300;\n\nTfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {\n  const TfLiteTensor* input_tensor = GetInput(context, node, 0);\n  TF_LITE_ENSURE(context, input_tensor != nullptr);\n  tflite::StringRef input = tflite::GetString(input_tensor, 0);\n\n  string result(absl::AsciiStrToLower(absl::string_view(input.str, input.len)));\n  absl::StripAsciiWhitespace(&result);\n  // Do not remove commas, semi-colons or colons from the sentences as they can\n  // indicate the beginning of a new clause.\n  RE2::GlobalReplace(&result, kPunctuationsRegex, \"\");\n  RE2::GlobalReplace(&result, \"\\\\s('t|'nt|n't|'d|'ll|'s|'m|'ve|'re)([\\\\s,;:/])\",\n                     \"\\\\1\\\\2\");\n  RE2::GlobalReplace(&result, \"\\\\s('t|'nt|n't|'d|'ll|'s|'m|'ve|'re)$\", \"\\\\1\");\n  for (auto iter = kRegexTransforms->begin(); iter != kRegexTransforms->end();\n       iter++) {\n    RE2::GlobalReplace(&result, iter->first, iter->second);\n  }\n\n  // Treat questions & interjections as special cases.\n  RE2::GlobalReplace(&result, \"([?])+\", \"\\\\1\");\n  RE2::GlobalReplace(&result, \"([!])+\", \"\\\\1\");\n  RE2::GlobalReplace(&result, \"([^?!]+)([?!])\", \"\\\\1 \\\\2 \");\n  RE2::GlobalReplace(&result, \"([?!])([?!])\", \"\\\\1 \\\\2\");\n\n  RE2::GlobalReplace(&result, \"[\\\\s,:;\\\\-&'\\\"]+$\", \"\");\n  RE2::GlobalReplace(&result, \"^[\\\\s,:;\\\\-&'\\\"]+\", \"\");\n  absl::StripAsciiWhitespace(&result);\n\n  // Add start and end token.\n  // Truncate input to maximum allowed size.\n  if (result.length() <= kMaxInputChars) {\n    absl::StrAppend(&result, \" \", kEndToken);\n  } else {\n    result = result.substr(0, kMaxInputChars);\n  }\n  result = absl::StrCat(kStartToken, \" \", result);\n\n  tflite::DynamicBuffer buf;\n  buf.AddString(result.data(), result.length());\n  buf.WriteToTensorAsVector(GetOutput(context, node, 0));\n  return kTfLiteOk;\n}\n\n}  // namespace normalize\n\nTfLiteRegistration* Register_NORMALIZE() {\n  static TfLiteRegistration r = {nullptr, nullptr, nullptr, normalize::Eval};\n  return &r;\n}\n\n}  // namespace custom\n}  // namespace ops\n}  // namespace tflite\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/libs/cc/ops/normalize_test.cc",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#include <vector>\n\n#include <gtest/gtest.h>\n#include \"tensorflow/lite/interpreter.h\"\n#include \"tensorflow/lite/kernels/register.h\"\n#include \"tensorflow/lite/kernels/test_util.h\"\n#include \"tensorflow/lite/model.h\"\n#include \"tensorflow/lite/string_util.h\"\n\nnamespace tflite {\n\nnamespace ops {\nnamespace custom {\nTfLiteRegistration* Register_NORMALIZE();\n\nnamespace {\n\nusing ::testing::ElementsAreArray;\n\nclass NormalizeOpModel : public SingleOpModel {\n public:\n  explicit NormalizeOpModel(const string& input) {\n    input_ = AddInput(TensorType_STRING);\n    output_ = AddOutput(TensorType_STRING);\n\n    SetCustomOp(\"Normalize\", {}, Register_NORMALIZE);\n    BuildInterpreter({{static_cast<int>(input.size())}});\n    PopulateStringTensor(input_, {input});\n  }\n\n  std::vector<string> GetStringOutput() {\n    TfLiteTensor* output = interpreter_->tensor(output_);\n    int num = GetStringCount(output);\n    std::vector<string> result(num);\n    for (int i = 0; i < num; i++) {\n      auto ref = GetString(output, i);\n      result[i] = string(ref.str, ref.len);\n    }\n    return result;\n  }\n\n private:\n  int input_;\n  int output_;\n};\n\nTEST(NormalizeOpTest, RegularInput) {\n  NormalizeOpModel m(\"I'm good; you're welcome\");\n  ASSERT_EQ(m.Invoke(), kTfLiteOk);\n  EXPECT_THAT(m.GetStringOutput(),\n              ElementsAreArray({\"<S> i am good; you are welcome <E>\"}));\n}\n\nTEST(NormalizeOpTest, OneInput) {\n  NormalizeOpModel m(\"Hi!!!!\");\n  ASSERT_EQ(m.Invoke(), kTfLiteOk);\n  EXPECT_THAT(m.GetStringOutput(), ElementsAreArray({\"<S> hi ! <E>\"}));\n}\n\nTEST(NormalizeOpTest, EmptyInput) {\n  NormalizeOpModel m(\"\");\n  ASSERT_EQ(m.Invoke(), kTfLiteOk);\n  EXPECT_THAT(m.GetStringOutput(), ElementsAreArray({\"<S>  <E>\"}));\n}\n\n}  // namespace\n}  // namespace custom\n}  // namespace ops\n}  // namespace tflite\n\nint main(int argc, char** argv) {\n  ::tflite::LogToStderr();\n  ::testing::InitGoogleTest(&argc, argv);\n  return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/libs/cc/ops/predict.cc",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n// Lookup projected hash signatures in Predictor model,\n// output predicted labels and weights in decreasing order.\n//\n// Input:\n//     Input[0]: A list of hash signatures. int32[num of input]\n//     Input[1]: Hash signature keys in the model. int32[keys of model]\n//     Input[2]: Labels in the model. int32[keys of model, item per entry]\n//     Input[3]: Weights in the model. float[keys of model, item per entry]\n//\n// Output:\n//     Output[0]: Predicted labels. int32[num of output]\n//     Output[1]: Predicted weights. float[num of output]\n//\n\n#include <algorithm>\n#include <cstdint>\n#include <cstdio>\n#include <cstdlib>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"tensorflow/lite/c/c_api_types.h\"\n#include \"tensorflow/lite/c/common.h\"\n\nnamespace tflite {\nnamespace ops {\nnamespace custom {\n\nnamespace predict {\n\nstruct PredictOption {\n  int32_t num_output;\n  float weight_threshold;\n\n  static PredictOption* Cast(void* ptr) {\n    return reinterpret_cast<PredictOption*>(ptr);\n  }\n};\n\nbool WeightGreater(const std::pair<int32_t, float>& a,\n                   const std::pair<int32_t, float>& b) {\n  return a.second > b.second;\n}\n\nvoid* Init(TfLiteContext* context, const char* custom_option, size_t length) {\n  if (custom_option == nullptr || length != sizeof(PredictOption)) {\n    fprintf(stderr, \"No Custom option set\\n\");\n    exit(1);\n  }\n  PredictOption* option = new PredictOption;\n  int offset = 0;\n  option->num_output =\n      *reinterpret_cast<const int32_t*>(custom_option + offset);\n  offset += sizeof(int32_t);\n  option->weight_threshold =\n      *reinterpret_cast<const float*>(custom_option + offset);\n  return reinterpret_cast<void*>(option);\n}\n\nvoid Free(TfLiteContext* context, void* buffer) {\n  delete PredictOption::Cast(buffer);\n}\n\nTfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {\n  TF_LITE_ENSURE_EQ(context, node->inputs->size, 4);\n  TF_LITE_ENSURE_EQ(context, node->outputs->size, 2);\n\n  TfLiteTensor* lookup = &context->tensors[node->inputs->data[0]];\n  TfLiteTensor* model_key = &context->tensors[node->inputs->data[1]];\n  TfLiteTensor* model_label = &context->tensors[node->inputs->data[2]];\n  TfLiteTensor* model_weight = &context->tensors[node->inputs->data[3]];\n  TF_LITE_ENSURE_EQ(context, lookup->type, kTfLiteInt32);\n  TF_LITE_ENSURE_EQ(context, model_key->type, kTfLiteInt32);\n  TF_LITE_ENSURE_EQ(context, model_label->type, kTfLiteInt32);\n  TF_LITE_ENSURE_EQ(context, model_weight->type, kTfLiteFloat32);\n  TF_LITE_ENSURE_EQ(context, lookup->dims->size, 1);\n  TF_LITE_ENSURE_EQ(context, model_key->dims->size, 1);\n  TF_LITE_ENSURE_EQ(context, model_label->dims->size, 2);\n  TF_LITE_ENSURE_EQ(context, model_weight->dims->size, 2);\n  TF_LITE_ENSURE_EQ(context, model_key->dims->data[0],\n                    model_label->dims->data[0]);\n  TF_LITE_ENSURE_EQ(context, model_key->dims->data[0],\n                    model_weight->dims->data[0]);\n  TF_LITE_ENSURE_EQ(context, model_label->dims->data[1],\n                    model_weight->dims->data[1]);\n\n  PredictOption* option = PredictOption::Cast(node->user_data);\n  TfLiteTensor* output_label = &context->tensors[node->outputs->data[0]];\n  TfLiteTensor* output_weight = &context->tensors[node->outputs->data[1]];\n  TF_LITE_ENSURE_EQ(context, output_label->type, kTfLiteInt32);\n  TF_LITE_ENSURE_EQ(context, output_weight->type, kTfLiteFloat32);\n\n  TfLiteIntArray* label_size = TfLiteIntArrayCreate(1);\n  label_size->data[0] = option->num_output;\n  TfLiteIntArray* weight_size = TfLiteIntArrayCreate(1);\n  weight_size->data[0] = option->num_output;\n  TfLiteStatus status =\n      context->ResizeTensor(context, output_label, label_size);\n  if (status != kTfLiteOk) {\n    return status;\n  }\n  return context->ResizeTensor(context, output_weight, weight_size);\n}\n\nTfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {\n  TfLiteTensor* lookup = &context->tensors[node->inputs->data[0]];\n  TfLiteTensor* model_key = &context->tensors[node->inputs->data[1]];\n  TfLiteTensor* model_label = &context->tensors[node->inputs->data[2]];\n  TfLiteTensor* model_weight = &context->tensors[node->inputs->data[3]];\n\n  // Aggregate by key\n  std::unordered_map<int32_t, float> aggregation;\n  const int num_input = lookup->dims->data[0];\n  const int num_rows = model_key->dims->data[0];\n  const int items = model_label->dims->data[1];\n  int* model_key_end = model_key->data.i32 + num_rows;\n\n  for (int i = 0; i < num_input; i++) {\n    int* ptr = std::lower_bound(model_key->data.i32, model_key_end,\n                                lookup->data.i32[i]);\n    if (ptr != nullptr && ptr != model_key_end && *ptr == lookup->data.i32[i]) {\n      int idx = ptr - model_key->data.i32;\n      for (int j = 0; j < items; j++) {\n        aggregation[model_label->data.i32[idx * items + j]] +=\n            model_weight->data.f[idx * items + j] / num_input;\n      }\n    }\n  }\n\n  // Sort by value\n  std::vector<std::pair<int32_t, float>> sorted_labels(aggregation.begin(),\n                                                       aggregation.end());\n  std::sort(sorted_labels.begin(), sorted_labels.end(), WeightGreater);\n\n  PredictOption* option = PredictOption::Cast(node->user_data);\n  TfLiteTensor* output_label = &context->tensors[node->outputs->data[0]];\n  TfLiteTensor* output_weight = &context->tensors[node->outputs->data[1]];\n  for (int i = 0; i < output_label->dims->data[0]; i++) {\n    if (i >= sorted_labels.size() ||\n        sorted_labels[i].second < option->weight_threshold) {\n      // Set -1 to avoid lookup message with id 0, which is set for backoff.\n      output_label->data.i32[i] = -1;\n      output_weight->data.f[i] = 0.0f;\n    } else {\n      output_label->data.i32[i] = sorted_labels[i].first;\n      output_weight->data.f[i] = sorted_labels[i].second;\n    }\n  }\n\n  return kTfLiteOk;\n}\n\n}  // namespace predict\n\nTfLiteRegistration* Register_PREDICT() {\n  static TfLiteRegistration r = {predict::Init, predict::Free, predict::Prepare,\n                                 predict::Eval};\n  return &r;\n}\n\n}  // namespace custom\n}  // namespace ops\n}  // namespace tflite\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/libs/cc/ops/predict_test.cc",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#include <vector>\n\n#include <gtest/gtest.h>\n#include \"tensorflow/lite/interpreter.h\"\n#include \"tensorflow/lite/kernels/register.h\"\n#include \"tensorflow/lite/kernels/test_util.h\"\n#include \"tensorflow/lite/model.h\"\n#include \"tensorflow/lite/string_util.h\"\n\nnamespace tflite {\n\nnamespace ops {\nnamespace custom {\nTfLiteRegistration* Register_PREDICT();\n\nnamespace {\n\nusing ::testing::ElementsAreArray;\n\nclass PredictOpModel : public SingleOpModel {\n public:\n  PredictOpModel(std::initializer_list<int> input_signature_shape,\n                 std::initializer_list<int> key_shape,\n                 std::initializer_list<int> labelweight_shape, int num_output,\n                 float threshold) {\n    input_signature_ = AddInput(TensorType_INT32);\n    model_key_ = AddInput(TensorType_INT32);\n    model_label_ = AddInput(TensorType_INT32);\n    model_weight_ = AddInput(TensorType_FLOAT32);\n    output_label_ = AddOutput(TensorType_INT32);\n    output_weight_ = AddOutput(TensorType_FLOAT32);\n\n    std::vector<uint8_t> predict_option;\n    writeInt32(num_output, &predict_option);\n    writeFloat32(threshold, &predict_option);\n    SetCustomOp(\"Predict\", predict_option, Register_PREDICT);\n    BuildInterpreter({{input_signature_shape, key_shape, labelweight_shape,\n                       labelweight_shape}});\n  }\n\n  void SetInputSignature(std::initializer_list<int> data) {\n    PopulateTensor<int>(input_signature_, data);\n  }\n\n  void SetModelKey(std::initializer_list<int> data) {\n    PopulateTensor<int>(model_key_, data);\n  }\n\n  void SetModelLabel(std::initializer_list<int> data) {\n    PopulateTensor<int>(model_label_, data);\n  }\n\n  void SetModelWeight(std::initializer_list<float> data) {\n    PopulateTensor<float>(model_weight_, data);\n  }\n\n  std::vector<int> GetLabel() { return ExtractVector<int>(output_label_); }\n  std::vector<float> GetWeight() {\n    return ExtractVector<float>(output_weight_);\n  }\n\n  void writeFloat32(float value, std::vector<uint8_t>* data) {\n    union {\n      float v;\n      uint8_t r[4];\n    } float_to_raw;\n    float_to_raw.v = value;\n    for (unsigned char i : float_to_raw.r) {\n      data->push_back(i);\n    }\n  }\n\n  void writeInt32(int32_t value, std::vector<uint8_t>* data) {\n    union {\n      int32_t v;\n      uint8_t r[4];\n    } int32_to_raw;\n    int32_to_raw.v = value;\n    for (unsigned char i : int32_to_raw.r) {\n      data->push_back(i);\n    }\n  }\n\n private:\n  int input_signature_;\n  int model_key_;\n  int model_label_;\n  int model_weight_;\n  int output_label_;\n  int output_weight_;\n};\n\nTEST(PredictOpTest, AllLabelsAreValid) {\n  PredictOpModel m({4}, {5}, {5, 2}, 2, 0.0001);\n  m.SetInputSignature({1, 3, 7, 9});\n  m.SetModelKey({1, 2, 4, 6, 7});\n  m.SetModelLabel({11, 12, 11, 12, 11, 12, 11, 12, 11, 12});\n  m.SetModelWeight({0.1, 0.2, 0.1, 0.2, 0.1, 0.2, 0.1, 0.2, 0.1, 0.2});\n  ASSERT_EQ(m.Invoke(), kTfLiteOk);\n  EXPECT_THAT(m.GetLabel(), ElementsAreArray({12, 11}));\n  EXPECT_THAT(m.GetWeight(), ElementsAreArray(ArrayFloatNear({0.1, 0.05})));\n}\n\nTEST(PredictOpTest, MoreLabelsThanRequired) {\n  PredictOpModel m({4}, {5}, {5, 2}, 1, 0.0001);\n  m.SetInputSignature({1, 3, 7, 9});\n  m.SetModelKey({1, 2, 4, 6, 7});\n  m.SetModelLabel({11, 12, 11, 12, 11, 12, 11, 12, 11, 12});\n  m.SetModelWeight({0.1, 0.2, 0.1, 0.2, 0.1, 0.2, 0.1, 0.2, 0.1, 0.2});\n  ASSERT_EQ(m.Invoke(), kTfLiteOk);\n  EXPECT_THAT(m.GetLabel(), ElementsAreArray({12}));\n  EXPECT_THAT(m.GetWeight(), ElementsAreArray(ArrayFloatNear({0.1})));\n}\n\nTEST(PredictOpTest, OneLabelDoesNotPassThreshold) {\n  PredictOpModel m({4}, {5}, {5, 2}, 2, 0.07);\n  m.SetInputSignature({1, 3, 7, 9});\n  m.SetModelKey({1, 2, 4, 6, 7});\n  m.SetModelLabel({11, 12, 11, 12, 11, 12, 11, 12, 11, 12});\n  m.SetModelWeight({0.1, 0.2, 0.1, 0.2, 0.1, 0.2, 0.1, 0.2, 0.1, 0.2});\n  ASSERT_EQ(m.Invoke(), kTfLiteOk);\n  EXPECT_THAT(m.GetLabel(), ElementsAreArray({12, -1}));\n  EXPECT_THAT(m.GetWeight(), ElementsAreArray(ArrayFloatNear({0.1, 0})));\n}\n\nTEST(PredictOpTest, NoneLabelPassThreshold) {\n  PredictOpModel m({4}, {5}, {5, 2}, 2, 0.6);\n  m.SetInputSignature({1, 3, 7, 9});\n  m.SetModelKey({1, 2, 4, 6, 7});\n  m.SetModelLabel({11, 12, 11, 12, 11, 12, 11, 12, 11, 12});\n  m.SetModelWeight({0.1, 0.2, 0.1, 0.2, 0.1, 0.2, 0.1, 0.2, 0.1, 0.2});\n  ASSERT_EQ(m.Invoke(), kTfLiteOk);\n  EXPECT_THAT(m.GetLabel(), ElementsAreArray({-1, -1}));\n  EXPECT_THAT(m.GetWeight(), ElementsAreArray(ArrayFloatNear({0, 0})));\n}\n\nTEST(PredictOpTest, OnlyOneLabelGenerated) {\n  PredictOpModel m({4}, {5}, {5, 2}, 2, 0.0001);\n  m.SetInputSignature({1, 3, 7, 9});\n  m.SetModelKey({1, 2, 4, 6, 7});\n  m.SetModelLabel({11, 0, 11, 0, 11, 0, 11, 0, 11, 0});\n  m.SetModelWeight({0.1, 0, 0.1, 0, 0.1, 0, 0.1, 0, 0.1, 0});\n  ASSERT_EQ(m.Invoke(), kTfLiteOk);\n  EXPECT_THAT(m.GetLabel(), ElementsAreArray({11, -1}));\n  EXPECT_THAT(m.GetWeight(), ElementsAreArray(ArrayFloatNear({0.05, 0})));\n}\n\nTEST(PredictOpTest, NoLabelGenerated) {\n  PredictOpModel m({4}, {5}, {5, 2}, 2, 0.0001);\n  m.SetInputSignature({5, 3, 7, 9});\n  m.SetModelKey({1, 2, 4, 6, 7});\n  m.SetModelLabel({11, 0, 11, 0, 11, 0, 11, 0, 0, 0});\n  m.SetModelWeight({0.1, 0, 0.1, 0, 0.1, 0, 0.1, 0, 0, 0});\n  ASSERT_EQ(m.Invoke(), kTfLiteOk);\n  EXPECT_THAT(m.GetLabel(), ElementsAreArray({-1, -1}));\n  EXPECT_THAT(m.GetWeight(), ElementsAreArray(ArrayFloatNear({0, 0})));\n}\n\n}  // namespace\n}  // namespace custom\n}  // namespace ops\n}  // namespace tflite\n\nint main(int argc, char** argv) {\n  ::tflite::LogToStderr();\n  ::testing::InitGoogleTest(&argc, argv);\n  return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/libs/cc/predictor.cc",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#include \"cc/predictor.h\"\n\n#include \"absl/strings/str_split.h\"\n#include \"re2/re2.h\"\n#include \"tensorflow/lite/interpreter.h\"\n#include \"tensorflow/lite/kernels/register.h\"\n#include \"tensorflow/lite/model.h\"\n#include \"tensorflow/lite/op_resolver.h\"\n#include \"tensorflow/lite/string_util.h\"\n\nvoid RegisterSelectedOps(::tflite::MutableOpResolver* resolver);\n\nnamespace tflite {\nnamespace custom {\nnamespace smartreply {\n\n// Split sentence into segments (using punctuation).\nstd::vector<std::string> SplitSentence(const std::string& input) {\n  string result(input);\n\n  RE2::GlobalReplace(&result, \"([?.!,])+\", \" \\\\1\");\n  RE2::GlobalReplace(&result, \"([?.!,])+\\\\s+\", \"\\\\1\\t\");\n  RE2::GlobalReplace(&result, \"[ ]+\", \" \");\n  RE2::GlobalReplace(&result, \"\\t+$\", \"\");\n\n  return absl::StrSplit(result, '\\t');\n}\n\n// Predict with TfLite model.\nvoid ExecuteTfLite(const std::string& sentence,\n                   ::tflite::Interpreter* interpreter,\n                   std::map<std::string, float>* response_map) {\n  {\n    TfLiteTensor* input = interpreter->tensor(interpreter->inputs()[0]);\n    tflite::DynamicBuffer buf;\n    buf.AddString(sentence.data(), sentence.length());\n    buf.WriteToTensorAsVector(input);\n    interpreter->AllocateTensors();\n\n    interpreter->Invoke();\n\n    TfLiteTensor* messages = interpreter->tensor(interpreter->outputs()[0]);\n    TfLiteTensor* confidence = interpreter->tensor(interpreter->outputs()[1]);\n\n    for (int i = 0; i < confidence->dims->data[0]; i++) {\n      float weight = confidence->data.f[i];\n      auto response_text = tflite::GetString(messages, i);\n      if (response_text.len > 0) {\n        (*response_map)[string(response_text.str, response_text.len)] += weight;\n      }\n    }\n  }\n}\n\nvoid GetSegmentPredictions(\n    const std::vector<std::string>& input,\n    const ::tflite::FlatBufferModel& model, const SmartReplyConfig& config,\n    std::vector<PredictorResponse>* predictor_responses) {\n  // Initialize interpreter\n  std::unique_ptr<::tflite::Interpreter> interpreter;\n  ::tflite::MutableOpResolver resolver;\n  RegisterSelectedOps(&resolver);\n  ::tflite::InterpreterBuilder(model, resolver)(&interpreter);\n\n  if (!model.initialized()) {\n    fprintf(stderr, \"Failed to mmap model \\n\");\n    return;\n  }\n\n  // Execute Tflite Model\n  std::map<std::string, float> response_map;\n  std::vector<std::string> sentences;\n  for (const std::string& str : input) {\n    std::vector<std::string> splitted_str = SplitSentence(str);\n    sentences.insert(sentences.end(), splitted_str.begin(), splitted_str.end());\n  }\n  for (const auto& sentence : sentences) {\n    ExecuteTfLite(sentence, interpreter.get(), &response_map);\n  }\n\n  // Generate the result.\n  for (const auto& iter : response_map) {\n    PredictorResponse prediction(iter.first, iter.second);\n    predictor_responses->emplace_back(prediction);\n  }\n  std::sort(predictor_responses->begin(), predictor_responses->end(),\n            [](const PredictorResponse& a, const PredictorResponse& b) {\n              return a.GetScore() > b.GetScore();\n            });\n\n  // Add backoff response.\n  for (const auto& backoff : config.backoff_responses) {\n    if (predictor_responses->size() >= config.num_response) {\n      break;\n    }\n    predictor_responses->emplace_back(backoff, config.backoff_confidence);\n  }\n}\n\n}  // namespace smartreply\n}  // namespace custom\n}  // namespace tflite\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/libs/cc/predictor.h",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#ifndef TENSORFLOW_LITE_EXAMPLES_SMARTREPLY_PREDICTOR_H_\n#define TENSORFLOW_LITE_EXAMPLES_SMARTREPLY_PREDICTOR_H_\n\n#include <string>\n#include <vector>\n\n#include \"tensorflow/lite/model.h\"\n\nnamespace tflite {\nnamespace custom {\nnamespace smartreply {\n\nconst int kDefaultNumResponse = 10;\nconst float kDefaultBackoffConfidence = 1e-4;\n\nclass PredictorResponse;\nstruct SmartReplyConfig;\n\n// With a given string as input, predict the response with a Tflite model.\n// When config.backoff_response is not empty, predictor_responses will be filled\n// with messagees from backoff response.\nvoid GetSegmentPredictions(const std::vector<std::string>& input,\n                           const ::tflite::FlatBufferModel& model,\n                           const SmartReplyConfig& config,\n                           std::vector<PredictorResponse>* predictor_responses);\n\n// Data object used to hold a single predictor response.\n// It includes messages, and confidence.\nclass PredictorResponse {\n public:\n  PredictorResponse(const std::string& response_text, float score) {\n    response_text_ = response_text;\n    prediction_score_ = score;\n  }\n\n  // Accessor methods.\n  const std::string& GetText() const { return response_text_; }\n  float GetScore() const { return prediction_score_; }\n\n private:\n  std::string response_text_ = \"\";\n  float prediction_score_ = 0.0;\n};\n\n// Configurations for SmartReply.\nstruct SmartReplyConfig {\n  // Maximum responses to return.\n  int num_response;\n  // Default confidence for backoff responses.\n  float backoff_confidence;\n  // Backoff responses are used when predicted responses cannot fulfill the\n  // list.\n  std::vector<std::string> backoff_responses;\n\n  SmartReplyConfig(const std::vector<std::string>& backoff_responses)\n      : num_response(kDefaultNumResponse),\n        backoff_confidence(kDefaultBackoffConfidence),\n        backoff_responses(backoff_responses) {}\n};\n\n}  // namespace smartreply\n}  // namespace custom\n}  // namespace tflite\n\n#endif  // TENSORFLOW_LITE_EXAMPLES_SMARTREPLY_PREDICTOR_H_\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/libs/cc/predictor_test.cc",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#include \"cc/predictor.h\"\n\n#include <fstream>\n#include <unordered_set>\n\n#include <gmock/gmock.h>\n#include <gtest/gtest.h>\n#include \"absl/strings/str_cat.h\"\n#include \"absl/strings/str_split.h\"\n#include \"tensorflow/core/platform/test.h\"\n#include \"tensorflow/lite/string_util.h\"\n#include \"tensorflow/lite/testing/util.h\"\n\nnamespace tflite {\nnamespace custom {\nnamespace smartreply {\nnamespace {\n\nconst char kSmartReply[] = \"cc/testdata/\";  // NOLINT\nconst char kModel[] = \"smartreply.tflite\";\nconst char kSamples[] = \"smartreply_samples.tsv\";\n\nstring GetModelFilePath() {\n  return absl::StrCat(kSmartReply, kModel);\n}\n\nstring GetSamplesFilePath() {\n  return absl::StrCat(kSmartReply, kSamples);\n}\n\nMATCHER_P(IncludeAnyResponesIn, expected_response, \"contains the response\") {\n  bool has_expected_response = false;\n  for (const auto &item : *arg) {\n    const string &response = item.GetText();\n    if (expected_response.find(response) != expected_response.end()) {\n      has_expected_response = true;\n      break;\n    }\n  }\n  return has_expected_response;\n}\n\nclass PredictorTest : public ::testing::Test {\n protected:\n  PredictorTest() {}\n  ~PredictorTest() override {}\n\n  void SetUp() override {\n    model_ = tflite::FlatBufferModel::BuildFromFile(GetModelFilePath().c_str());\n    ASSERT_NE(model_.get(), nullptr);\n  }\n\n  std::unique_ptr<::tflite::FlatBufferModel> model_;\n};\n\nTEST_F(PredictorTest, GetSegmentPredictions) {\n  std::vector<PredictorResponse> predictions;\n\n  GetSegmentPredictions({\"Welcome\"}, *model_, /*config=*/{{}}, &predictions);\n  EXPECT_GT(predictions.size(), 0);\n\n  float max = 0;\n  for (const auto &item : predictions) {\n    if (item.GetScore() > max) {\n      max = item.GetScore();\n    }\n  }\n\n  EXPECT_GT(max, 0.3);\n  EXPECT_THAT(\n      &predictions,\n      IncludeAnyResponesIn(std::unordered_set<string>({\"Thanks very much\"})));\n}\n\nTEST_F(PredictorTest, TestTwoSentences) {\n  std::vector<PredictorResponse> predictions;\n\n  GetSegmentPredictions({\"Hello\", \"How are you?\"}, *model_, /*config=*/{{}},\n                        &predictions);\n  EXPECT_GT(predictions.size(), 0);\n\n  float max = 0;\n  for (const auto &item : predictions) {\n    if (item.GetScore() > max) {\n      max = item.GetScore();\n    }\n  }\n\n  EXPECT_GT(max, 0.3);\n  EXPECT_THAT(&predictions, IncludeAnyResponesIn(std::unordered_set<string>(\n                                {\"Hi, how are you doing?\"})));\n}\n\nTEST_F(PredictorTest, TestBackoff) {\n  std::vector<PredictorResponse> predictions;\n\n  GetSegmentPredictions({\"你好\"}, *model_, /*config=*/{{}}, &predictions);\n  EXPECT_EQ(predictions.size(), 0);\n\n  // Backoff responses are returned in order.\n  GetSegmentPredictions({\"你好\"}, *model_, /*config=*/{{\"Yes\", \"Ok\"}},\n                        &predictions);\n  EXPECT_EQ(predictions.size(), 2);\n  EXPECT_EQ(predictions[0].GetText(), \"Yes\");\n  EXPECT_EQ(predictions[1].GetText(), \"Ok\");\n}\n\nTEST_F(PredictorTest, BatchTest) {\n  int total_items = 0;\n  int total_responses = 0;\n  int total_triggers = 0;\n\n  string line;\n  std::ifstream fin(GetSamplesFilePath());\n  while (std::getline(fin, line)) {\n    const std::vector<string> fields = absl::StrSplit(line, '\\t');\n    if (fields.empty()) {\n      continue;\n    }\n\n    // Parse sample file and predict\n    const string &msg = fields[0];\n    std::vector<PredictorResponse> predictions;\n    GetSegmentPredictions({msg}, *model_, /*config=*/{{}}, &predictions);\n\n    // Validate response and generate stats.\n    total_items++;\n    total_responses += predictions.size();\n    if (!predictions.empty()) {\n      total_triggers++;\n    }\n    EXPECT_THAT(&predictions, IncludeAnyResponesIn(std::unordered_set<string>(\n                                  fields.begin() + 1, fields.end())));\n  }\n\n  EXPECT_EQ(total_triggers, total_items);\n  EXPECT_GE(total_responses, total_triggers);\n}\n\n}  // namespace\n}  // namespace smartreply\n}  // namespace custom\n}  // namespace tflite\n\nint main(int argc, char **argv) {\n  ::tflite::LogToStderr();\n  ::testing::InitGoogleTest(&argc, argv);\n  return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/libs/cc/smartreply_jni.cc",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#include <jni.h>\n#include <utility>\n#include <vector>\n\n#include \"cc/predictor.h\"\n#include \"tensorflow/lite/model.h\"\n\nconst char kIllegalStateException[] = \"java/lang/IllegalStateException\";\nconst char kSmartReply[] = \"org/tensorflow/lite/examples/smartreply/SmartReply\";\n\nusing tflite::custom::smartreply::GetSegmentPredictions;\nusing tflite::custom::smartreply::PredictorResponse;\n\ntemplate <typename T>\nT CheckNotNull(JNIEnv* env, T&& t) {\n  if (t == nullptr) {\n    env->ThrowNew(env->FindClass(kIllegalStateException), \"\");\n    return nullptr;\n  }\n  return std::forward<T>(t);\n}\n\nstd::vector<std::string> jniStringArrayToVector(JNIEnv* env,\n                                                jobjectArray string_array) {\n  int count = env->GetArrayLength(string_array);\n  std::vector<std::string> result;\n  for (int i = 0; i < count; i++) {\n    auto jstr =\n        reinterpret_cast<jstring>(env->GetObjectArrayElement(string_array, i));\n    const char* raw_str = env->GetStringUTFChars(jstr, JNI_FALSE);\n    result.emplace_back(std::string(raw_str));\n    env->ReleaseStringUTFChars(jstr, raw_str);\n  }\n  return result;\n}\n\nstruct JNIStorage {\n  std::vector<std::string> backoff_list;\n  std::unique_ptr<::tflite::FlatBufferModel> model;\n};\n\nextern \"C\" JNIEXPORT jlong JNICALL\nJava_org_tensorflow_lite_examples_smartreply_SmartReplyClient_loadJNI(\n    JNIEnv* env, jobject thiz, jobject model_buffer,\n    jobjectArray backoff_list) {\n  const char* buf =\n      static_cast<char*>(env->GetDirectBufferAddress(model_buffer));\n  jlong capacity = env->GetDirectBufferCapacity(model_buffer);\n\n  JNIStorage* storage = new JNIStorage;\n  storage->model = tflite::FlatBufferModel::BuildFromBuffer(\n      buf, static_cast<size_t>(capacity));\n  storage->backoff_list = jniStringArrayToVector(env, backoff_list);\n\n  if (!storage->model) {\n    delete storage;\n    env->ThrowNew(env->FindClass(kIllegalStateException), \"\");\n    return 0;\n  }\n  return reinterpret_cast<jlong>(storage);\n}\n\nextern \"C\" JNIEXPORT jobjectArray JNICALL\nJava_org_tensorflow_lite_examples_smartreply_SmartReplyClient_predictJNI(\n    JNIEnv* env, jobject /*thiz*/, jlong storage_ptr, jobjectArray input_text) {\n  // Predict\n  if (storage_ptr == 0) {\n    return nullptr;\n  }\n  JNIStorage* storage = reinterpret_cast<JNIStorage*>(storage_ptr);\n  if (storage == nullptr) {\n    return nullptr;\n  }\n  std::vector<PredictorResponse> responses;\n  GetSegmentPredictions(jniStringArrayToVector(env, input_text),\n                        *storage->model, {storage->backoff_list}, &responses);\n\n  // Create a SmartReply[] to return back to Java\n  jclass smart_reply_class = CheckNotNull(env, env->FindClass(kSmartReply));\n  if (env->ExceptionCheck()) {\n    return nullptr;\n  }\n  jmethodID smart_reply_ctor = CheckNotNull(\n      env,\n      env->GetMethodID(smart_reply_class, \"<init>\", \"(Ljava/lang/String;F)V\"));\n  if (env->ExceptionCheck()) {\n    return nullptr;\n  }\n  jobjectArray array = CheckNotNull(\n      env, env->NewObjectArray(responses.size(), smart_reply_class, nullptr));\n  if (env->ExceptionCheck()) {\n    return nullptr;\n  }\n  for (int i = 0; i < responses.size(); i++) {\n    jstring text =\n        CheckNotNull(env, env->NewStringUTF(responses[i].GetText().data()));\n    if (env->ExceptionCheck()) {\n      return nullptr;\n    }\n    jobject reply = env->NewObject(smart_reply_class, smart_reply_ctor, text,\n                                   responses[i].GetScore());\n    env->SetObjectArrayElement(array, i, reply);\n  }\n  return array;\n}\n\nextern \"C\" JNIEXPORT void JNICALL\nJava_org_tensorflow_lite_examples_smartreply_SmartReplyClient_unloadJNI(\n    JNIEnv* env, jobject thiz, jlong storage_ptr) {\n  if (storage_ptr != 0) {\n    JNIStorage* storage = reinterpret_cast<JNIStorage*>(storage_ptr);\n    delete storage;\n  }\n}\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/libs/cc/testdata/BUILD",
    "content": "package(\n    default_applicable_licenses = [\"//third_party/py/tensorflow_examples:license\"],\n    default_visibility = [\"//visibility:public\"],\n    licenses = [\"notice\"],  # Apache 2.0\n)\n\nexports_files(glob([\"*\"]))\n\nfilegroup(\n    name = \"testdata\",\n    srcs = glob([\n        \"**/*\",\n    ]),\n)\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/libs/cc/testdata/smartreply_samples.tsv",
    "content": "any chance ur free tonight\tMaybe not\nany updates?\tNo update yet\nanything i can do to help?\tNo, but thanks\tNo, but thank you\tNo, but thanks for asking\nbe safe.\tI will be\tWill do my best\tThanks, I will\ncongratulations\tThanks thanks\tCongratulations\ncool, let me know when you have time\tCool\tYes very cool\tYeah, cool\ndrive safe\tThank you, I will\tHome now\tI will thanks\nhang in there, you'll be okay\tDoing my best\tOf course we will\nhappy birthday!\tHey, thanks\nhappy new year!\tWish you the same\tThanks and same to you\nhave a safe flight\tThanks, love you too\tSafe travels\nhey\tWhat is up?\tHow it going?\tCan I help you?\nhey, got a sec?\tWhat is up?\tHow it going?\tCan I help you?\nhow are you doing?\tGreat and you?\tI am doing great\nhow are you feeling\tFeeling okay\tA little better\tMuch much better\nhow was your weekend?\tIt was real good\nhow you doing\tOkay and you\nhugs.\tSo sweet\tThanks sweetie\tTake care of yourself\ni'm bored\tSorry to hear that\tJoin the club\tNo you are not\ni'm planning on coming next week. let me know if that works.\tWorks\tPerfect, thanks\ni'm sick\tSorry to hear that\ni'm so happy for you\tThanks me too\ni'm so hungry\tHaha me too\ni'm sorry\tNo I am sorry\tWhy sorry?\tNo worries love\ni'm sorry, i'm going to have to cancel.\tNo I am sorry\tWhy sorry?\tNo worries love\nis there anything i can do to help?\tNo, but thanks\tNo, but thanks for asking\nlunch?\tYes coming\nokay. lemme know as soon as you find out.\tAny more questions?\tIt is done\nomg amazing\tSo amazing\non my way\tOkay see you soon\tCool, see you soon\tOh wow, ok\noops, mistexted.\tOops\tHaha, oh well\tThat was funny\nsafe travels.\tThanks, love you too\tSafe travels\nso sorry\tSo sorry\nsorry, i can't.\tNo worries at all\tSorry what?\nsorry, i can't do saturday\tNo worries at all\nthank you so much.\tYou are so welcome\tYou are so very welcome\tYou are most welcome\nthanks for coming\tIt was my pleasure\nthanks, this has been great.\tGlad to help\tSo happy for you\ntomorrow would be ideal.\tYes it would\ntried calling\tTry again?\nugh, my flight is delayed.\tUgh indeed\nwhat are you guys up to tonight?\tNothing planned\nwhat day works best for you\tAny day\nwhat do you want for dinner\tYour call\tWhatever is fine\nwhat time will you be home?\tNot sure why\nwhere are you?!?\tAt my house\nwish you were here.\tI wish the same\tMe too honey\nyou're amazing\tYou are too\tYou are amazing\tI am\nyou're marvelous\tYou are too\nyou're the best.\tI do my best\tYou are the best\tWell, I try"
  },
  {
    "path": "lite/examples/smart_reply/android/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.tensorflow.lite.examples.smartreply\">\n\n  <application\n      android:allowBackup=\"true\"\n      android:icon=\"@mipmap/ic_launcher\"\n      android:label=\"@string/tfe_sr_app_name\"\n      android:theme=\"@style/AppTheme.SmartReply\"\n      android:roundIcon=\"@mipmap/ic_launcher_round\">\n\n    <activity\n        android:name=\".MainActivity\"\n        android:configChanges=\"orientation|keyboardHidden|screenSize\"\n        android:windowSoftInputMode=\"adjustResize\"\n        android:screenOrientation=\"portrait\">\n      <intent-filter>\n        <action android:name=\"android.intent.action.MAIN\"/>\n        <category android:name=\"android.intent.category.LAUNCHER\"/>\n      </intent-filter>\n    </activity>\n  </application>\n\n</manifest>\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/src/main/assets/backoff_response.txt",
    "content": "Ok\nYes\nNo\n👍\n☺\n😟\n❤️\nLol\nThanks\nGot it\nDone\nNice\nI don't know\nWhat?\nWhy?\nWhat's up?\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/src/main/java/org/tensorflow/lite/examples/smartreply/AssetsUtil.java",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\npackage org.tensorflow.lite.examples.smartreply;\n\nimport static android.content.res.AssetManager.ACCESS_BUFFER;\nimport static android.os.ParcelFileDescriptor.MODE_READ_ONLY;\n\nimport android.content.Context;\nimport android.content.res.AssetFileDescriptor;\nimport android.os.ParcelFileDescriptor;\nimport com.google.common.io.ByteStreams;\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\n/** Helper to load assets. */\npublic class AssetsUtil {\n\n  private AssetsUtil() {}\n\n  /**\n   * Gets AssetFileDescriptor directly for given a path, or returns its copy by caching for the\n   * compressed one.\n   */\n  public static AssetFileDescriptor getAssetFileDescriptorOrCached(\n      Context context, String assetPath) throws IOException {\n    try {\n      return context.getAssets().openFd(assetPath);\n    } catch (FileNotFoundException e) {\n      // If it cannot read from asset file (probably compressed), try copying to cache folder and\n      // reloading.\n      File cacheFile = new File(context.getCacheDir(), assetPath);\n      cacheFile.getParentFile().mkdirs();\n      copyToCacheFile(context, assetPath, cacheFile);\n      ParcelFileDescriptor cachedFd = ParcelFileDescriptor.open(cacheFile, MODE_READ_ONLY);\n      return new AssetFileDescriptor(cachedFd, 0, cacheFile.length());\n    }\n  }\n\n  private static void copyToCacheFile(Context context, String assetPath, File cacheFile)\n      throws IOException {\n    try (InputStream inputStream = context.getAssets().open(assetPath, ACCESS_BUFFER);\n        FileOutputStream fileOutputStream = new FileOutputStream(cacheFile, false)) {\n      ByteStreams.copy(inputStream, fileOutputStream);\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/src/main/java/org/tensorflow/lite/examples/smartreply/MainActivity.java",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.examples.smartreply;\n\nimport android.os.Bundle;\nimport android.os.Handler;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.util.Log;\nimport android.view.KeyEvent;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.EditText;\nimport android.widget.ScrollView;\nimport android.widget.TextView;\n\n/**\n * The main (and only) activity of this demo app. Displays a text box which updates as messages are\n * received.\n */\npublic class MainActivity extends AppCompatActivity {\n  private static final String TAG = \"SmartReplyDemo\";\n  private SmartReplyClient client;\n\n  private TextView messageTextView;\n  private EditText messageInput;\n  private ScrollView scrollView;\n\n  private Handler handler;\n\n  @Override\n  protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    Log.v(TAG, \"onCreate\");\n    setContentView(R.layout.tfe_sr_main_activity);\n\n    client = new SmartReplyClient(getApplicationContext());\n    handler = new Handler();\n\n    scrollView = findViewById(R.id.scroll_view);\n    messageTextView = findViewById(R.id.message_text);\n\n    messageInput = findViewById(R.id.message_input);\n    messageInput.setOnKeyListener(\n        (view, keyCode, keyEvent) -> {\n          if (keyCode == KeyEvent.KEYCODE_ENTER && keyEvent.getAction() == KeyEvent.ACTION_UP) {\n            // Send message when pressing Enter on keyboard.\n            send(messageInput.getText().toString());\n            return true;\n          }\n          return false;\n        });\n\n    Button sendButton = findViewById(R.id.send_button);\n    sendButton.setOnClickListener((View v) -> send(messageInput.getText().toString()));\n  }\n\n  @Override\n  protected void onStart() {\n    super.onStart();\n    Log.v(TAG, \"onStart\");\n    handler.post(\n        () -> {\n          client.loadModel();\n        });\n  }\n\n  @Override\n  protected void onStop() {\n    super.onStop();\n    Log.v(TAG, \"onStop\");\n    handler.post(\n        () -> {\n          client.unloadModel();\n        });\n  }\n\n  private void send(final String message) {\n    handler.post(\n        () -> {\n          StringBuilder textToShow = new StringBuilder();\n          textToShow.append(\"Input: \").append(message).append(\"\\n\\n\");\n\n          // Get suggested replies from the model.\n          SmartReply[] ans = client.predict(new String[] {message});\n          for (SmartReply reply : ans) {\n            textToShow.append(\"Reply: \").append(reply.getText()).append(\"\\n\");\n          }\n          textToShow.append(\"------\").append(\"\\n\");\n\n          runOnUiThread(\n              () -> {\n                // Show the message and suggested replies on screen.\n                messageTextView.append(textToShow);\n\n                // Clear the input box\n                messageInput.setText(null);\n\n                // Scroll to the bottom to show latest entry's classification result.\n                scrollView.post(() -> scrollView.fullScroll(View.FOCUS_DOWN));\n              });\n        });\n  }\n}\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/src/main/java/org/tensorflow/lite/examples/smartreply/SmartReply.java",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.examples.smartreply;\n\nimport androidx.annotation.Keep;\n\n/**\n * SmartReply contains predicted message, and confidence.\n *\n * <p>NOTE: this class used by JNI, class name and constructor should not be obfuscated.\n */\n@Keep\npublic class SmartReply {\n\n  private final String text;\n  private final float score;\n\n  @Keep\n  public SmartReply(String text, float score) {\n    this.text = text;\n    this.score = score;\n  }\n\n  public String getText() {\n    return text;\n  }\n\n  public float getScore() {\n    return score;\n  }\n}\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/src/main/java/org/tensorflow/lite/examples/smartreply/SmartReplyClient.java",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\npackage org.tensorflow.lite.examples.smartreply;\n\nimport android.content.Context;\nimport android.content.res.AssetFileDescriptor;\nimport android.util.Log;\nimport androidx.annotation.Keep;\nimport androidx.annotation.WorkerThread;\nimport java.io.BufferedReader;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.nio.MappedByteBuffer;\nimport java.nio.channels.FileChannel;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/** Interface to load TfLite model and provide predictions. */\npublic class SmartReplyClient implements AutoCloseable {\n  private static final String TAG = \"SmartReplyDemo\";\n  private static final String MODEL_PATH = \"smartreply.tflite\";\n  private static final String BACKOFF_PATH = \"backoff_response.txt\";\n  private static final String JNI_LIB = \"smartreply_jni\";\n\n  private final Context context;\n  private long storage;\n  private MappedByteBuffer model;\n\n  private volatile boolean isLibraryLoaded;\n\n  public SmartReplyClient(Context context) {\n    this.context = context;\n  }\n\n  public boolean isLoaded() {\n    return storage != 0;\n  }\n\n  @WorkerThread\n  public synchronized void loadModel() {\n    if (!isLibraryLoaded) {\n      System.loadLibrary(JNI_LIB);\n      isLibraryLoaded = true;\n    }\n\n    try {\n      model = loadModelFile();\n      String[] backoff = loadBackoffList();\n      storage = loadJNI(model, backoff);\n    } catch (IOException e) {\n      Log.e(TAG, \"Fail to load model\", e);\n      return;\n    }\n  }\n\n  @WorkerThread\n  public synchronized SmartReply[] predict(String[] input) {\n    if (storage != 0) {\n      return predictJNI(storage, input);\n    } else {\n      return new SmartReply[] {};\n    }\n  }\n\n  @WorkerThread\n  public synchronized void unloadModel() {\n    close();\n  }\n\n  @Override\n  public synchronized void close() {\n    if (storage != 0) {\n      unloadJNI(storage);\n      storage = 0;\n    }\n  }\n\n  private MappedByteBuffer loadModelFile() throws IOException {\n    try (AssetFileDescriptor fileDescriptor =\n            AssetsUtil.getAssetFileDescriptorOrCached(context, MODEL_PATH);\n        FileInputStream inputStream = new FileInputStream(fileDescriptor.getFileDescriptor())) {\n      FileChannel fileChannel = inputStream.getChannel();\n      long startOffset = fileDescriptor.getStartOffset();\n      long declaredLength = fileDescriptor.getDeclaredLength();\n      return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength);\n    }\n  }\n\n  private String[] loadBackoffList() throws IOException {\n    List<String> labelList = new ArrayList<String>();\n    try (BufferedReader reader =\n        new BufferedReader(new InputStreamReader(context.getAssets().open(BACKOFF_PATH)))) {\n      String line;\n      while ((line = reader.readLine()) != null) {\n        if (!line.isEmpty()) {\n          labelList.add(line);\n        }\n      }\n    }\n    String[] ans = new String[labelList.size()];\n    labelList.toArray(ans);\n    return ans;\n  }\n\n  @Keep\n  private native long loadJNI(MappedByteBuffer buffer, String[] backoff);\n\n  @Keep\n  private native SmartReply[] predictJNI(long storage, String[] text);\n\n  @Keep\n  private native void unloadJNI(long storage);\n}\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillColor=\"#26A69A\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n</vector>\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n  <path\n      android:fillType=\"evenOdd\"\n      android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\">\n    <aapt:attr name=\"android:fillColor\">\n      <gradient\n          android:endX=\"78.5885\"\n          android:endY=\"90.9159\"\n          android:startX=\"48.7653\"\n          android:startY=\"61.0927\"\n          android:type=\"linear\">\n        <item\n            android:color=\"#44000000\"\n            android:offset=\"0.0\"/>\n        <item\n            android:color=\"#00000000\"\n            android:offset=\"1.0\"/>\n      </gradient>\n    </aapt:attr>\n  </path>\n  <path\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"nonZero\"\n      android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/src/main/res/layout/tfe_sr_main_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:layout_margin=\"@dimen/tfe_sr_activity_margin\"\n    tools:context=\".MainActivity\">\n\n    <ScrollView\n        android:id=\"@+id/scroll_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:layout_constraintBottom_toTopOf=\"@+id/message_input\">\n\n        <TextView\n            android:id=\"@+id/message_text\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\" />\n    </ScrollView>\n\n    <EditText\n        android:id=\"@+id/message_input\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:hint=\"@string/tfe_sr_edit_text_hint\"\n        android:inputType=\"textNoSuggestions\"\n        android:importantForAutofill=\"no\"\n        app:layout_constraintBaseline_toBaselineOf=\"@+id/send_button\"\n        app:layout_constraintEnd_toStartOf=\"@+id/send_button\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintBottom_toBottomOf=\"parent\" />\n\n    <Button\n        android:id=\"@+id/send_button\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/tfe_sr_button_send\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@+id/message_input\"\n        />\n\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <color name=\"tfe_color_primary\">#ffa800</color>\n  <color name=\"tfe_color_primary_dark\">#ff6f00</color>\n  <color name=\"tfe_color_accent\">#425066</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <dimen name=\"tfe_sr_activity_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/src/main/res/values/strings.xml",
    "content": "<resources>\n  <string name=\"tfe_sr_app_name\" translatable=\"false\">TFL Smart Reply</string>\n  <string name=\"tfe_sr_button_send\" translatable=\"false\">Send</string>\n  <string name=\"tfe_sr_edit_text_hint\" translatable=\"false\">Enter your message...</string>\n</resources>\n"
  },
  {
    "path": "lite/examples/smart_reply/android/app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n  <!-- Base application theme. -->\n  <style name=\"AppTheme.SmartReply\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n    <!-- Customize your theme here. -->\n    <item name=\"colorPrimary\">@color/tfe_color_primary</item>\n    <item name=\"colorPrimaryDark\">@color/tfe_color_primary_dark</item>\n    <item name=\"colorAccent\">@color/tfe_color_accent</item>\n  </style>\n\n</resources>\n"
  },
  {
    "path": "lite/examples/smart_reply/android/build.gradle",
    "content": "\nbuildscript {\n\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:4.2.0'\n        classpath 'de.undercouch:gradle-download-task:4.0.2'\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n        maven {\n            name 'ossrh-snapshot'\n            url 'https://oss.sonatype.org/content/repositories/snapshots'\n        }\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}"
  },
  {
    "path": "lite/examples/smart_reply/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/smart_reply/android/gradle.properties",
    "content": "android.useAndroidX=true"
  },
  {
    "path": "lite/examples/smart_reply/android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/smart_reply/android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/smart_reply/android/how-to-build.md",
    "content": "# How to Build Smart Reply Demo App.\n\n## How to Build with Android Studio (GUI)\n\n### Prerequisites\n\n*   If you don't have already, install\n    [Android Studio](https://developer.android.com/studio/index.html), following\n    the instructions on the website.\n\n*   Android Studio 4.2 or above.\n\n*   You need an Android device or Android emulator and Android development\n    environment with minimum API 19.\n\n### Building and Run with Android Studio.\n\n*   Open Android Studio, and from the Welcome screen, select `Open an existing\n    Android Studio project`.\n\n*   From the Open File or Project window that appears, navigate to and select\n    the `lite/examples/smart_reply/android` directory from wherever you cloned\n    the TensorFlow Lite sample GitHub repo.\n\n*   You may also need to install various platforms and tools according to error\n    messages.\n\n*   Select menu `Build -> Make Project` to build the app. (Ctrl+F9, depending on\n    your version).\n\n*   Click menu `Run -> Run 'app'`. (Shift+F10, depending on your version)\n\n## How to Build App with Gradle (Command Line).\n\n### Step 1. Download pre-built AAR package containing custom ops.\n\nThe Smart Reply demo app contains custom ops to achieve machine learning\nfeature. It calls C++ ops through JNI that preprocesses the text from raw input.\n\nWe have pre-built [AAR package](https://storage.googleapis.com/download.tensorflow.org/models/tflite/smartreply/smartreply_runtime_aar.aar)\n  released, and it contains those custom ops that will be linked dynamically in\n  the app. The corresponding smart reply [TF Lite model](https://storage.googleapis.com/download.tensorflow.org/models/tflite/smartreply/smartreply.tflite)\n  is also provided.\n\nChange directory to Folder `android`, and download AAR:\n\n```\ncd lite/examples/smart_reply/android  # Your android folder\n\n# Run gradle to download.\n./gradlew :app:downloadAAR :app:downloadLiteModel\n```\n\nThe above command downloads prebuilt AAR package with custom ops and TFLite\n  model. You will have AAR package `smartreply_*_.aar` in folder `apps/libs`\n  and `smartreply_*_.aar` in `apps/src/main/assets` and `apps/libs/cc/testdata`.\n\n### Step 2. Use Gradle to build the app.\n\nThen, use Gradle to build the Android app. Gradle will download dependencies and\nfinish building process. It creates android app under `app/build/outputs/apk`\ndirectory.\n\n```\n./gradlew build\n```\n\n*Note that*: Either system environment variable `ANDROID_HOME` or `sdk.dir` in\n  file `local.properties` for gradle should be set to Android SDK path.\n\n### Step 3. Install apk.\n\nOnce packages of `debug` and `release` versions are created. You can install\nthis app on your phone or use emulator for debugging.\n\nThe following command uses `adb` to install the app to an Android phone or\n  emulator connected.\n\n```\nadb install app/build/outputs/apk/debug/app-debug.apk\n```\n\nThen, you may be able to play with the app.\n\n## Optional: How to build AAR package from source code.\n\nIf you changed C++ ops or JNI from source code, you may want to build AAR from\nsource. This also requires `.tflite` models of your own in `testdata` or\ndownloaded from Step 1.\n\nThe procedure is to 1) build AAR package containing JNI (.so) lib, and 2) copy\nto Folder `libs`. (The following is tested under Linux and Mac OS.)\n\n### Require: Bazel installed https://bazel.build/ (version >= 1.0.0).\n\nFirstly, current recommended bazel version is 3.0.0 to align with TensorFlow\nsource code.\n\n-   You may use [bazelisk](https://github.com/bazelbuild/bazelisk)\n    ([release](https://github.com/bazelbuild/bazelisk/releases)) to automaticlly\n    upgrades to the specific version via `.bazelversion` file.\n\n-   The WORKSPACE file will pull TensorFlow source code, and it futher requires\n    TensorFlow's python dependencies installed before building. (Tips: If you\n    notice some missing Python packages during `bazel` command, please use `pip\n    install <package>` to install it.)\n\nYou need to set environment variables ANDROID_HOME and ANDROID_NDK_HOME for\nAndroid SDK and NDK respectively.\n\n```\n# Notes: Below is just one example. It depends on YOUR OWN installation.\nexport ANDROID_HOME=$HOME/Android/Sdk\nexport ANDROID_NDK_HOME=$HOME/Android/Sdk/ndk/20.0.5594570\n```\n\nSecondly, use Bazel to build AAR package from JNI source code and include .so\nlib inside:\n\n```\ncd app/libs\n\nbazel build cc:smartreply_runtime_aar\n```\n\nBy default, it builds ops for multiple cpus (with options: `--fat_apk_cpu=x86,x86_64,arm64-v8a,armeabi-v7a`).\n\nThirdly, copy AAR package to `libs` folder from your bazel root folder.\n\n```\ncp bazel-bin/cc/smartreply_runtime_aar.aar ./smartreply_runtime_aar.aar\n```\n\nIn addition, if you want to build optimized package you may add options `-c opt`\nor selectively choose some option in `--fat_apk_cpu`. For example,\n\n```\nbazel build -c opt --fat_apk_cpu=arm64-v8a,armeabi-v7a cc:smartreply_runtime_aar\n```\n"
  },
  {
    "path": "lite/examples/smart_reply/android/settings.gradle",
    "content": "rootProject.name = 'TFLite Smart Reply Demo App'\ninclude ':app'"
  },
  {
    "path": "lite/examples/sound_classification/README.md",
    "content": "# Sound Classification\n\n## Overview\nThis directory contains end-to-end samples that perform sound classification\non mobile using TensorFlow Lite models.\n\n* An [iOS app](ios/) that uses the TensorFlow Lite model to perform\nsound classification.\n"
  },
  {
    "path": "lite/examples/sound_classification/ios/Podfile",
    "content": "platform :ios, '12.0'\n\ntarget 'SoundClassification' do\n  # Comment the next line if you don't want to use dynamic frameworks\n  use_frameworks!\n\n  pod 'TensorFlowLiteSwift', '~> 0.0.1-nightly'\n  pod 'TensorFlowLiteSelectTfOps', '~> 0.0.1-nightly'\nend\n"
  },
  {
    "path": "lite/examples/sound_classification/ios/README.md",
    "content": "# TensorFlow Lite sound classification iOS example application\n\n## Overview\n\nThis is an example application for [TensorFlow Lite](https://tensorflow.org/lite)\non iOS.\n\n### Model\n\nThe model will be downloaded as part of the build process. Also, you can use\nyour own model generated on\n[Teachable Machine](https://teachablemachine.withgoogle.com/train/audio). For an\nexplanation of training the model, see [Build sound classification models for\nmobile apps with Teachable Machine and\nTFLite](https://blog.tensorflow.org/2020/12/build-sound-classification-models-for-mobile-apps-with-teachable-machine-and-tflite.html).\n\n### iOS app details\n\nThe app is written entirely in Swift and uses the TensorFlow Lite\n[Swift library](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/experimental/swift)\nfor performing sound classification.\n\n## Requirements\n\n*   Device with iOS 12.0 or above\n\n*   Xcode 10.0 or above\n\n*   Valid Apple Developer ID\n\n*   Xcode command-line tools (run `xcode-select --install`)\n\n*   [CocoaPods](https://cocoapods.org/) (run `sudo gem install cocoapods`)\n\nIf this is a new install, you will need to run the Xcode application once to\nagree to the license before continuing.\n\nNote:\nThe demo app requires `SelectTfOps` library which only works on a real iOS\ndevice. You can build it and run with the iPhone Simulator, but the app will\nraise a exception while initializing TensorFlow Lite runtime.\n\n## Build and run\n\n1.  Clone this GitHub repository to your workstation. `git clone\n    https://github.com/tensorflow/examples.git`\n\n2.  Install the pod to generate the workspace file: `cd\n    examples/lite/examples/sound_classification/ios && pod install`\n\nNote: If you have installed this pod before and that command doesn't work, try\n`pod update`.\n\nAt the end of this step you should have a directory called\n`SoundClassification.xcworkspace`.\n\n1.  Open the project in Xcode with the following command: `open\n    SoundClassification.xcworkspace`\n\nThis launches Xcode and opens the `SoundClassification` project.\n\n1.  Select the `SoundClassification` project in the left hand navigation to open\n    the project configuration. In the **Signing** section of the **General**\n    tab, select your development team from the dropdown.\n\n2.  In order to build the project, you must modify the **Bundle Identifier** in\n    the **Identity** section so that it is unique across all Xcode projects. To\n    create a unique identifier, try adding your initials and a number to the end\n    of the string.\n\n3.  With an iOS device connected, build and run the app in Xcode.\n\nYou'll have to grant permissions for the app to use the device's camera. Point\nthe camera at various objects and enjoy seeing how the model classifies things!\n\n## Model references\n_Do not delete the empty references_ to the .tflite and .txt files after you\nclone the repo and open the project. These references will be fulfilled once the\nmodel and label files are downloaded when the application is built and run for\nthe first time. If you delete the references to them, you can still find that\nthe .tflite and .txt files are downloaded to the Model folder, the next time you\nbuild the application. You will have to add the references to these files in the\nbundle separately in that case.\n"
  },
  {
    "path": "lite/examples/sound_classification/ios/RunScripts/download_models.sh",
    "content": "#!/bin/bash\n# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\nset -ex\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nMODELS_URL=\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/sound_classification/snap_clap.tflite\"\nDOWNLOADS_DIR=$(mktemp -d)\n\ncd \"$SCRIPT_DIR\"\n\ndownload() {\n  local usage=\"Usage: download URL DIR\"\n  local url=\"${1:?${usage}}\"\n  local dir=\"${2:?${usage}}\"\n  echo \"downloading ${url}\" >&2\n  mkdir -p \"${dir}\"\n  tempdir=$(mktemp -d)\n\n  curl -L ${url} > ${tempdir}/sound_classification.tflite\n  cp -R ${tempdir}/* ${dir}/\n  rm -rf ${tempdir}\n}\n\nif [ -f ../SoundClassification/Model/sound_classification.tflite ]\nthen\necho \"File already exists.\"\nexit 0\nfi\n\ndownload \"${MODELS_URL}\" \"${DOWNLOADS_DIR}/models\"\n\nfile ${DOWNLOADS_DIR}/models\n\ncp ${DOWNLOADS_DIR}/models/* ../SoundClassification/Model\n"
  },
  {
    "path": "lite/examples/sound_classification/ios/SoundClassification/AppDelegate.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n  var window: UIWindow?\n\n  func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n  ) -> Bool {\n    return true\n  }\n}\n"
  },
  {
    "path": "lite/examples/sound_classification/ios/SoundClassification/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"40.png\",\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"filename\" : \"60.png\",\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"filename\" : \"29.png\",\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"1x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"filename\" : \"58.png\",\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"filename\" : \"87.png\",\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"filename\" : \"80.png\",\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"filename\" : \"120.png\",\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"filename\" : \"120.png\",\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"60x60\"\n    },\n    {\n      \"filename\" : \"180.png\",\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"60x60\"\n    },\n    {\n      \"filename\" : \"20.png\",\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"filename\" : \"40.png\",\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"filename\" : \"29.png\",\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"filename\" : \"58.png\",\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"filename\" : \"40.png\",\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"filename\" : \"80.png\",\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"filename\" : \"50.png\",\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"50x50\"\n    },\n    {\n      \"filename\" : \"100.png\",\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"50x50\"\n    },\n    {\n      \"filename\" : \"72.png\",\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"72x72\"\n    },\n    {\n      \"filename\" : \"144.png\",\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"72x72\"\n    },\n    {\n      \"filename\" : \"76.png\",\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"76x76\"\n    },\n    {\n      \"filename\" : \"152.png\",\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"76x76\"\n    },\n    {\n      \"filename\" : \"167.png\",\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"83.5x83.5\"\n    },\n    {\n      \"filename\" : \"1024.png\",\n      \"idiom\" : \"ios-marketing\",\n      \"scale\" : \"1x\",\n      \"size\" : \"1024x1024\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "lite/examples/sound_classification/ios/SoundClassification/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "lite/examples/sound_classification/ios/SoundClassification/Assets.xcassets/tfl2_logo_dark.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"filename\" : \"tfl2_logo_dark.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "lite/examples/sound_classification/ios/SoundClassification/AudioInputManager.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\nimport AVFoundation\n\npublic protocol AudioInputManagerDelegate: class {\n  func audioInputManagerDidFailToAchievePermission(_ audioInputManager: AudioInputManager)\n  func audioInputManager(_ audioInputManager: AudioInputManager, didCaptureChannelData: [Int16])\n}\n\npublic class AudioInputManager {\n  // MARK: - Constants\n  public let bufferSize: Int\n\n  private let sampleRate: Int\n  private let conversionQueue = DispatchQueue(label: \"conversionQueue\")\n\n  // MARK: - Variables\n  public weak var delegate: AudioInputManagerDelegate?\n\n  private var audioEngine = AVAudioEngine()\n\n  // MARK: - Methods\n\n  public init(sampleRate: Int) {\n    self.sampleRate = sampleRate\n    self.bufferSize = sampleRate * 2\n  }\n\n  public func checkPermissionsAndStartTappingMicrophone() {\n    switch AVAudioSession.sharedInstance().recordPermission {\n    case .granted:\n      startTappingMicrophone()\n    case .denied:\n      delegate?.audioInputManagerDidFailToAchievePermission(self)\n    case .undetermined:\n      requestPermissions()\n    @unknown default:\n      fatalError()\n    }\n  }\n\n  public func requestPermissions() {\n    AVAudioSession.sharedInstance().requestRecordPermission { granted in\n      if granted {\n        self.startTappingMicrophone()\n      } else {\n        self.checkPermissionsAndStartTappingMicrophone()\n      }\n    }\n  }\n\n  /// Starts tapping the microphone input and converts it into the format for which the model is trained and\n  /// periodically returns it in the block\n  public func startTappingMicrophone() {\n    let inputNode = audioEngine.inputNode\n    let inputFormat = inputNode.outputFormat(forBus: 0)\n    guard let recordingFormat = AVAudioFormat(\n      commonFormat: .pcmFormatInt16,\n      sampleRate: Double(sampleRate),\n      channels: 1,\n      interleaved: true\n    ), let formatConverter = AVAudioConverter(from:inputFormat, to: recordingFormat) else { return }\n\n    // installs a tap on the audio engine and specifying the buffer size and the input format.\n    inputNode.installTap(onBus: 0, bufferSize: AVAudioFrameCount(bufferSize), format: inputFormat) {\n      buffer, _ in\n\n      self.conversionQueue.async {\n        // An AVAudioConverter is used to convert the microphone input to the format required\n        // for the model.(pcm 16)\n        guard let pcmBuffer = AVAudioPCMBuffer(\n          pcmFormat: recordingFormat,\n          frameCapacity: AVAudioFrameCount(recordingFormat.sampleRate * 2.0)\n        ) else { return }\n\n        var error: NSError?\n        let inputBlock: AVAudioConverterInputBlock = { _, outStatus in\n          outStatus.pointee = AVAudioConverterInputStatus.haveData\n          return buffer\n        }\n\n        formatConverter.convert(to: pcmBuffer, error: &error, withInputFrom: inputBlock)\n\n        if let error = error {\n          print(error.localizedDescription)\n          return\n        }\n        if let channelData = pcmBuffer.int16ChannelData {\n          let channelDataValue = channelData.pointee\n          let channelDataValueArray = stride(\n            from: 0,\n            to: Int(pcmBuffer.frameLength),\n            by: buffer.stride\n          ).map { channelDataValue[$0] }\n\n          // Converted pcm 16 values are delegated to the controller.\n          self.delegate?.audioInputManager(self, didCaptureChannelData: channelDataValueArray)\n        }\n      }\n    }\n\n    audioEngine.prepare()\n    do {\n      try audioEngine.start()\n    } catch {\n      print(error.localizedDescription)\n    }\n  }\n\n}\n"
  },
  {
    "path": "lite/examples/sound_classification/ios/SoundClassification/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"13122.16\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"13104.12\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" xcode11CocoaTouchSystemColor=\"systemBackgroundColor\" cocoaTouchSystemColor=\"whiteColor\"/>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "lite/examples/sound_classification/ios/SoundClassification/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"17701\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <device id=\"retina6_7\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"17703\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"ViewController\" customModule=\"SoundClassification\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"428\" height=\"926\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <imageView clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleAspectFit\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" image=\"tfl2_logo_dark\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"lfi-nw-UIF\">\n                                <rect key=\"frame\" x=\"16\" y=\"59.999999999999993\" width=\"174.66666666666666\" height=\"30.666666666666664\"/>\n                            </imageView>\n                            <tableView clipsSubviews=\"YES\" contentMode=\"scaleToFill\" alwaysBounceVertical=\"YES\" dataMode=\"prototypes\" style=\"plain\" separatorStyle=\"default\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"28\" sectionFooterHeight=\"28\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"9gB-Zg-0TM\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"114.66666666666669\" width=\"428\" height=\"777.33333333333326\"/>\n                                <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                <color key=\"sectionIndexBackgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                <prototypes>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" insetsLayoutMarginsFromSafeArea=\"NO\" selectionStyle=\"blue\" hidesAccessoryWhenEditing=\"NO\" indentationLevel=\"1\" indentationWidth=\"0.0\" reuseIdentifier=\"probabilityCell\" id=\"jad-bN-t9t\" customClass=\"ProbabilityTableViewCell\" customModule=\"SoundClassification\" customModuleProvider=\"target\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"28\" width=\"428\" height=\"59.333332061767578\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"jad-bN-t9t\" id=\"mF2-ck-1r7\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"428\" height=\"59.333332061767578\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Label\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Sma-M2-skP\">\n                                                    <rect key=\"frame\" x=\"20\" y=\"19\" width=\"80\" height=\"21.333333333333329\"/>\n                                                    <constraints>\n                                                        <constraint firstAttribute=\"width\" relation=\"greaterThanOrEqual\" constant=\"80\" id=\"3jx-1U-YDb\"/>\n                                                    </constraints>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <color key=\"textColor\" white=\"0.0\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                                <progressView opaque=\"NO\" contentMode=\"scaleToFill\" verticalHuggingPriority=\"750\" progress=\"0.5\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"7f4-Cb-maP\">\n                                                    <rect key=\"frame\" x=\"108\" y=\"27.666666666666668\" width=\"284\" height=\"4\"/>\n                                                </progressView>\n                                            </subviews>\n                                            <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                            <constraints>\n                                                <constraint firstItem=\"7f4-Cb-maP\" firstAttribute=\"trailing\" secondItem=\"mF2-ck-1r7\" secondAttribute=\"trailingMargin\" constant=\"-16\" id=\"6To-12-5aR\"/>\n                                                <constraint firstItem=\"Sma-M2-skP\" firstAttribute=\"leading\" secondItem=\"mF2-ck-1r7\" secondAttribute=\"leadingMargin\" id=\"A9a-Sm-cHo\"/>\n                                                <constraint firstItem=\"Sma-M2-skP\" firstAttribute=\"top\" secondItem=\"mF2-ck-1r7\" secondAttribute=\"topMargin\" constant=\"8\" id=\"CfK-0p-2gm\"/>\n                                                <constraint firstItem=\"7f4-Cb-maP\" firstAttribute=\"centerY\" secondItem=\"mF2-ck-1r7\" secondAttribute=\"centerY\" id=\"RMb-cU-YeY\"/>\n                                                <constraint firstAttribute=\"bottomMargin\" secondItem=\"Sma-M2-skP\" secondAttribute=\"bottom\" constant=\"8\" id=\"lwc-6p-Ovf\"/>\n                                                <constraint firstItem=\"7f4-Cb-maP\" firstAttribute=\"leading\" secondItem=\"Sma-M2-skP\" secondAttribute=\"trailingMargin\" constant=\"16\" id=\"ulF-KF-OmH\"/>\n                                            </constraints>\n                                        </tableViewCellContentView>\n                                        <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                        <connections>\n                                            <outlet property=\"label\" destination=\"Sma-M2-skP\" id=\"JPu-yY-jhc\"/>\n                                            <outlet property=\"progressView\" destination=\"7f4-Cb-maP\" id=\"KKH-z7-hv4\"/>\n                                        </connections>\n                                    </tableViewCell>\n                                </prototypes>\n                                <sections/>\n                            </tableView>\n                        </subviews>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                        <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                        <constraints>\n                            <constraint firstItem=\"lfi-nw-UIF\" firstAttribute=\"top\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"top\" constant=\"16\" id=\"BPu-Yh-XE5\"/>\n                            <constraint firstItem=\"lfi-nw-UIF\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" constant=\"16\" id=\"Nnf-5V-mny\"/>\n                            <constraint firstItem=\"6Tk-OE-BBY\" firstAttribute=\"bottom\" secondItem=\"9gB-Zg-0TM\" secondAttribute=\"bottom\" id=\"jrM-xq-eEM\"/>\n                            <constraint firstItem=\"9gB-Zg-0TM\" firstAttribute=\"top\" secondItem=\"lfi-nw-UIF\" secondAttribute=\"bottom\" constant=\"24\" id=\"pCs-qG-Y27\"/>\n                            <constraint firstItem=\"9gB-Zg-0TM\" firstAttribute=\"trailing\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"trailing\" id=\"pU4-Sl-UbZ\"/>\n                            <constraint firstItem=\"9gB-Zg-0TM\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" id=\"xJe-Bk-Bku\"/>\n                        </constraints>\n                    </view>\n                    <connections>\n                        <outlet property=\"tableView\" destination=\"9gB-Zg-0TM\" id=\"h7d-bJ-ubD\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"-196.26168224299064\" y=\"66.738660907127439\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"tfl2_logo_dark\" width=\"174.66667175292969\" height=\"30.666666030883789\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "lite/examples/sound_classification/ios/SoundClassification/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>NSMicrophoneUsageDescription</key>\n\t<string>This app will use the microphone to get audio input in order to classify the sound made by user.</string>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "lite/examples/sound_classification/ios/SoundClassification/Model/labels.txt",
    "content": "0 snap\n1 _background_noise_\n2 clap\n"
  },
  {
    "path": "lite/examples/sound_classification/ios/SoundClassification/ProbabilityTableViewCell.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\nclass ProbabilityTableViewCell: UITableViewCell {\n  @IBOutlet weak var label: UILabel!\n  @IBOutlet weak var progressView: UIProgressView!\n}\n"
  },
  {
    "path": "lite/examples/sound_classification/ios/SoundClassification/SoundClassifier.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Foundation\nimport TensorFlowLite\n\npublic protocol SoundClassifierDelegate: class {\n  func soundClassifier(\n    _ soundClassifier: SoundClassifier,\n    didInterpreteProbabilities probabilities: [Float32]\n  )\n}\n\n/// Performs classification on sound.\n/// The API supports models which accept sound input via `Int16` sound buffer and one classification output tensor.\n/// The output of the recognition is emitted as delegation.\npublic class SoundClassifier {\n  // MARK: - Constants\n  private let modelFileName: String\n  private let modelFileExtension: String\n  private let labelFilename: String\n  private let labelFileExtension: String\n  private let audioBufferInputTensorIndex: Int = 0\n\n  // MARK: - Variables\n  public weak var delegate: SoundClassifierDelegate?\n\n  /// Sample rate for input sound buffer. Caution: generally this value is a bit less than 1 second's audio sample.\n  private(set) var sampleRate = 0\n  /// Lable names described in the lable file\n  private(set) var labelNames: [String] = []\n  private var interpreter: Interpreter!\n\n  // MARK: - Public Methods\n\n  public init(\n    modelFileName: String,\n    modelFileExtension: String = \"tflite\",\n    labelFilename: String = \"labels\",\n    labelFileExtension: String = \"txt\",\n    delegate: SoundClassifierDelegate? = nil\n  ) {\n    self.modelFileName = modelFileName\n    self.modelFileExtension = modelFileExtension\n    self.labelFilename = labelFilename\n    self.labelFileExtension = labelFileExtension\n    self.delegate = delegate\n\n    setupInterpreter()\n  }\n\n  /// Invokes the `Interpreter` and processes and returns the inference results.\n  public func start(inputBuffer: [Int16]) {\n    let outputTensor: Tensor\n    do {\n      let audioBufferData = int16ArrayToData(inputBuffer)\n      try interpreter.copy(audioBufferData, toInputAt: audioBufferInputTensorIndex)\n      try interpreter.invoke()\n\n      outputTensor = try interpreter.output(at: 0)\n    } catch let error {\n      print(\">>> Failed to invoke the interpreter with error: \\(error.localizedDescription)\")\n      return\n    }\n\n    // Gets the formatted and averaged results.\n    let probabilities = dataToFloatArray(outputTensor.data) ?? []\n    delegate?.soundClassifier(self, didInterpreteProbabilities: probabilities)\n  }\n\n  // MARK: - Private Methods\n\n  private func setupInterpreter() {\n    guard let modelPath = Bundle.main.path(\n      forResource: modelFileName,\n      ofType: modelFileExtension\n    ) else { return }\n\n    do {\n      interpreter = try Interpreter(modelPath: modelPath)\n\n      try interpreter.allocateTensors()\n      let inputShape = try interpreter.input(at: 0).shape\n      sampleRate = inputShape.dimensions[1]\n\n      try interpreter.invoke()\n\n      labelNames = loadLabels()\n    } catch {\n      print(\"Failed to create the interpreter with error: \\(error.localizedDescription)\")\n      return\n    }\n  }\n\n  private func loadLabels() -> [String] {\n    guard let labelPath = Bundle.main.path(\n      forResource: labelFilename,\n      ofType: labelFileExtension\n    ) else { return [] }\n\n    var content = \"\"\n    do {\n      content = try String(contentsOfFile: labelPath, encoding: .utf8)\n      let labels = content.components(separatedBy: \"\\n\")\n        .filter { !$0.isEmpty }\n        .compactMap { line -> String in\n          let splitPair = line.components(separatedBy: \" \")\n          let label = splitPair[1]\n          let titleCasedLabel = label.components(separatedBy: \"_\")\n            .compactMap { $0.capitalized }\n            .joined(separator: \" \")\n          return titleCasedLabel\n        }\n      return labels\n    } catch {\n      print(\"Failed to load label content: '\\(content)' with error: \\(error.localizedDescription)\")\n      return []\n    }\n  }\n\n  /// Creates a new buffer by copying the buffer pointer of the given `Int16` array.\n  private func int16ArrayToData(_ buffer: [Int16]) -> Data {\n    let floatData = buffer.map { Float($0) / Float(Int16.max) }\n    return floatData.withUnsafeBufferPointer(Data.init)\n  }\n\n  /// Creates a new array from the bytes of the given unsafe data.\n  /// - Returns: `nil` if `unsafeData.count` is not a multiple of `MemoryLayout<Float>.stride`.\n  private func dataToFloatArray(_ data: Data) -> [Float]? {\n    guard data.count % MemoryLayout<Float>.stride == 0 else { return nil }\n\n    #if swift(>=5.0)\n    return data.withUnsafeBytes { .init($0.bindMemory(to: Float.self)) }\n    #else\n    return data.withUnsafeBytes {\n      .init(UnsafeBufferPointer<Float>(\n        start: $0,\n        count: unsafeData.count / MemoryLayout<Element>.stride\n      ))\n    }\n    #endif // swift(>=5.0)\n  }\n}\n"
  },
  {
    "path": "lite/examples/sound_classification/ios/SoundClassification/ViewController.swift",
    "content": "// Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\nclass ViewController: UIViewController {\n  // MARK: - Variables\n  @IBOutlet weak var tableView: UITableView!\n\n  private var audioInputManager: AudioInputManager!\n  private var soundClassifier: SoundClassifier!\n  private var bufferSize: Int = 0\n  private var probabilities: [Float32] = []\n\n  // MARK: - View controller lifecycle methods\n\n  override func viewDidLoad() {\n    super.viewDidLoad()\n\n    tableView.dataSource = self\n    tableView.backgroundColor = .white\n    tableView.tableFooterView = UIView()\n\n    soundClassifier = SoundClassifier(modelFileName: \"sound_classification\", delegate: self)\n\n    startAudioRecognition()\n  }\n\n  // MARK: - Private Methods\n\n  /// Initializes the AudioInputManager and starts recognizing on the output buffers.\n  private func startAudioRecognition() {\n    audioInputManager = AudioInputManager(sampleRate: soundClassifier.sampleRate)\n    audioInputManager.delegate = self\n\n    bufferSize = audioInputManager.bufferSize\n\n    audioInputManager.checkPermissionsAndStartTappingMicrophone()\n  }\n\n  private func runModel(inputBuffer: [Int16]) {\n    soundClassifier.start(inputBuffer: inputBuffer)\n  }\n}\n\nextension ViewController: AudioInputManagerDelegate {\n  func audioInputManagerDidFailToAchievePermission(_ audioInputManager: AudioInputManager) {\n    let alertController = UIAlertController(\n      title: \"Microphone Permissions Denied\",\n      message: \"Microphone permissions have been denied for this app. You can change this by going to Settings\",\n      preferredStyle: .alert\n    )\n\n    let cancelAction = UIAlertAction(title: \"Cancel\", style: .cancel, handler: nil)\n    let settingsAction = UIAlertAction(title: \"Settings\", style: .default) { _ in\n      UIApplication.shared.open(\n        URL(string: UIApplication.openSettingsURLString)!,\n        options: [:],\n        completionHandler: nil\n      )\n    }\n    alertController.addAction(cancelAction)\n    alertController.addAction(settingsAction)\n\n    present(alertController, animated: true, completion: nil)\n  }\n\n  func audioInputManager(\n    _ audioInputManager: AudioInputManager,\n    didCaptureChannelData channelData: [Int16]\n  ) {\n    let sampleRate = soundClassifier.sampleRate\n    self.runModel(inputBuffer: Array(channelData[0..<sampleRate]))\n    self.runModel(inputBuffer: Array(channelData[sampleRate..<bufferSize]))\n  }\n}\n\nextension ViewController: SoundClassifierDelegate {\n  func soundClassifier(\n    _ soundClassifier: SoundClassifier,\n    didInterpreteProbabilities probabilities: [Float32]\n  ) {\n    self.probabilities = probabilities\n    DispatchQueue.main.async {\n      self.tableView.reloadData()\n    }\n  }\n}\n\n// MARK: - UITableViewDataSource\nextension ViewController: UITableViewDataSource {\n  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {\n    return probabilities.count\n  }\n\n  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {\n    guard let cell = tableView.dequeueReusableCell(\n      withIdentifier: \"probabilityCell\",\n      for: indexPath\n    ) as? ProbabilityTableViewCell else { return UITableViewCell() }\n\n    cell.label.text = soundClassifier.labelNames[indexPath.row]\n    UIView.animate(withDuration: 0.4) {\n      cell.progressView.setProgress(self.probabilities[indexPath.row], animated: true)\n    }\n    return cell\n  }\n}\n"
  },
  {
    "path": "lite/examples/sound_classification/ios/SoundClassification.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 51;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t66049BD7077F73C9210873F2 /* Pods_SoundClassification.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA70AA685F986DCEB5D61176 /* Pods_SoundClassification.framework */; };\n\t\tC83580DC253929A7007E9603 /* labels.txt in Resources */ = {isa = PBXBuildFile; fileRef = C83580DA253929A7007E9603 /* labels.txt */; };\n\t\tC83580DD253929A7007E9603 /* sound_classification.tflite in Resources */ = {isa = PBXBuildFile; fileRef = C83580DB253929A7007E9603 /* sound_classification.tflite */; };\n\t\tC83B9C82256BB4C000EA2282 /* AudioInputManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83B9C81256BB4C000EA2282 /* AudioInputManager.swift */; };\n\t\tC83B9C8D256D38DB00EA2282 /* SoundClassifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83B9C8C256D38DB00EA2282 /* SoundClassifier.swift */; };\n\t\tC86E5C9F2525DB8400F53BEA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86E5C9E2525DB8400F53BEA /* AppDelegate.swift */; };\n\t\tC86E5CA32525DB8400F53BEA /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86E5CA22525DB8400F53BEA /* ViewController.swift */; };\n\t\tC86E5CA62525DB8400F53BEA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C86E5CA42525DB8400F53BEA /* Main.storyboard */; };\n\t\tC86E5CA82525DB8600F53BEA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C86E5CA72525DB8600F53BEA /* Assets.xcassets */; };\n\t\tC86E5CAB2525DB8600F53BEA /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C86E5CA92525DB8600F53BEA /* LaunchScreen.storyboard */; };\n\t\tC8FF9D332577CFB2004ED628 /* ProbabilityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8FF9D322577CFB2004ED628 /* ProbabilityTableViewCell.swift */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\tC86E5CB22525DB8700F53BEA /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = C86E5C932525DB8400F53BEA /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = C86E5C9A2525DB8400F53BEA;\n\t\t\tremoteInfo = SoundClassification;\n\t\t};\n\t\tC86E5CBD2525DB8700F53BEA /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = C86E5C932525DB8400F53BEA /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = C86E5C9A2525DB8400F53BEA;\n\t\t\tremoteInfo = SoundClassification;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXFileReference section */\n\t\t5AFAE9051D797536BC360191 /* Pods-SoundClassification.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-SoundClassification.release.xcconfig\"; path = \"Target Support Files/Pods-SoundClassification/Pods-SoundClassification.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t8D3C72E2E6452206717A5F8A /* Pods-SoundClassification.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-SoundClassification.debug.xcconfig\"; path = \"Target Support Files/Pods-SoundClassification/Pods-SoundClassification.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tAA70AA685F986DCEB5D61176 /* Pods_SoundClassification.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SoundClassification.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tC83580DA253929A7007E9603 /* labels.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = labels.txt; sourceTree = \"<group>\"; };\n\t\tC83580DB253929A7007E9603 /* sound_classification.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = sound_classification.tflite; sourceTree = \"<group>\"; };\n\t\tC83B9C81256BB4C000EA2282 /* AudioInputManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioInputManager.swift; sourceTree = \"<group>\"; };\n\t\tC83B9C8C256D38DB00EA2282 /* SoundClassifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoundClassifier.swift; sourceTree = \"<group>\"; };\n\t\tC86E5C9B2525DB8400F53BEA /* SoundClassification.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SoundClassification.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tC86E5C9E2525DB8400F53BEA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\tC86E5CA22525DB8400F53BEA /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = \"<group>\"; };\n\t\tC86E5CA52525DB8400F53BEA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\tC86E5CA72525DB8600F53BEA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tC86E5CAA2525DB8600F53BEA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\tC86E5CAC2525DB8600F53BEA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tC86E5CB12525DB8700F53BEA /* SoundClassificationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SoundClassificationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tC86E5CBC2525DB8700F53BEA /* SoundClassificationUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SoundClassificationUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tC8FF9D322577CFB2004ED628 /* ProbabilityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProbabilityTableViewCell.swift; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tC86E5C982525DB8400F53BEA /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t66049BD7077F73C9210873F2 /* Pods_SoundClassification.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tC86E5CAE2525DB8700F53BEA /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tC86E5CB92525DB8700F53BEA /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t5116BDF9419EF28534A8B4DC /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t8D3C72E2E6452206717A5F8A /* Pods-SoundClassification.debug.xcconfig */,\n\t\t\t\t5AFAE9051D797536BC360191 /* Pods-SoundClassification.release.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tC83580D925392981007E9603 /* Model */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tC83580DA253929A7007E9603 /* labels.txt */,\n\t\t\t\tC83580DB253929A7007E9603 /* sound_classification.tflite */,\n\t\t\t);\n\t\t\tpath = Model;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tC86E5C922525DB8400F53BEA = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tC86E5C9D2525DB8400F53BEA /* SoundClassification */,\n\t\t\t\tC86E5C9C2525DB8400F53BEA /* Products */,\n\t\t\t\t5116BDF9419EF28534A8B4DC /* Pods */,\n\t\t\t\tE1567F2DAE1E99ED1CCE7C9E /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tC86E5C9C2525DB8400F53BEA /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tC86E5C9B2525DB8400F53BEA /* SoundClassification.app */,\n\t\t\t\tC86E5CB12525DB8700F53BEA /* SoundClassificationTests.xctest */,\n\t\t\t\tC86E5CBC2525DB8700F53BEA /* SoundClassificationUITests.xctest */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tC86E5C9D2525DB8400F53BEA /* SoundClassification */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tC83580D925392981007E9603 /* Model */,\n\t\t\t\tC86E5C9E2525DB8400F53BEA /* AppDelegate.swift */,\n\t\t\t\tC86E5CA22525DB8400F53BEA /* ViewController.swift */,\n\t\t\t\tC86E5CA42525DB8400F53BEA /* Main.storyboard */,\n\t\t\t\tC86E5CA72525DB8600F53BEA /* Assets.xcassets */,\n\t\t\t\tC86E5CA92525DB8600F53BEA /* LaunchScreen.storyboard */,\n\t\t\t\tC86E5CAC2525DB8600F53BEA /* Info.plist */,\n\t\t\t\tC83B9C81256BB4C000EA2282 /* AudioInputManager.swift */,\n\t\t\t\tC83B9C8C256D38DB00EA2282 /* SoundClassifier.swift */,\n\t\t\t\tC8FF9D322577CFB2004ED628 /* ProbabilityTableViewCell.swift */,\n\t\t\t);\n\t\t\tpath = SoundClassification;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tE1567F2DAE1E99ED1CCE7C9E /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAA70AA685F986DCEB5D61176 /* Pods_SoundClassification.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\tC86E5C9A2525DB8400F53BEA /* SoundClassification */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = C86E5CC52525DB8700F53BEA /* Build configuration list for PBXNativeTarget \"SoundClassification\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tB9FB71F77ABF6F7212D7C0F7 /* [CP] Check Pods Manifest.lock */,\n\t\t\t\tC853F2EE25B06E5600E4EE95 /* Download TFLite model */,\n\t\t\t\tC86E5C972525DB8400F53BEA /* Sources */,\n\t\t\t\tC86E5C982525DB8400F53BEA /* Frameworks */,\n\t\t\t\tC86E5C992525DB8400F53BEA /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = SoundClassification;\n\t\t\tproductName = SoundClassification;\n\t\t\tproductReference = C86E5C9B2525DB8400F53BEA /* SoundClassification.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\tC86E5CB02525DB8700F53BEA /* SoundClassificationTests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = C86E5CC82525DB8700F53BEA /* Build configuration list for PBXNativeTarget \"SoundClassificationTests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tC86E5CAD2525DB8700F53BEA /* Sources */,\n\t\t\t\tC86E5CAE2525DB8700F53BEA /* Frameworks */,\n\t\t\t\tC86E5CAF2525DB8700F53BEA /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tC86E5CB32525DB8700F53BEA /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = SoundClassificationTests;\n\t\t\tproductName = SoundClassificationTests;\n\t\t\tproductReference = C86E5CB12525DB8700F53BEA /* SoundClassificationTests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\n\t\tC86E5CBB2525DB8700F53BEA /* SoundClassificationUITests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = C86E5CCB2525DB8700F53BEA /* Build configuration list for PBXNativeTarget \"SoundClassificationUITests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tC86E5CB82525DB8700F53BEA /* Sources */,\n\t\t\t\tC86E5CB92525DB8700F53BEA /* Frameworks */,\n\t\t\t\tC86E5CBA2525DB8700F53BEA /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tC86E5CBE2525DB8700F53BEA /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = SoundClassificationUITests;\n\t\t\tproductName = SoundClassificationUITests;\n\t\t\tproductReference = C86E5CBC2525DB8700F53BEA /* SoundClassificationUITests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.ui-testing\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tC86E5C932525DB8400F53BEA /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 1170;\n\t\t\t\tLastUpgradeCheck = 1200;\n\t\t\t\tORGANIZATIONNAME = Tensorflow;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tC86E5C9A2525DB8400F53BEA = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 11.7;\n\t\t\t\t\t};\n\t\t\t\t\tC86E5CB02525DB8700F53BEA = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 11.7;\n\t\t\t\t\t\tTestTargetID = C86E5C9A2525DB8400F53BEA;\n\t\t\t\t\t};\n\t\t\t\t\tC86E5CBB2525DB8700F53BEA = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 11.7;\n\t\t\t\t\t\tTestTargetID = C86E5C9A2525DB8400F53BEA;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = C86E5C962525DB8400F53BEA /* Build configuration list for PBXProject \"SoundClassification\" */;\n\t\t\tcompatibilityVersion = \"Xcode 10.0\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = C86E5C922525DB8400F53BEA;\n\t\t\tproductRefGroup = C86E5C9C2525DB8400F53BEA /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tC86E5C9A2525DB8400F53BEA /* SoundClassification */,\n\t\t\t\tC86E5CB02525DB8700F53BEA /* SoundClassificationTests */,\n\t\t\t\tC86E5CBB2525DB8700F53BEA /* SoundClassificationUITests */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\tC86E5C992525DB8400F53BEA /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tC86E5CAB2525DB8600F53BEA /* LaunchScreen.storyboard in Resources */,\n\t\t\t\tC83580DD253929A7007E9603 /* sound_classification.tflite in Resources */,\n\t\t\t\tC86E5CA82525DB8600F53BEA /* Assets.xcassets in Resources */,\n\t\t\t\tC86E5CA62525DB8400F53BEA /* Main.storyboard in Resources */,\n\t\t\t\tC83580DC253929A7007E9603 /* labels.txt in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tC86E5CAF2525DB8700F53BEA /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tC86E5CBA2525DB8700F53BEA /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\tB9FB71F77ABF6F7212D7C0F7 /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-SoundClassification-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\tC853F2EE25B06E5600E4EE95 /* Download TFLite model */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Download TFLite model\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"$SRCROOT/RunScripts/download_models.sh\\\"\\n\";\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tC86E5C972525DB8400F53BEA /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tC86E5CA32525DB8400F53BEA /* ViewController.swift in Sources */,\n\t\t\t\tC86E5C9F2525DB8400F53BEA /* AppDelegate.swift in Sources */,\n\t\t\t\tC8FF9D332577CFB2004ED628 /* ProbabilityTableViewCell.swift in Sources */,\n\t\t\t\tC83B9C8D256D38DB00EA2282 /* SoundClassifier.swift in Sources */,\n\t\t\t\tC83B9C82256BB4C000EA2282 /* AudioInputManager.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tC86E5CAD2525DB8700F53BEA /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tC86E5CB82525DB8700F53BEA /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\tC86E5CB32525DB8700F53BEA /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = C86E5C9A2525DB8400F53BEA /* SoundClassification */;\n\t\t\ttargetProxy = C86E5CB22525DB8700F53BEA /* PBXContainerItemProxy */;\n\t\t};\n\t\tC86E5CBE2525DB8700F53BEA /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = C86E5C9A2525DB8400F53BEA /* SoundClassification */;\n\t\t\ttargetProxy = C86E5CBD2525DB8700F53BEA /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin PBXVariantGroup section */\n\t\tC86E5CA42525DB8400F53BEA /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tC86E5CA52525DB8400F53BEA /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tC86E5CA92525DB8600F53BEA /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tC86E5CAA2525DB8600F53BEA /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\tC86E5CC32525DB8700F53BEA /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tOTHER_LDFLAGS = \"\";\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tC86E5CC42525DB8700F53BEA /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tOTHER_LDFLAGS = \"\";\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tC86E5CC62525DB8700F53BEA /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 8D3C72E2E6452206717A5F8A /* Pods-SoundClassification.debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tDEVELOPMENT_TEAM = EQHXZ8M8AV;\n\t\t\t\tINFOPLIST_FILE = SoundClassification/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tOTHER_LDFLAGS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"-ObjC\",\n\t\t\t\t\t\"-l\\\"c++\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"TensorFlowLite\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"TensorFlowLiteC\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"TensorFlowLiteSelectTfOps\\\"\",\n\t\t\t\t\t\"-weak_framework\",\n\t\t\t\t\t\"\\\"CoreML\\\"\",\n\t\t\t\t\t\"-force_load\",\n\t\t\t\t\t\"$(SRCROOT)/Pods/TensorFlowLiteSelectTfOps/Frameworks/TensorFlowLiteSelectTfOps.xcframework/ios-arm64/TensorFlowLiteSelectTfOps.framework/TensorFlowLiteSelectTfOps\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tensorflow.SoundClassification;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"Google Development\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = 1;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tC86E5CC72525DB8700F53BEA /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 5AFAE9051D797536BC360191 /* Pods-SoundClassification.release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tDEVELOPMENT_TEAM = EQHXZ8M8AV;\n\t\t\t\tINFOPLIST_FILE = SoundClassification/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tOTHER_LDFLAGS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"-ObjC\",\n\t\t\t\t\t\"-l\\\"c++\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"TensorFlowLite\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"TensorFlowLiteC\\\"\",\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\t\"\\\"TensorFlowLiteSelectTfOps\\\"\",\n\t\t\t\t\t\"-weak_framework\",\n\t\t\t\t\t\"\\\"CoreML\\\"\",\n\t\t\t\t\t\"-force_load\",\n\t\t\t\t\t\"$(SRCROOT)/Pods/TensorFlowLiteSelectTfOps/Frameworks/TensorFlowLiteSelectTfOps.framework/TensorFlowLiteSelectTfOps\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tensorflow.SoundClassification;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"Google Development\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = 1;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tC86E5CC92525DB8700F53BEA /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tINFOPLIST_FILE = SoundClassificationTests/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.7;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tensorflow.SoundClassificationTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/SoundClassification.app/SoundClassification\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tC86E5CCA2525DB8700F53BEA /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tINFOPLIST_FILE = SoundClassificationTests/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.7;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tensorflow.SoundClassificationTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/SoundClassification.app/SoundClassification\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tC86E5CCC2525DB8700F53BEA /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tINFOPLIST_FILE = SoundClassificationUITests/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tensorflow.SoundClassificationUITests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tTEST_TARGET_NAME = SoundClassification;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tC86E5CCD2525DB8700F53BEA /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tINFOPLIST_FILE = SoundClassificationUITests/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.tensorflow.SoundClassificationUITests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tTEST_TARGET_NAME = SoundClassification;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tC86E5C962525DB8400F53BEA /* Build configuration list for PBXProject \"SoundClassification\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tC86E5CC32525DB8700F53BEA /* Debug */,\n\t\t\t\tC86E5CC42525DB8700F53BEA /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tC86E5CC52525DB8700F53BEA /* Build configuration list for PBXNativeTarget \"SoundClassification\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tC86E5CC62525DB8700F53BEA /* Debug */,\n\t\t\t\tC86E5CC72525DB8700F53BEA /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tC86E5CC82525DB8700F53BEA /* Build configuration list for PBXNativeTarget \"SoundClassificationTests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tC86E5CC92525DB8700F53BEA /* Debug */,\n\t\t\t\tC86E5CCA2525DB8700F53BEA /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tC86E5CCB2525DB8700F53BEA /* Build configuration list for PBXNativeTarget \"SoundClassificationUITests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tC86E5CCC2525DB8700F53BEA /* Debug */,\n\t\t\t\tC86E5CCD2525DB8700F53BEA /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = C86E5C932525DB8400F53BEA /* Project object */;\n}\n"
  },
  {
    "path": "lite/examples/sound_classification/raspberry_pi/README.md",
    "content": "# TensorFlow Lite Python audio classification example with Raspberry Pi.\n\nThis example uses [TensorFlow Lite](https://tensorflow.org/lite) with Python on\na Raspberry Pi to perform real-time audio classification using audio streamed\nfrom the microphone.\n\nAt the end of this page, there are extra steps to accelerate the example using\nthe Coral USB Accelerator to increase inference speed.\n\n## Set up your hardware\n\nBefore you begin, you need to\n[set up your Raspberry Pi](https://projects.raspberrypi.org/en/projects/raspberry-pi-setting-up)\nwith Raspberry Pi OS (preferably updated to Buster).\n\nRaspberry Pi doesn't have a microphone integrated on its board, so you need to\nplug in a USB microphone to record audio.\n\n## Install the TensorFlow Lite runtime\n\nIn this project, all you need from the TensorFlow Lite API is the `Interpreter`\nclass. So instead of installing the large `tensorflow` package, we're using the\nmuch smaller `tflite_runtime` package.\n\nTo install this on your Raspberry Pi, follow the instructions in the\n[Python quickstart](https://www.tensorflow.org/lite/guide/python#install_tensorflow_lite_for_python).\n\nYou can install the TFLite runtime using this script.\n\n```\nsh setup.sh\n```\n\n## Download the example files\n\nFirst, clone this Git repo onto your Raspberry Pi like this:\n\n```\ngit clone https://github.com/tensorflow/examples.git --depth 1\n```\n\nThen use our script to install a couple Python packages, and download the TFLite\nmodel:\n\n```\ncd lite/examples/sound_classification/raspberry_pi\n\n# Run this script to install the required dependencies and download the TFLite models.\nsh setup.sh\n```\n\n## Run the example\n\n```\npython3 classify.py\n```\n\n*   You can optionally specify the `model` parameter to set the TensorFlow Lite\n    model to be used:\n    *   The default value is `yamnet.tflite`\n*   You can optionally specify the `maxResults` parameter to limit the list of\n    classification results:\n    *   Supported value: A positive integer.\n    *   Default value: `5`.\n*   Example usage:\n\n```\npython3 classify.py \\\n  --model yamnet.tflite \\\n  --maxResults 5\n```\n\n## Speed up the inference time (optional)\n\nIf you want to speed up the inference time, you can attach an ML accelerator\nsuch as the\n[Coral USB Accelerator](https://coral.withgoogle.com/products/accelerator)—a USB\naccessory that adds the\n[Edge TPU ML accelerator](https://coral.withgoogle.com/docs/edgetpu/faq/) to any\nLinux-based system.\n\nIf you have a Coral USB Accelerator, you can run the sample with it enabled:\n\n1.  First, be sure you have completed the\n    [USB Accelerator setup instructions](https://coral.withgoogle.com/docs/accelerator/get-started/).\n\n2.  Run the audio classification script using the Edge TPU TFLite model and\n    enable the Edge TPU option.\n\n```\npython3 classify.py \\\n  --model yamnet_edgetpu.tflite \\\n  --enableEdgeTPU\n```\n\nFor more information about creating and running TensorFlow Lite models with\nCoral devices, read\n[TensorFlow models on the Edge TPU](https://coral.withgoogle.com/docs/edgetpu/models-intro/).\n\nFor more information about executing inferences with TensorFlow Lite, read\n[TensorFlow Lite inference](https://www.tensorflow.org/lite/guide/inference).\n"
  },
  {
    "path": "lite/examples/sound_classification/raspberry_pi/classify.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Main scripts to run audio classification.\"\"\"\n\nimport argparse\nimport time\n\nfrom tflite_support.task import audio\nfrom tflite_support.task import core\nfrom tflite_support.task import processor\nfrom utils import Plotter\n\n\ndef run(model: str, max_results: int, score_threshold: float,\n        overlapping_factor: float, num_threads: int,\n        enable_edgetpu: bool) -> None:\n  \"\"\"Continuously run inference on audio data acquired from the device.\n\n  Args:\n    model: Name of the TFLite audio classification model.\n    max_results: Maximum number of classification results to display.\n    score_threshold: The score threshold of classification results.\n    overlapping_factor: Target overlapping between adjacent inferences.\n    num_threads: Number of CPU threads to run the model.\n    enable_edgetpu: Whether to run the model on EdgeTPU.\n  \"\"\"\n\n  if (overlapping_factor <= 0) or (overlapping_factor >= 1.0):\n    raise ValueError('Overlapping factor must be between 0 and 1.')\n\n  if (score_threshold < 0) or (score_threshold > 1.0):\n    raise ValueError('Score threshold must be between (inclusive) 0 and 1.')\n\n  # Initialize the audio classification model.\n  base_options = core.BaseOptions(\n      file_name=model, use_coral=enable_edgetpu, num_threads=num_threads)\n  classification_options = processor.ClassificationOptions(\n      max_results=max_results, score_threshold=score_threshold)\n  options = audio.AudioClassifierOptions(\n      base_options=base_options, classification_options=classification_options)\n  classifier = audio.AudioClassifier.create_from_options(options)\n\n  # Initialize the audio recorder and a tensor to store the audio input.\n  audio_record = classifier.create_audio_record()\n  tensor_audio = classifier.create_input_tensor_audio()\n\n  # We'll try to run inference every interval_between_inference seconds.\n  # This is usually half of the model's input length to create an overlapping\n  # between incoming audio segments to improve classification accuracy.\n  input_length_in_second = float(len(\n      tensor_audio.buffer)) / tensor_audio.format.sample_rate\n  interval_between_inference = input_length_in_second * (1 - overlapping_factor)\n  pause_time = interval_between_inference * 0.1\n  last_inference_time = time.time()\n\n  # Initialize a plotter instance to display the classification results.\n  plotter = Plotter()\n\n  # Start audio recording in the background.\n  audio_record.start_recording()\n\n  # Loop until the user close the classification results plot.\n  while True:\n    # Wait until at least interval_between_inference seconds has passed since\n    # the last inference.\n    now = time.time()\n    diff = now - last_inference_time\n    if diff < interval_between_inference:\n      time.sleep(pause_time)\n      continue\n    last_inference_time = now\n\n    # Load the input audio and run classify.\n    tensor_audio.load_from_audio_record(audio_record)\n    result = classifier.classify(tensor_audio)\n\n    # Plot the classification results.\n    plotter.plot(result)\n\n\ndef main():\n  parser = argparse.ArgumentParser(\n      formatter_class=argparse.ArgumentDefaultsHelpFormatter)\n  parser.add_argument(\n      '--model',\n      help='Name of the audio classification model.',\n      required=False,\n      default='yamnet.tflite')\n  parser.add_argument(\n      '--maxResults',\n      help='Maximum number of results to show.',\n      required=False,\n      default=5)\n  parser.add_argument(\n      '--overlappingFactor',\n      help='Target overlapping between adjacent inferences. Value must be in (0, 1)',\n      required=False,\n      default=0.5)\n  parser.add_argument(\n      '--scoreThreshold',\n      help='The score threshold of classification results.',\n      required=False,\n      default=0.0)\n  parser.add_argument(\n      '--numThreads',\n      help='Number of CPU threads to run the model.',\n      required=False,\n      default=4)\n  parser.add_argument(\n      '--enableEdgeTPU',\n      help='Whether to run the model on EdgeTPU.',\n      action='store_true',\n      required=False,\n      default=False)\n  args = parser.parse_args()\n\n  run(args.model, int(args.maxResults), float(args.scoreThreshold),\n      float(args.overlappingFactor), int(args.numThreads),\n      bool(args.enableEdgeTPU))\n\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "lite/examples/sound_classification/raspberry_pi/requirements.txt",
    "content": "argparse\nmatplotlib>=3.2.1\nsounddevice>=0.4.1\nscipy>=1.7.3 # Only required for test code.\ntflite-support>=0.4.0\n"
  },
  {
    "path": "lite/examples/sound_classification/raspberry_pi/setup.sh",
    "content": "#!/bin/bash\n\nif [ $# -eq 0 ]; then\n  DATA_DIR=\"./\"\nelse\n  DATA_DIR=\"$1\"\nfi\n\n# Install Python dependencies\npython3 -m pip install -r requirements.txt\n\n# Download TF Lite models with metadata.\nFILE=${DATA_DIR}/yamnet.tflite\nif [ ! -f \"$FILE\" ]; then\n  curl \\\n    -L 'https://tfhub.dev/google/lite-model/yamnet/classification/tflite/1?lite-format=tflite' \\\n    -o ${FILE}\nfi\n\nFILE=${DATA_DIR}/yamnet_edgetpu.tflite\nif [ ! -f \"$FILE\" ]; then\n  curl \\\n    -L 'https://tfhub.dev/google/coral-model/yamnet/classification/coral/1?coral-format=tflite' \\\n    -o ${FILE}\nfi\necho -e \"Downloaded files are in ${DATA_DIR}\""
  },
  {
    "path": "lite/examples/sound_classification/raspberry_pi/test_data/ground_truth.csv",
    "content": "label,score\nCat,0.73828125\nAnimal,0.66796875\n\"Domestic animals, pets\",0.66796875\nMeow,0.4140625\nCaterwaul,0.33203125\n"
  },
  {
    "path": "lite/examples/sound_classification/raspberry_pi/utils.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"A module with util functions.\"\"\"\nimport sys\n\nfrom matplotlib import rcParams\nimport matplotlib.pyplot as plt\n\nrcParams.update({\n    # Set the plot left margin so that the labels are visible.\n    'figure.subplot.left': 0.3,\n\n    # Hide the bottom toolbar.\n    'toolbar': 'None'\n})\n\n\nclass Plotter(object):\n  \"\"\"An util class to display the classification results.\"\"\"\n\n  _PAUSE_TIME = 0.05\n  \"\"\"Time for matplotlib to wait for UI event.\"\"\"\n\n  def __init__(self) -> None:\n    fig, self._axes = plt.subplots()\n    fig.canvas.manager.set_window_title('Audio classification')\n\n    # Stop the program when the ESC key is pressed.\n    def event_callback(event):\n      if event.key == 'escape':\n        sys.exit(0)\n\n    fig.canvas.mpl_connect('key_press_event', event_callback)\n\n    plt.show(block=False)\n\n  # TODO(khanhlvg): Add type hint for result once ClassificationResult added\n  # to tflite_support.task.processor module\n  def plot(self, result) -> None:\n    \"\"\"Plot the audio classification result.\n\n    Args:\n      result: Classification results returned by an audio classification\n        model.\n    \"\"\"\n    # Clear the axes\n    self._axes.cla()\n    self._axes.set_title('Press ESC to exit.')\n    self._axes.set_xlim((0, 1))\n\n    # Plot the results so that the most probable category comes at the top.\n    classification = result.classifications[0]\n    label_list = [category.class_name for category in classification.classes]\n    score_list = [category.score for category in classification.classes]\n    self._axes.barh(label_list[::-1], score_list[::-1])\n\n    # Wait for the UI event.\n    plt.pause(self._PAUSE_TIME)\n"
  },
  {
    "path": "lite/examples/speech_commands/README.md",
    "content": "# Speech Commands\n\n## Overview\n\n**Warning**: This is deprecated, and please refers to\n[Sound Classification](../sound_classification/README.md)\nsamples with latest technologies.\n\nThis directory contains end-to-end samples that performs recognition of speech\ncommands on mobile, highlighting the spoken word.\n\n* An [Android app](android/) that uses the TensorFlow Lite model to recognize\nspeech commands.\n* An [iOS app](ios/) that uses the TensorFlow Lite model to recognize\nspeech commands.\n* A [guide](ml/) to generate speech commands TFLite model.\n"
  },
  {
    "path": "lite/examples/speech_commands/ios/Podfile",
    "content": "# Uncomment the next line to define a global platform for your project\n platform :ios, '12.0'\n\ntarget 'SpeechCommands' do\n  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks\n  use_frameworks!\n  pod 'TensorFlowLiteSwift'\nend\n"
  },
  {
    "path": "lite/examples/speech_commands/ios/README.md",
    "content": "# TensorFlow Lite Speech Command Recognition iOS Example Application\n\n**iOS Versions Supported:** iOS 12.0 and above.\n**Xcode Version Required:** 10.0 and above\n\n## Overview\n\n**Warning**: This is deprecated, and please refers to\n[Sound Classification](../../sound_classification/ios/) ios\nsample with latest technologies.\n\nThis app recognizes the specified set of voice commands from the microphone on the device. When the user speaks, commands for which the model is trained are identified.\n\nThese instructions will walk you through building and running the demo on an iOS device.\n\nThe model files are downloaded via scripts in Xcode when you build and run. You don't need to do any steps to download TFLite models into the project explicitly.\n\n<!-- TODO(b/124116863): Add app screenshot. -->\n\n## Prerequisites\n\n* You must have Xcode installed\n\n* You must have a valid Apple Developer ID\n\n* The demo app requires the microphone and must be executed on a real iOS device. You can build it and run with the iPhone Simulator but the app raises a camera not found exception.\n\n* You don't need to build the entire TensorFlow library to run the demo, it uses CocoaPods to download the TensorFlow Lite library.\n\n* You'll also need the Xcode command-line tools:\n```xcode-select --install```\nIf this is a new install, you will need to run the Xcode application once to agree to the license before continuing.\n## Building the iOS Demo App\n\n1. Install CocoaPods if you don't have it.\n```sudo gem install cocoapods```\n\n2. Install the pod to generate the workspace file:\n```cd examples/speech_commands/ios/```\n```pod install```\nIf you have installed this pod before and that command doesn't work, try\n```pod update```\nAt the end of this step you should have a file called ```SpeechCommands.xcworkspace```\n\n3. Open **SpeechCommands.xcworkspace** in Xcode.\n\n4. Please change the bundle identifier to a unique identifier and select your development team in **'General->Signing'** before building the application if you are using an iOS device.\n\n5. Build and run the app in Xcode.\nYou'll have to grant permissions for the app to use the device's microphone. The commands spoken by the user will be identified using the microphone input!\n\n### Additional Note\n_Please do not delete the empty references_ to the .tflite and .txt files after you clone the repo and open the project. These references will be fixed as the model and label files are downloaded when the application is built and run for the first time. If you delete the references, you can still find that the .tflite and .txt files are downloaded to the Model folder when you build the application. You will have to add the references manually to run the application after deleting.\n\n## Model Used\n\nYou can find a detailed tutorial about training and running audio recognition model [here](https://www.tensorflow.org/tutorials/sequences/audio_recognition). The TFLite model can be downloaded [here](https://storage.googleapis.com/download.tensorflow.org/models/tflite/conv_actions_tflite.zip). The architecture of this model is based on the paper [Convolutional Neural Networks for Small-footprint Keyword Spotting](https://www.isca-speech.org/archive/interspeech_2015/papers/i15_1478.pdf).\n\nThe percentage displayed is average command recognition over a window duration (1000ms).\n\n## iOS App Details\n\nThe app is written entirely in Swift and uses the TensorFlow Lite\n[Swift library](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/swift)\nfor performing speech commands.\n\nNote: Objective-C developers should use the TensorFlow Lite\n[Objective-C library](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/objc).\n"
  },
  {
    "path": "lite/examples/speech_commands/ios/RunScripts/download_models.sh",
    "content": "#!/bin/bash\n# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\nset -ex\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nMODELS_URL=\"https://storage.googleapis.com/download.tensorflow.org/models/tflite/conv_actions_tflite.zip\"\nDOWNLOADS_DIR=$(mktemp -d)\n\ncd \"$SCRIPT_DIR\"\n\ndownload_and_extract() {\n  local usage=\"Usage: download_and_extract URL DIR\"\n  local url=\"${1:?${usage}}\"\n  local dir=\"${2:?${usage}}\"\n  echo \"downloading ${url}\" >&2\n  mkdir -p \"${dir}\"\n  tempdir=$(mktemp -d)\n  tempdir2=$(mktemp -d)\n\n  curl -L ${url} > ${tempdir}/zipped.zip\n  unzip ${tempdir}/zipped.zip -d ${tempdir2}\n\n  # If the zip file contains nested directories, extract the files from the\n  # inner directory.\n  if ls ${tempdir2}/*/* 1> /dev/null 2>&1; then\n    # unzip has no strip components, so unzip to a temp dir, and move the\n    # files we want from the tempdir to destination.\n    cp -R ${tempdir2}/*/* ${dir}/\n  else\n    cp -R ${tempdir2}/* ${dir}/\n  fi\n  rm -rf ${tempdir2} ${tempdir}\n}\n\nif [ -f ../SpeechCommands/Model/conv_actions_frozen.tflite ]\nthen\necho \"File exists. Exiting...\"\nexit 0\nfi\n\ndownload_and_extract \"${MODELS_URL}\" \"${DOWNLOADS_DIR}/models\"\n\nfile ${DOWNLOADS_DIR}/models\n\ncp ${DOWNLOADS_DIR}/models/* ../SpeechCommands/Model\n\n"
  },
  {
    "path": "lite/examples/speech_commands/ios/SpeechCommands/AppDelegate.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n  var window: UIWindow?\n\n  func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil\n  ) -> Bool {\n    return true\n  }\n}\n"
  },
  {
    "path": "lite/examples/speech_commands/ios/SpeechCommands/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\": [\n    {\n      \"size\": \"60x60\",\n      \"expected-size\": \"180\",\n      \"filename\": \"180.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"3x\"\n    },\n    {\n      \"size\": \"40x40\",\n      \"expected-size\": \"80\",\n      \"filename\": \"80.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"40x40\",\n      \"expected-size\": \"120\",\n      \"filename\": \"120.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"3x\"\n    },\n    {\n      \"size\": \"60x60\",\n      \"expected-size\": \"120\",\n      \"filename\": \"120.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"58\",\n      \"filename\": \"58.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"29\",\n      \"filename\": \"29.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"87\",\n      \"filename\": \"87.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"3x\"\n    },\n    {\n      \"size\": \"20x20\",\n      \"expected-size\": \"40\",\n      \"filename\": \"40.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"20x20\",\n      \"expected-size\": \"60\",\n      \"filename\": \"60.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"3x\"\n    },\n    {\n      \"size\": \"1024x1024\",\n      \"filename\": \"1024.png\",\n      \"expected-size\": \"1024\",\n      \"idiom\": \"ios-marketing\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"40x40\",\n      \"expected-size\": \"80\",\n      \"filename\": \"80.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"72x72\",\n      \"expected-size\": \"72\",\n      \"filename\": \"72.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"76x76\",\n      \"expected-size\": \"152\",\n      \"filename\": \"152.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"50x50\",\n      \"expected-size\": \"100\",\n      \"filename\": \"100.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"58\",\n      \"filename\": \"58.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"76x76\",\n      \"expected-size\": \"76\",\n      \"filename\": \"76.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"29\",\n      \"filename\": \"29.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"50x50\",\n      \"expected-size\": \"50\",\n      \"filename\": \"50.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"72x72\",\n      \"expected-size\": \"144\",\n      \"filename\": \"144.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"40x40\",\n      \"expected-size\": \"40\",\n      \"filename\": \"40.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"83.5x83.5\",\n      \"expected-size\": \"167\",\n      \"filename\": \"167.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"20x20\",\n      \"expected-size\": \"20\",\n      \"filename\": \"20.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"20x20\",\n      \"expected-size\": \"40\",\n      \"filename\": \"40.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    }\n  ]\n}\n"
  },
  {
    "path": "lite/examples/speech_commands/ios/SpeechCommands/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/speech_commands/ios/SpeechCommands/Assets.xcassets/base.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"base.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"base@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"base@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/speech_commands/ios/SpeechCommands/Assets.xcassets/border_color.colorset/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  },\n  \"colors\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"color\" : {\n        \"color-space\" : \"srgb\",\n        \"components\" : {\n          \"red\" : \"0xC7\",\n          \"alpha\" : \"1.000\",\n          \"blue\" : \"0xD8\",\n          \"green\" : \"0xD0\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "lite/examples/speech_commands/ios/SpeechCommands/Assets.xcassets/inner_shadow_color.colorset/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  },\n  \"colors\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"color\" : {\n        \"color-space\" : \"srgb\",\n        \"components\" : {\n          \"red\" : \"250\",\n          \"alpha\" : \"1.000\",\n          \"blue\" : \"0\",\n          \"green\" : \"141\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "lite/examples/speech_commands/ios/SpeechCommands/Assets.xcassets/tfl_logo.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"tfl_logo.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"tfl_logo@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"tfl_logo@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/speech_commands/ios/SpeechCommands/AudioInputManager/AudioInputManager.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\nimport AVFoundation\n\n\nprotocol AudioInputManagerDelegate {\n  func showCameraPermissionsDeniedAlert()\n  func didOutput(channelData: [Int16])\n}\n\nclass AudioInputManager: NSObject {\n\n  // MARK: Constants\n  let bufferSize: Int\n  private let sampleRate: Int\n\n  var delegate: AudioInputManagerDelegate?\n\n  // MARK: AVAudioEngine\n  private var audioEngine: AVAudioEngine = AVAudioEngine()\n\n  // MARK: Instance Variables\n  private let conversionQueue = DispatchQueue(label: \"conversionQueue\")\n\n  /**\n   The initializer initializes the AudioInputManager with the required sample rate for the audio\n   output.\n   */\n  init(sampleRate: Int) {\n    self.sampleRate = sampleRate\n\n    // We are setting the buffer size to two times the Sample rate\n    bufferSize = self.sampleRate * 2\n    super.init()\n  }\n\n  func checkPermissionsAndStartTappingMicrophone() {\n    switch AVAudioSession.sharedInstance().recordPermission {\n\n    case .granted:\n      startTappingMicrophone()\n    case .denied:\n      delegate?.showCameraPermissionsDeniedAlert()\n    case .undetermined:\n      requestPermissions()\n    }\n  }\n\n  func requestPermissions() {\n    AVAudioSession.sharedInstance().requestRecordPermission { (granted) in\n      if granted {\n        self.startTappingMicrophone()\n      }\n      else {\n        self.checkPermissionsAndStartTappingMicrophone()\n      }\n    }\n  }\n\n  /** This method starts tapping the microphone input and converts it into the format for which the model is trained and periodically returns it in the block\n   */\n  func startTappingMicrophone() {\n    let inputNode = audioEngine.inputNode\n    let inputFormat = inputNode.outputFormat(forBus: 0)\n    let recordingFormat = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: Double(sampleRate), channels: 1, interleaved: true)\n    guard let formatConverter =  AVAudioConverter(from:inputFormat, to: recordingFormat!) else {\n      return\n    }\n\n    // We install a tap on the audio engine and specifying the buffer size and the input format.\n    audioEngine.inputNode.installTap(onBus: 0, bufferSize: AVAudioFrameCount(bufferSize), format: inputFormat) { (buffer, time) in\n\n      self.conversionQueue.async {\n\n        // An AVAudioConverter is used to convert the microphone input to the format required for the model.(pcm 16)\n        let pcmBuffer = AVAudioPCMBuffer(pcmFormat: recordingFormat!, frameCapacity: AVAudioFrameCount(recordingFormat!.sampleRate * 2.0))\n        var error: NSError? = nil\n\n        let inputBlock: AVAudioConverterInputBlock = {inNumPackets, outStatus in\n          outStatus.pointee = AVAudioConverterInputStatus.haveData\n          return buffer\n        }\n\n        formatConverter.convert(to: pcmBuffer!, error: &error, withInputFrom: inputBlock)\n\n        if error != nil {\n          print(error!.localizedDescription)\n        }\n        else if let channelData = pcmBuffer!.int16ChannelData {\n\n          let channelDataValue = channelData.pointee\n          let channelDataValueArray = stride(from: 0,\n                                             to: Int(pcmBuffer!.frameLength),\n                                             by: buffer.stride).map{ channelDataValue[$0] }\n\n          // Converted pcm 16 values are delegated to the controller.\n          self.delegate?.didOutput(channelData: channelDataValueArray)\n         // completion(channelDataValueArray)\n        }\n\n      }\n    }\n\n    audioEngine.prepare()\n    do {\n      try audioEngine.start()\n    }\n    catch {\n      print(error.localizedDescription)\n    }\n  }\n\n}\n"
  },
  {
    "path": "lite/examples/speech_commands/ios/SpeechCommands/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"13122.16\" systemVersion=\"17A277\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"13104.12\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "lite/examples/speech_commands/ios/SpeechCommands/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"14313.3.2\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <device id=\"retina4_7\" orientation=\"portrait\">\n        <adaptation id=\"fullscreen\"/>\n    </device>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"14283.1\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"ViewController\" customModule=\"SpeechCommands\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <collectionView clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"scaleToFill\" dataMode=\"prototypes\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"yOA-Ss-Dfm\">\n                                <rect key=\"frame\" x=\"16\" y=\"156\" width=\"343\" height=\"333\"/>\n                                <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                <collectionViewFlowLayout key=\"collectionViewLayout\" minimumLineSpacing=\"15\" minimumInteritemSpacing=\"15\" id=\"9Q7-7C-Bpf\">\n                                    <size key=\"itemSize\" width=\"234\" height=\"89\"/>\n                                    <size key=\"headerReferenceSize\" width=\"0.0\" height=\"0.0\"/>\n                                    <size key=\"footerReferenceSize\" width=\"0.0\" height=\"0.0\"/>\n                                    <inset key=\"sectionInset\" minX=\"0.0\" minY=\"0.0\" maxX=\"0.0\" maxY=\"0.0\"/>\n                                </collectionViewFlowLayout>\n                                <cells>\n                                    <collectionViewCell opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" reuseIdentifier=\"WORD_CELL\" id=\"ub4-nx-ty8\" customClass=\"WordCell\" customModule=\"SpeechCommands\" customModuleProvider=\"target\">\n                                        <rect key=\"frame\" x=\"54.5\" y=\"0.0\" width=\"234\" height=\"89\"/>\n                                        <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMaxY=\"YES\"/>\n                                        <view key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" insetsLayoutMarginsFromSafeArea=\"NO\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"234\" height=\"89\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <imageView userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"XB5-XS-9Lz\">\n                                                    <rect key=\"frame\" x=\"1\" y=\"1\" width=\"232\" height=\"87\"/>\n                                                </imageView>\n                                                <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Label\" textAlignment=\"center\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Y46-18-DYi\">\n                                                    <rect key=\"frame\" x=\"18\" y=\"36\" width=\"198\" height=\"17\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                                    <color key=\"textColor\" red=\"0.48627450980392156\" green=\"0.53333333333333333\" blue=\"0.56470588235294117\" alpha=\"1\" colorSpace=\"calibratedRGB\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </view>\n                                        <constraints>\n                                            <constraint firstAttribute=\"trailing\" secondItem=\"XB5-XS-9Lz\" secondAttribute=\"trailing\" constant=\"1\" id=\"2bz-bq-yJM\"/>\n                                            <constraint firstAttribute=\"trailingMargin\" secondItem=\"Y46-18-DYi\" secondAttribute=\"trailing\" constant=\"10\" id=\"8qJ-jp-bnf\"/>\n                                            <constraint firstItem=\"Y46-18-DYi\" firstAttribute=\"leading\" secondItem=\"ub4-nx-ty8\" secondAttribute=\"leadingMargin\" constant=\"10\" id=\"OKL-O4-ew8\"/>\n                                            <constraint firstAttribute=\"bottom\" secondItem=\"XB5-XS-9Lz\" secondAttribute=\"bottom\" constant=\"1\" id=\"oKq-vz-9Xi\"/>\n                                            <constraint firstItem=\"XB5-XS-9Lz\" firstAttribute=\"top\" secondItem=\"ub4-nx-ty8\" secondAttribute=\"top\" constant=\"1\" id=\"vSQ-Z0-KIO\"/>\n                                            <constraint firstItem=\"XB5-XS-9Lz\" firstAttribute=\"leading\" secondItem=\"ub4-nx-ty8\" secondAttribute=\"leading\" constant=\"1\" id=\"wZP-on-CQI\"/>\n                                            <constraint firstItem=\"Y46-18-DYi\" firstAttribute=\"centerY\" secondItem=\"ub4-nx-ty8\" secondAttribute=\"centerY\" id=\"weU-Dd-xYQ\"/>\n                                        </constraints>\n                                        <connections>\n                                            <outlet property=\"backgroundImageView\" destination=\"XB5-XS-9Lz\" id=\"o57-Bi-J2G\"/>\n                                            <outlet property=\"nameLabel\" destination=\"Y46-18-DYi\" id=\"fO6-fw-mxG\"/>\n                                        </connections>\n                                    </collectionViewCell>\n                                </cells>\n                                <connections>\n                                    <outlet property=\"dataSource\" destination=\"BYZ-38-t0r\" id=\"1Vg-yG-8Om\"/>\n                                    <outlet property=\"delegate\" destination=\"BYZ-38-t0r\" id=\"iHs-Pk-Ufc\"/>\n                                </connections>\n                            </collectionView>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"eLZ-D1-WeJ\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"100\"/>\n                                <subviews>\n                                    <imageView userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" image=\"tfl_logo\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"H4t-9s-RXO\">\n                                        <rect key=\"frame\" x=\"16\" y=\"56\" width=\"160\" height=\"24\"/>\n                                        <constraints>\n                                            <constraint firstAttribute=\"width\" constant=\"160\" id=\"KKd-eh-Udf\"/>\n                                            <constraint firstAttribute=\"height\" constant=\"24\" id=\"L4r-gK-5aQ\"/>\n                                        </constraints>\n                                    </imageView>\n                                </subviews>\n                                <color key=\"backgroundColor\" red=\"0.27058823529411763\" green=\"0.35686274509803922\" blue=\"0.39215686274509803\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"bottom\" secondItem=\"H4t-9s-RXO\" secondAttribute=\"bottom\" constant=\"20\" id=\"Wao-lD-YWc\"/>\n                                    <constraint firstItem=\"H4t-9s-RXO\" firstAttribute=\"leading\" secondItem=\"eLZ-D1-WeJ\" secondAttribute=\"leading\" constant=\"16\" id=\"Xnb-QK-NcU\"/>\n                                    <constraint firstAttribute=\"height\" constant=\"100\" id=\"w2u-z8-9N2\"/>\n                                </constraints>\n                            </view>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"W8b-G1-atV\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"100\" width=\"375\" height=\"40\"/>\n                                <subviews>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Say one of the words below.\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"fQt-R3-ibX\">\n                                        <rect key=\"frame\" x=\"80\" y=\"10\" width=\"215.5\" height=\"20\"/>\n                                        <fontDescription key=\"fontDescription\" name=\"HelveticaNeue\" family=\"Helvetica Neue\" pointSize=\"17\"/>\n                                        <color key=\"textColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                </subviews>\n                                <color key=\"backgroundColor\" red=\"0.0\" green=\"0.0\" blue=\"0.0\" alpha=\"0.50341497319999995\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                <constraints>\n                                    <constraint firstItem=\"fQt-R3-ibX\" firstAttribute=\"centerX\" secondItem=\"W8b-G1-atV\" secondAttribute=\"centerX\" id=\"7zw-N8-dQA\"/>\n                                    <constraint firstItem=\"fQt-R3-ibX\" firstAttribute=\"centerY\" secondItem=\"W8b-G1-atV\" secondAttribute=\"centerY\" id=\"izu-dt-eRg\"/>\n                                    <constraint firstAttribute=\"height\" constant=\"40\" id=\"on6-ni-wBw\"/>\n                                </constraints>\n                            </view>\n                            <containerView opaque=\"NO\" contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"eJi-X5-uvC\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"505\" width=\"375\" height=\"162\"/>\n                                <color key=\"backgroundColor\" red=\"0.95686274509803915\" green=\"0.95686274509803915\" blue=\"0.95686274509803915\" alpha=\"1\" colorSpace=\"calibratedRGB\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"height\" constant=\"162\" id=\"tqx-J9-cXP\"/>\n                                </constraints>\n                                <connections>\n                                    <segue destination=\"c7R-fs-Dsr\" kind=\"embed\" identifier=\"EMBED\" id=\"9Up-eB-Wud\"/>\n                                </connections>\n                            </containerView>\n                        </subviews>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <constraints>\n                            <constraint firstItem=\"6Tk-OE-BBY\" firstAttribute=\"trailing\" secondItem=\"yOA-Ss-Dfm\" secondAttribute=\"trailing\" constant=\"16\" id=\"5LB-K7-JYs\"/>\n                            <constraint firstItem=\"6Tk-OE-BBY\" firstAttribute=\"trailing\" secondItem=\"eJi-X5-uvC\" secondAttribute=\"trailing\" id=\"AOa-i3-3iU\"/>\n                            <constraint firstItem=\"eJi-X5-uvC\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" id=\"GYf-fy-xuw\"/>\n                            <constraint firstItem=\"yOA-Ss-Dfm\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" constant=\"16\" id=\"HlR-iL-1zk\"/>\n                            <constraint firstItem=\"eJi-X5-uvC\" firstAttribute=\"bottom\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"bottom\" id=\"Jf4-lS-uhO\"/>\n                            <constraint firstItem=\"eLZ-D1-WeJ\" firstAttribute=\"top\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"top\" id=\"LJJ-Jk-Qsp\"/>\n                            <constraint firstItem=\"yOA-Ss-Dfm\" firstAttribute=\"top\" secondItem=\"W8b-G1-atV\" secondAttribute=\"bottom\" constant=\"16\" id=\"Rxf-eK-7xn\"/>\n                            <constraint firstItem=\"W8b-G1-atV\" firstAttribute=\"trailing\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"trailing\" id=\"Vw5-di-Zqv\"/>\n                            <constraint firstItem=\"eJi-X5-uvC\" firstAttribute=\"top\" secondItem=\"yOA-Ss-Dfm\" secondAttribute=\"bottom\" constant=\"16\" id=\"gIr-Sr-YJv\"/>\n                            <constraint firstItem=\"W8b-G1-atV\" firstAttribute=\"top\" secondItem=\"eLZ-D1-WeJ\" secondAttribute=\"bottom\" id=\"hCw-xG-6tO\"/>\n                            <constraint firstItem=\"eLZ-D1-WeJ\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" id=\"jaw-IC-RRS\"/>\n                            <constraint firstItem=\"W8b-G1-atV\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" id=\"pLa-ba-BDp\"/>\n                            <constraint firstItem=\"eLZ-D1-WeJ\" firstAttribute=\"trailing\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"trailing\" id=\"vQS-Vs-F77\"/>\n                        </constraints>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                    <nil key=\"simulatedTopBarMetrics\"/>\n                    <connections>\n                        <outlet property=\"collectionView\" destination=\"yOA-Ss-Dfm\" id=\"GeY-YM-OmO\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"312.80000000000001\" y=\"-668.81559220389806\"/>\n        </scene>\n        <!--Inference View Controller-->\n        <scene sceneID=\"NaL-en-2UU\">\n            <objects>\n                <viewController id=\"c7R-fs-Dsr\" customClass=\"InferenceViewController\" customModule=\"ObjectDetection\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"boG-L9-pNa\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"162\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <tableView clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" alwaysBounceVertical=\"YES\" scrollEnabled=\"NO\" dataMode=\"prototypes\" style=\"plain\" separatorStyle=\"none\" allowsSelection=\"NO\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"28\" sectionFooterHeight=\"28\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"EOX-yY-QxH\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"16\" width=\"375\" height=\"93\"/>\n                                <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                <prototypes>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" reuseIdentifier=\"INFO_CELL\" rowHeight=\"161\" id=\"jPU-sG-357\" customClass=\"InfoCell\" customModule=\"ObjectDetection\" customModuleProvider=\"target\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"28\" width=\"375\" height=\"161\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"jPU-sG-357\" id=\"cgS-vf-fJQ\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"161\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Label\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" numberOfLines=\"0\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"00V-hU-BAh\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"5\" width=\"36\" height=\"17\"/>\n                                                    <constraints>\n                                                        <constraint firstAttribute=\"height\" constant=\"17\" id=\"YU0-oJ-bc3\"/>\n                                                    </constraints>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" weight=\"medium\" pointSize=\"14\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                                <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Label\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"PQ5-EM-zCC\">\n                                                    <rect key=\"frame\" x=\"323\" y=\"5\" width=\"36\" height=\"17\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" weight=\"medium\" pointSize=\"14\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                                <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"2DP-PX-0En\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"42\" width=\"343\" height=\"1\"/>\n                                                    <color key=\"backgroundColor\" red=\"0.45882352939999999\" green=\"0.45882352939999999\" blue=\"0.45882352939999999\" alpha=\"0.19554016490000001\" colorSpace=\"custom\" customColorSpace=\"calibratedRGB\"/>\n                                                    <constraints>\n                                                        <constraint firstAttribute=\"height\" constant=\"1\" id=\"Hrr-kz-Emx\"/>\n                                                    </constraints>\n                                                </view>\n                                            </subviews>\n                                            <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                            <constraints>\n                                                <constraint firstItem=\"PQ5-EM-zCC\" firstAttribute=\"centerY\" secondItem=\"00V-hU-BAh\" secondAttribute=\"centerY\" id=\"05s-j1-737\"/>\n                                                <constraint firstItem=\"2DP-PX-0En\" firstAttribute=\"top\" secondItem=\"00V-hU-BAh\" secondAttribute=\"bottom\" constant=\"20\" id=\"OUx-KG-oPm\"/>\n                                                <constraint firstAttribute=\"trailing\" secondItem=\"2DP-PX-0En\" secondAttribute=\"trailing\" constant=\"16\" id=\"RSy-R8-t63\"/>\n                                                <constraint firstItem=\"00V-hU-BAh\" firstAttribute=\"top\" secondItem=\"cgS-vf-fJQ\" secondAttribute=\"top\" constant=\"5\" id=\"fFm-xX-OVQ\"/>\n                                                <constraint firstAttribute=\"trailing\" secondItem=\"PQ5-EM-zCC\" secondAttribute=\"trailing\" constant=\"16\" id=\"hzI-uE-HWD\"/>\n                                                <constraint firstItem=\"00V-hU-BAh\" firstAttribute=\"leading\" secondItem=\"cgS-vf-fJQ\" secondAttribute=\"leading\" constant=\"16\" id=\"os9-wX-CxM\"/>\n                                                <constraint firstItem=\"2DP-PX-0En\" firstAttribute=\"leading\" secondItem=\"cgS-vf-fJQ\" secondAttribute=\"leading\" constant=\"16\" id=\"vzX-cO-Riw\"/>\n                                            </constraints>\n                                        </tableViewCellContentView>\n                                        <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                        <connections>\n                                            <outlet property=\"fieldNameLabel\" destination=\"00V-hU-BAh\" id=\"7bW-Iy-64u\"/>\n                                            <outlet property=\"infoLabel\" destination=\"PQ5-EM-zCC\" id=\"jxW-bS-z50\"/>\n                                        </connections>\n                                    </tableViewCell>\n                                </prototypes>\n                                <connections>\n                                    <outlet property=\"dataSource\" destination=\"c7R-fs-Dsr\" id=\"HEo-Gr-Iw1\"/>\n                                    <outlet property=\"delegate\" destination=\"c7R-fs-Dsr\" id=\"BYD-K0-zdm\"/>\n                                </connections>\n                            </tableView>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"tWg-LV-XaE\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"109\" width=\"375\" height=\"53\"/>\n                                <subviews>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Threads\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"pEN-dk-TWG\">\n                                        <rect key=\"frame\" x=\"16\" y=\"10\" width=\"53\" height=\"17\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"1\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" numberOfLines=\"0\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"7JK-pj-e7C\">\n                                        <rect key=\"frame\" x=\"250.5\" y=\"10\" width=\"6.5\" height=\"17\"/>\n                                        <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                        <nil key=\"textColor\"/>\n                                        <nil key=\"highlightedColor\"/>\n                                    </label>\n                                    <stepper opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" maximumValue=\"100\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"YWy-5o-JJE\">\n                                        <rect key=\"frame\" x=\"265\" y=\"4\" width=\"94\" height=\"29\"/>\n                                        <color key=\"tintColor\" red=\"0.45882352939999999\" green=\"0.45882352939999999\" blue=\"0.45882352939999999\" alpha=\"1\" colorSpace=\"calibratedRGB\"/>\n                                        <connections>\n                                            <action selector=\"onClickThreadStepper:\" destination=\"c7R-fs-Dsr\" eventType=\"valueChanged\" id=\"A9F-3M-XkH\"/>\n                                        </connections>\n                                    </stepper>\n                                </subviews>\n                                <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"height\" constant=\"53\" id=\"5YT-Iq-5tx\"/>\n                                    <constraint firstItem=\"YWy-5o-JJE\" firstAttribute=\"leading\" secondItem=\"7JK-pj-e7C\" secondAttribute=\"trailing\" constant=\"8\" id=\"Abe-UR-CZw\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"YWy-5o-JJE\" secondAttribute=\"trailing\" constant=\"16\" id=\"DZK-Qg-gdw\"/>\n                                    <constraint firstItem=\"7JK-pj-e7C\" firstAttribute=\"centerY\" secondItem=\"pEN-dk-TWG\" secondAttribute=\"centerY\" id=\"KlZ-I0-Ngx\"/>\n                                    <constraint firstItem=\"pEN-dk-TWG\" firstAttribute=\"top\" secondItem=\"tWg-LV-XaE\" secondAttribute=\"top\" constant=\"10\" id=\"nAt-ph-uNe\"/>\n                                    <constraint firstItem=\"pEN-dk-TWG\" firstAttribute=\"leading\" secondItem=\"tWg-LV-XaE\" secondAttribute=\"leading\" constant=\"16\" id=\"u3S-eU-ONB\"/>\n                                    <constraint firstItem=\"YWy-5o-JJE\" firstAttribute=\"centerY\" secondItem=\"pEN-dk-TWG\" secondAttribute=\"centerY\" id=\"ype-NS-XeY\"/>\n                                </constraints>\n                            </view>\n                        </subviews>\n                        <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                        <constraints>\n                            <constraint firstItem=\"tWg-LV-XaE\" firstAttribute=\"leading\" secondItem=\"QBu-VQ-shf\" secondAttribute=\"leading\" id=\"35b-js-0jS\"/>\n                            <constraint firstItem=\"EOX-yY-QxH\" firstAttribute=\"trailing\" secondItem=\"QBu-VQ-shf\" secondAttribute=\"trailing\" id=\"EYk-nd-ovC\"/>\n                            <constraint firstItem=\"tWg-LV-XaE\" firstAttribute=\"top\" secondItem=\"EOX-yY-QxH\" secondAttribute=\"bottom\" id=\"c3N-3X-7gQ\"/>\n                            <constraint firstItem=\"EOX-yY-QxH\" firstAttribute=\"leading\" secondItem=\"QBu-VQ-shf\" secondAttribute=\"leading\" id=\"daZ-hr-3Xw\"/>\n                            <constraint firstItem=\"tWg-LV-XaE\" firstAttribute=\"bottom\" secondItem=\"QBu-VQ-shf\" secondAttribute=\"bottom\" id=\"hkx-Dg-zK4\"/>\n                            <constraint firstItem=\"EOX-yY-QxH\" firstAttribute=\"top\" secondItem=\"QBu-VQ-shf\" secondAttribute=\"top\" constant=\"16\" id=\"ll6-cp-dpD\"/>\n                            <constraint firstItem=\"tWg-LV-XaE\" firstAttribute=\"trailing\" secondItem=\"QBu-VQ-shf\" secondAttribute=\"trailing\" id=\"qbw-Lz-yFC\"/>\n                        </constraints>\n                        <viewLayoutGuide key=\"safeArea\" id=\"QBu-VQ-shf\"/>\n                    </view>\n                    <connections>\n                        <outlet property=\"stepperValueLabel\" destination=\"7JK-pj-e7C\" id=\"rgz-kF-ffP\"/>\n                        <outlet property=\"tableView\" destination=\"EOX-yY-QxH\" id=\"0QD-ko-vxa\"/>\n                        <outlet property=\"threadStepper\" destination=\"YWy-5o-JJE\" id=\"M9i-It-BYi\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"vKe-OZ-1Ip\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"1273\" y=\"-371\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"tfl_logo\" width=\"160\" height=\"24\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "lite/examples/speech_commands/ios/SpeechCommands/Cells/InfoCell.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\nclass InfoCell: UITableViewCell {\n  @IBOutlet weak var fieldNameLabel: UILabel!\n  @IBOutlet weak var infoLabel: UILabel!\n}\n"
  },
  {
    "path": "lite/examples/speech_commands/ios/SpeechCommands/Cells/WordCell.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\nclass WordCell: UICollectionViewCell {\n  @IBOutlet weak var nameLabel: UILabel!\n  @IBOutlet weak var backgroundImageView: UIImageView!\n\n  private let cornerRadius: CGFloat = 8.0\n  var borderColor: UIColor = UIColor.clear\n\n  override func draw(_ rect: CGRect) {\n    borderColor.setStroke()\n\n    let path = UIBezierPath(roundedRect: self.bounds.insetBy(dx: 1.0, dy: 1.0), cornerRadius: cornerRadius)\n    path.stroke()\n  }\n}\n"
  },
  {
    "path": "lite/examples/speech_commands/ios/SpeechCommands/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>TFL Speech</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>NSMicrophoneUsageDescription</key>\n\t<string>This app will use the microphone to get audio input inorder to detect the commands spoken by user.</string>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UIRequiresFullScreen</key>\n\t<true/>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UIUserInterfaceStyle</key>\n\t<string>Light</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "lite/examples/speech_commands/ios/SpeechCommands/Model/.gitignore",
    "content": "*.txt\n*.tflite"
  },
  {
    "path": "lite/examples/speech_commands/ios/SpeechCommands/ModelDataHandler/ModelDataHandler.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport TensorFlowLite\nimport UIKit\n\n/// A result from invoking the `Interpreter`.\nstruct Result {\n  let recognizedCommand: RecognizedCommand?\n  let inferenceTime: Double\n}\n\n/// Information about a model file or labels file.\ntypealias FileInfo = (name: String, extension: String)\n\n/// Information about the ConvActions model.\nenum ConvActions {\n  static let modelInfo: FileInfo = (name: \"conv_actions_frozen\", extension: \"tflite\")\n  static let labelsInfo: FileInfo = (name: \"conv_actions_labels\", extension: \"txt\")\n}\n\n/// This class handles all data preprocessing and makes calls to run inference on a given audio\n/// buffer by invoking the TensorFlow Lite `Interpreter`. It then formats the inferences obtained\n/// and averages the recognized commands by running them through RecognizeCommands.\nclass ModelDataHandler {\n\n  // MARK: - Internal Properties\n\n  /// The current thread count used by the TensorFlow Lite Interpreter.\n  let threadCount: Int\n\n  let threadCountLimit = 10\n  let sampleRate = 16000\n\n  // MARK: - Private Properties\n\n  private var buffer:[Int] = []\n  private var recognizeCommands: RecognizeCommands?\n  private let audioBufferInputTensorIndex = 0\n  private let sampleRateInputTensorIndex = 1\n  private let labelOffset = 2\n  private let sampleDuration = 1000\n  private let minimumCount = 3\n  private let averageWindowDuration = 1000.0\n  private let suppressionMs = 1500.0\n  private let threshold = 0.5\n  private let minTimeBetweenSamples = 30.0\n  private let maxInt16AsFloat32: Float32 = 32767.0\n\n  /// List of labels from the given labels file.\n  private var labels: [String] = []\n\n  /// TensorFlow Lite `Interpreter` object for performing inference on a given model.\n  private var interpreter: Interpreter\n\n  private var recordingLength: Int {\n    return (sampleRate * sampleDuration) / 1000\n  }\n\n  // MARK: - Initialization\n\n  /// A failable initializer for `ModelDataHandler`. A new instance is created if the model and\n  /// labels files are successfully loaded from the app's main bundle. Default `threadCount` is 1.\n  init?(modelFileInfo: FileInfo, labelsFileInfo: FileInfo, threadCount: Int = 1) {\n    let modelFilename = modelFileInfo.name\n\n    // Construct the path to the model file.\n    guard let modelPath = Bundle.main.path(\n      forResource: modelFilename,\n      ofType: modelFileInfo.extension\n    ) else {\n      print(\"Failed to load the model file with name: \\(modelFilename).\")\n      return nil\n    }\n\n    // Specify the options for the `Interpreter`.\n    self.threadCount = threadCount\n    var options = Interpreter.Options()\n    options.threadCount = threadCount\n    do {\n      // Create the `Interpreter`.\n      interpreter = try Interpreter(modelPath: modelPath, options: options)\n      // Allocate memory for the model's input `Tensor`s.\n      try interpreter.allocateTensors()\n    } catch let error {\n      print(\"Failed to create the interpreter with error: \\(error.localizedDescription)\")\n      return nil\n    }\n    loadLabels(fileInfo: labelsFileInfo)\n    recognizeCommands = RecognizeCommands(\n      averageWindowDuration: averageWindowDuration,\n      detectionThreshold: 0.3,\n      minimumTimeBetweenSamples: minTimeBetweenSamples,\n      suppressionTime: suppressionMs,\n      minimumCount: minimumCount,\n      classLabels: labels\n    )\n  }\n\n  // MARK: - Internal Methods\n\n  /// Invokes the `Interpreter` and processes and returns the inference results.\n  func runModel(onBuffer buffer: [Int16]) -> Result? {\n    let interval: TimeInterval\n    let outputTensor: Tensor\n    do {\n      // Copy the `[Int16]` buffer data as an array of `Float`s to the audio buffer input `Tensor`'s.\n      let audioBufferData = Data(copyingBufferOf: buffer.map { Float($0) / maxInt16AsFloat32 })\n      try interpreter.copy(audioBufferData, toInputAt: audioBufferInputTensorIndex)\n\n      // Copy the sample rate data to the sample rate input `Tensor`.\n      var rate = Int32(sampleRate)\n      let sampleRateData = Data(bytes: &rate, count: MemoryLayout.size(ofValue: rate))\n      try interpreter.copy(sampleRateData, toInputAt: sampleRateInputTensorIndex)\n\n      // Run inference by invoking the `Interpreter`.\n      let startDate = Date()\n      try interpreter.invoke()\n      interval = Date().timeIntervalSince(startDate) * 1000\n\n      // Get the output `Tensor` to process the inference results.\n      outputTensor = try interpreter.output(at: 0)\n    } catch let error {\n      print(\"Failed to invoke the interpreter with error: \\(error.localizedDescription)\")\n      return nil\n    }\n\n    // Gets the formatted and averaged results.\n    let scores = [Float32](unsafeData: outputTensor.data) ?? []\n    let command =  getResults(withScores: scores)\n\n    // Returns result.\n    let result = Result(recognizedCommand: command, inferenceTime: interval)\n\n    return result\n  }\n\n  /// Returns the labels other than silence and unknown for display.\n  func offsetLabelsForDisplay() -> [String] {\n    return Array(labels[labelOffset..<labels.count])\n  }\n\n  // MARK: - Private Methods\n\n  /// Formats the results and runs them through Recognize Commands to average the results over a\n  /// window duration.\n  private func getResults(withScores scores: [Float]) -> RecognizedCommand? {\n\n    var results: [Float] = []\n    for i in 0..<labels.count {\n      results.append(scores[i])\n    }\n\n    // Runs results through recognize commands.\n    let command = recognizeCommands?.process(\n      latestResults: results,\n      currentTime: Date().timeIntervalSince1970 * 1000\n    )\n\n    // Check if command is new and the identified result is not silence or unknown.\n    guard let newCommand = command,\n      let index = labels.index(of: newCommand.name),\n      newCommand.isNew,\n      index >= labelOffset\n    else {\n        return nil\n    }\n    return newCommand\n  }\n\n  /// Loads the labels from the labels file and stores them in the `labels` property.\n  private func loadLabels(fileInfo: FileInfo) {\n    let filename = fileInfo.name\n    let fileExtension = fileInfo.extension\n    guard let fileURL = Bundle.main.url(forResource: filename, withExtension: fileExtension) else {\n      fatalError(\"Labels file not found in bundle. Please add a labels file with name \" +\n                   \"\\(filename).\\(fileExtension) and try again.\")\n    }\n    do {\n      let contents = try String(contentsOf: fileURL, encoding: .utf8)\n      labels = contents.components(separatedBy: .newlines)\n    } catch {\n      fatalError(\"Labels file named \\(filename).\\(fileExtension) cannot be read. Please add a \" +\n                   \"valid labels file and try again.\")\n    }\n  }\n}\n\n// MARK: - Extensions\n\nextension Data {\n  /// Creates a new buffer by copying the buffer pointer of the given array.\n  ///\n  /// - Warning: The given array's element type `T` must be trivial in that it can be copied bit\n  ///     for bit with no indirection or reference-counting operations; otherwise, reinterpreting\n  ///     data from the resulting buffer has undefined behavior.\n  /// - Parameter array: An array with elements of type `T`.\n  init<T>(copyingBufferOf array: [T]) {\n    self = array.withUnsafeBufferPointer(Data.init)\n  }\n}\n\nextension Array {\n  /// Creates a new array from the bytes of the given unsafe data.\n  ///\n  /// - Warning: The array's `Element` type must be trivial in that it can be copied bit for bit\n  ///     with no indirection or reference-counting operations; otherwise, copying the raw bytes in\n  ///     the `unsafeData`'s buffer to a new array returns an unsafe copy.\n  /// - Note: Returns `nil` if `unsafeData.count` is not a multiple of\n  ///     `MemoryLayout<Element>.stride`.\n  /// - Parameter unsafeData: The data containing the bytes to turn into an array.\n  init?(unsafeData: Data) {\n    guard unsafeData.count % MemoryLayout<Element>.stride == 0 else { return nil }\n    #if swift(>=5.0)\n    self = unsafeData.withUnsafeBytes { .init($0.bindMemory(to: Element.self)) }\n    #else\n    self = unsafeData.withUnsafeBytes {\n      .init(UnsafeBufferPointer<Element>(\n        start: $0,\n        count: unsafeData.count / MemoryLayout<Element>.stride\n      ))\n    }\n    #endif  // swift(>=5.0)\n  }\n}\n"
  },
  {
    "path": "lite/examples/speech_commands/ios/SpeechCommands/ModelDataHandler/RecognizeCommands.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport Foundation\n\nstruct RecognizedCommand {\n  var score: Float\n  var name: String\n  var isNew: Bool\n}\n\n/**\n This class smoothes out the results by averaging them over a window duration and making sure the\n commands are not duplicated for display.\n */\nclass RecognizeCommands {\n  // MARK: Structures that handles results.\n  private struct Command {\n    var score: Float\n    let name: String\n  }\n\n  private struct ResultsAtTime {\n    let time: TimeInterval\n    let scores: [Float]\n  }\n\n  // MARK: Constants\n  private let averageWindowDuration: Double\n  private let suppressionTime: Double\n  private let minimumCount: Int\n  private let minimumTimeBetweenSamples: Double\n  private let detectionThreshold: Float\n  private let classLabels: [String]\n  private let silenceLabel = \"_silence_\"\n  private var previousTopLabel = \"_silence_\"\n\n\n  private var previousTopScore: Float = 0.0\n  private var previousTopLabelTime: TimeInterval = Date.distantPast.timeIntervalSince1970 * 1000\n  private var previousResults: [ResultsAtTime] = []\n\n  /**\n   Initializes RecognizeCommands with specified parameters.\n   */\n  init(averageWindowDuration: Double, detectionThreshold: Float, minimumTimeBetweenSamples: Double, suppressionTime: Double, minimumCount: Int, classLabels: [String]) {\n    self.averageWindowDuration = averageWindowDuration\n    self.detectionThreshold = detectionThreshold\n    self.minimumTimeBetweenSamples = minimumTimeBetweenSamples\n    self.suppressionTime = suppressionTime\n    self.minimumCount = minimumCount\n    self.classLabels = classLabels\n  }\n\n  /**\n   This function averages the results obtained over an average window duration and prunes out any\n   old results.\n   */\n  func process(latestResults: [Float], currentTime: TimeInterval) -> RecognizedCommand? {\n\n    guard latestResults.count == classLabels.count else {\n      fatalError(\"There should be \\(classLabels.count) in results. But there are \\(latestResults.count) results\")\n    }\n\n    // Checks if the new results were identified at a later time than the currently identified\n    // results.\n    if let first = previousResults.first, first.time > currentTime {\n      fatalError(\"Results should be provided in increasing time order\")\n    }\n\n    if let lastResult = previousResults.last {\n\n      let timeSinceMostRecent = currentTime - previousResults[previousResults.count - 1].time\n\n      // If not enough time has passed after the last inference, we return the previously identified\n      // result as legitimate one.\n      if timeSinceMostRecent < minimumTimeBetweenSamples {\n        return RecognizedCommand(score: previousTopScore, name: previousTopLabel, isNew: false)\n      }\n    }\n\n    // Appends the new results to the identified results\n    let results: ResultsAtTime = ResultsAtTime(time: currentTime, scores: latestResults)\n\n    previousResults.append(results)\n\n    let timeLimit = currentTime - averageWindowDuration\n\n    // Flushes out all the results currently held that less than the average window duration since\n    // they are considered too old for averaging.\n    while previousResults[0].time < timeLimit {\n      previousResults.removeFirst()\n\n      guard previousResults.count > 0 else {\n        break\n      }\n    }\n\n    // If number of results currently held to average is less than a minimum count, return the score\n    // as zero so that no command is identified.\n    if previousResults.count < minimumCount {\n      return RecognizedCommand(score: 0.0, name: previousTopLabel, isNew: false)\n    }\n\n    // Creates an average of the scores of each classes currently held by this class.\n    var averageScores:[Command] = []\n    for i in 0...classLabels.count - 1 {\n\n      let command = Command(score: 0.0, name: classLabels[i])\n      averageScores.append(command)\n\n    }\n\n    for result in previousResults {\n\n      let scores = result.scores\n      for i in 0...scores.count - 1 {\n        averageScores[i].score = averageScores[i].score + scores[i] / Float(previousResults.count)\n\n      }\n    }\n\n    // Sorts scores in descending order of confidence.\n    averageScores.sort { (first, second) -> Bool in\n      return first.score > second.score\n    }\n\n    var timeSinceLastTop: Double = 0.0\n\n    // If silence was detected previously, consider the current result with the best average as a\n    // new command to be displayed.\n    if (previousTopLabel == silenceLabel ||\n        previousTopLabelTime == (Date.distantPast.timeIntervalSince1970 * 1000)) {\n\n      timeSinceLastTop = Date.distantFuture.timeIntervalSince1970 * 1000\n    }\n    else {\n      timeSinceLastTop = currentTime - previousTopLabelTime\n    }\n\n    // Return the results\n    var isNew = false\n    if (averageScores[0].score > detectionThreshold && timeSinceLastTop > suppressionTime) {\n\n      previousTopScore = averageScores[0].score\n      previousTopLabel = averageScores[0].name\n      previousTopLabelTime = currentTime\n      isNew = true\n    }\n    else {\n      isNew = false\n    }\n\n    return RecognizedCommand(\n        score: previousTopScore, name: previousTopLabel, isNew: isNew)\n  }\n}\n"
  },
  {
    "path": "lite/examples/speech_commands/ios/SpeechCommands/ViewControllers/InferenceViewController.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n// MARK: InferenceViewControllerDelegate Method Declarations\nprotocol InferenceViewControllerDelegate {\n  /**\n   This method is called when the user changes the stepper value to update number of threads used\n   for inference.\n   */\n  func didChangeThreadCount(to count: Int)\n}\n\nclass InferenceViewController: UIViewController {\n\n  // MARK: Sections and Information to display\n  private enum InferenceSections: Int, CaseIterable {\n    case InferenceInfo\n  }\n\n  private enum InferenceInfo: Int, CaseIterable {\n    case SampleRate\n    case InferenceTime\n\n    func displayString() -> String {\n\n      var toReturn = \"\"\n\n      switch self {\n      case .SampleRate:\n        toReturn = \"Sample Rate\"\n\n      case .InferenceTime:\n        toReturn = \"Inference Time\"\n\n      }\n      return toReturn\n    }\n  }\n\n  // MARK: Storyboard Outlets\n  @IBOutlet weak var tableView: UITableView!\n  @IBOutlet weak var threadStepper: UIStepper!\n  @IBOutlet weak var stepperValueLabel: UILabel!\n\n  // MARK: Constants\n  private let normalCellHeight: CGFloat = 27.0\n  private let separatorCellHeight: CGFloat = 42.0\n  private let bottomSpacing: CGFloat = 21.0\n  private let minThreadCount = 1\n  private let bottomSheetButtonDisplayHeight: CGFloat = 44.0\n  private let infoTextColor = UIColor.black\n  private let lightTextInfoColor = UIColor(displayP3Red: 117.0/255.0, green: 117.0/255.0, blue: 117.0/255.0, alpha: 1.0)\n  private let infoFont = UIFont.systemFont(ofSize: 14.0, weight: .regular)\n  private let highlightedFont = UIFont.systemFont(ofSize: 14.0, weight: .medium)\n\n  // MARK: Instance Variables\n  var inferenceTime: Double = 0\n  var sampleRate: Int = 0\n  var threadCountLimit: Int = 0\n  var currentThreadCount: Int = 0\n\n  // MARK: Delegate\n  var delegate: InferenceViewControllerDelegate?\n\n  // MARK: Computed properties\n  var collapsedHeight: CGFloat {\n    return bottomSheetButtonDisplayHeight\n  }\n\n  override func viewDidLoad() {\n    super.viewDidLoad()\n\n    // Set up stepper\n    threadStepper.isUserInteractionEnabled = true\n    threadStepper.maximumValue = Double(threadCountLimit)\n    threadStepper.minimumValue = Double(minThreadCount)\n    threadStepper.value = Double(currentThreadCount)\n  }\n\n  // MARK: Buttion Actions\n  /**\n   Delegate the change of number of threads to View Controller and change the stepper display.\n   */\n  @IBAction func onClickThreadStepper(_ sender: Any) {\n    delegate?.didChangeThreadCount(to: Int(threadStepper.value))\n    currentThreadCount = Int(threadStepper.value)\n    stepperValueLabel.text = \"\\(currentThreadCount)\"\n  }\n\n  func refreshResults() {\n    self.tableView.reloadData()\n  }\n}\n\n// MARK: UITableView Data Source\nextension InferenceViewController: UITableViewDelegate, UITableViewDataSource {\n\n  func numberOfSections(in tableView: UITableView) -> Int {\n\n    return InferenceSections.allCases.count\n  }\n\n  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {\n\n    guard let inferenceSection = InferenceSections(rawValue: section) else {\n      return 0\n    }\n\n    var rowCount = 0\n    switch inferenceSection {\n    case .InferenceInfo:\n      rowCount = InferenceInfo.allCases.count\n    }\n    return rowCount\n  }\n\n  func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {\n\n    var height: CGFloat = 0.0\n\n    guard let inferenceSection = InferenceSections(rawValue: indexPath.section) else {\n      return height\n    }\n\n    switch inferenceSection {\n    case .InferenceInfo:\n      if indexPath.row == InferenceInfo.allCases.count - 1 {\n        height = separatorCellHeight + bottomSpacing\n      }\n      else {\n        height = normalCellHeight\n      }\n    }\n    return height\n  }\n\n  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {\n\n    let cell = tableView.dequeueReusableCell(withIdentifier: \"INFO_CELL\") as! InfoCell\n\n    guard let inferenceSection = InferenceSections(rawValue: indexPath.section) else {\n      return cell\n    }\n\n    var fieldName = \"\"\n    var info = \"\"\n\n    switch inferenceSection {\n    case .InferenceInfo:\n      let tuple = displayStringsForInferenceInfo(atRow: indexPath.row)\n      fieldName = tuple.0\n      info = tuple.1\n\n    }\n    cell.fieldNameLabel.font = infoFont\n    cell.fieldNameLabel.textColor = infoTextColor\n    cell.fieldNameLabel.text = fieldName\n    cell.infoLabel.text = info\n    return cell\n  }\n\n  // MARK: Format Display of information in the bottom sheet\n  /**\n   This method formats the display of additional information relating to the inferences.\n   */\n  func displayStringsForInferenceInfo(atRow row: Int) -> (String, String) {\n\n    var fieldName: String = \"\"\n    var info: String = \"\"\n\n    guard let inferenceInfo = InferenceInfo(rawValue: row) else {\n      return (fieldName, info)\n    }\n\n    fieldName = inferenceInfo.displayString()\n\n    switch inferenceInfo {\n    case .SampleRate:\n      info = \"\\(sampleRate) Hz\"\n    case .InferenceTime:\n      info = String(format: \"%.2fms\", inferenceTime)\n    }\n\n    return(fieldName, info)\n  }\n}\n"
  },
  {
    "path": "lite/examples/speech_commands/ios/SpeechCommands/ViewControllers/ViewController.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\nclass ViewController: UIViewController {\n\n  // MARK: Storyboard Outlets\n  @IBOutlet weak var collectionView: UICollectionView!\n\n  // MARK: Constants\n  private let unselectedFontColor = UIColor(\n      displayP3Red: 124.0/255.0, green: 136.0/255.0, blue: 144.0/255.0, alpha: 1.0)\n  private let selectedFontColor = UIColor(\n      displayP3Red: 250.0/255.0, green: 141.0/255.0, blue: 0.0/255.0, alpha: 1.0)\n  private let unselectedBorderColor = UIColor(\n      displayP3Red: 199.0/255.0, green: 208.0/255.0, blue: 216.0/255.0, alpha: 1.0)\n  private let collectionViewPadding: CGFloat = 15.0\n  private let highlightTime: Double = 0.5\n  private let imageInset: CGFloat = 8.0\n\n  // MARK: Objects Handling Core Functionality\n  private var modelDataHandler: ModelDataHandler? =\n    ModelDataHandler(modelFileInfo: ConvActions.modelInfo, labelsFileInfo: ConvActions.labelsInfo)\n  private var audioInputManager: AudioInputManager?\n  private var inferenceViewController: InferenceViewController?\n\n  // MARK: Instance Variables\n  private var words: [String] = []\n  private var result: Result?\n  private var highlightedCommand: RecognizedCommand?\n  private var bufferSize: Int = 0\n\n  // MARK: View Handling Methods\n  override func viewDidLoad() {\n    super.viewDidLoad()\n\n    guard let handler = modelDataHandler else {\n      return\n    }\n\n    // Displays lables\n    words = handler.offsetLabelsForDisplay()\n    self.collectionView.reloadData()\n\n    startAudioRecognition()\n\n  }\n\n  override var preferredStatusBarStyle : UIStatusBarStyle {\n    return .lightContent\n  }\n\n  // MARK: Storyboard Segue Handlers\n  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {\n    super.prepare(for: segue, sender: sender)\n\n    if segue.identifier == \"EMBED\" {\n\n      guard let tempModelDataHandler = modelDataHandler else {\n        return\n      }\n      inferenceViewController = segue.destination as? InferenceViewController\n      inferenceViewController?.sampleRate = Int(tempModelDataHandler.sampleRate)\n      inferenceViewController?.threadCountLimit = tempModelDataHandler.threadCountLimit\n      inferenceViewController?.currentThreadCount = tempModelDataHandler.threadCount\n      inferenceViewController?.delegate = self\n    }\n  }\n\n  /**\n   Initializes the AudioInputManager and starts recognizing on the output buffers.\n   */\n  private func startAudioRecognition() {\n\n    guard let handler = modelDataHandler else {\n      return\n    }\n\n    audioInputManager = AudioInputManager(sampleRate: handler.sampleRate)\n    audioInputManager?.delegate = self\n\n    guard let workingAudioInputManager = audioInputManager else {\n      return\n    }\n\n    bufferSize = workingAudioInputManager.bufferSize\n\n    workingAudioInputManager.checkPermissionsAndStartTappingMicrophone()\n//    workingAudioInputManager.start { (channelDataArray) in\n//\n//      self.runModel(onBuffer: Array(channelDataArray[0..<handler.sampleRate]))\n//      self.runModel(onBuffer: Array(channelDataArray[handler.sampleRate..<bufferSize]))\n//    }\n  }\n\n  /**\n   This method runs hands off inference to the ModelDataHandler by passing the audio buffer.\n   */\n  private func runModel(onBuffer buffer: [Int16]) {\n\n    result = modelDataHandler?.runModel(onBuffer: buffer)\n\n    // Updates the results on the screen.\n    DispatchQueue.main.async {\n      self.refreshInferenceTime()\n      guard let recognizedCommand = self.result?.recognizedCommand else {\n        return\n      }\n      self.highlightedCommand =  recognizedCommand\n      self.highlightResult()\n    }\n  }\n\n  /**\n   Highlights the recognized command in the UICollectionView for the specified time.\n   */\n  private func highlightResult() {\n\n    DispatchQueue.main.async {\n\n      self.collectionView.reloadData()\n      self.perform(#selector(ViewController.unhighlightResult), with: nil, afterDelay: self.highlightTime)\n    }\n  }\n\n  /**\n   Unhighlights the recognized command in the UICollectionView.\n   */\n  @objc func unhighlightResult() {\n    highlightedCommand = nil\n\n    collectionView.reloadData()\n  }\n\n  /**\n   Refreshes the additional information displayed by InferenceViewController.\n   */\n  func refreshInferenceTime() {\n\n    var inferenceTime: Double = 0.0\n    if let result = self.result {\n      inferenceTime = result.inferenceTime\n    }\n    inferenceViewController?.inferenceTime = inferenceTime\n    inferenceViewController?.refreshResults()\n  }\n}\n\n// MARK: InferenceViewControllerDelegate Methods\nextension ViewController: InferenceViewControllerDelegate {\n  func didChangeThreadCount(to count: Int) {\n    if modelDataHandler?.threadCount == count { return }\n    modelDataHandler = ModelDataHandler(\n      modelFileInfo: ConvActions.modelInfo,\n      labelsFileInfo: ConvActions.labelsInfo,\n      threadCount: count\n    )\n  }\n}\n\n// MARK: UICollectionView DataSource and Delegate\nextension ViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {\n\n  // Get item size of the collection view with respect to it's current width and height.\n  private func itemSize() -> CGSize {\n    let width = (self.collectionView.bounds.size.width - collectionViewPadding) / 2.0\n    let rows: CGFloat = CGFloat(words.count / 2)\n    let height =  (self.collectionView.bounds.size.height - ((CGFloat(rows - 1) * collectionViewPadding))) /  rows\n\n    return CGSize(width: width, height: height)\n  }\n\n  func numberOfSections(in collectionView: UICollectionView) -> Int {\n    return 1\n  }\n\n  func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {\n    return words.count\n  }\n\n  func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {\n\n    var borderColor = UIColor.clear\n    let wordCell = cell as? WordCell\n\n    let word = words[indexPath.item]\n\n    if let recognizedCommand = highlightedCommand, recognizedCommand.name == word {\n      borderColor = UIColor.clear\n    }\n    else {\n      borderColor = unselectedBorderColor\n    }\n\n    wordCell?.borderColor = borderColor\n    wordCell?.setNeedsDisplay()\n  }\n\n  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {\n\n    return itemSize()\n  }\n\n  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {\n\n    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: \"WORD_CELL\", for: indexPath) as! WordCell\n\n    let word = words[indexPath.item]\n\n    var backgroundImage: UIImage?\n    var fontColor = unselectedFontColor\n    var name = word.capitalized\n\n    if let recognizedCommand = highlightedCommand, recognizedCommand.name == word {\n      backgroundImage = UIImage(named: \"base\")?.resizableImage(withCapInsets: UIEdgeInsets(top: imageInset, left: imageInset, bottom: imageInset, right: imageInset), resizingMode: .stretch)\n      fontColor = selectedFontColor\n      name = word.capitalized + \" (\\(Int(recognizedCommand.score * 100.0))%)\"\n    }\n\n    cell.backgroundImageView.image = backgroundImage\n    cell.nameLabel.textColor = fontColor\n    cell.nameLabel.text = name\n\n    return cell\n  }\n\n}\n\nextension ViewController: AudioInputManagerDelegate {\n\n  func didOutput(channelData: [Int16]) {\n\n    guard let handler = modelDataHandler else {\n      return\n    }\n\n    self.runModel(onBuffer: Array(channelData[0..<handler.sampleRate]))\n    self.runModel(onBuffer: Array(channelData[handler.sampleRate..<bufferSize]))\n  }\n\n  func showCameraPermissionsDeniedAlert() {\n\n    let alertController = UIAlertController(title: \"Microphone Permissions Denied\", message: \"Microphone permissions have been denied for this app. You can change this by going to Settings\", preferredStyle: .alert)\n\n    let cancelAction = UIAlertAction(title: \"Cancel\", style: .cancel, handler: nil)\n    let settingsAction = UIAlertAction(title: \"Settings\", style: .default) { (action) in\n      UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)\n    }\n\n    alertController.addAction(cancelAction)\n    alertController.addAction(settingsAction)\n\n    present(alertController, animated: true, completion: nil)\n  }\n}\n"
  },
  {
    "path": "lite/examples/speech_commands/ios/SpeechCommands.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tAA055D862162210E00B25948 /* conv_actions_labels.txt in Resources */ = {isa = PBXBuildFile; fileRef = AA055D842162210E00B25948 /* conv_actions_labels.txt */; };\n\t\tAA055D872162210E00B25948 /* conv_actions_frozen.tflite in Resources */ = {isa = PBXBuildFile; fileRef = AA055D852162210E00B25948 /* conv_actions_frozen.tflite */; };\n\t\tAA1C17622186E2F0006F9564 /* WordCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C17612186E2F0006F9564 /* WordCell.swift */; };\n\t\tAA1C176A2186FA70006F9564 /* InferenceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C17692186FA70006F9564 /* InferenceViewController.swift */; };\n\t\tAA1C176C2186FA9E006F9564 /* InfoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C176B2186FA9E006F9564 /* InfoCell.swift */; };\n\t\tAA31546D21612AF7004B2732 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA31546C21612AF7004B2732 /* ViewController.swift */; };\n\t\tAA3BF5DD2154CA9F00796012 /* ModelDataHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3BF5DC2154CA9F00796012 /* ModelDataHandler.swift */; };\n\t\tAA7006D021526754003E34C0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA7006CF21526754003E34C0 /* AppDelegate.swift */; };\n\t\tAA7006D721526757003E34C0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AA7006D621526757003E34C0 /* Assets.xcassets */; };\n\t\tAA7006DA21526757003E34C0 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA7006D821526757003E34C0 /* LaunchScreen.storyboard */; };\n\t\tAA73C15E216120FA0063142C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA73C15C216120F90063142C /* Main.storyboard */; };\n\t\tAA73C161216126B20063142C /* AudioInputManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA73C160216126B20063142C /* AudioInputManager.swift */; };\n\t\tAAE67264215DFA1900C6E344 /* RecognizeCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE67263215DFA1900C6E344 /* RecognizeCommands.swift */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\tAA055D842162210E00B25948 /* conv_actions_labels.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = conv_actions_labels.txt; sourceTree = \"<group>\"; };\n\t\tAA055D852162210E00B25948 /* conv_actions_frozen.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = conv_actions_frozen.tflite; sourceTree = \"<group>\"; };\n\t\tAA1C17612186E2F0006F9564 /* WordCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordCell.swift; sourceTree = \"<group>\"; };\n\t\tAA1C17692186FA70006F9564 /* InferenceViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InferenceViewController.swift; sourceTree = \"<group>\"; };\n\t\tAA1C176B2186FA9E006F9564 /* InfoCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InfoCell.swift; sourceTree = \"<group>\"; };\n\t\tAA31546C21612AF7004B2732 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ViewController.swift; path = SpeechCommands/ViewControllers/ViewController.swift; sourceTree = SOURCE_ROOT; };\n\t\tAA3BF5DC2154CA9F00796012 /* ModelDataHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModelDataHandler.swift; sourceTree = \"<group>\"; };\n\t\tAA7006CC21526754003E34C0 /* SpeechCommands.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SpeechCommands.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tAA7006CF21526754003E34C0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\tAA7006D621526757003E34C0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tAA7006D921526757003E34C0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\tAA7006DB21526757003E34C0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tAA73C15D216120F90063142C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\tAA73C160216126B20063142C /* AudioInputManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioInputManager.swift; sourceTree = \"<group>\"; };\n\t\tAAE67263215DFA1900C6E344 /* RecognizeCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecognizeCommands.swift; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tAA7006C921526754003E34C0 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tAA055D7A2161FC0100B25948 /* ViewControllers */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAA1C17692186FA70006F9564 /* InferenceViewController.swift */,\n\t\t\t\tAA31546C21612AF7004B2732 /* ViewController.swift */,\n\t\t\t);\n\t\t\tpath = ViewControllers;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAA055D83216220E700B25948 /* Model */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAA055D852162210E00B25948 /* conv_actions_frozen.tflite */,\n\t\t\t\tAA055D842162210E00B25948 /* conv_actions_labels.txt */,\n\t\t\t);\n\t\t\tpath = Model;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAA1C175D2186E2A2006F9564 /* Cells */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAA1C176B2186FA9E006F9564 /* InfoCell.swift */,\n\t\t\t\tAA1C17612186E2F0006F9564 /* WordCell.swift */,\n\t\t\t);\n\t\t\tpath = Cells;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAA3BF5DB2154CA9F00796012 /* ModelDataHandler */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAA3BF5DC2154CA9F00796012 /* ModelDataHandler.swift */,\n\t\t\t\tAAE67263215DFA1900C6E344 /* RecognizeCommands.swift */,\n\t\t\t);\n\t\t\tpath = ModelDataHandler;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAA7006C321526754003E34C0 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAA7006CE21526754003E34C0 /* SpeechCommands */,\n\t\t\t\tAA7006CD21526754003E34C0 /* Products */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAA7006CD21526754003E34C0 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAA7006CC21526754003E34C0 /* SpeechCommands.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAA7006CE21526754003E34C0 /* SpeechCommands */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAA1C175D2186E2A2006F9564 /* Cells */,\n\t\t\t\tAA055D83216220E700B25948 /* Model */,\n\t\t\t\tAA055D7A2161FC0100B25948 /* ViewControllers */,\n\t\t\t\tAA73C15F2161269B0063142C /* AudioInputManager */,\n\t\t\t\tAA3BF5DB2154CA9F00796012 /* ModelDataHandler */,\n\t\t\t\tAA7006CF21526754003E34C0 /* AppDelegate.swift */,\n\t\t\t\tAA73C15C216120F90063142C /* Main.storyboard */,\n\t\t\t\tAA7006D821526757003E34C0 /* LaunchScreen.storyboard */,\n\t\t\t\tAA7006D621526757003E34C0 /* Assets.xcassets */,\n\t\t\t\tAA7006DB21526757003E34C0 /* Info.plist */,\n\t\t\t);\n\t\t\tpath = SpeechCommands;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAA73C15F2161269B0063142C /* AudioInputManager */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAA73C160216126B20063142C /* AudioInputManager.swift */,\n\t\t\t);\n\t\t\tpath = AudioInputManager;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\tAA7006CB21526754003E34C0 /* SpeechCommands */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = AA7006F421526757003E34C0 /* Build configuration list for PBXNativeTarget \"SpeechCommands\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tAA31546E2161EE1F004B2732 /* ShellScript */,\n\t\t\t\tAA7006C821526754003E34C0 /* Sources */,\n\t\t\t\tAA7006C921526754003E34C0 /* Frameworks */,\n\t\t\t\tAA7006CA21526754003E34C0 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = SpeechCommands;\n\t\t\tproductName = SpeechCommands;\n\t\t\tproductReference = AA7006CC21526754003E34C0 /* SpeechCommands.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tAA7006C421526754003E34C0 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 0940;\n\t\t\t\tLastUpgradeCheck = 0940;\n\t\t\t\tORGANIZATIONNAME = \"Y Media Labs\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tAA7006CB21526754003E34C0 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.4.1;\n\t\t\t\t\t\tLastSwiftMigration = 0940;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = AA7006C721526754003E34C0 /* Build configuration list for PBXProject \"SpeechCommands\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = AA7006C321526754003E34C0;\n\t\t\tproductRefGroup = AA7006CD21526754003E34C0 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tAA7006CB21526754003E34C0 /* SpeechCommands */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\tAA7006CA21526754003E34C0 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tAA73C15E216120FA0063142C /* Main.storyboard in Resources */,\n\t\t\t\tAA055D872162210E00B25948 /* conv_actions_frozen.tflite in Resources */,\n\t\t\t\tAA055D862162210E00B25948 /* conv_actions_labels.txt in Resources */,\n\t\t\t\tAA7006DA21526757003E34C0 /* LaunchScreen.storyboard in Resources */,\n\t\t\t\tAA7006D721526757003E34C0 /* Assets.xcassets in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\tAA31546E2161EE1F004B2732 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 12;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"$SRCROOT/RunScripts/download_models.sh\\\"\\n\";\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tAA7006C821526754003E34C0 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tAA7006D021526754003E34C0 /* AppDelegate.swift in Sources */,\n\t\t\t\tAAE67264215DFA1900C6E344 /* RecognizeCommands.swift in Sources */,\n\t\t\t\tAA31546D21612AF7004B2732 /* ViewController.swift in Sources */,\n\t\t\t\tAA1C176A2186FA70006F9564 /* InferenceViewController.swift in Sources */,\n\t\t\t\tAA3BF5DD2154CA9F00796012 /* ModelDataHandler.swift in Sources */,\n\t\t\t\tAA1C17622186E2F0006F9564 /* WordCell.swift in Sources */,\n\t\t\t\tAA73C161216126B20063142C /* AudioInputManager.swift in Sources */,\n\t\t\t\tAA1C176C2186FA9E006F9564 /* InfoCell.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\tAA7006D821526757003E34C0 /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tAA7006D921526757003E34C0 /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAA73C15C216120F90063142C /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tAA73C15D216120F90063142C /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\tAA7006F221526757003E34C0 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 11.4;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = \"\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tAA7006F321526757003E34C0 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 11.4;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tSWIFT_VERSION = \"\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tAA7006F521526757003E34C0 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tINFOPLIST_FILE = SpeechCommands/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.tensorflow.SpeechCommands;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 4.2;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tAA7006F621526757003E34C0 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tINFOPLIST_FILE = SpeechCommands/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.tensorflow.SpeechCommands;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"\";\n\t\t\t\tSWIFT_VERSION = 4.2;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tAA7006C721526754003E34C0 /* Build configuration list for PBXProject \"SpeechCommands\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tAA7006F221526757003E34C0 /* Debug */,\n\t\t\t\tAA7006F321526757003E34C0 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tAA7006F421526757003E34C0 /* Build configuration list for PBXNativeTarget \"SpeechCommands\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tAA7006F521526757003E34C0 /* Debug */,\n\t\t\t\tAA7006F621526757003E34C0 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = AA7006C421526754003E34C0 /* Project object */;\n}\n"
  },
  {
    "path": "lite/examples/speech_commands/ml/.gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n.hypothesis/\n.pytest_cache/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# pyenv\n.python-version\n\n# celery beat schedule file\ncelerybeat-schedule\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n\n# Data Resources\ndata/\nexport/*.tflite\nexport/*.pb\ncheckpoints/"
  },
  {
    "path": "lite/examples/speech_commands/ml/README.md",
    "content": "# TensorFlow Lite Speech Command Recognition\n\n## Getting Started\n\n### Prerequisites\n- Python 3.5+\n- Keras 2.1.6 or higher\n- pandas and pandas-ml\n- TensorFlow 1.5 or higher\n\n## TensorFlow Speech Commands Dataset\nThe dataset has 65,000 one-second long utterances of 30 short words, by thousands of different people\n\n### Classes\nIn this example, only 10 classes will be picked for the TensorFlow Lite speech commands application.\n`stop down off right up go on yes left no`\n\n### Downloading\nRun the download script to load the dataset into the local filesystem.\n```\npython download.py\n```\n\n## Audio Processing\nData generation involves producing raw PCM wavform data containing a desired number of samples and at fixed sample rate and the following configuration is used\n\n| Samples        | Sample Rate           | Clip Duration (ms)  |\n| ------------- |:-------------:| -----:|\n| 16000      | 16000 | 1000 |\n\n## Model Architecture\nIt is mostly a time stacked VGG-esque model with 1D Convolutions for temporal data such as audio waveforms. A one-dimensional dilated convolutional layer serving as the context convolution (`context_conv`) is employed to extract a wider field of the data. This is followed by a dimensionality reduction in the `reduce_conv` layer which employes 1-D MaxPooling to reduce the number of parameters passed to the subsequent layer.\n\n## Training\nThe model can be trained by running train.py\n\n### Usage\n```\npython train.py [-h] [-sample_rate SAMPLE_RATE] \\\n              [-batch_size BATCH_SIZE] \\\n              [-output_representation OUTPUT_REPRESENTATION] \\\n              -data_dirs DATA_DIRS [DATA_DIRS ...]\n```\n\n### Example of training the model\n```\npython train.py -sample_rate 16000 -batch_size 64 -output_representation raw -data_dirs data/train\n```\n\n## Results\nAfter the training the model for 100 epochs, the following confusion matrix was generated for assessing classification performance.\n\n`[099]: val_categorical_accuracy: 0.94`\n| Predicted     | _silence_     | down   | go  | left   | no  | off   | on  | right  | stop   | two   | up  | yes |\n| ------------- |:-------------:| ------:| ---:| ------:| ---:| -----:| ----:| -------:| -------:| ------:| ----:|-----:|\n| Actual |\n_silence_ | 322 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0\ndown | 0 | 240 | 6 | 0 | 7 | 0 | 1 | 0 | 0 | 4 | 0 | 0\ngo | 5 | 3 | 223 | 0 | 2 | 1 | 1 | 0 | 0 | 15 | 2 | 0\nleft | 0 | 0 | 0 | 221 | 2 | 0 | 0 | 1 | 0 | 8 | 0 | 7\nno | 0 | 0 | 4 | 0 | 246 | 0 | 2 | 0 | 0 | 14 | 0 | 0\noff | 0 | 0 | 1 | 0 | 0 | 229 | 2 | 0 | 0 | 5 | 15 | 0\non | 3 | 0 | 0 | 0 | 0 | 7 | 227 | 0 | 0 | 14 | 1 | 0\nright | 0 | 0 | 0 | 4 | 0 | 0 | 0 | 222 | 0 | 21 | 3 | 1\nstop | 2 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 224 | 8 | 3 | 0\ntwo | 6 | 4 | 5 | 7 | 6 | 2 | 6 | 3 | 0 | 1468 | 4 | 0\nup | 1 | 0 | 0 | 0 | 0 | 10 | 1 | 0 | 0 | 11 | 230 | 0\nyes | 2 | 1 | 0 | 2 | 4 | 0 | 1 | 0 | 0 | 6 | 0 | 240\n\n## Built With\n\n* [Keras](https://keras.io/) - Deep Learning Framework\n* [TensorFlow](http://tensorflow.org/) - Machine Learning Library\n\n## References\nFor more details on the dataset, visit [here](https://ai.googleblog.com/2017/08/launching-speech-commands-dataset.html)\n"
  },
  {
    "path": "lite/examples/speech_commands/ml/callbacks.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport numpy as np\nfrom pandas_ml import ConfusionMatrix\nfrom keras.callbacks import Callback\n\n\ndef log_loss(y_true, y_pred, eps=1e-12):\n  y_pred = np.clip(y_pred, eps, 1. - eps)\n  ce = -(np.sum(y_true * np.log(y_pred), axis=1))\n  mce = ce.mean()\n  return mce\n\n\nclass ConfusionMatrixCallback(Callback):\n\n  def __init__(self, validation_data, validation_steps, wanted_words, all_words,\n               label2int):\n    self.validation_data = validation_data\n    self.validation_steps = validation_steps\n    self.wanted_words = wanted_words\n    self.all_words = all_words\n    self.label2int = label2int\n    self.int2label = {v: k for k, v in label2int.items()}\n    with open('confusion_matrix.txt', 'w'):\n      pass\n    with open('wanted_confusion_matrix.txt', 'w'):\n      pass\n\n  def accuracies(self, confusion_val):\n    accuracies = []\n    for i in range(confusion_val.shape[0]):\n      num = confusion_val[i, :].sum()\n      if num:\n        accuracies.append(confusion_val[i, i] / num)\n      else:\n        accuracies.append(0.0)\n    accuracies = np.float32(accuracies)\n    return accuracies\n\n  def accuracy(self, confusion_val):\n    num_correct = 0\n    for i in range(confusion_val.shape[0]):\n      num_correct += confusion_val[i, i]\n    accuracy = float(num_correct) / confusion_val.sum()\n    return accuracy\n\n  def on_epoch_end(self, epoch, logs=None):\n    y_true, y_pred = [], []\n    for i in range(self.validation_steps):\n      X_batch, y_true_batch = next(self.validation_data)\n      y_pred_batch = self.model.predict(X_batch)\n\n      y_true.extend(y_true_batch)\n      y_pred.extend(y_pred_batch)\n\n    y_true = np.float32(y_true)\n    y_pred = np.float32(y_pred)\n    val_loss = log_loss(y_true, y_pred)\n    # map integer labels to strings\n    y_true = list(y_true.argmax(axis=-1))\n    y_pred = list(y_pred.argmax(axis=-1))\n    y_true = [self.int2label[y] for y in y_true]\n    y_pred = [self.int2label[y] for y in y_pred]\n    confusion = ConfusionMatrix(y_true, y_pred)\n    accs = self.accuracies(confusion._df_confusion.values)\n    acc = self.accuracy(confusion._df_confusion.values)\n    # same for wanted words\n    y_true = [y if y in self.wanted_words else '_unknown_' for y in y_true]\n    y_pred = [y if y in self.wanted_words else '_unknown_' for y in y_pred]\n    wanted_words_confusion = ConfusionMatrix(y_true, y_pred)\n    wanted_accs = self.accuracies(wanted_words_confusion._df_confusion.values)\n    acc_line = ('\\n[%03d]: val_categorical_accuracy: %.2f, '\n                'val_mean_categorical_accuracy_wanted: %.2f') % (\n                    epoch, acc, wanted_accs.mean())  # noqa\n    with open('confusion_matrix.txt', 'a') as f:\n      f.write('%s\\n' % acc_line)\n      f.write(confusion.to_dataframe().to_string())\n\n    with open('wanted_confusion_matrix.txt', 'a') as f:\n      f.write('%s\\n' % acc_line)\n      f.write(wanted_words_confusion.to_dataframe().to_string())\n\n    logs['val_loss'] = val_loss\n    logs['val_categorical_accuracy'] = acc\n    logs['val_mean_categorical_accuracy_all'] = accs.mean()\n    logs['val_mean_categorical_accuracy_wanted'] = wanted_accs.mean()\n"
  },
  {
    "path": "lite/examples/speech_commands/ml/classes.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nfrom collections import OrderedDict\nfrom generator import prepare_words_list\n\n\ndef get_classes(wanted_only=False):\n  if wanted_only:\n    classes = 'stop down off right up go on yes left no'\n    classes = classes.split(' ')\n    assert len(classes) == 10\n  else:\n    classes = ('sheila nine stop bed four six down bird marvin cat off right '\n               'seven eight up three happy go zero on wow dog yes five one tree'\n               ' house two left no')  # noqa\n    classes = classes.split(' ')\n    assert len(classes) == 30\n  return classes\n\n\ndef get_int2label(wanted_only=False, extend_reversed=False):\n  classes = get_classes(\n      wanted_only=wanted_only, extend_reversed=extend_reversed)\n  classes = prepare_words_list(classes)\n  int2label = {i: l for i, l in enumerate(classes)}\n  int2label = OrderedDict(sorted(int2label.items(), key=lambda x: x[0]))\n  return int2label\n\n\ndef get_label2int(wanted_only=False, extend_reversed=False):\n  classes = get_classes(\n      wanted_only=wanted_only, extend_reversed=extend_reversed)\n  classes = prepare_words_list(classes)\n  label2int = {l: i for i, l in enumerate(classes)}\n  label2int = OrderedDict(sorted(label2int.items(), key=lambda x: x[1]))\n  return label2int\n"
  },
  {
    "path": "lite/examples/speech_commands/ml/download.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nimport wget\nimport tarfile\n\nfrom shutil import rmtree\n\nDATASET_URL = 'http://download.tensorflow.org/data/speech_commands_v0.01.tar.gz'\nARCHIVE = os.path.basename(DATASET_URL)\n\nwget.download(DATASET_URL)\n\nif os.path.exists('data'):\n  rmtree('data')\n\nos.makedirs('data/train')\n\nwith tarfile.open(ARCHIVE, 'r:gz') as tar:\n  tar.extractall(path='data/train')\n\nos.remove(ARCHIVE)\n"
  },
  {
    "path": "lite/examples/speech_commands/ml/export/__init__.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n"
  },
  {
    "path": "lite/examples/speech_commands/ml/export/convert_keras_lite.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow.compat.v1 as tf\n\nkeras_model = \"../conv_1d_time_stacked_model/ep-084-vl-0.2595.hdf5\"\ninput_arrays = [\"the_input\"]\noutput_arrays = [\"the_output\"]\n\nconverter = tf.lite.TFLiteConverter\nconverter = converter.from_keras_model_file(keras_model, input_arrays,\n                                            output_arrays)\ntflite_model = converter.convert()\nopen(\"converted_speed_keras_model.tflite\", \"wb\").write(tflite_model)\n"
  },
  {
    "path": "lite/examples/speech_commands/ml/export/convert_keras_to_quantized.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\n\"\"\"Input arguments.\n\nnum_output: this value has nothing to do with the number of classes, batch_size,\netc.,\nand it is mostly equal to 1. If the network is a **multi-stream network**\n(forked network with multiple outputs), set the value to the number of outputs.\n\nquantize: if set to True, use the quantize feature of Tensorflow\n(https://www.tensorflow.org/performance/quantization) [default: False]\n\nuse_theano: Thaeno and Tensorflow implement convolution in different ways.\nWhen using Keras with Theano backend, the order is set to 'channels_first'.\nThis feature is not fully tested, and doesn't work with quantizization [default:\nFalse]\n\ninput_fld: directory holding the keras weights file [default: .]\n\noutput_fld: destination directory to save the tensorflow files [default: .]\n\ninput_model_file: name of the input weight file [default: 'model.h5']\n\noutput_model_file: name of the output weight file [default:\nargs.input_model_file + '.pb']\n\ngraph_def: if set to True, will write the graph definition as an ascii file\n[default: False]\n\noutput_graphdef_file: if graph_def is set to True, the file name of the\ngraph definition [default: model.ascii]\n\noutput_node_prefix: the prefix to use for output nodes. [default: output_node]\n\n\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport argparse\nfrom keras import backend as K\nfrom model import conv_1d_time_stacked_model\nfrom pathlib import Path\nimport tensorflow.compat.v1 as tf\n\nparser = argparse.ArgumentParser(description='set input arguments')\nparser.add_argument(\n    '-input_fld', action='store', dest='input_fld', type=str, default='.')\nparser.add_argument(\n    '-output_fld', action='store', dest='output_fld', type=str, default='')\nparser.add_argument(\n    '-input_model_file',\n    action='store',\n    dest='input_model_file',\n    type=str,\n    default='model.h5')\nparser.add_argument(\n    '-output_model_file',\n    action='store',\n    dest='output_model_file',\n    type=str,\n    default='')\nparser.add_argument(\n    '-output_graphdef_file',\n    action='store',\n    dest='output_graphdef_file',\n    type=str,\n    default='model.ascii')\nparser.add_argument(\n    '-num_outputs', action='store', dest='num_outputs', type=int, default=1)\nparser.add_argument(\n    '-graph_def', action='store', dest='graph_def', type=bool, default=False)\nparser.add_argument(\n    '-output_node_prefix',\n    action='store',\n    dest='output_node_prefix',\n    type=str,\n    default='output_node')\nparser.add_argument(\n    '-quantize', action='store', dest='quantize', type=bool, default=False)\nparser.add_argument(\n    '-theano_backend',\n    action='store',\n    dest='theano_backend',\n    type=bool,\n    default=False)\nparser.add_argument('-f')\nargs = parser.parse_args()\nparser.print_help()\nprint('input args: ', args)\n\nif args.theano_backend and args.quantize:\n  raise ValueError('Quantize feature does not work with theano backend.')\n\noutput_fld = args.input_fld if not args.output_fld else args.output_fld\nif not args.output_model_file:\n  args.output_model_file = str(Path(args.input_model_file).name) + '.pb'\nPath(output_fld).mkdir(parents=True, exist_ok=True)\nweight_file_path = str(Path(args.input_fld) / args.input_model_file)\n\n# Load keras model and rename output\n\n# In[ ]:\n\nK.set_learning_phase(0)\nif args.theano_backend:\n  K.set_image_data_format('channels_first')\nelse:\n  K.set_image_data_format('channels_last')\n\ntry:\n  fingerprint_size = 16000\n  label_count = 12\n  net_model = conv_1d_time_stacked_model(\n      fingerprint_size, num_classes=label_count)\n  net_model.load_weights('../conv_1d_time_stacked_model/ep-022-vl-0.2864.hdf5')\n\nexcept ValueError as err:\n  print(\n      \"\"\"Input file specified ({}) only holds the weights, and not the model definition.\n    Save the model using mode.save(filename.h5) which will contain the network architecture\n    as well as its weights.\n    If the model is saved using model.save_weights(filename.h5), the model architecture is\n    expected to be saved separately in a json format and loaded prior to loading the weights.\n    Check the keras documentation for more details (https://keras.io/getting-started/faq/)\"\"\"\n      .format(weight_file_path))\n  raise err\nnum_output = args.num_outputs\npred = [None] * num_output\npred_node_names = [None] * num_output\nfor i in range(num_output):\n  pred_node_names[i] = args.output_node_prefix + str(i)\n  pred[i] = tf.identity(net_model.outputs[i], name=pred_node_names[i])\nprint('output nodes names are: ', pred_node_names)\n\n# [optional] write graph definition in ascii\n\n# In[ ]:\n\nsess = K.get_session()\n\nif args.graph_def:\n  f = args.output_graphdef_file\n  tf.io.write_graph(sess.graph.as_graph_def(), output_fld, f, as_text=True)\n  print('saved the graph definition in ascii format at: ',\n        str(Path(output_fld) / f))\n\n# convert variables to constants and save\n\n# In[ ]:\n\nif args.quantize:\n  # graph_transforms will not be available for future versions.\n  from tensorflow.compat.v1.tools.graph_transforms import TransformGraph  # pylint: disable=g-import-not-at-top\n  transforms = ['quantize_weights', 'quantize_nodes']\n  transformed_graph_def = TransformGraph(sess.graph.as_graph_def(), [],\n                                         pred_node_names, transforms)\n  constant_graph = tf.graph_util.convert_variables_to_constants(\n      sess, transformed_graph_def, pred_node_names)\nelse:\n  constant_graph = tf.graph_util.convert_variables_to_constants(\n      sess, sess.graph.as_graph_def(), pred_node_names)\ntf.io.write_graph(\n    constant_graph, output_fld, args.output_model_file, as_text=False)\nprint('saved the freezed graph (ready for inference) at: ',\n      str(Path(output_fld) / args.output_model_file))\n"
  },
  {
    "path": "lite/examples/speech_commands/ml/export/convert_tensorflow_lite.sh",
    "content": "#!/usr/bin/env bash\n\ntflite_convert --output_file converted_speech_model.tflite \\\n--graph_def_file model.h5.pb \\\n--output_format TFLITE \\\n--inference_type FLOAT \\\n--inference_input_type FLOAT \\\n--input_arrays input_1 \\\n--output_arrays output_node0 \\\n#--allow_custom_ops\n"
  },
  {
    "path": "lite/examples/speech_commands/ml/generator.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport hashlib\nimport math\nimport os.path\nimport random\nimport re\nimport sys\n\nimport numpy as np\nfrom six.moves import xrange  # pylint: disable=redefined-builtin\nimport tensorflow.compat.v1 as tf\n\nfrom utils import tf_roll\n\nMAX_NUM_WAVS_PER_CLASS = 2**27 - 1  # ~134M\nSILENCE_LABEL = '_silence_'\nSILENCE_INDEX = 0\nUNKNOWN_WORD_LABEL = '_unknown_'\nUNKNOWN_WORD_INDEX = 1\nBACKGROUND_NOISE_DIR_NAME = '_background_noise_'\nRANDOM_SEED = 59185\n\n\ndef prepare_words_list(wanted_words):\n  \"\"\"Prepends common tokens to the custom word list.\"\"\"\n  return [SILENCE_LABEL, UNKNOWN_WORD_LABEL] + wanted_words\n\n\ndef which_set(filename, validation_percentage, testing_percentage):\n  \"\"\"Determines which data partition the file should belong to.\"\"\"\n  dir_name = os.path.basename(os.path.dirname(filename))\n  if dir_name == 'unknown_unknown':\n    return 'training'\n\n  base_name = os.path.basename(filename)\n  hash_name = re.sub(r'_nohash_.*$', '', base_name)\n\n  hash_name_hashed = hashlib.sha1(tf.compat.as_bytes(hash_name)).hexdigest()\n  percentage_hash = ((int(hash_name_hashed, 16) % (MAX_NUM_WAVS_PER_CLASS + 1))\n                     * (100.0 / MAX_NUM_WAVS_PER_CLASS))\n  if percentage_hash < validation_percentage:\n    result = 'validation'\n  elif percentage_hash < (testing_percentage + validation_percentage):\n    result = 'testing'\n  else:\n    result = 'training'\n  return result\n\n\ndef load_wav_file(filename):\n  \"\"\"Loads an audio file and returns a float PCM-encoded array of samples.\"\"\"\n  with tf.Session(graph=tf.Graph()) as sess:\n    wav_filename_placeholder = tf.placeholder(tf.string, [])\n    wav_loader = tf.io.read_file(wav_filename_placeholder)\n    wav_decoder = tf.audio.decode_wav(wav_loader, desired_channels=1)\n    return sess.run(\n        wav_decoder, feed_dict={\n            wav_filename_placeholder: filename\n        }).audio.flatten()\n\n\ndef save_wav_file(filename, wav_data, sample_rate):\n  \"\"\"Saves audio sample data to a .wav audio file.\"\"\"\n  with tf.Session(graph=tf.Graph()) as sess:\n    wav_filename_placeholder = tf.placeholder(tf.string, [])\n    sample_rate_placeholder = tf.placeholder(tf.int32, [])\n    wav_data_placeholder = tf.placeholder(tf.float32, [None, 1])\n    wav_encoder = tf.audio.encode_wav(wav_data_placeholder,\n                                      sample_rate_placeholder)\n    wav_saver = tf.io.write_file(wav_filename_placeholder, wav_encoder)\n    sess.run(\n        wav_saver,\n        feed_dict={\n            wav_filename_placeholder: filename,\n            sample_rate_placeholder: sample_rate,\n            wav_data_placeholder: np.reshape(wav_data, (-1, 1))\n        })\n\n\nclass AudioProcessor(object):\n  \"\"\"Handles loading, partitioning, and preparing audio training data.\"\"\"\n\n  def __init__(self,\n               data_dirs,\n               silence_percentage,\n               unknown_percentage,\n               wanted_words,\n               validation_percentage,\n               testing_percentage,\n               model_settings,\n               output_representation=False):\n    self.data_dirs = data_dirs\n    assert output_representation in {'raw', 'spec', 'mfcc', 'mfcc_and_raw'}\n    self.output_representation = output_representation\n    self.model_settings = model_settings\n    for data_dir in self.data_dirs:\n      self.maybe_download_and_extract_dataset(data_dir)\n    self.prepare_data_index(silence_percentage, unknown_percentage,\n                            wanted_words, validation_percentage,\n                            testing_percentage)\n    self.prepare_background_data()\n    self.prepare_processing_graph(model_settings)\n\n  def maybe_download_and_extract_dataset(self, data_dir):\n    if not os.path.exists(data_dir):\n      print('Please download the dataset!')\n      sys.exit(0)\n\n  def prepare_data_index(self, silence_percentage, unknown_percentage,\n                         wanted_words, validation_percentage,\n                         testing_percentage):\n    \"\"\"Prepares a list of the samples organized by set and label.\"\"\"\n    random.seed(RANDOM_SEED)\n    wanted_words_index = {}\n    for index, wanted_word in enumerate(wanted_words):\n      wanted_words_index[wanted_word] = index + 2\n    self.data_index = {'validation': [], 'testing': [], 'training': []}\n    unknown_index = {'validation': [], 'testing': [], 'training': []}\n    all_words = {}\n    # Look through all the subfolders to find audio samples\n    for data_dir in self.data_dirs:\n      search_path = os.path.join(data_dir, '*', '*.wav')\n      for wav_path in tf.io.gfile.glob(search_path):\n        word = re.search('.*/([^/]+)/.*.wav', wav_path).group(1).lower()\n        # Treat the '_background_noise_' folder as a special case,\n        # since we expect it to contain long audio samples we mix in\n        # to improve training.\n        if word == BACKGROUND_NOISE_DIR_NAME:\n          continue\n        all_words[word] = True\n        set_index = which_set(wav_path, validation_percentage,\n                              testing_percentage)\n        # If it's a known class, store its detail, otherwise add it to the list\n        # we'll use to train the unknown label.\n        if word in wanted_words_index:\n          self.data_index[set_index].append({'label': word, 'file': wav_path})\n        else:\n          unknown_index[set_index].append({'label': word, 'file': wav_path})\n      if not all_words:\n        raise Exception('No .wavs found at ' + search_path)\n      for index, wanted_word in enumerate(wanted_words):\n        if wanted_word not in all_words:\n          raise Exception('Expected to find ' + wanted_word +\n                          ' in labels but only found ' +\n                          ', '.join(all_words.keys()))\n    # We need an arbitrary file to load as the input for the silence samples.\n    # It's multiplied by zero later, so the content doesn't matter.\n    silence_wav_path = self.data_index['training'][0]['file']\n    for set_index in ['validation', 'testing', 'training']:\n      set_size = len(self.data_index[set_index])\n      silence_size = int(math.ceil(set_size * silence_percentage / 100))\n      for _ in range(silence_size):\n        self.data_index[set_index].append({\n            'label': SILENCE_LABEL,\n            'file': silence_wav_path\n        })\n      # Pick some unknowns to add to each partition of the data set.\n      random.shuffle(unknown_index[set_index])\n      unknown_size = int(math.ceil(set_size * unknown_percentage / 100))\n      self.data_index[set_index].extend(unknown_index[set_index][:unknown_size])\n    # Make sure the ordering is random.\n    for set_index in ['validation', 'testing', 'training']:\n      # not really needed since the indices are chosen by random\n      random.shuffle(self.data_index[set_index])\n    # Prepare the rest of the result data structure.\n    self.words_list = prepare_words_list(wanted_words)\n    self.word_to_index = {}\n    for word in all_words:\n      if word in wanted_words_index:\n        self.word_to_index[word] = wanted_words_index[word]\n      else:\n        self.word_to_index[word] = UNKNOWN_WORD_INDEX\n    self.word_to_index[SILENCE_LABEL] = SILENCE_INDEX\n\n  def prepare_background_data(self):\n    \"\"\"Searches a folder for background noise audio and loads it into memory.\"\"\"\n    self.background_data = []\n    background_dir = os.path.join(self.data_dirs[0], BACKGROUND_NOISE_DIR_NAME)\n    if not os.path.exists(background_dir):\n      return self.background_data\n    with tf.Session(graph=tf.Graph()) as sess:\n      wav_filename_placeholder = tf.placeholder(tf.string, [])\n      wav_loader = tf.io.read_file(wav_filename_placeholder)\n      wav_decoder = tf.audio.decode_wav(wav_loader, desired_channels=1)\n      search_path = os.path.join(self.data_dirs[0], BACKGROUND_NOISE_DIR_NAME,\n                                 '*.wav')\n      for wav_path in tf.io.gfile.glob(search_path):\n        wav_data = sess.run(\n            wav_decoder, feed_dict={\n                wav_filename_placeholder: wav_path\n            }).audio.flatten()\n        self.background_data.append(wav_data)\n      if not self.background_data:\n        raise Exception('No background wav files were found in ' + search_path)\n\n  def prepare_processing_graph(self, model_settings):\n    \"\"\"Builds a TensorFlow graph to apply the input distortions.\"\"\"\n    desired_samples = model_settings['desired_samples']\n    self.wav_filename_placeholder_ = tf.placeholder(\n        tf.string, [], name='filename')\n    wav_loader = tf.io.read_file(self.wav_filename_placeholder_)\n    wav_decoder = tf.audio.decode_wav(\n        wav_loader, desired_channels=1, desired_samples=desired_samples)\n    # Allow the audio sample's volume to be adjusted.\n    self.foreground_volume_placeholder_ = tf.placeholder(\n        tf.float32, [], name='foreground_volme')\n    scaled_foreground = tf.multiply(wav_decoder.audio,\n                                    self.foreground_volume_placeholder_)\n    # Shift the sample's start position, and pad any gaps with zeros.\n    self.time_shift_placeholder_ = tf.placeholder(tf.int32, name='timeshift')\n    shifted_foreground = tf_roll(scaled_foreground,\n                                 self.time_shift_placeholder_)\n    # Mix in background noise.\n    self.background_data_placeholder_ = tf.placeholder(\n        tf.float32, [desired_samples, 1], name='background_data')\n    self.background_volume_placeholder_ = tf.placeholder(\n        tf.float32, [], name='background_volume')\n    background_mul = tf.multiply(self.background_data_placeholder_,\n                                 self.background_volume_placeholder_)\n    background_add = tf.add(background_mul, shifted_foreground)\n    # removed clipping: tf.clip_by_value(background_add, -1.0, 1.0)\n    self.background_clamp_ = background_add\n    self.background_clamp_ = tf.reshape(self.background_clamp_,\n                                        (1, model_settings['desired_samples']))\n    # Run the spectrogram and MFCC ops to get a 2D 'fingerprint' of the audio.\n    stfts = tf.signal.stft(\n        self.background_clamp_,\n        frame_length=model_settings['window_size_samples'],\n        frame_step=model_settings['window_stride_samples'],\n        fft_length=None)\n    self.spectrogram_ = tf.abs(stfts)\n    num_spectrogram_bins = self.spectrogram_.shape[-1].value\n    lower_edge_hertz, upper_edge_hertz = 80.0, 7600.0\n    linear_to_mel_weight_matrix = \\\n        tf.signal.linear_to_mel_weight_matrix(\n            model_settings['dct_coefficient_count'],\n            num_spectrogram_bins, model_settings['sample_rate'],\n            lower_edge_hertz, upper_edge_hertz)\n    mel_spectrograms = tf.tensordot(self.spectrogram_,\n                                    linear_to_mel_weight_matrix, 1)\n    mel_spectrograms.set_shape(self.spectrogram_.shape[:-1].concatenate(\n        linear_to_mel_weight_matrix.shape[-1:]))\n    log_mel_spectrograms = tf.log(mel_spectrograms + 1e-6)\n    self.mfcc_ = tf.signal.mfccs_from_log_mel_spectrograms(\n        log_mel_spectrograms)[:, :, :\n                              model_settings['num_log_mel_features']]  # :13\n\n  def set_size(self, mode):\n    \"\"\"Calculates the number of samples in the dataset partition.\"\"\"\n    return len(self.data_index[mode])\n\n  def get_data(self,\n               how_many,\n               offset,\n               background_frequency,\n               background_volume_range,\n               foreground_frequency,\n               foreground_volume_range,\n               time_shift_frequency,\n               time_shift_range,\n               mode,\n               sess,\n               flip_frequency=0.0,\n               silence_volume_range=0.0):\n    \"\"\"Gather samples from the data set, applying transformations as needed.\"\"\"\n    # Pick one of the partitions to choose samples from.\n    model_settings = self.model_settings\n    candidates = self.data_index[mode]\n    if how_many == -1:\n      sample_count = len(candidates)\n    else:\n      sample_count = max(0, min(how_many, len(candidates) - offset))\n    # Data and labels will be populated and returned.\n    if self.output_representation == 'raw':\n      data_dim = model_settings['desired_samples']\n    elif self.output_representation == 'spec':\n      data_dim = model_settings['spectrogram_length'] * model_settings[\n          'spectrogram_frequencies']\n    elif self.output_representation == 'mfcc':\n      data_dim = model_settings['spectrogram_length'] * \\\n                 model_settings['num_log_mel_features']\n    elif self.output_representation == 'mfcc_and_raw':\n      data_dim = model_settings['spectrogram_length'] * \\\n                 model_settings['num_log_mel_features']\n      raw_data = np.zeros((sample_count, model_settings['desired_samples']))\n\n    data = np.zeros((sample_count, data_dim))\n    labels = np.zeros((sample_count, model_settings['label_count']))\n    desired_samples = model_settings['desired_samples']\n    use_background = self.background_data and (mode == 'training')\n    pick_deterministically = (mode != 'training')\n    # Use the processing graph we created earlier to repeatedly to generate the\n    # final output sample data we'll use in training.\n    for i in xrange(offset, offset + sample_count):\n      # Pick which audio sample to use.\n      if how_many == -1 or pick_deterministically:\n        sample_index = i\n        sample = candidates[sample_index]\n      else:\n        sample_index = np.random.randint(len(candidates))\n        sample = candidates[sample_index]\n\n      # If we're time shifting, set up the offset for this sample.\n      if np.random.uniform(0.0, 1.0) < time_shift_frequency:\n        time_shift = np.random.randint(time_shift_range[0],\n                                       time_shift_range[1] + 1)\n      else:\n        time_shift = 0\n      input_dict = {\n          self.wav_filename_placeholder_: sample['file'],\n          self.time_shift_placeholder_: time_shift,\n      }\n      # Choose a section of background noise to mix in.\n      if use_background:\n        background_index = np.random.randint(len(self.background_data))\n        background_samples = self.background_data[background_index]\n        background_offset = np.random.randint(\n            0,\n            len(background_samples) - model_settings['desired_samples'])\n        background_clipped = background_samples[background_offset:(\n            background_offset + desired_samples)]\n        background_reshaped = background_clipped.reshape([desired_samples, 1])\n        if np.random.uniform(0, 1) < background_frequency:\n          background_volume = np.random.uniform(0, background_volume_range)\n        else:\n          background_volume = 0.0\n          # silence class with all zeros is boring!\n          if sample['label'] == SILENCE_LABEL and \\\n                  np.random.uniform(0, 1) < 0.9:\n            background_volume = np.random.uniform(0, silence_volume_range)\n      else:\n        background_reshaped = np.zeros([desired_samples, 1])\n        background_volume = 0.0\n      input_dict[self.background_data_placeholder_] = background_reshaped\n      input_dict[self.background_volume_placeholder_] = background_volume\n      # If we want silence, mute out the main sample but leave the background.\n      if sample['label'] == SILENCE_LABEL:\n        input_dict[self.foreground_volume_placeholder_] = 0.0\n      else:\n        # Turn it up or down\n        foreground_volume = 1.0\n        if np.random.uniform(0, 1) < foreground_frequency:\n          foreground_volume = 1.0 + np.random.uniform(-foreground_volume_range,\n                                                      foreground_volume_range)\n        # flip sign\n        if np.random.uniform(0, 1) < flip_frequency:\n          foreground_volume *= -1.0\n        input_dict[self.foreground_volume_placeholder_] = foreground_volume\n\n      # Run the graph to produce the output audio.\n      if self.output_representation == 'raw':\n        data[i - offset, :] = sess.run(\n            self.background_clamp_, feed_dict=input_dict).flatten()\n      elif self.output_representation == 'spec':\n        data[i - offset, :] = sess.run(\n            self.spectrogram_, feed_dict=input_dict).flatten()\n      elif self.output_representation == 'mfcc':\n        data[i - offset, :] = sess.run(\n            self.mfcc_, feed_dict=input_dict).flatten()\n      elif self.output_representation == 'mfcc_and_raw':\n        raw_val, mfcc_val = sess.run([self.background_clamp_, self.mfcc_],\n                                     feed_dict=input_dict)\n        data[i - offset, :] = mfcc_val.flatten()\n        raw_data[i - offset, :] = raw_val.flatten()\n\n      label_index = self.word_to_index[sample['label']]\n      labels[i - offset, label_index] = 1\n\n    if self.output_representation != 'mfcc_and_raw':\n      return data, labels\n    else:\n      return [data, raw_data], labels\n\n  def get_unprocessed_data(self, how_many, model_settings, mode):\n    \"\"\"Gets sample data without transformations.\"\"\"\n    candidates = self.data_index[mode]\n    if how_many == -1:\n      sample_count = len(candidates)\n    else:\n      sample_count = how_many\n    desired_samples = model_settings['desired_samples']\n    words_list = self.words_list\n    data = np.zeros((sample_count, desired_samples))\n    labels = []\n    with tf.Session(graph=tf.Graph()) as sess:\n      wav_filename_placeholder = tf.placeholder(tf.string, [], name='filename')\n      wav_loader = tf.io.read_file(wav_filename_placeholder)\n      wav_decoder = tf.audio.decode_wav(\n          wav_loader, desired_channels=1, desired_samples=desired_samples)\n      foreground_volume_placeholder = tf.placeholder(\n          tf.float32, [], name='foreground_volume')\n      scaled_foreground = tf.multiply(wav_decoder.audio,\n                                      foreground_volume_placeholder)\n      for i in range(sample_count):\n        if how_many == -1:\n          sample_index = i\n        else:\n          sample_index = np.random.randint(len(candidates))\n        sample = candidates[sample_index]\n        input_dict = {wav_filename_placeholder: sample['file']}\n        if sample['label'] == SILENCE_LABEL:\n          input_dict[foreground_volume_placeholder] = 0\n        else:\n          input_dict[foreground_volume_placeholder] = 1\n        data[i, :] = sess.run(scaled_foreground, feed_dict=input_dict).flatten()\n        label_index = self.word_to_index[sample['label']]\n        labels.append(words_list[label_index])\n    return data, labels\n\n  def summary(self):\n    \"\"\"Prints a summary of classes and label distributions.\"\"\"\n    set_counts = {}\n    print('There are %d classes.' % (len(self.word_to_index)))\n    print(\"1%% <-> %d samples in 'training'\" % int(\n        self.set_size('training') / 100))\n    for set_index in ['training', 'validation', 'testing']:\n      counts = {k: 0 for k in sorted(self.word_to_index.keys())}\n      num_total = self.set_size(set_index)\n      for data_point in self.data_index[set_index]:\n        counts[data_point['label']] += (1.0 / num_total) * 100.0\n      set_counts[set_index] = counts\n\n    print('%-13s%-6s%-6s%-6s' % ('', 'Train', 'Val', 'Test'))\n    for label_name in sorted(\n        self.word_to_index.keys(), key=self.word_to_index.get):\n      line = '%02d %-12s: ' % (self.word_to_index[label_name], label_name)\n      for set_index in ['training', 'validation', 'testing']:\n        line += '%.1f%% ' % (set_counts[set_index][label_name])\n      print(line)\n"
  },
  {
    "path": "lite/examples/speech_commands/ml/model.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport keras\nfrom keras.layers import *\nfrom keras.regularizers import l2\nfrom keras.models import Model\n\n\ndef preprocess(x):\n  x = (x + 0.8) / 7.0\n  x = K.clip(x, -5, 5)\n  return x\n\n\ndef preprocess_raw(x):\n  return x\n\n\nPreprocess = Lambda(preprocess)\n\nPreprocessRaw = Lambda(preprocess_raw)\n\n\ndef relu6(x):\n  return K.relu(x, max_value=6)\n\n\ndef conv_1d_time_stacked_model(input_size=16000, num_classes=11):\n  \"\"\" Creates a 1D model for temporal data.\n\n  Note: Use only\n  with compute_mfcc = False (e.g. raw waveform data).\n  Args:\n    input_size: How big the input vector is.\n    num_classes: How many classes are to be recognized.\n\n  Returns:\n    Compiled keras model\n  \"\"\"\n  input_layer = Input(shape=[input_size])\n  x = input_layer\n  x = Reshape([800, 20])(x)\n  x = PreprocessRaw(x)\n\n  def _reduce_conv(x, num_filters, k, strides=2, padding='valid'):\n    x = Conv1D(\n        num_filters,\n        k,\n        padding=padding,\n        use_bias=False,\n        kernel_regularizer=l2(0.00001))(\n            x)\n    x = BatchNormalization()(x)\n    x = Activation(relu6)(x)\n    x = MaxPool1D(pool_size=3, strides=strides, padding=padding)(x)\n    return x\n\n  def _context_conv(x, num_filters, k, dilation_rate=1, padding='valid'):\n    x = Conv1D(\n        num_filters,\n        k,\n        padding=padding,\n        dilation_rate=dilation_rate,\n        kernel_regularizer=l2(0.00001),\n        use_bias=False)(\n            x)\n    x = BatchNormalization()(x)\n    x = Activation(relu6)(x)\n    return x\n\n  x = _context_conv(x, 32, 1)\n  x = _reduce_conv(x, 48, 3)\n  x = _context_conv(x, 48, 3)\n  x = _reduce_conv(x, 96, 3)\n  x = _context_conv(x, 96, 3)\n  x = _reduce_conv(x, 128, 3)\n  x = _context_conv(x, 128, 3)\n  x = _reduce_conv(x, 160, 3)\n  x = _context_conv(x, 160, 3)\n  x = _reduce_conv(x, 192, 3)\n  x = _context_conv(x, 192, 3)\n  x = _reduce_conv(x, 256, 3)\n  x = _context_conv(x, 256, 3)\n\n  x = Dropout(0.3)(x)\n  x = Conv1D(num_classes, 5, activation='softmax')(x)\n  x = Reshape([-1])(x)\n\n  model = Model(input_layer, x, name='conv_1d_time_stacked')\n  model.compile(\n      optimizer=keras.optimizers.Adam(lr=3e-4),\n      loss=keras.losses.categorical_crossentropy,\n      metrics=[keras.metrics.categorical_accuracy])\n  return model\n\n\ndef speech_model(model_type, input_size, num_classes=11, *args, **kwargs):\n  if model_type == 'conv_1d_time_stacked':\n    return conv_1d_time_stacked_model(input_size, num_classes)\n  else:\n    raise ValueError('Invalid model: %s' % model_type)\n\n\ndef prepare_model_settings(label_count,\n                           sample_rate,\n                           clip_duration_ms,\n                           window_size_ms,\n                           window_stride_ms,\n                           dct_coefficient_count,\n                           num_log_mel_features,\n                           output_representation='raw'):\n  \"\"\"Calculates common settings needed for all models.\"\"\"\n  desired_samples = int(sample_rate * clip_duration_ms / 1000)\n  window_size_samples = int(sample_rate * window_size_ms / 1000)\n  window_stride_samples = int(sample_rate * window_stride_ms / 1000)\n  length_minus_window = (desired_samples - window_size_samples)\n  spectrogram_frequencies = 257\n  if length_minus_window < 0:\n    spectrogram_length = 0\n  else:\n    spectrogram_length = 1 + int(length_minus_window / window_stride_samples)\n\n  if output_representation == 'mfcc':\n    fingerprint_size = num_log_mel_features * spectrogram_length\n  elif output_representation == 'raw':\n    fingerprint_size = desired_samples\n  elif output_representation == 'spec':\n    fingerprint_size = spectrogram_frequencies * spectrogram_length\n  elif output_representation == 'mfcc_and_raw':\n    fingerprint_size = num_log_mel_features * spectrogram_length\n  return {\n      'desired_samples': desired_samples,\n      'window_size_samples': window_size_samples,\n      'window_stride_samples': window_stride_samples,\n      'spectrogram_length': spectrogram_length,\n      'spectrogram_frequencies': spectrogram_frequencies,\n      'dct_coefficient_count': dct_coefficient_count,\n      'fingerprint_size': fingerprint_size,\n      'label_count': label_count,\n      'sample_rate': sample_rate,\n      'num_log_mel_features': num_log_mel_features\n  }\n"
  },
  {
    "path": "lite/examples/speech_commands/ml/requirements.txt",
    "content": "Keras==2.2.0\npandas==0.22.0\npandas-ml==0.5.0\ntensorflow>=1.14.0\ntensorflow-gpu>=1.14.0\nwget==3.2\n"
  },
  {
    "path": "lite/examples/speech_commands/ml/train.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport argparse\nimport os\n\nimport tensorflow.compat.v1 as tf\nfrom keras import backend as K\nfrom keras.callbacks import ModelCheckpoint, ReduceLROnPlateau\nfrom keras.callbacks import TensorBoard\nfrom callbacks import ConfusionMatrixCallback\nfrom model import speech_model, prepare_model_settings\nfrom generator import AudioProcessor, prepare_words_list\nfrom classes import get_classes\nfrom utils import data_gen\n\nparser = argparse.ArgumentParser(description='set input arguments')\n\nparser.add_argument(\n    '-sample_rate',\n    action='store',\n    dest='sample_rate',\n    type=int,\n    default=16000,\n    help='Sample rate of audio')\nparser.add_argument(\n    '-batch_size',\n    action='store',\n    dest='batch_size',\n    type=int,\n    default=32,\n    help='Size of the training batch')\nparser.add_argument(\n    '-output_representation',\n    action='store',\n    dest='output_representation',\n    type=str,\n    default='raw',\n    help='raw, spec, mfcc or mfcc_and_raw')\nparser.add_argument(\n    '-data_dirs',\n    '--list',\n    dest='data_dirs',\n    nargs='+',\n    required=True,\n    help='<Required> The list of data directories. e.g., data/train')\n\nargs = parser.parse_args()\nparser.print_help()\nprint('input args: ', args)\n\nif __name__ == '__main__':\n  gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=1.0)\n  sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options))\n  K.set_session(sess)\n  data_dirs = args.data_dirs\n  output_representation = args.output_representation\n  sample_rate = args.sample_rate\n  batch_size = args.batch_size\n  classes = get_classes(wanted_only=True)\n  model_settings = prepare_model_settings(\n      label_count=len(prepare_words_list(classes)),\n      sample_rate=sample_rate,\n      clip_duration_ms=1000,\n      window_size_ms=30.0,\n      window_stride_ms=10.0,\n      dct_coefficient_count=80,\n      num_log_mel_features=60,\n      output_representation=output_representation)\n\n  print(model_settings)\n\n  ap = AudioProcessor(\n      data_dirs=data_dirs,\n      wanted_words=classes,\n      silence_percentage=13.0,\n      unknown_percentage=60.0,\n      validation_percentage=10.0,\n      testing_percentage=0.0,\n      model_settings=model_settings,\n      output_representation=output_representation)\n  train_gen = data_gen(ap, sess, batch_size=batch_size, mode='training')\n  val_gen = data_gen(ap, sess, batch_size=batch_size, mode='validation')\n\n  model = speech_model(\n      'conv_1d_time_stacked',\n      model_settings['fingerprint_size']\n      if output_representation != 'raw' else model_settings['desired_samples'],\n      # noqa\n      num_classes=model_settings['label_count'],\n      **model_settings)\n\n  # embed()\n  checkpoints_path = os.path.join('checkpoints', 'conv_1d_time_stacked_model')\n  if not os.path.exists(checkpoints_path):\n    os.makedirs(checkpoints_path)\n\n  callbacks = [\n      ConfusionMatrixCallback(\n          val_gen,\n          ap.set_size('validation') // batch_size,\n          wanted_words=prepare_words_list(get_classes(wanted_only=True)),\n          all_words=prepare_words_list(classes),\n          label2int=ap.word_to_index),\n      ReduceLROnPlateau(\n          monitor='val_categorical_accuracy',\n          mode='max',\n          factor=0.5,\n          patience=4,\n          verbose=1,\n          min_lr=1e-5),\n      TensorBoard(log_dir='logs'),\n      ModelCheckpoint(\n          os.path.join(checkpoints_path,\n                       'ep-{epoch:03d}-vl-{val_loss:.4f}.hdf5'),\n          save_best_only=True,\n          monitor='val_categorical_accuracy',\n          mode='max')\n  ]\n  model.fit_generator(\n      train_gen,\n      steps_per_epoch=ap.set_size('training') // batch_size,\n      epochs=100,\n      verbose=1,\n      callbacks=callbacks)\n\n  eval_res = model.evaluate_generator(val_gen,\n                                      ap.set_size('validation') // batch_size)\n  print(eval_res)\n"
  },
  {
    "path": "lite/examples/speech_commands/ml/utils.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow.compat.v1 as tf\n\n\ndef data_gen(audio_processor,\n             sess,\n             batch_size=128,\n             background_frequency=0.3,\n             background_volume_range=0.15,\n             foreground_frequency=0.3,\n             foreground_volume_range=0.15,\n             time_shift_frequency=0.3,\n             time_shift_range=[-500, 0],\n             mode='validation',\n             flip_frequency=0.0,\n             silence_volume_range=0.3):\n  ep_count = 0\n  offset = 0\n  if mode != 'training':\n    background_frequency = 0.0\n    background_volume_range = 0.0\n    foreground_frequency = 0.0\n    foreground_volume_range = 0.0\n    time_shift_frequency = 0.0\n    time_shift_range = [0, 0]\n    flip_frequency = 0.0\n    # silence_volume_range: stays the same for validation\n  while True:\n    X, y = audio_processor.get_data(\n        how_many=batch_size,\n        offset=0 if mode == 'training' else offset,\n        background_frequency=background_frequency,\n        background_volume_range=background_volume_range,\n        foreground_frequency=foreground_frequency,\n        foreground_volume_range=foreground_volume_range,\n        time_shift_frequency=time_shift_frequency,\n        time_shift_range=time_shift_range,\n        mode=mode,\n        sess=sess,\n        flip_frequency=flip_frequency,\n        silence_volume_range=silence_volume_range)\n    offset += batch_size\n    if offset > audio_processor.set_size(mode) - batch_size:\n      offset = 0\n      print('\\n[Ep:%03d: %s-mode]' % (ep_count, mode))\n      ep_count += 1\n    yield X, y\n\n\ndef tf_roll(a, shift, a_len=16000):\n  # https://stackoverflow.com/questions/42651714/vector-shift-roll-in-tensorflow\n  def roll_left(a, shift, a_len):\n    shift %= a_len\n    rolled = tf.concat([a[a_len - shift:, :], a[:a_len - shift, :]], axis=0)\n    return rolled\n\n  def roll_right(a, shift, a_len):\n    shift = -shift\n    shift %= a_len\n    rolled = tf.concat([a[shift:, :], a[:shift, :]], axis=0)\n    return rolled\n\n  # https://stackoverflow.com/questions/35833011/how-to-add-if-condition-in-a-tensorflow-graph\n  return tf.cond(\n      tf.greater_equal(shift, 0),\n      true_fn=lambda: roll_left(a, shift, a_len),\n      false_fn=lambda: roll_right(a, shift, a_len))\n"
  },
  {
    "path": "lite/examples/speech_recognition/android/app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillColor=\"#26A69A\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n</vector>\n"
  },
  {
    "path": "lite/examples/speech_recognition/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n  <path\n      android:fillType=\"evenOdd\"\n      android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\">\n    <aapt:attr name=\"android:fillColor\">\n      <gradient\n          android:endX=\"78.5885\"\n          android:endY=\"90.9159\"\n          android:startX=\"48.7653\"\n          android:startY=\"61.0927\"\n          android:type=\"linear\">\n        <item\n            android:color=\"#44000000\"\n            android:offset=\"0.0\"/>\n        <item\n            android:color=\"#00000000\"\n            android:offset=\"1.0\"/>\n      </gradient>\n    </aapt:attr>\n  </path>\n  <path\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"nonZero\"\n      android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/speech_recognition/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/examples/speech_recognition/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/examples/speech_recognition/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/speech_recognition/android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/speech_recognition/android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/style_transfer/android/README.md",
    "content": "# TensorFlow Lite Style Transfer Demo\n\n### Overview\n\nArtistic style transfer is an optimization technique used to take two images: a\ncontent image and a style reference image (such as an artwork by a famous\npainter) and blend them together so the output image looks like the content\nimage, but “painted” in the style of the style reference image. These\ninstructions walk you through building and running the demo on an Android\ndevice.\n\nThe model files are downloaded via Gradle scripts when you build and run the\napp. You don't need to do any steps to download TFLite models into the project\nexplicitly.\n\nThis application should be run on a physical Android device.\n\n![Camera screen.](screenshot1.jpg?raw=true \"Camera screen\")\n\n![Transformation screen.](screenshot2.jpg?raw=true \"Transformation screen\")\n\nTest image\nfrom [Pixabay](https://pixabay.com/photos/tiger-head-face-feline-wild-cat-2923186/)\n\n## Build the demo using Android Studio\n\n### Prerequisites\n\n* The **[Android Studio](https://developer.android.com/studio/index.html)**\n  IDE (Android Studio 2021.2.1 or newer). This sample has been tested on Android\n  Studio Chipmunk.\n\n* A physical Android device with a minimum OS version of SDK 23 (Android 6.0 -\n  Marshmallow) with developer mode enabled. The process of enabling developer\n  mode may vary by device.\n\n### Building\n\n* Open Android Studio. From the Welcome screen, select Open an existing Android\n  Studio project.\n\n* From the Open File or Project window that appears, navigate to and select the\n  tensorflow-lite/examples/style_transfer/android directory. Click OK.\n\n* If it asks you to do a Gradle Sync, click OK.\n\n* With your Android device connected to your computer and developer mode\n  enabled, click on the green Run arrow in Android Studio.\n\n### Models used\n\nDownloading, extraction, and placing the models into the assets folder is\nmanaged automatically by the download.gradle file.\n\n### Resources used\n\n* [TensorFlow Lite](https://www.tensorflow.org/lite)\n* [Train the style transfer model and export to TensorFlow Lite](https://github.com/tensorflow/magenta/tree/master/magenta/models/arbitrary_image_stylization#train-a-model-on-a-large-dataset-with-data-augmentation-to-run-on-mobile)\n* [Neural Style Transfer with TensorFlow](https://www.tensorflow.org/tutorials/generative/style_transfer)\n* [CameraX](https://developer.android.com/training/camerax)\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/build.gradle",
    "content": "plugins {\n    id 'com.android.application'\n    id 'org.jetbrains.kotlin.android'\n    id 'kotlin-kapt'\n    id 'androidx.navigation.safeargs'\n    id 'de.undercouch.download'\n}\n\nandroid {\n    compileSdk 32\n\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.styletransfer\"\n        minSdk 23\n        targetSdk 32\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n    kotlinOptions {\n        jvmTarget = '1.8'\n    }\n\n    buildFeatures {\n        viewBinding true\n    }\n\n    androidResources {\n        noCompress 'tflite'\n    }\n}\n\n// import DownloadModels task\nproject.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'\nproject.ext.TEST_ASSETS_DIR = projectDir.toString() + '/src/androidTest/assets'\n\n// Download default models; if you wish to use your own models then\n// place them in the \"assets\" directory and comment out this line.\n//apply from: \"download_model.gradle\"\n\napply from: 'download_model.gradle'\n\ndependencies {\n\n    // Kotlin lang\n    implementation 'androidx.core:core-ktx:1.15.0'\n\n    // App compat and UI things\n    implementation 'androidx.appcompat:appcompat:1.5.1'\n    implementation 'com.google.android.material:material:1.6.1'\n    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'\n\n    // Navigation library\n    def nav_version = \"2.5.2\"\n    implementation \"androidx.navigation:navigation-fragment-ktx:$nav_version\"\n    implementation \"androidx.navigation:navigation-ui-ktx:$nav_version\"\n\n    // CameraX core library\n    def camerax_version = '1.2.0-alpha04'\n    implementation \"androidx.camera:camera-core:$camerax_version\"\n\n    // CameraX Camera2 extensions\n    implementation \"androidx.camera:camera-camera2:$camerax_version\"\n\n    // CameraX Lifecycle library\n    implementation \"androidx.camera:camera-lifecycle:$camerax_version\"\n\n    // CameraX View class\n    implementation \"androidx.camera:camera-view:$camerax_version\"\n\n    //WindowManager\n    implementation 'androidx.window:window:1.1.0-alpha03'\n\n    // Glide\n    implementation 'com.github.bumptech.glide:glide:4.13.2'\n    kapt \"com.github.bumptech.glide:compiler:4.13.2\"\n\n    // Unit testing\n    testImplementation 'junit:junit:4.13.2'\n\n    // Instrumented testing\n    androidTestImplementation 'androidx.test.ext:junit:1.1.3'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n    androidTestImplementation 'com.google.truth:truth:1.1.3'\n\n    // Tensorflow lite dependencies\n    implementation 'org.tensorflow:tensorflow-lite:2.9.0'\n    implementation 'org.tensorflow:tensorflow-lite-gpu:2.9.0'\n    implementation 'org.tensorflow:tensorflow-lite-support:0.4.2'\n    implementation 'org.tensorflow:tensorflow-lite-select-tf-ops:2.9.0'\n}\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/download_model.gradle",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ntask downloadModelFile(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/style_transfer/android/magenta_arbitrary-image-stylization-v1-256_int8_prediction_1.tflite'\n    dest project.ext.ASSET_DIR + '/predict_int8.tflite'\n    overwrite false\n}\n\ntask downloadModelFile0(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/style_transfer/android/magenta_arbitrary-image-stylization-v1-256_int8_transfer_1.tflite'\n    dest project.ext.ASSET_DIR + '/transfer_int8.tflite'\n    overwrite false\n}\n\ntask downloadModelFile1(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/style_transfer/android/magenta_arbitrary-image-stylization-v1-256_fp16_prediction_1.tflite'\n    dest project.ext.ASSET_DIR + '/predict_float16.tflite'\n    overwrite false\n}\n\ntask downloadModelFile2(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/style_transfer/android/magenta_arbitrary-image-stylization-v1-256_fp16_transfer_1.tflite'\n    dest project.ext.ASSET_DIR + '/transfer_float16.tflite'\n    overwrite false\n}\n\ntask copyTestModel(type: Copy, dependsOn: downloadModelFile) {\n    from project.ext.ASSET_DIR + '/predict_int8.tflite'\n    into project.ext.TEST_ASSETS_DIR\n}\n\ntask copyTestModel0(type: Copy, dependsOn: downloadModelFile) {\n    from project.ext.ASSET_DIR + '/transfer_int8.tflite'\n    into project.ext.TEST_ASSETS_DIR\n}\n\npreBuild.dependsOn downloadModelFile, downloadModelFile0, downloadModelFile1,\n        downloadModelFile2, copyTestModel, copyTestModel0\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/androidTest/java/org/tensorflow/lite/examples/styletransfer/StyleTransferHelperTest.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.styletransfer\n\nimport android.content.res.AssetManager\nimport android.graphics.Bitmap\nimport android.graphics.BitmapFactory\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport com.google.common.truth.Truth.assertThat\n\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\nimport java.io.InputStream\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass StyleTransferHelperTest {\n    companion object {\n        private const val INPUT_IMAGE = \"input_image.jpg\"\n        private const val INPUT_STYLE = \"input_style.jpg\"\n        private const val EXPECTED_IMAGE = \"expected_image.png\"\n        private const val EXPECTED_OUTPUT_TOLERANCE = 1e-2\n    }\n\n    @Test\n    fun executeResultShouldNotChange() {\n        val styleTransferHelper =\n            StyleTransferHelper(context = InstrumentationRegistry\n                .getInstrumentation().context,\n                styleTransferListener = object\n                    : StyleTransferHelper.StyleTransferListener {\n                    override fun onError(error: String) {\n                        // no-op\n                    }\n\n                    override fun onResult(bitmap: Bitmap, inferenceTime: Long) {\n\n                        // Verify output bitmap.\n                        val expectedPixels =\n                            getPixels(loadImage(EXPECTED_IMAGE)!!)\n                        val resultPixels = getPixels(bitmap)\n                        assertThat(resultPixels.size).isEqualTo(expectedPixels.size)\n                        var inconsistentPixels = 0\n                        for (i in resultPixels.indices) {\n                            if (resultPixels[i] != expectedPixels[i]) {\n                                inconsistentPixels++\n                            }\n                        }\n\n                        assertThat(inconsistentPixels.toDouble() / resultPixels.size)\n                            .isLessThan(EXPECTED_OUTPUT_TOLERANCE)\n                    }\n                })\n        styleTransferHelper.setStyleImage(loadImage(INPUT_STYLE)!!)\n        styleTransferHelper.transfer(loadImage(INPUT_IMAGE)!!)\n    }\n\n    @Throws(Exception::class)\n    private fun loadImage(fileName: String): Bitmap? {\n        val assetManager: AssetManager =\n            InstrumentationRegistry.getInstrumentation().context.assets\n        val inputStream: InputStream = assetManager.open(fileName)\n        return BitmapFactory.decodeStream(inputStream)\n    }\n\n    private fun getPixels(bitmap: Bitmap): IntArray {\n        val width = bitmap.width\n        val height = bitmap.height\n        val pixels = IntArray(width * height)\n        bitmap.getPixels(pixels, 0, width, 0, 0, width, height)\n        return pixels\n    }\n}\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:dist=\"http://schemas.android.com/apk/distribution\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"org.tensorflow.lite.examples.styletransfer\">\n\n    <!-- Enable instant app support -->\n    <dist:module dist:instant=\"true\" />\n\n    <!-- Declare features -->\n    <uses-feature android:name=\"android.hardware.camera\" />\n\n    <!-- Declare permissions -->\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:taskAffinity=\"\"\n        tools:ignore=\"AllowBackup\">\n\n        <activity\n            android:name=\".MainActivity\"\n            android:clearTaskOnLaunch=\"true\"\n            android:configChanges=\"orientation|screenLayout|screenSize|smallestScreenSize\"\n            android:exported=\"true\"\n            android:resizeableActivity=\"true\"\n            android:rotationAnimation=\"seamless\"\n            android:theme=\"@style/AppTheme\"\n            tools:targetApi=\"O\">\n\n            <!-- Main app intent filter -->\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n\n            <!-- Declare notch support -->\n            <meta-data\n                android:name=\"android.notch_support\"\n                android:value=\"true\" />\n        </activity>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/java/org/tensorflow/lite/examples/styletransfer/MainActivity.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.styletransfer\n\nimport android.os.Bundle\nimport androidx.activity.viewModels\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.navigation.findNavController\nimport androidx.navigation.fragment.NavHostFragment\nimport androidx.navigation.ui.AppBarConfiguration\nimport androidx.navigation.ui.NavigationUI.setupActionBarWithNavController\nimport androidx.navigation.ui.navigateUp\nimport org.tensorflow.lite.examples.styletransfer.databinding.ActivityMainBinding\n\nclass MainActivity : AppCompatActivity() {\n    private lateinit var activityMainBinding: ActivityMainBinding\n    private lateinit var appBarConfiguration: AppBarConfiguration\n    private val viewModel: MainViewModel by viewModels()\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)\n        setContentView(activityMainBinding.root)\n\n        // Config action bar\n        setSupportActionBar(activityMainBinding.toolbar)\n        supportActionBar?.setDisplayShowTitleEnabled(false)\n        val navHostFragment =\n            supportFragmentManager.findFragmentById(R.id.fragment_container) as\n                    NavHostFragment\n        appBarConfiguration = AppBarConfiguration.Builder(\n            R.id.permissions_fragment, R.id.camera_fragment\n        ).build()\n        setupActionBarWithNavController(\n            this, navHostFragment.navController, appBarConfiguration\n        )\n    }\n\n    override fun onSupportNavigateUp(): Boolean {\n        val navController =\n            findNavController(R.id.fragment_container)\n        return navController.navigateUp(appBarConfiguration)\n                || super.onSupportNavigateUp()\n    }\n}\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/java/org/tensorflow/lite/examples/styletransfer/MainViewModel.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.styletransfer\n\nimport android.graphics.Bitmap\nimport android.graphics.BitmapFactory\nimport android.graphics.Matrix\nimport androidx.lifecycle.ViewModel\nimport java.nio.ByteBuffer\n\nclass MainViewModel : ViewModel() {\n\n    private val _inputBitmap = SingleLiveEvent<Bitmap>()\n    val inputBitmap get() = _inputBitmap\n\n    // Store helper setting\n    var defaultModelNumThreads: Int = 2\n    var defaultModelDelegate: Int = 0\n    var defaultModel: Int = 0\n\n    // Convert bytebuffer to Bitmap and rotate for ready to show on Ui and\n    // transfer\n    fun setInputImage(buffer: ByteBuffer, rotation: Int) {\n        val bytes = ByteArray(buffer.capacity())\n        buffer.get(bytes)\n        var bitmapBuffer = BitmapFactory.decodeByteArray(\n            bytes, 0,\n            bytes.size, null\n        )\n        val matrix = Matrix()\n        matrix.postRotate(rotation.toFloat())\n        bitmapBuffer = Bitmap.createBitmap(\n            bitmapBuffer, 0, 0, bitmapBuffer\n                .width, bitmapBuffer.height, matrix, true\n        )\n\n        _inputBitmap.postValue(bitmapBuffer)\n    }\n\n    fun getInputBitmap() = _inputBitmap.value\n}\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/java/org/tensorflow/lite/examples/styletransfer/SingleLiveEvent.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \npackage org.tensorflow.lite.examples.styletransfer\n\nimport android.util.Log\nimport androidx.annotation.MainThread\nimport androidx.lifecycle.LifecycleOwner\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.Observer\nimport java.util.concurrent.atomic.AtomicBoolean\n\n/**\n * A lifecycle-aware observable that sends only new updates after subscription, used for events like\n * navigation and Snackbar messages.\n *\n *\n * This avoids a common problem with events: on configuration change (like rotation) an update\n * can be emitted if the observer is active. This LiveData only calls the observable if there's an\n * explicit call to setValue() or call().\n *\n *\n * Note that only one observer is going to be notified of changes.\n */\nclass SingleLiveEvent<T> : MutableLiveData<T?>() {\n    private val mPending = AtomicBoolean(false)\n\n    @MainThread\n    override fun observe(owner: LifecycleOwner, observer: Observer<in T?>) {\n        if (hasActiveObservers()) {\n            Log.w(\n                TAG,\n                \"Multiple observers registered but only one will be notified of changes.\"\n            )\n        }\n        // Observe the internal MutableLiveData\n        super.observe(owner\n        ) { t: T? ->\n            if (mPending.compareAndSet(true, false)) {\n                observer.onChanged(t)\n            }\n        }\n    }\n\n    @MainThread\n    override fun setValue(t: T?) {\n        mPending.set(true)\n        super.setValue(t)\n    }\n\n    companion object {\n        private const val TAG = \"SingleLiveEvent\"\n    }\n}\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/java/org/tensorflow/lite/examples/styletransfer/StyleTransferHelper.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.styletransfer\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.os.SystemClock\nimport android.util.Log\nimport org.tensorflow.lite.DataType\nimport org.tensorflow.lite.Interpreter\nimport org.tensorflow.lite.gpu.CompatibilityList\nimport org.tensorflow.lite.gpu.GpuDelegate\nimport org.tensorflow.lite.nnapi.NnApiDelegate\nimport org.tensorflow.lite.support.common.FileUtil\nimport org.tensorflow.lite.support.common.ops.DequantizeOp\nimport org.tensorflow.lite.support.common.ops.NormalizeOp\nimport org.tensorflow.lite.support.image.ImageProcessor\nimport org.tensorflow.lite.support.image.TensorImage\nimport org.tensorflow.lite.support.image.ops.ResizeOp\nimport org.tensorflow.lite.support.image.ops.ResizeWithCropOrPadOp\nimport org.tensorflow.lite.support.tensorbuffer.TensorBuffer\nimport java.lang.Integer.min\n\nclass StyleTransferHelper(\n    var numThreads: Int = 2,\n    var currentDelegate: Int = 0,\n    var currentModel: Int = 0,\n    val context: Context,\n    val styleTransferListener: StyleTransferListener?\n) {\n    private var interpreterPredict: Interpreter? = null\n    private var interpreterTransform: Interpreter? = null\n    private var styleImage: Bitmap? = null\n    private var inputPredictTargetWidth = 0\n    private var inputPredictTargetHeight = 0\n    private var inputTransformTargetWidth = 0\n    private var inputTransformTargetHeight = 0\n    private var outputPredictShape = intArrayOf()\n    private var outputTransformShape = intArrayOf()\n\n    init {\n        if (setupStyleTransfer()) {\n            inputPredictTargetHeight = interpreterPredict!!.getInputTensor(0)\n                .shape()[1]\n            inputPredictTargetWidth = interpreterPredict!!.getInputTensor(0)\n                .shape()[2]\n            outputPredictShape = interpreterPredict!!.getOutputTensor(0).shape()\n\n            inputTransformTargetHeight =\n                interpreterTransform!!.getInputTensor(0)\n                    .shape()[1]\n            inputTransformTargetWidth = interpreterTransform!!.getInputTensor(0)\n                .shape()[2]\n            outputTransformShape =\n                interpreterTransform!!.getOutputTensor(0).shape()\n\n        } else {\n            styleTransferListener?.onError(\"TFLite failed to init.\")\n        }\n    }\n\n    private fun setupStyleTransfer(): Boolean {\n        val tfliteOption = Interpreter.Options()\n        tfliteOption.numThreads = numThreads\n\n        when (currentDelegate) {\n            DELEGATE_CPU -> {\n                // Default\n            }\n            DELEGATE_GPU -> {\n                if (CompatibilityList().isDelegateSupportedOnThisDevice) {\n                    tfliteOption.addDelegate(GpuDelegate())\n                } else {\n                    styleTransferListener?.onError(\"GPU is not supported on this device\")\n                }\n            }\n            DELEGATE_NNAPI -> {\n                tfliteOption.addDelegate(NnApiDelegate())\n            }\n        }\n        val modelPredict: String\n        val modelTransfer: String\n        if (currentModel == MODEL_INT8) {\n            modelPredict = \"predict_int8.tflite\"\n            modelTransfer = \"transfer_int8.tflite\"\n        } else {\n            modelPredict = \"predict_float16.tflite\"\n            modelTransfer = \"transfer_float16.tflite\"\n        }\n\n        try {\n            interpreterPredict = Interpreter(\n                FileUtil.loadMappedFile(\n                    context,\n                    modelPredict,\n                ), tfliteOption\n            )\n\n            interpreterTransform = Interpreter(\n                FileUtil.loadMappedFile(\n                    context,\n                    modelTransfer\n                ), tfliteOption\n            )\n\n            return true\n        } catch (e: Exception) {\n            styleTransferListener?.onError(\n                \"Style transfer failed to initialize. See error logs for \" +\n                        \"details\"\n            )\n            Log.e(TAG, \"TFLite failed to load model with error: \" + e.message)\n            return false\n        }\n\n    }\n\n    fun transfer(bitmap: Bitmap) {\n        if (interpreterPredict == null || interpreterTransform == null) {\n            setupStyleTransfer()\n        }\n\n        if (styleImage == null) {\n            styleTransferListener?.onError(\n                \"Please select the style before run the transforming\"\n            )\n            return\n        }\n        // Inference time is the difference between the system time at the start and finish of the\n        // process\n        var inferenceTime = SystemClock.uptimeMillis()\n\n        processInputImage(\n            bitmap,\n            inputTransformTargetWidth,\n            inputTransformTargetHeight\n        )?.let { inputImage ->\n            processInputImage(\n                styleImage!!,\n                inputPredictTargetWidth,\n                inputPredictTargetHeight\n            )?.let { styleImage ->\n                val predictOutput = TensorBuffer.createFixedSize(\n                    outputPredictShape, DataType.FLOAT32\n                )\n                // The results of this inference could be reused given the style does not change\n                // That would be a good practice in case this was applied to a video stream.\n                interpreterPredict?.run(styleImage.buffer, predictOutput.buffer)\n\n                val transformInput =\n                    arrayOf(inputImage.buffer, predictOutput.buffer)\n                val outputImage = TensorBuffer.createFixedSize(\n                    outputTransformShape, DataType.FLOAT32\n                )\n                interpreterTransform?.runForMultipleInputsOutputs(\n                    transformInput,\n                    mapOf(Pair(0, outputImage.buffer))\n                )\n                getOutputImage(outputImage)?.let { outputBitmap ->\n                    inferenceTime = SystemClock.uptimeMillis() - inferenceTime\n\n                    styleTransferListener?.onResult(outputBitmap, inferenceTime)\n                }\n            }\n        }\n    }\n\n    fun setStyleImage(bitmap: Bitmap) {\n        styleImage = bitmap\n    }\n\n    fun clearStyleTransferHelper() {\n        interpreterPredict = null\n        interpreterTransform = null\n    }\n\n    // Preprocess the image and convert it into a TensorImage for\n    // transformation.\n    private fun processInputImage(\n        image: Bitmap,\n        targetWidth: Int,\n        targetHeight: Int\n    ): TensorImage? {\n        val height = image.height\n        val width = image.width\n        val cropSize = min(height, width)\n        val imageProcessor = ImageProcessor.Builder()\n            .add(ResizeWithCropOrPadOp(cropSize, cropSize))\n            .add(\n                ResizeOp(\n                    targetHeight,\n                    targetWidth,\n                    ResizeOp.ResizeMethod.BILINEAR\n                )\n            )\n            .add(NormalizeOp(0f, 255f))\n            .build()\n        val tensorImage = TensorImage(DataType.FLOAT32)\n        tensorImage.load(image)\n        return imageProcessor.process(tensorImage)\n    }\n\n    // Convert output bytebuffer to bitmap image.\n    private fun getOutputImage(output: TensorBuffer): Bitmap? {\n        val imagePostProcessor = ImageProcessor.Builder()\n            .add(DequantizeOp(0f, 255f)).build()\n        val tensorImage = TensorImage(DataType.FLOAT32)\n        tensorImage.load(output)\n        return imagePostProcessor.process(tensorImage).bitmap\n    }\n\n    interface StyleTransferListener {\n        fun onError(error: String)\n        fun onResult(bitmap: Bitmap, inferenceTime: Long)\n    }\n\n    companion object {\n        const val DELEGATE_CPU = 0\n        const val DELEGATE_GPU = 1\n        const val DELEGATE_NNAPI = 2\n        const val MODEL_INT8 = 0\n\n        private const val TAG = \"Style Transfer Helper\"\n    }\n}\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/java/org/tensorflow/lite/examples/styletransfer/fragments/CameraFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.styletransfer.fragments\n\nimport android.annotation.SuppressLint\nimport android.content.res.Configuration\nimport android.os.Bundle\nimport android.util.Log\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.Toast\nimport androidx.camera.core.AspectRatio\nimport androidx.camera.core.ImageProxy\nimport androidx.camera.core.Camera\nimport androidx.camera.core.CameraSelector\nimport androidx.camera.core.Preview\nimport androidx.camera.core.ImageCapture\nimport androidx.camera.core.ImageCaptureException\nimport androidx.camera.lifecycle.ProcessCameraProvider\nimport androidx.core.content.ContextCompat\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.activityViewModels\nimport androidx.navigation.Navigation\nimport org.tensorflow.lite.examples.styletransfer.MainViewModel\nimport org.tensorflow.lite.examples.styletransfer.R\nimport org.tensorflow.lite.examples.styletransfer.databinding.FragmentCameraBinding\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Executors\n\n\nclass CameraFragment : Fragment() {\n\n    companion object {\n        private const val TAG = \"Camera Fragment\"\n    }\n\n    private var _fragmentCameraBinding: FragmentCameraBinding? = null\n    private val fragmentCameraBinding get() = _fragmentCameraBinding!!\n\n    private var preview: Preview? = null\n    private var imageCapture: ImageCapture? = null\n    private var camera: Camera? = null\n    private var cameraProvider: ProcessCameraProvider? = null\n    private val viewModel: MainViewModel by activityViewModels()\n\n    /** Blocking camera operations are performed using this executor */\n    private lateinit var cameraExecutor: ExecutorService\n\n    override fun onResume() {\n        super.onResume()\n\n        if (!PermissionsFragment.hasPermissions(requireContext())) {\n            Navigation.findNavController(\n                requireActivity(),\n                R.id.fragment_container\n            ).navigate(CameraFragmentDirections.actionCameraToPermissions())\n        }\n    }\n\n    override fun onDestroyView() {\n        _fragmentCameraBinding = null\n        super.onDestroyView()\n\n        // Shut down our background executor\n        cameraExecutor.shutdown()\n    }\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View {\n        _fragmentCameraBinding =\n            FragmentCameraBinding.inflate(inflater, container, false)\n\n        return fragmentCameraBinding.root\n    }\n\n    @SuppressLint(\"MissingPermission\")\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n        cameraExecutor = Executors.newSingleThreadExecutor()\n\n        fragmentCameraBinding.viewFinder.post {\n            // Set up the camera and its use cases\n            setUpCamera()\n        }\n        viewModel.inputBitmap.observe(viewLifecycleOwner) {\n            Navigation.findNavController(\n                requireActivity(),\n                R.id.fragment_container\n            ).navigate(\n                CameraFragmentDirections.actionCameraFragmentToTransformationFragment()\n            )\n        }\n        fragmentCameraBinding.fabCapture.setOnClickListener {\n            takePicture()\n        }\n    }\n\n    // Initialize CameraX, and prepare to bind the camera use cases\n    private fun setUpCamera() {\n        val cameraProviderFuture =\n            ProcessCameraProvider.getInstance(requireContext())\n        cameraProviderFuture.addListener(\n            {\n                // CameraProvider\n                cameraProvider = cameraProviderFuture.get()\n\n                // Build and bind the camera use cases\n                bindCameraUseCases()\n            },\n            ContextCompat.getMainExecutor(requireContext())\n        )\n    }\n\n    override fun onConfigurationChanged(newConfig: Configuration) {\n        super.onConfigurationChanged(newConfig)\n        imageCapture?.targetRotation =\n            fragmentCameraBinding.viewFinder.display.rotation\n    }\n\n    // Declare and bind preview, capture and analysis use cases\n    private fun bindCameraUseCases() {\n\n        // CameraProvider\n        val cameraProvider =\n            cameraProvider\n                ?: throw IllegalStateException(\"Camera initialization failed.\")\n\n        // CameraSelector - makes assumption that we're only using the back camera\n        val cameraSelector = CameraSelector.Builder()\n            .requireLensFacing(CameraSelector.LENS_FACING_BACK).build()\n\n        // Preview. Only using the 4:3 ratio because this is the closest to our models\n        preview = Preview.Builder()\n            .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n            .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)\n            .build()\n\n        imageCapture = ImageCapture.Builder()\n            .setTargetAspectRatio(AspectRatio.RATIO_4_3)\n            .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)\n            .build()\n\n        // Must unbind the use-cases before rebinding them\n        cameraProvider.unbindAll()\n\n        try {\n            // A variable number of use-cases can be passed here -\n            // camera provides access to CameraControl & CameraInfo\n            camera = cameraProvider.bindToLifecycle(\n                this,\n                cameraSelector,\n                preview,\n                imageCapture\n            )\n\n            // Attach the viewfinder's surface provider to preview use case\n            preview?.setSurfaceProvider(fragmentCameraBinding.viewFinder.surfaceProvider)\n        } catch (exc: Exception) {\n            Log.e(TAG, \"Use case binding failed\", exc)\n        }\n    }\n\n    private fun takePicture() {\n        imageCapture?.takePicture(cameraExecutor, object : ImageCapture\n        .OnImageCapturedCallback() {\n            override fun onCaptureSuccess(image: ImageProxy) {\n                super.onCaptureSuccess(image)\n                viewModel.setInputImage(\n                    image.planes[0].buffer,\n                    image.imageInfo.rotationDegrees\n                )\n                image.close()\n            }\n\n            override fun onError(exception: ImageCaptureException) {\n                super.onError(exception)\n                activity?.runOnUiThread {\n                    Toast.makeText(\n                        requireContext(), \"Take picture error!\", Toast\n                            .LENGTH_SHORT\n                    ).show()\n                }\n            }\n        })\n    }\n}\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/java/org/tensorflow/lite/examples/styletransfer/fragments/PermissionsFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.styletransfer.fragments\n\nimport android.Manifest\nimport android.content.Context\nimport android.content.pm.PackageManager\nimport android.os.Bundle\nimport android.widget.Toast\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.core.content.ContextCompat\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.lifecycleScope\nimport androidx.navigation.Navigation\nimport org.tensorflow.lite.examples.styletransfer.R\n\nprivate val PERMISSIONS_REQUIRED = arrayOf(Manifest.permission.CAMERA)\n\nclass PermissionsFragment : Fragment() {\n\n    private val requestPermissionLauncher =\n        registerForActivityResult(\n            ActivityResultContracts.RequestPermission()\n        ) { isGranted: Boolean ->\n            if (isGranted) {\n                Toast.makeText(context, \"Permission request granted\", Toast.LENGTH_LONG).show()\n                navigateToCamera()\n            } else {\n                Toast.makeText(context, \"Permission request denied\", Toast.LENGTH_LONG).show()\n            }\n        }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        when (PackageManager.PERMISSION_GRANTED) {\n            ContextCompat.checkSelfPermission(\n                requireContext(),\n                Manifest.permission.CAMERA\n            ) -> {\n                navigateToCamera()\n            }\n            else -> {\n                requestPermissionLauncher.launch(\n                    Manifest.permission.CAMERA\n                )\n            }\n        }\n    }\n\n    private fun navigateToCamera() {\n        lifecycleScope.launchWhenStarted {\n            Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(\n                PermissionsFragmentDirections.actionPermissionsToCamera()\n            )\n        }\n    }\n\n    companion object {\n\n        /** Convenience method used to check if all permissions required by this app are granted */\n        fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all {\n            ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/java/org/tensorflow/lite/examples/styletransfer/fragments/StyleAdapter.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.styletransfer.fragments\n\nimport android.annotation.SuppressLint\nimport android.net.Uri\nimport android.view.LayoutInflater\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport com.bumptech.glide.Glide\nimport org.tensorflow.lite.examples.styletransfer.databinding.ItemStyleBinding\n\nclass StyleAdapter(\n    private val styles: MutableList<Style>, private val\n    selected: (Int) -> Unit\n) : RecyclerView\n.Adapter<StyleAdapter.StyleViewHolder>() {\n    private var previousPosition = RecyclerView.NO_POSITION\n\n    @SuppressLint(\"NotifyDataSetChanged\")\n    fun setSelected(position: Int, isSelected: Boolean) {\n        styles[position].isSelected = isSelected\n        previousPosition = position\n        notifyDataSetChanged()\n    }\n\n    override fun onCreateViewHolder(\n        parent: ViewGroup,\n        viewType: Int\n    ): StyleViewHolder {\n        val binding = ItemStyleBinding.inflate(\n            LayoutInflater.from(parent.context),\n            parent,\n            false\n        )\n        return StyleViewHolder(binding)\n    }\n\n    override fun onBindViewHolder(holder: StyleViewHolder, position: Int) {\n        holder.bind(styles[position])\n    }\n\n    override fun getItemCount(): Int = styles.size\n\n    @SuppressLint(\"NotifyDataSetChanged\")\n    inner class StyleViewHolder(private val binding: ItemStyleBinding) :\n        RecyclerView.ViewHolder(binding.root) {\n        init {\n            binding.imgStyle.setOnClickListener {\n                selected.invoke(adapterPosition)\n                styles[adapterPosition].isSelected = true\n                if (previousPosition > RecyclerView.NO_POSITION) {\n                    styles[previousPosition].isSelected = false\n                }\n                previousPosition = adapterPosition\n                notifyDataSetChanged()\n            }\n        }\n\n        fun bind(style: Style) {\n            binding.root.isSelected = style.isSelected\n            Glide.with(binding.root.context)\n                .load(Uri.parse(\"file:///android_asset/thumbnails/${style.imagePath}\"))\n                .centerCrop().into(binding.imgStyle)\n        }\n    }\n\n    data class Style(val imagePath: String, var isSelected: Boolean = false)\n}\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/java/org/tensorflow/lite/examples/styletransfer/fragments/TransformationFragment.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.styletransfer.fragments\n\nimport android.content.res.AssetManager\nimport android.graphics.Bitmap\nimport android.graphics.BitmapFactory\nimport android.os.Bundle\nimport androidx.fragment.app.Fragment\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.AdapterView\nimport android.widget.Toast\nimport androidx.core.content.ContextCompat\nimport androidx.fragment.app.activityViewModels\nimport androidx.recyclerview.widget.DividerItemDecoration\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.bumptech.glide.Glide\nimport org.tensorflow.lite.examples.styletransfer.MainViewModel\nimport org.tensorflow.lite.examples.styletransfer.R\nimport org.tensorflow.lite.examples.styletransfer.StyleTransferHelper\nimport org.tensorflow.lite.examples.styletransfer.databinding.FragmentTransformationBinding\nimport java.io.InputStream\n\nclass TransformationFragment : Fragment(),\n    StyleTransferHelper.StyleTransferListener {\n\n    private var _fragmentTransformationBinding: FragmentTransformationBinding? =\n        null\n    private val fragmentTransformationBinding get() = _fragmentTransformationBinding!!\n    private val viewModel: MainViewModel by activityViewModels()\n    private lateinit var styleTransferHelper: StyleTransferHelper\n\n    override fun onCreateView(\n        inflater: LayoutInflater, container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View {\n        // Inflate the layout for this fragment\n        _fragmentTransformationBinding =\n            FragmentTransformationBinding.inflate(inflater, container, false)\n\n        return fragmentTransformationBinding.root\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n\n        styleTransferHelper = StyleTransferHelper(\n            numThreads = viewModel.defaultModelNumThreads,\n            currentDelegate = viewModel.defaultModelDelegate,\n            currentModel = viewModel.defaultModel,\n            context = requireContext(),\n            styleTransferListener = this\n        )\n\n        // Set default value for controller view\n        fragmentTransformationBinding.spinnerDelegate.setSelection(\n            viewModel.defaultModelDelegate,\n            false\n        )\n        fragmentTransformationBinding.spinnerModel.setSelection(\n            viewModel.defaultModel,\n            false\n        )\n        fragmentTransformationBinding.threadsValue.text =\n            viewModel.defaultModelNumThreads.toString()\n\n\n        // Setup list style image\n        getListStyle().let { styles ->\n            with(fragmentTransformationBinding.recyclerViewStyle) {\n                val linearLayoutManager = LinearLayoutManager(\n                    context,\n                    LinearLayoutManager.HORIZONTAL, false\n                )\n                layoutManager = linearLayoutManager\n\n                val dividerItemDecoration = DividerItemDecoration(\n                    context,\n                    linearLayoutManager.orientation\n                )\n                dividerItemDecoration.setDrawable(\n                    ContextCompat.getDrawable\n                        (context, R.drawable.decoration_divider)!!\n                )\n                addItemDecoration(dividerItemDecoration)\n                adapter = StyleAdapter(styles) { pos ->\n                    getBitmapFromAssets(\n                        \"thumbnails/${styles[pos].imagePath}\"\n                    )?.let {\n                        styleTransferHelper.setStyleImage(it)\n                    }\n                }.apply {\n                    // Set default style image\n                    setSelected(0, true)\n                    getBitmapFromAssets(\"thumbnails/${styles[0].imagePath}\")?.let {\n                        styleTransferHelper.setStyleImage(it)\n                    }\n                }\n            }\n        }\n\n        // Attach listeners to UI control widgets\n        initControllerViews()\n        viewModel.getInputBitmap()?.let { originalBitmap ->\n            // Display the original at the first time.\n            Glide.with(requireActivity()).load(originalBitmap).centerCrop()\n                .into(fragmentTransformationBinding.imgStyled)\n\n            fragmentTransformationBinding.btnTransfer.setOnClickListener {\n                styleTransferHelper.transfer(originalBitmap)\n            }\n        }\n    }\n\n    override fun onError(error: String) {\n        activity?.runOnUiThread {\n            Toast.makeText(requireContext(), error, Toast.LENGTH_SHORT).show()\n        }\n    }\n\n    override fun onResult(bitmap: Bitmap, inferenceTime: Long) {\n        activity?.runOnUiThread {\n            Glide.with(requireContext()).load(bitmap).centerCrop()\n                .into(fragmentTransformationBinding.imgStyled)\n            fragmentTransformationBinding.inferenceTimeVal.text =\n                String.format(\"%d ms\", inferenceTime)\n        }\n    }\n\n    private fun initControllerViews() {\n        // When clicked, decrease the number of threads used for transformation\n        fragmentTransformationBinding.threadsMinus.setOnClickListener {\n            if (styleTransferHelper.numThreads > 1) {\n                styleTransferHelper.numThreads--\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, increase the number of threads used for transformation\n        fragmentTransformationBinding.threadsPlus.setOnClickListener {\n            if (styleTransferHelper.numThreads < 4) {\n                styleTransferHelper.numThreads++\n                updateControlsUi()\n            }\n        }\n\n        // When clicked, change the underlying hardware used for inference.\n        // Current options are CPU, GPU, and NNAPI\n        fragmentTransformationBinding.spinnerDelegate.onItemSelectedListener =\n            object : AdapterView.OnItemSelectedListener {\n                override fun onItemSelected(\n                    parent: AdapterView<*>?,\n                    view: View?,\n                    position: Int,\n                    id: Long\n                ) {\n                    styleTransferHelper.currentDelegate = position\n                    viewModel.defaultModelDelegate = position\n                    updateControlsUi()\n                }\n\n                override fun onNothingSelected(parent: AdapterView<*>?) {\n                    /* no op */\n                }\n            }\n\n        // When clicked, change the underlying model used for object\n        // transformation\n        fragmentTransformationBinding.spinnerModel.onItemSelectedListener =\n            object : AdapterView.OnItemSelectedListener {\n                override fun onItemSelected(\n                    parent: AdapterView<*>?,\n                    view: View?,\n                    position: Int,\n                    id: Long\n                ) {\n                    styleTransferHelper.currentModel = position\n                    viewModel.defaultModel = position\n                    updateControlsUi()\n                }\n\n                override fun onNothingSelected(parent: AdapterView<*>?) {\n                    /* no op */\n                }\n            }\n    }\n\n    // Update the values displayed in the controller view. Reset transfer.\n    private fun updateControlsUi() {\n        viewModel.defaultModelNumThreads = styleTransferHelper.numThreads\n        fragmentTransformationBinding.threadsValue.text =\n            styleTransferHelper.numThreads.toString()\n        // Needs to be cleared instead of reinitialized because the GPU\n        // delegate needs to be initialized on the thread using it when applicable\n        styleTransferHelper.clearStyleTransferHelper()\n    }\n\n    private fun getListStyle(): MutableList<StyleAdapter.Style> {\n        val styles = mutableListOf<StyleAdapter.Style>()\n        requireActivity().assets.list(\"thumbnails\")?.forEach {\n            styles.add(StyleAdapter.Style(it))\n        }\n        return styles\n    }\n\n    private fun getBitmapFromAssets(fileName: String): Bitmap? {\n        val assetManager: AssetManager = requireActivity().assets\n        return try {\n            val istr: InputStream = assetManager.open(fileName)\n            val bitmap = BitmapFactory.decodeStream(istr)\n            istr.close()\n            bitmap\n        } catch (e: Exception) {\n            null\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/res/drawable/bg_item_style.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/bg_item_style_selected\" android:state_selected=\"true\" />\n</selector>\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/res/drawable/bg_item_style_selected.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <stroke\n        android:width=\"3dp\"\n        android:color=\"@color/tflite_orange_color\" />\n</shape>\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/res/drawable/decoration_divider.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <solid android:color=\"@android:color/transparent\" />\n    <size android:width=\"5dp\" />\n</shape>\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/res/drawable/ic_baseline_camera_enhance.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#FFFFFF\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M9,3L7.17,5L4,5c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,7c0,-1.1 -0.9,-2 -2,-2h-3.17L15,3L9,3zM12,18c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z\"/>\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M12,17l1.25,-2.75L16,13l-2.75,-1.25L12,9l-1.25,2.75L8,13l2.75,1.25z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/res/drawable/ic_minus.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13H5v-2h14v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/res/drawable/ic_plus.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path android:pathData=\"M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"85.84757\"\n                android:endY=\"92.4963\"\n                android:startX=\"42.9492\"\n                android:startY=\"49.59793\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/transparent\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <androidx.fragment.app.FragmentContainerView\n            android:id=\"@+id/fragment_container\"\n            android:name=\"androidx.navigation.fragment.NavHostFragment\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginTop=\"?android:attr/actionBarSize\"\n            android:background=\"@android:color/transparent\"\n            android:keepScreenOn=\"true\"\n            app:defaultNavHost=\"true\"\n            app:navGraph=\"@navigation/nav_graph\"\n            tools:context=\".MainActivity\" />\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:layout_alignParentTop=\"true\"\n            android:background=\"@color/toolbar_background\" />\n\n        <ImageView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:layout_centerHorizontal=\"true\"\n            android:contentDescription=\"@null\"\n            android:src=\"@drawable/tfl_logo\" />\n    </RelativeLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/res/layout/fragment_camera.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/camera_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.camera.view.PreviewView\n        android:id=\"@+id/view_finder\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:scaleType=\"fillStart\" />\n\n    <com.google.android.material.floatingactionbutton.FloatingActionButton\n        android:id=\"@+id/fabCapture\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_margin=\"@dimen/fr_camera_btn_take_picture_margin\"\n        android:backgroundTint=\"@color/tflite_orange_color\"\n        android:contentDescription=\"@string/fr_camera_btn_take_picture\"\n        app:srcCompat=\"@drawable/ic_baseline_camera_enhance\" />\n</RelativeLayout>\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/res/layout/fragment_transformation.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:padding=\"@dimen/controller_padding\">\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:id=\"@+id/linearLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintBottom_toTopOf=\"@id/llSetting\"\n        app:layout_constraintTop_toTopOf=\"parent\">\n\n        <ImageView\n            android:id=\"@+id/imgStyled\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:adjustViewBounds=\"true\"\n            android:contentDescription=\"@null\"\n            app:layout_constraintBottom_toBottomOf=\"parent\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"parent\" />\n    </androidx.constraintlayout.widget.ConstraintLayout>\n\n    <androidx.appcompat.widget.LinearLayoutCompat\n        android:id=\"@+id/llSetting\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        app:layout_constraintBottom_toBottomOf=\"parent\">\n\n        <androidx.recyclerview.widget.RecyclerView\n            android:id=\"@+id/recyclerViewStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/controller_default_row_margin\" />\n        <!-- Inference time row -->\n        <androidx.appcompat.widget.LinearLayoutCompat\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/controller_default_row_margin\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/inference_time_label\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:text=\"@string/label_inference_time\"\n                android:textColor=\"@color/control_ui_text_color\"\n                android:textSize=\"@dimen/controller_text_size\" />\n\n            <TextView\n                android:id=\"@+id/inference_time_val\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:gravity=\"end\"\n                android:text=\"@string/default_inference_time\"\n                android:textColor=\"@color/control_ui_text_color\"\n                android:textSize=\"@dimen/controller_text_size\" />\n        </androidx.appcompat.widget.LinearLayoutCompat>\n\n        <!-- Number of threads adjustment row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/controller_default_row_margin\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:text=\"@string/label_threads\"\n                android:textColor=\"@color/control_ui_text_color\"\n                android:textSize=\"@dimen/controller_text_size\" />\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:gravity=\"center_vertical\"\n                android:orientation=\"horizontal\">\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threads_minus\"\n                    android:layout_width=\"@dimen/controller_control_btn_size\"\n                    android:layout_height=\"@dimen/controller_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_thread_button_minus\"\n                    android:src=\"@drawable/ic_minus\" />\n\n                <TextView\n                    android:id=\"@+id/threads_value\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginLeft=\"@dimen/controller_control_text_side_margin\"\n                    android:layout_marginRight=\"@dimen/controller_control_text_side_margin\"\n                    android:gravity=\"center\"\n                    android:minEms=\"@integer/controller_control_text_min_ems\"\n                    android:text=\"2\"\n                    android:textColor=\"@color/control_ui_text_color\"\n                    android:textSize=\"@dimen/controller_text_size\" />\n\n                <androidx.appcompat.widget.AppCompatImageButton\n                    android:id=\"@+id/threads_plus\"\n                    android:layout_width=\"@dimen/controller_control_btn_size\"\n                    android:layout_height=\"@dimen/controller_control_btn_size\"\n                    android:contentDescription=\"@string/alt_bottom_sheet_thread_button_plus\"\n                    android:src=\"@drawable/ic_plus\" />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <!-- Delegate selection row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/controller_default_row_margin\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/label_delegate\"\n                android:textColor=\"@color/control_ui_text_color\"\n                android:textSize=\"@dimen/controller_text_size\" />\n\n            <androidx.appcompat.widget.AppCompatSpinner\n                android:id=\"@+id/spinner_delegate\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:entries=\"@array/delegate_spinner_titles\"\n                android:minWidth=\"@dimen/controller_spinner_delegate_min_width\"\n                android:spinnerMode=\"dropdown\"\n                android:theme=\"@style/BottomSheetSpinnerItemStyle\" />\n\n        </RelativeLayout>\n\n        <!-- Model selection row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/controller_default_row_margin\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/label_models\"\n                android:textColor=\"@color/control_ui_text_color\"\n                android:textSize=\"@dimen/controller_text_size\" />\n\n            <androidx.appcompat.widget.AppCompatSpinner\n                android:id=\"@+id/spinner_model\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:entries=\"@array/models_spinner_titles\"\n                android:minWidth=\"@dimen/controller_spinner_delegate_min_width\"\n                android:spinnerMode=\"dropdown\"\n                android:theme=\"@style/BottomSheetSpinnerItemStyle\" />\n        </RelativeLayout>\n\n        <Button\n            android:id=\"@+id/btnTransfer\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:backgroundTint=\"@color/tflite_orange_color\"\n            android:text=\"@string/btn_transfer\"\n            android:textColor=\"@android:color/white\" />\n    </androidx.appcompat.widget.LinearLayoutCompat>\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/res/layout/item_style.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@drawable/bg_item_style\"\n    android:padding=\"3dp\">\n\n    <ImageView\n        android:id=\"@+id/imgStyle\"\n        android:layout_width=\"@dimen/item_style_width\"\n        android:layout_height=\"0dp\"\n        android:contentDescription=\"@null\"\n        android:scaleType=\"centerCrop\"\n        app:layout_constraintDimensionRatio=\"1:1\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/res/navigation/nav_graph.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<navigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/nav_graph\"\n    app:startDestination=\"@id/permissions_fragment\">\n\n    <fragment\n        android:id=\"@+id/permissions_fragment\"\n        android:name=\"org.tensorflow.lite.examples.styletransfer.fragments.PermissionsFragment\"\n        android:label=\"PermissionsFragment\">\n\n        <action\n            android:id=\"@+id/action_permissions_to_camera\"\n            app:destination=\"@id/camera_fragment\"\n            app:popUpTo=\"@id/permissions_fragment\"\n            app:popUpToInclusive=\"true\" />\n\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/camera_fragment\"\n        android:name=\"org.tensorflow.lite.examples.styletransfer.fragments.CameraFragment\"\n        android:label=\"CameraFragment\">\n\n        <action\n            android:id=\"@+id/action_camera_to_permissions\"\n            app:destination=\"@id/permissions_fragment\" />\n        <action\n            android:id=\"@+id/action_camera_fragment_to_transformation_fragment\"\n            app:destination=\"@id/transformation_fragment\" />\n    </fragment>\n\n    <fragment\n        android:id=\"@+id/transformation_fragment\"\n        android:name=\"org.tensorflow.lite.examples.styletransfer.fragments.TransformationFragment\"\n        android:label=\"fragment_transformation\"\n        tools:layout=\"@layout/fragment_transformation\">\n        <action\n            android:id=\"@+id/action_transformation_fragment_to_camera_fragment\"\n            app:destination=\"@id/camera_fragment\" />\n    </fragment>\n</navigation>\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- TF Branding Orange -->\n    <color name=\"tflite_orange_color\">#FF6F00</color>\n    <color name=\"toolbar_background\">#EEEEEE</color>\n    <color name=\"ic_launcher_background\">#FFFFFF</color>\n    <color name=\"control_ui_text_color\">@android:color/black</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Controller UI -->\n    <dimen name=\"controller_text_size\">14sp</dimen>\n    <dimen name=\"controller_padding\">16dp</dimen>\n    <dimen name=\"controller_default_row_margin\">10dp</dimen>\n    <dimen name=\"controller_control_btn_size\">48dp</dimen>\n    <dimen name=\"controller_control_text_side_margin\">10dp</dimen>\n    <dimen name=\"controller_spinner_delegate_min_width\">160dp</dimen>\n    <dimen name=\"fr_camera_btn_take_picture_margin\">16dp</dimen>\n    <dimen name=\"item_style_width\">76dp</dimen>\n    <integer name=\"controller_control_text_min_ems\">3</integer>\n</resources>\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">TFLite Style Transfer Demo App</string>\n\n    <string name=\"alt_bottom_sheet_thread_button_minus\">Decrease the number of threads used</string>\n    <string name=\"alt_bottom_sheet_thread_button_plus\">Increase the number of threads used</string>\n    <string name=\"label_inference_time\">Inference Time</string>\n    <string name=\"label_fps\">Frames per Second</string>\n    <string name=\"label_threads\">Number of Threads</string>\n    <string name=\"label_delegate\">Delegate</string>\n    <string name=\"label_models\">ML Model</string>\n    <string name=\"default_threshold\">0.50</string>\n    <string name=\"default_inference_time\">0ms</string>\n    <string name=\"default_max_results\">3</string>\n    <string name=\"fr_camera_btn_take_picture\">Take Picture</string>\n    <string name=\"btn_transfer\">Run</string>\n\n    <string-array name=\"delegate_spinner_titles\">\n        <item>CPU</item>\n        <item>GPU</item>\n        <item>NNAPI</item>\n    </string-array>\n\n    <string-array name=\"models_spinner_titles\">\n        <item>INT8 Quantized</item>\n        <item>F16 Quantized</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "lite/examples/style_transfer/android/app/src/main/res/values/style.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\" />\n\n    <style name=\"BottomSheetSpinnerItemStyle\" parent=\"Widget.AppCompat.Light.DropDownItem.Spinner\">\n        <item name=\"android:textSize\">@dimen/controller_text_size</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "lite/examples/style_transfer/android/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n    dependencies {\n        classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.5.2'\n        classpath 'de.undercouch:gradle-download-task:4.1.2'\n    }\n}\n\nplugins {\n    id 'com.android.application' version '7.2.0' apply false\n    id 'com.android.library' version '7.2.0' apply false\n    id 'org.jetbrains.kotlin.android' version '1.7.10' apply false\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}"
  },
  {
    "path": "lite/examples/style_transfer/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/style_transfer/android/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app\"s APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official\n# Enables namespacing of each library's R class so that its R class includes only the\n# resources declared in the library itself and none from the library's dependencies,\n# thereby reducing the size of the R class for that library\nandroid.nonTransitiveRClass=true"
  },
  {
    "path": "lite/examples/style_transfer/android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/style_transfer/android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/style_transfer/android/settings.gradle",
    "content": "pluginManagement {\n    repositories {\n        gradlePluginPortal()\n        google()\n        mavenCentral()\n    }\n}\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\nrootProject.name = \"TFLite Style Transfer Demo App\"\ninclude ':app'\n"
  },
  {
    "path": "lite/examples/style_transfer/ios/Podfile",
    "content": "# Uncomment the next line to define a global platform for your project\nplatform :ios, '12.0'\n\ntarget 'StyleTransfer' do\n  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks\n  use_frameworks!\n\n  # Pods for StyleTransfer\n  pod 'TensorFlowLiteSwift', '~> 0.0.1-nightly', :subspecs => ['Metal']\n\nend\n"
  },
  {
    "path": "lite/examples/style_transfer/ios/README.md",
    "content": "# Style Transfer iOS sample\n\n![Screenshot](https://storage.googleapis.com/download.tensorflow.org/models/tflite/arbitrary_style_transfer/architecture.png)\n\n## Requirements\n\n*   Xcode 11 or higher (installed on a Mac machine)\n*   An iOS Simulator or device running iOS 10 or above\n*   Xcode command-line tools (run `xcode-select --install`)\n*   CocoaPods (run `sudo gem install cocoapods`)\n\n## Build and run\n\n1.  Clone the TensorFlow examples GitHub repository to your computer to get the\n    demo application. `git clone https://github.com/tensorflow/examples` 1.\n    Install the pod to generate the workspace file: `cd\n    examples/lite/examples/style_transfer/ios && pod install` Note: If you have\n    installed this pod before and that command doesn't work, try `pod update`.\n    At the end of this step you should have a directory called\n    `StyleTransfer.xcworkspace`.\n1.  Open the project in Xcode with the following command:\n    `open StyleTransfer.xcworkspace`<br/>\n    This launches Xcode and opens the `StyleTransfer` project.\n    1.  Select `Product -> Run` to install the app on an iOS Simulator or a\n        physical device.\n"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/AppDelegate.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport TensorFlowLite\nimport UIKit\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n  var window: UIWindow?\n\n  func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n  ) -> Bool {\n    return true\n  }\n\n}\n"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\": [\n    {\n      \"size\": \"60x60\",\n      \"expected-size\": \"180\",\n      \"filename\": \"180.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"3x\"\n    },\n    {\n      \"size\": \"40x40\",\n      \"expected-size\": \"80\",\n      \"filename\": \"80.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"40x40\",\n      \"expected-size\": \"120\",\n      \"filename\": \"120.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"3x\"\n    },\n    {\n      \"size\": \"60x60\",\n      \"expected-size\": \"120\",\n      \"filename\": \"120.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"58\",\n      \"filename\": \"58.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"29\",\n      \"filename\": \"29.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"87\",\n      \"filename\": \"87.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"3x\"\n    },\n    {\n      \"size\": \"20x20\",\n      \"expected-size\": \"40\",\n      \"filename\": \"40.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"20x20\",\n      \"expected-size\": \"60\",\n      \"filename\": \"60.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"iphone\",\n      \"scale\": \"3x\"\n    },\n    {\n      \"size\": \"1024x1024\",\n      \"filename\": \"1024.png\",\n      \"expected-size\": \"1024\",\n      \"idiom\": \"ios-marketing\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"40x40\",\n      \"expected-size\": \"80\",\n      \"filename\": \"80.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"72x72\",\n      \"expected-size\": \"72\",\n      \"filename\": \"72.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"76x76\",\n      \"expected-size\": \"152\",\n      \"filename\": \"152.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"50x50\",\n      \"expected-size\": \"100\",\n      \"filename\": \"100.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"58\",\n      \"filename\": \"58.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"76x76\",\n      \"expected-size\": \"76\",\n      \"filename\": \"76.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"29x29\",\n      \"expected-size\": \"29\",\n      \"filename\": \"29.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"50x50\",\n      \"expected-size\": \"50\",\n      \"filename\": \"50.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"72x72\",\n      \"expected-size\": \"144\",\n      \"filename\": \"144.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"40x40\",\n      \"expected-size\": \"40\",\n      \"filename\": \"40.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"83.5x83.5\",\n      \"expected-size\": \"167\",\n      \"filename\": \"167.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    },\n    {\n      \"size\": \"20x20\",\n      \"expected-size\": \"20\",\n      \"filename\": \"20.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"1x\"\n    },\n    {\n      \"size\": \"20x20\",\n      \"expected-size\": \"40\",\n      \"filename\": \"40.png\",\n      \"folder\": \"Assets.xcassets/AppIcon.appiconset/\",\n      \"idiom\": \"ipad\",\n      \"scale\": \"2x\"\n    }\n  ]\n}\n"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/photo_camera.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"photo_camera_2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"photo_camera_3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/photo_library.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"photo_library_2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"photo_library_3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style0.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style0.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style1.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style1.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style10.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style10.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style11.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style11.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style12.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style12.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style13.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style13.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style14.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style14.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style15.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style15.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style16.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style16.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style17.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style17.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style18.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style18.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style19.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style19.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style2.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style2.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style20.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style20.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style21.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style21.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style22.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style22.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style23.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style23.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style24.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style24.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style25.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style25.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style3.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style3.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style4.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style4.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style5.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style5.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style6.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style6.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style7.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style7.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style8.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style8.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Assets.xcassets/style9.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"style9.jpg\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"15705\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <device id=\"retina6_1\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"15706\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"896\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" textAlignment=\"center\" lineBreakMode=\"tailTruncation\" numberOfLines=\"0\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"rpk-Mj-4RB\">\n                                <rect key=\"frame\" x=\"97.5\" y=\"394.5\" width=\"219\" height=\"107.5\"/>\n                                <string key=\"text\">Style Transfer\nwith\nTensorFlow Lite</string>\n                                <fontDescription key=\"fontDescription\" type=\"boldSystem\" pointSize=\"30\"/>\n                                <color key=\"textColor\" systemColor=\"systemBlueColor\" red=\"0.0\" green=\"0.47843137250000001\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                <nil key=\"highlightedColor\"/>\n                            </label>\n                        </subviews>\n                        <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\" cocoaTouchSystemColor=\"whiteColor\"/>\n                        <constraints>\n                            <constraint firstItem=\"rpk-Mj-4RB\" firstAttribute=\"centerY\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerY\" id=\"4HS-bD-n3S\"/>\n                            <constraint firstItem=\"rpk-Mj-4RB\" firstAttribute=\"centerX\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerX\" id=\"OiY-re-5MG\"/>\n                        </constraints>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"15705\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" colorMatched=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <device id=\"retina6_1\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"15706\"/>\n        <capability name=\"collection view cell content view\" minToolsVersion=\"11.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"ViewController\" customModule=\"TFL_Style_Transfer\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"UhJ-jg-kFY\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"TqK-8z-fNG\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"896\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <imageView clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"8lT-sf-xuk\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"94\" width=\"414\" height=\"414\"/>\n                                <color key=\"backgroundColor\" red=\"0.0\" green=\"0.47843137250000001\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                                <constraints>\n                                    <constraint firstAttribute=\"width\" secondItem=\"8lT-sf-xuk\" secondAttribute=\"height\" multiplier=\"1:1\" id=\"8XK-Qi-m6A\"/>\n                                </constraints>\n                            </imageView>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Yg9-nM-9J0\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"44\" width=\"414\" height=\"50\"/>\n                                <subviews>\n                                    <button opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"2ym-n6-JSB\">\n                                        <rect key=\"frame\" x=\"298\" y=\"7\" width=\"36\" height=\"36\"/>\n                                        <state key=\"normal\" image=\"photo_library\"/>\n                                        <connections>\n                                            <action selector=\"onTapPhotoLibrary:\" destination=\"BYZ-38-t0r\" eventType=\"touchUpInside\" id=\"y9h-O6-v7c\"/>\n                                        </connections>\n                                    </button>\n                                    <button opaque=\"NO\" contentMode=\"scaleToFill\" enabled=\"NO\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"qim-4p-Nop\">\n                                        <rect key=\"frame\" x=\"80\" y=\"7\" width=\"36\" height=\"36\"/>\n                                        <state key=\"normal\" image=\"photo_camera\"/>\n                                        <connections>\n                                            <action selector=\"onTapOpenCamera:\" destination=\"BYZ-38-t0r\" eventType=\"touchUpInside\" id=\"BlS-nz-h4q\"/>\n                                        </connections>\n                                    </button>\n                                </subviews>\n                                <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\" cocoaTouchSystemColor=\"whiteColor\"/>\n                                <constraints>\n                                    <constraint firstItem=\"qim-4p-Nop\" firstAttribute=\"leading\" secondItem=\"Yg9-nM-9J0\" secondAttribute=\"leading\" constant=\"80\" id=\"6RE-N1-q5w\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"2ym-n6-JSB\" secondAttribute=\"trailing\" constant=\"80\" id=\"8c9-0O-Xow\"/>\n                                    <constraint firstItem=\"2ym-n6-JSB\" firstAttribute=\"centerY\" secondItem=\"Yg9-nM-9J0\" secondAttribute=\"centerY\" id=\"OI2-kw-GhF\"/>\n                                    <constraint firstAttribute=\"height\" constant=\"50\" id=\"VjL-51-Jtg\"/>\n                                    <constraint firstItem=\"2ym-n6-JSB\" firstAttribute=\"centerY\" secondItem=\"qim-4p-Nop\" secondAttribute=\"centerY\" id=\"yDZ-ho-qna\"/>\n                                </constraints>\n                            </view>\n                            <scrollView clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"scaleToFill\" showsHorizontalScrollIndicator=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Dy2-B8-THR\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"508\" width=\"414\" height=\"354\"/>\n                                <subviews>\n                                    <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"5dC-eb-FHY\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"332\"/>\n                                        <subviews>\n                                            <segmentedControl opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"left\" contentVerticalAlignment=\"top\" segmentControlStyle=\"plain\" selectedSegmentIndex=\"0\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"j88-xS-beZ\">\n                                                <rect key=\"frame\" x=\"120.5\" y=\"8\" width=\"173\" height=\"32\"/>\n                                                <segments>\n                                                    <segment title=\"Input\"/>\n                                                    <segment title=\"Style\"/>\n                                                    <segment title=\"Result\"/>\n                                                </segments>\n                                                <connections>\n                                                    <action selector=\"onSegmentChanged:\" destination=\"BYZ-38-t0r\" eventType=\"valueChanged\" id=\"g0u-ht-D7A\"/>\n                                                </connections>\n                                            </segmentedControl>\n                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Style transfer time\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"bLI-a1-79J\">\n                                                <rect key=\"frame\" x=\"8\" y=\"153.5\" width=\"398\" height=\"21\"/>\n                                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                <nil key=\"textColor\"/>\n                                                <nil key=\"highlightedColor\"/>\n                                            </label>\n                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Crop To Square\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Tis-0d-4Zb\">\n                                                <rect key=\"frame\" x=\"8\" y=\"52\" width=\"119\" height=\"21\"/>\n                                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                <nil key=\"textColor\"/>\n                                                <nil key=\"highlightedColor\"/>\n                                            </label>\n                                            <switch opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" on=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"jhc-xp-ef8\">\n                                                <rect key=\"frame\" x=\"357\" y=\"47\" width=\"51\" height=\"31\"/>\n                                                <connections>\n                                                    <action selector=\"onCropSwitchValueChanged:\" destination=\"BYZ-38-t0r\" eventType=\"valueChanged\" id=\"B6c-bK-JLb\"/>\n                                                </connections>\n                                            </switch>\n                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Style predict time\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" numberOfLines=\"0\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"kaC-2f-KHe\">\n                                                <rect key=\"frame\" x=\"8\" y=\"125\" width=\"398\" height=\"20.5\"/>\n                                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                <nil key=\"textColor\"/>\n                                                <nil key=\"highlightedColor\"/>\n                                            </label>\n                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Selected Style\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"iKP-H6-uOF\">\n                                                <rect key=\"frame\" x=\"8\" y=\"190.5\" width=\"110\" height=\"21\"/>\n                                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                <nil key=\"textColor\"/>\n                                                <nil key=\"highlightedColor\"/>\n                                            </label>\n                                            <imageView clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleAspectFit\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"NBJ-fY-j3b\">\n                                                <rect key=\"frame\" x=\"362\" y=\"179\" width=\"44\" height=\"44\"/>\n                                                <constraints>\n                                                    <constraint firstAttribute=\"height\" constant=\"44\" id=\"I9B-ix-ZWO\"/>\n                                                    <constraint firstAttribute=\"width\" constant=\"44\" id=\"v58-7t-CZe\"/>\n                                                </constraints>\n                                            </imageView>\n                                            <button opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" buttonType=\"roundedRect\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"x4E-J7-5hQ\">\n                                                <rect key=\"frame\" x=\"126\" y=\"186\" width=\"53\" height=\"30\"/>\n                                                <state key=\"normal\" title=\"Change\"/>\n                                                <connections>\n                                                    <action selector=\"onTapChangeStyleButton:\" destination=\"BYZ-38-t0r\" eventType=\"touchUpInside\" id=\"Omq-ef-Bsc\"/>\n                                                </connections>\n                                            </button>\n                                            <button opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" buttonType=\"roundedRect\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"V5H-FC-fxv\">\n                                                <rect key=\"frame\" x=\"144\" y=\"240\" width=\"126\" height=\"30\"/>\n                                                <state key=\"normal\" title=\"Run Style Transfer\"/>\n                                                <connections>\n                                                    <action selector=\"onTapRunButton:\" destination=\"BYZ-38-t0r\" eventType=\"touchUpInside\" id=\"I8q-Y8-Lao\"/>\n                                                </connections>\n                                            </button>\n                                            <button opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" buttonType=\"roundedRect\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"UCi-ia-Mdy\">\n                                                <rect key=\"frame\" x=\"165\" y=\"278\" width=\"84\" height=\"30\"/>\n                                                <state key=\"normal\" title=\"Paste Image\"/>\n                                                <connections>\n                                                    <action selector=\"onTapPasteImage:\" destination=\"BYZ-38-t0r\" eventType=\"touchUpInside\" id=\"2sh-M3-cJA\"/>\n                                                </connections>\n                                            </button>\n                                            <switch opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" on=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"wHu-xO-GTh\">\n                                                <rect key=\"frame\" x=\"357\" y=\"86\" width=\"51\" height=\"31\"/>\n                                            </switch>\n                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Run inference on GPU\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"hzO-Vo-upI\">\n                                                <rect key=\"frame\" x=\"8\" y=\"91\" width=\"169\" height=\"21\"/>\n                                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                <nil key=\"textColor\"/>\n                                                <nil key=\"highlightedColor\"/>\n                                            </label>\n                                        </subviews>\n                                        <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\" cocoaTouchSystemColor=\"whiteColor\"/>\n                                        <constraints>\n                                            <constraint firstItem=\"NBJ-fY-j3b\" firstAttribute=\"centerY\" secondItem=\"iKP-H6-uOF\" secondAttribute=\"centerY\" id=\"17j-3o-U0u\"/>\n                                            <constraint firstItem=\"j88-xS-beZ\" firstAttribute=\"top\" secondItem=\"5dC-eb-FHY\" secondAttribute=\"top\" constant=\"8\" id=\"3rz-j2-W5a\"/>\n                                            <constraint firstItem=\"UCi-ia-Mdy\" firstAttribute=\"centerX\" secondItem=\"5dC-eb-FHY\" secondAttribute=\"centerX\" id=\"3yG-5F-OT4\"/>\n                                            <constraint firstItem=\"iKP-H6-uOF\" firstAttribute=\"top\" secondItem=\"bLI-a1-79J\" secondAttribute=\"bottom\" constant=\"16\" id=\"4ft-kB-wpF\"/>\n                                            <constraint firstItem=\"NBJ-fY-j3b\" firstAttribute=\"leading\" relation=\"greaterThanOrEqual\" secondItem=\"x4E-J7-5hQ\" secondAttribute=\"trailing\" constant=\"8\" id=\"7tJ-Pr-A3N\"/>\n                                            <constraint firstAttribute=\"trailing\" secondItem=\"jhc-xp-ef8\" secondAttribute=\"trailing\" constant=\"8\" id=\"9MM-76-aeD\"/>\n                                            <constraint firstItem=\"j88-xS-beZ\" firstAttribute=\"centerX\" secondItem=\"5dC-eb-FHY\" secondAttribute=\"centerX\" id=\"9mf-4R-87Z\"/>\n                                            <constraint firstItem=\"hzO-Vo-upI\" firstAttribute=\"leading\" secondItem=\"5dC-eb-FHY\" secondAttribute=\"leading\" constant=\"8\" id=\"Kom-Cm-TXn\"/>\n                                            <constraint firstItem=\"Tis-0d-4Zb\" firstAttribute=\"leading\" secondItem=\"5dC-eb-FHY\" secondAttribute=\"leading\" constant=\"8\" id=\"Qmt-vM-XIw\"/>\n                                            <constraint firstAttribute=\"trailing\" secondItem=\"bLI-a1-79J\" secondAttribute=\"trailing\" constant=\"8\" id=\"RRr-yj-For\"/>\n                                            <constraint firstAttribute=\"trailingMargin\" secondItem=\"NBJ-fY-j3b\" secondAttribute=\"trailing\" id=\"Snx-RL-uq4\"/>\n                                            <constraint firstItem=\"jhc-xp-ef8\" firstAttribute=\"leading\" relation=\"greaterThanOrEqual\" secondItem=\"Tis-0d-4Zb\" secondAttribute=\"trailing\" priority=\"250\" constant=\"8\" symbolic=\"YES\" id=\"UgQ-5H-XsD\"/>\n                                            <constraint firstItem=\"kaC-2f-KHe\" firstAttribute=\"top\" secondItem=\"wHu-xO-GTh\" secondAttribute=\"bottom\" constant=\"8\" id=\"UtO-HK-C20\"/>\n                                            <constraint firstItem=\"x4E-J7-5hQ\" firstAttribute=\"centerY\" secondItem=\"iKP-H6-uOF\" secondAttribute=\"centerY\" id=\"WOZ-Rz-Bb5\"/>\n                                            <constraint firstItem=\"wHu-xO-GTh\" firstAttribute=\"top\" secondItem=\"jhc-xp-ef8\" secondAttribute=\"bottom\" constant=\"8\" id=\"XkA-wh-qEo\"/>\n                                            <constraint firstItem=\"wHu-xO-GTh\" firstAttribute=\"centerY\" secondItem=\"hzO-Vo-upI\" secondAttribute=\"centerY\" id=\"Xmf-9k-NcE\"/>\n                                            <constraint firstItem=\"iKP-H6-uOF\" firstAttribute=\"leading\" secondItem=\"5dC-eb-FHY\" secondAttribute=\"leadingMargin\" id=\"Y9u-uq-SQt\"/>\n                                            <constraint firstAttribute=\"trailing\" secondItem=\"wHu-xO-GTh\" secondAttribute=\"trailing\" constant=\"8\" id=\"Zra-l2-arf\"/>\n                                            <constraint firstItem=\"UCi-ia-Mdy\" firstAttribute=\"top\" secondItem=\"V5H-FC-fxv\" secondAttribute=\"bottom\" constant=\"8\" id=\"bay-pg-xoI\"/>\n                                            <constraint firstAttribute=\"trailing\" secondItem=\"kaC-2f-KHe\" secondAttribute=\"trailing\" constant=\"8\" id=\"dt2-3d-fEP\"/>\n                                            <constraint firstItem=\"bLI-a1-79J\" firstAttribute=\"top\" secondItem=\"kaC-2f-KHe\" secondAttribute=\"bottom\" constant=\"8\" id=\"gQq-7U-5ZK\"/>\n                                            <constraint firstAttribute=\"bottom\" secondItem=\"UCi-ia-Mdy\" secondAttribute=\"bottom\" constant=\"24\" id=\"hbK-Js-bHQ\"/>\n                                            <constraint firstItem=\"V5H-FC-fxv\" firstAttribute=\"top\" secondItem=\"x4E-J7-5hQ\" secondAttribute=\"bottom\" constant=\"24\" id=\"icX-lM-MTE\"/>\n                                            <constraint firstItem=\"V5H-FC-fxv\" firstAttribute=\"centerX\" secondItem=\"5dC-eb-FHY\" secondAttribute=\"centerX\" id=\"idS-QL-51C\"/>\n                                            <constraint firstItem=\"jhc-xp-ef8\" firstAttribute=\"centerY\" secondItem=\"Tis-0d-4Zb\" secondAttribute=\"centerY\" id=\"kdw-VI-dzG\"/>\n                                            <constraint firstItem=\"kaC-2f-KHe\" firstAttribute=\"leading\" secondItem=\"5dC-eb-FHY\" secondAttribute=\"leading\" constant=\"8\" id=\"lVP-jd-uIL\"/>\n                                            <constraint firstItem=\"jhc-xp-ef8\" firstAttribute=\"top\" secondItem=\"j88-xS-beZ\" secondAttribute=\"bottom\" constant=\"8\" id=\"ryW-jY-8dC\"/>\n                                            <constraint firstItem=\"bLI-a1-79J\" firstAttribute=\"leading\" secondItem=\"5dC-eb-FHY\" secondAttribute=\"leading\" constant=\"8\" id=\"tkW-gF-sJA\"/>\n                                            <constraint firstItem=\"wHu-xO-GTh\" firstAttribute=\"leading\" relation=\"greaterThanOrEqual\" secondItem=\"hzO-Vo-upI\" secondAttribute=\"trailing\" constant=\"8\" id=\"vTk-9q-OPv\"/>\n                                            <constraint firstItem=\"x4E-J7-5hQ\" firstAttribute=\"leading\" secondItem=\"iKP-H6-uOF\" secondAttribute=\"trailing\" constant=\"8\" id=\"viu-LB-Pfm\"/>\n                                        </constraints>\n                                    </view>\n                                </subviews>\n                                <constraints>\n                                    <constraint firstItem=\"5dC-eb-FHY\" firstAttribute=\"top\" secondItem=\"Dy2-B8-THR\" secondAttribute=\"top\" id=\"60V-CR-lmW\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"5dC-eb-FHY\" secondAttribute=\"trailing\" id=\"HEL-dd-aAp\"/>\n                                    <constraint firstItem=\"5dC-eb-FHY\" firstAttribute=\"leading\" secondItem=\"Dy2-B8-THR\" secondAttribute=\"leading\" id=\"IBJ-PS-Hai\"/>\n                                    <constraint firstItem=\"5dC-eb-FHY\" firstAttribute=\"centerX\" secondItem=\"Dy2-B8-THR\" secondAttribute=\"centerX\" id=\"P0L-pO-SKh\"/>\n                                    <constraint firstAttribute=\"bottom\" secondItem=\"5dC-eb-FHY\" secondAttribute=\"bottom\" id=\"waS-Zf-hWh\"/>\n                                </constraints>\n                            </scrollView>\n                        </subviews>\n                        <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\" cocoaTouchSystemColor=\"whiteColor\"/>\n                        <constraints>\n                            <constraint firstItem=\"Yg9-nM-9J0\" firstAttribute=\"leading\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"leading\" id=\"4am-fJ-WFG\"/>\n                            <constraint firstItem=\"Dy2-B8-THR\" firstAttribute=\"leading\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"leading\" id=\"5Uw-2Z-NVX\"/>\n                            <constraint firstItem=\"Dy2-B8-THR\" firstAttribute=\"bottom\" secondItem=\"TqK-8z-fNG\" secondAttribute=\"top\" id=\"DXp-6Z-iaO\"/>\n                            <constraint firstItem=\"8lT-sf-xuk\" firstAttribute=\"trailing\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"trailing\" id=\"EoR-E5-PFR\"/>\n                            <constraint firstItem=\"8lT-sf-xuk\" firstAttribute=\"leading\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"leading\" id=\"IVR-CW-0dc\"/>\n                            <constraint firstItem=\"Yg9-nM-9J0\" firstAttribute=\"centerX\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"centerX\" id=\"LGc-SG-v5K\"/>\n                            <constraint firstItem=\"Yg9-nM-9J0\" firstAttribute=\"top\" secondItem=\"UhJ-jg-kFY\" secondAttribute=\"bottom\" id=\"Qz8-jA-3YQ\"/>\n                            <constraint firstItem=\"Dy2-B8-THR\" firstAttribute=\"trailing\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"trailing\" id=\"VW3-nz-Wcv\"/>\n                            <constraint firstItem=\"Yg9-nM-9J0\" firstAttribute=\"trailing\" secondItem=\"8bC-Xf-vdC\" secondAttribute=\"trailing\" id=\"jid-Zu-De7\"/>\n                            <constraint firstItem=\"Dy2-B8-THR\" firstAttribute=\"top\" secondItem=\"8lT-sf-xuk\" secondAttribute=\"bottom\" id=\"pVj-cm-G2G\"/>\n                            <constraint firstItem=\"8lT-sf-xuk\" firstAttribute=\"top\" secondItem=\"Yg9-nM-9J0\" secondAttribute=\"bottom\" id=\"zzN-vb-ut1\"/>\n                        </constraints>\n                    </view>\n                    <navigationItem key=\"navigationItem\" id=\"iE9-Ll-efq\"/>\n                    <connections>\n                        <outlet property=\"cropSwitch\" destination=\"jhc-xp-ef8\" id=\"ndw-B2-Vj9\"/>\n                        <outlet property=\"imageView\" destination=\"8lT-sf-xuk\" id=\"BtB-ts-BTv\"/>\n                        <outlet property=\"inferenceStatusLabel\" destination=\"kaC-2f-KHe\" id=\"e71-zS-dky\"/>\n                        <outlet property=\"legendLabel\" destination=\"bLI-a1-79J\" id=\"GT7-Zl-dKO\"/>\n                        <outlet property=\"pasteImageButton\" destination=\"UCi-ia-Mdy\" id=\"gFO-iq-xgF\"/>\n                        <outlet property=\"photoCameraButton\" destination=\"qim-4p-Nop\" id=\"y1o-Th-yVd\"/>\n                        <outlet property=\"runButton\" destination=\"V5H-FC-fxv\" id=\"dAe-gi-tet\"/>\n                        <outlet property=\"segmentedControl\" destination=\"j88-xS-beZ\" id=\"lHG-1F-UD5\"/>\n                        <outlet property=\"styleImageView\" destination=\"NBJ-fY-j3b\" id=\"JYl-Nb-jwJ\"/>\n                        <outlet property=\"useGPUSwitch\" destination=\"wHu-xO-GTh\" id=\"7gX-gs-CNo\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"1047.8260869565217\" y=\"109.82142857142857\"/>\n        </scene>\n        <!--Style Picker View Controller-->\n        <scene sceneID=\"zyI-Pe-UcK\">\n            <objects>\n                <collectionViewController storyboardIdentifier=\"StylePickerViewController\" id=\"LmU-vq-Tbb\" customClass=\"StylePickerViewController\" customModule=\"TFL_Style_Transfer\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <collectionView key=\"view\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"scaleToFill\" dataMode=\"prototypes\" id=\"Kw5-Pd-Ndb\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"896\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\" cocoaTouchSystemColor=\"whiteColor\"/>\n                        <collectionViewFlowLayout key=\"collectionViewLayout\" minimumLineSpacing=\"10\" minimumInteritemSpacing=\"10\" sectionInsetReference=\"layoutMargins\" id=\"x68-Py-Dgp\">\n                            <size key=\"itemSize\" width=\"250\" height=\"250\"/>\n                            <size key=\"headerReferenceSize\" width=\"0.0\" height=\"0.0\"/>\n                            <size key=\"footerReferenceSize\" width=\"0.0\" height=\"0.0\"/>\n                            <inset key=\"sectionInset\" minX=\"0.0\" minY=\"0.0\" maxX=\"0.0\" maxY=\"0.0\"/>\n                        </collectionViewFlowLayout>\n                        <cells>\n                            <collectionViewCell opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" reuseIdentifier=\"StylePickerCell\" id=\"FcA-rT-BxQ\" customClass=\"StylePickerCollectionViewCell\" customModule=\"TFL_Style_Transfer\" customModuleProvider=\"target\">\n                                <rect key=\"frame\" x=\"82\" y=\"8\" width=\"250\" height=\"250\"/>\n                                <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMaxY=\"YES\"/>\n                                <collectionViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" insetsLayoutMarginsFromSafeArea=\"NO\" id=\"sAa-ta-7Pc\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"250\" height=\"250\"/>\n                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                    <subviews>\n                                        <imageView clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleAspectFit\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"GR3-pl-Q0P\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"250\" height=\"250\"/>\n                                        </imageView>\n                                    </subviews>\n                                    <constraints>\n                                        <constraint firstAttribute=\"trailing\" secondItem=\"GR3-pl-Q0P\" secondAttribute=\"trailing\" id=\"Eph-hB-BMI\"/>\n                                        <constraint firstItem=\"GR3-pl-Q0P\" firstAttribute=\"top\" secondItem=\"sAa-ta-7Pc\" secondAttribute=\"top\" id=\"XOE-We-o4v\"/>\n                                        <constraint firstItem=\"GR3-pl-Q0P\" firstAttribute=\"leading\" secondItem=\"sAa-ta-7Pc\" secondAttribute=\"leading\" id=\"mDe-H5-2uz\"/>\n                                        <constraint firstAttribute=\"bottom\" secondItem=\"GR3-pl-Q0P\" secondAttribute=\"bottom\" id=\"z9q-Yr-5tg\"/>\n                                    </constraints>\n                                    <userDefinedRuntimeAttributes>\n                                        <userDefinedRuntimeAttribute type=\"number\" keyPath=\"layer.cornerRadius\">\n                                            <integer key=\"value\" value=\"8\"/>\n                                        </userDefinedRuntimeAttribute>\n                                        <userDefinedRuntimeAttribute type=\"boolean\" keyPath=\"layer.masksToBounds\" value=\"YES\"/>\n                                    </userDefinedRuntimeAttributes>\n                                </collectionViewCellContentView>\n                                <connections>\n                                    <outlet property=\"styleImageView\" destination=\"GR3-pl-Q0P\" id=\"KW8-SA-L3b\"/>\n                                </connections>\n                            </collectionViewCell>\n                        </cells>\n                        <connections>\n                            <outlet property=\"dataSource\" destination=\"LmU-vq-Tbb\" id=\"omA-yv-olm\"/>\n                            <outlet property=\"delegate\" destination=\"LmU-vq-Tbb\" id=\"jXx-xE-1GQ\"/>\n                        </connections>\n                    </collectionView>\n                </collectionViewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"fVV-yG-h6y\" userLabel=\"First Responder\" customClass=\"UIResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"1849\" y=\"108\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"photo_camera\" width=\"36\" height=\"36\"/>\n        <image name=\"photo_library\" width=\"36\" height=\"36\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>NSPhotoLibraryUsageDescription</key>\n\t<string>This app allows you to pick photos from your library and apply different art styles to them.</string>\n\t<key>NSCameraUsageDescription</key>\n\t<string>This app allows you to take pictures then apply different art styles to them.</string>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UIRequiresFullScreen</key>\n\t<true/>\n\t<key>UIStatusBarHidden</key>\n\t<true/>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UIUserInterfaceStyle</key>\n\t<string>Light</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/StylePickerViewController.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\nprotocol StylePickerViewControllerDelegate {\n\n  func picker(_: StylePickerViewController, didSelectStyle image: UIImage)\n\n}\n\nclass StylePickerViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {\n\n  var delegate: StylePickerViewControllerDelegate?\n\n  class func fromStoryboard() -> StylePickerViewController {\n    return UIStoryboard(name: \"Main\", bundle: nil)\n        .instantiateViewController(withIdentifier: \"StylePickerViewController\")\n        as! StylePickerViewController\n  }\n\n  private let dataSource = StylePickerDataSource()\n\n  override func viewDidLoad() {\n    super.viewDidLoad()\n    collectionView.dataSource = dataSource\n    collectionView.delegate = self\n  }\n\n  override func collectionView(_ collectionView: UICollectionView,\n                               didSelectItemAt indexPath: IndexPath) {\n    if let image = dataSource.imageForStyle(at: indexPath.item) {\n      collectionView.deselectItem(at: indexPath, animated: true)\n      delegate?.picker(self, didSelectStyle: image)\n      dismiss(animated: true, completion: nil)\n    }\n  }\n\n  func collectionView(_ collectionView: UICollectionView,\n                      layout collectionViewLayout: UICollectionViewLayout,\n                      sizeForItemAt indexPath: IndexPath) -> CGSize {\n    guard let layout = collectionViewLayout as? UICollectionViewFlowLayout else { return .zero }\n    let smallestDimension = collectionView.bounds.width < collectionView.bounds.height ?\n        collectionView.bounds.width : collectionView.bounds.height\n    let extraPadding: CGFloat = 3 // magic number\n    let itemDimension =\n        smallestDimension / 2 - collectionView.contentInset.left - collectionView.contentInset.right\n        - layout.sectionInset.left - layout.sectionInset.right - layout.minimumInteritemSpacing\n        - extraPadding\n    return CGSize(width: itemDimension, height: itemDimension)\n  }\n\n}\n\nclass StylePickerDataSource: NSObject, UICollectionViewDataSource {\n\n  static func defaultStyle() -> UIImage {\n    return UIImage(named: \"style24\")! // use great wave as default\n  }\n\n  lazy private var images: [UIImage] = {\n    var index = 0\n    var images: [UIImage] = []\n    while let image = UIImage(named: \"style\\(index)\") {\n      images.append(image)\n      index += 1\n    }\n    return images\n  }()\n\n  func imageForStyle(at index: Int) -> UIImage? {\n    return images[index]\n  }\n\n  func collectionView(_ collectionView: UICollectionView,\n                      numberOfItemsInSection section: Int) -> Int {\n    return images.count\n  }\n\n  func collectionView(_ collectionView: UICollectionView,\n                      cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {\n    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: \"StylePickerCell\",\n                                                  for: indexPath) as! StylePickerCollectionViewCell\n    let image = imageForStyle(at: indexPath.item)\n    cell.styleImageView.image = image\n    return cell\n  }\n\n}\n\nclass StylePickerCollectionViewCell: UICollectionViewCell {\n\n  @IBOutlet weak var styleImageView: UIImageView!\n\n}\n"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/StyleTransferer.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport TensorFlowLite\nimport UIKit\n\nclass StyleTransferer {\n\n  /// TensorFlow Lite `Interpreter` object for performing inference on a given model.\n  private var predictInterpreter: Interpreter\n  private var transferInterpreter: Interpreter\n\n  /// Dedicated DispatchQueue for TF Lite operations.\n  private let tfLiteQueue: DispatchQueue\n\n  // MARK: - Initialization\n\n  /// Create a Style Transferer instance with a quantized Int8 model that runs inference on the CPU.\n  static func newCPUStyleTransferer(\n    completion: @escaping ((Result<StyleTransferer>) -> Void)\n  ) -> () {\n    return StyleTransferer.newInstance(transferModel: Constants.Int8.transferModel,\n                                       predictModel: Constants.Int8.predictModel,\n                                       useMetalDelegate: false,\n                                       completion: completion)\n  }\n\n  static func newGPUStyleTransferer(\n    completion: @escaping ((Result<StyleTransferer>) -> Void)\n  ) -> () {\n    return StyleTransferer.newInstance(transferModel: Constants.Float16.transferModel,\n                                       predictModel: Constants.Float16.predictModel,\n                                       useMetalDelegate: true,\n                                       completion: completion)\n  }\n\n  /// Create a new Style Transferer instance.\n  static func newInstance(transferModel: String,\n                          predictModel: String,\n                          useMetalDelegate: Bool,\n                          completion: @escaping ((Result<StyleTransferer>) -> Void)) {\n    // Create a dispatch queue to ensure all operations on the Intepreter will run serially.\n    let tfLiteQueue = DispatchQueue(label: \"org.tensorflow.examples.lite.style_transfer\")\n\n    // Run initialization in background thread to avoid UI freeze.\n    tfLiteQueue.async {\n      // Construct the path to the model file.\n      guard\n          let transferModelPath = Bundle.main.path(\n            forResource: transferModel,\n            ofType: Constants.modelFileExtension\n          ),\n          let predictModelPath = Bundle.main.path(\n            forResource: predictModel,\n            ofType: Constants.modelFileExtension\n          )\n      else {\n        completion(.error(InitializationError.invalidModel(\n          \"One of the following models could not be loaded: \\(transferModel), \\(predictModel)\"\n        )))\n        return\n      }\n\n      // Specify the delegate for the TF Lite `Interpreter`.\n      let createDelegates: () -> [Delegate]? = {\n        if useMetalDelegate {\n          return [MetalDelegate()]\n        }\n        return nil\n      }\n      let createOptions: () -> Interpreter.Options? = {\n        if useMetalDelegate {\n          return nil\n        }\n        var options = Interpreter.Options()\n        options.threadCount = ProcessInfo.processInfo.processorCount >= 2 ? 2 : 1\n        return options\n      }\n\n      do {\n        // Create the `Interpreter`s.\n        let predictInterpreter = try Interpreter(\n          modelPath: predictModelPath,\n          options: createOptions(),\n          delegates: createDelegates()\n        )\n        let transferInterpreter = try Interpreter(\n          modelPath: transferModelPath,\n          options: createOptions(),\n          delegates: createDelegates()\n        )\n\n        // Allocate memory for the model's input `Tensor`s.\n        try predictInterpreter.allocateTensors()\n        try transferInterpreter.allocateTensors()\n\n        // Create an StyleTransferer instance and return.\n        let styleTransferer = StyleTransferer(\n          tfLiteQueue: tfLiteQueue,\n          predictInterpreter: predictInterpreter,\n          transferInterpreter: transferInterpreter\n        )\n        DispatchQueue.main.async {\n          completion(.success(styleTransferer))\n        }\n      } catch let error {\n        print(\"Failed to create the interpreter with error: \\(error.localizedDescription)\")\n        DispatchQueue.main.async {\n          completion(.error(InitializationError.internalError(error)))\n        }\n        return\n      }\n    }\n  }\n\n  /// Initialize Style Transferer instance.\n  fileprivate init(\n    tfLiteQueue: DispatchQueue,\n    predictInterpreter: Interpreter,\n    transferInterpreter: Interpreter\n  ) {\n    // Store TF Lite intepreter\n    self.predictInterpreter = predictInterpreter\n    self.transferInterpreter = transferInterpreter\n\n    // Store the dedicated DispatchQueue for TFLite.\n    self.tfLiteQueue = tfLiteQueue\n  }\n\n  // MARK: - Style Transfer\n\n  /// Run style transfer on a given image.\n  /// - Parameters\n  ///   - styleImage: the image to use as a style reference.\n  ///   - image: the target image.\n  ///   - completion: the callback to receive the style transfer result.\n  func runStyleTransfer(style styleImage: UIImage,\n                        image: UIImage,\n                        completion: @escaping ((Result<StyleTransferResult>) -> Void)) {\n    tfLiteQueue.async {\n      let outputTensor: Tensor\n      let startTime: Date = Date()\n      var preprocessingTime: TimeInterval = 0\n      var stylePredictTime: TimeInterval = 0\n      var styleTransferTime: TimeInterval = 0\n      var postprocessingTime: TimeInterval = 0\n\n      func timeSinceStart() -> TimeInterval {\n        return abs(startTime.timeIntervalSinceNow)\n      }\n\n      do {\n        // Preprocess style image.\n        guard\n          let styleRGBData = styleImage.scaledData(\n            with: Constants.styleImageSize,\n            isQuantized: false\n          )\n        else {\n          DispatchQueue.main.async {\n            completion(.error(StyleTransferError.invalidImage))\n          }\n          print(\"Failed to convert the style image buffer to RGB data.\")\n          return\n        }\n\n        guard\n          let inputRGBData = image.scaledData(\n            with: Constants.inputImageSize,\n            isQuantized: false\n          )\n        else {\n          DispatchQueue.main.async {\n            completion(.error(StyleTransferError.invalidImage))\n          }\n          print(\"Failed to convert the input image buffer to RGB data.\")\n          return\n        }\n\n        preprocessingTime = timeSinceStart()\n\n        // Copy the RGB data to the input `Tensor`.\n        try self.predictInterpreter.copy(styleRGBData, toInputAt: 0)\n\n        // Run inference by invoking the `Interpreter`.\n        try self.predictInterpreter.invoke()\n\n        // Get the output `Tensor` to process the inference results.\n        let predictResultTensor = try self.predictInterpreter.output(at: 0)\n\n        // Grab bottleneck data from output tensor.\n        let bottleneck = predictResultTensor.data\n\n        stylePredictTime = timeSinceStart() - preprocessingTime\n\n        // Copy the RGB and bottleneck data to the input `Tensor`.\n        try self.transferInterpreter.copy(inputRGBData, toInputAt: 0)\n        try self.transferInterpreter.copy(bottleneck, toInputAt: 1)\n\n        // Run inference by invoking the `Interpreter`.\n        try self.transferInterpreter.invoke()\n\n        // Get the result tensor\n        outputTensor = try self.transferInterpreter.output(at: 0)\n\n        styleTransferTime = timeSinceStart() - stylePredictTime - preprocessingTime\n\n      } catch let error {\n        print(\"Failed to invoke the interpreter with error: \\(error.localizedDescription)\")\n        DispatchQueue.main.async {\n          completion(.error(StyleTransferError.internalError(error)))\n        }\n        return\n      }\n\n      // Construct image from output tensor data\n      guard let cgImage = self.postprocessImageData(data: outputTensor.data) else {\n        DispatchQueue.main.async {\n          completion(.error(StyleTransferError.resultVisualizationError))\n        }\n        return\n      }\n\n      let outputImage = UIImage(cgImage: cgImage)\n\n      postprocessingTime =\n          timeSinceStart() - stylePredictTime - styleTransferTime - preprocessingTime\n\n      // Return the result.\n      DispatchQueue.main.async {\n        completion(\n          .success(\n            StyleTransferResult(\n              resultImage: outputImage,\n              preprocessingTime: preprocessingTime,\n              stylePredictTime: stylePredictTime,\n              styleTransferTime: styleTransferTime,\n              postprocessingTime: postprocessingTime\n            )\n          )\n        )\n      }\n    }\n  }\n\n    // MARK: - Utils\n\n  /// Turns TF model's float32 array output into one supported by `CGImage`. This method\n  /// assumes the provided data is the same format as the data returned from the output\n  /// tensor in `runStyleTransfer`, so it should not be used for general image processing.\n  /// - Parameter data: The image data to turn into a `CGImage`. This data must be a buffer of\n  ///   `Float32` values between 0 and 1 in RGB format.\n  /// - Parameter size: The expected size of the output image.\n  private func postprocessImageData(data: Data,\n                                    size: CGSize = Constants.inputImageSize) -> CGImage? {\n    let width = Int(size.width)\n    let height = Int(size.height)\n\n    let floats = data.toArray(type: Float32.self)\n\n    let bufferCapacity = width * height * 4\n    let unsafePointer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferCapacity)\n    let unsafeBuffer = UnsafeMutableBufferPointer<UInt8>(start: unsafePointer,\n                                                         count: bufferCapacity)\n    defer {\n      unsafePointer.deallocate()\n    }\n\n    for x in 0 ..< width {\n      for y in 0 ..< height {\n        let floatIndex = (y * width + x) * 3\n        let index = (y * width + x) * 4\n        let red = UInt8(floats[floatIndex] * 255)\n        let green = UInt8(floats[floatIndex + 1] * 255)\n        let blue = UInt8(floats[floatIndex + 2] * 255)\n\n        unsafeBuffer[index] = red\n        unsafeBuffer[index + 1] = green\n        unsafeBuffer[index + 2] = blue\n        unsafeBuffer[index + 3] = 0\n      }\n    }\n\n    let outData = Data(buffer: unsafeBuffer)\n\n    // Construct image from output tensor data\n    let alphaInfo = CGImageAlphaInfo.noneSkipLast\n    let bitmapInfo = CGBitmapInfo(rawValue: alphaInfo.rawValue)\n        .union(.byteOrder32Big)\n    let colorSpace = CGColorSpaceCreateDeviceRGB()\n    guard\n      let imageDataProvider = CGDataProvider(data: outData as CFData),\n      let cgImage = CGImage(\n        width: width,\n        height: height,\n        bitsPerComponent: 8,\n        bitsPerPixel: 32,\n        bytesPerRow: MemoryLayout<UInt8>.size * 4 * Int(Constants.inputImageSize.width),\n        space: colorSpace,\n        bitmapInfo: bitmapInfo,\n        provider: imageDataProvider,\n        decode: nil,\n        shouldInterpolate: false,\n        intent: .defaultIntent\n      )\n      else {\n        return nil\n    }\n    return cgImage\n  }\n\n}\n\n// MARK: - Types\n\n/// Representation of the style transfer result.\nstruct StyleTransferResult {\n\n  /// The resulting image from the style transfer.\n  let resultImage: UIImage\n\n  /// Time required to resize the input and style images and convert the image\n  /// data to a format the model can accept.\n  let preprocessingTime: TimeInterval\n\n  /// The style prediction model run time.\n  let stylePredictTime: TimeInterval\n\n  /// The style transfer model run time.\n  let styleTransferTime: TimeInterval\n\n  /// Time required to convert the model output data to a `CGImage`.\n  let postprocessingTime: TimeInterval\n\n}\n\n/// Convenient enum to return result with a callback\nenum Result<T> {\n  case success(T)\n  case error(Error)\n}\n\n/// Define errors that could happen in the initialization of this class\nenum InitializationError: Error {\n  // Invalid TF Lite model\n  case invalidModel(String)\n\n  // Invalid label list\n  case invalidLabelList(String)\n\n  // TF Lite Internal Error when initializing\n  case internalError(Error)\n}\n\n/// Define errors that could happen when running style transfer\nenum StyleTransferError: Error {\n  // Invalid input image\n  case invalidImage\n\n  // TF Lite Internal Error when initializing\n  case internalError(Error)\n\n  // Invalid input image\n  case resultVisualizationError\n}\n\n// MARK: - Constants\nprivate enum Constants {\n\n  // Namespace for quantized Int8 models.\n  enum Int8 {\n\n    static let predictModel = \"style_predict_quantized_256\"\n\n    static let transferModel = \"style_transfer_quantized_384\"\n\n  }\n\n  // Namespace for Float16 models, optimized for GPU inference.\n  enum Float16 {\n\n    static let predictModel = \"style_predict_f16_256\"\n\n    static let transferModel = \"style_transfer_f16_384\"\n\n  }\n\n  static let modelFileExtension = \"tflite\"\n\n  static let styleImageSize = CGSize(width: 256, height: 256)\n\n  static let inputImageSize = CGSize(width: 384, height: 384)\n\n}\n"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/TFLiteExtension.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport CoreGraphics\nimport Foundation\nimport UIKit\n\n// MARK: - UIImage\n\n/// Extension of iOS classes that is useful for working with TensorFlow Lite computer vision models.\nextension UIImage {\n\n  /// Creates and returns a new image scaled to the given size. The image preserves its original PNG\n  /// or JPEG bitmap info.\n  ///\n  /// - Parameter size: The size to scale the image to.\n  /// - Returns: The scaled image or `nil` if image could not be resized.\n  func scaledImage(with size: CGSize) -> UIImage? {\n    UIGraphicsBeginImageContextWithOptions(size, false, scale)\n    defer { UIGraphicsEndImageContext() }\n    draw(in: CGRect(origin: .zero, size: size))\n    return UIGraphicsGetImageFromCurrentImageContext()?.data.flatMap(UIImage.init)\n  }\n\n  /// Returns the data representation of the image after scaling to the given `size` and removing\n  /// the alpha component. This function assumes a batch size of one and three channels per image.\n  /// Changing these parameters in the TF Lite model will cause conflicts.\n  ///\n  /// - Parameters\n  ///   - size: Size to scale the image to (i.e. image size used while training the model).\n  ///   - isQuantized: Whether the model is quantized (i.e. fixed point values rather than floating\n  ///       point values).\n  /// - Returns: The scaled image as data or `nil` if the image could not be scaled.\n  func scaledData(with size: CGSize, isQuantized: Bool) -> Data? {\n    guard let cgImage = self.cgImage else { return nil }\n    return UIImage.normalizedData(from: cgImage, resizingTo: size, quantum: Float32.self)\n  }\n\n  /// Make the same image with orientation being `.up`.\n  /// - Returns:  A copy of the image with .up orientation or `nil` if the image could not be\n  /// rotated.\n  func transformOrientationToUp() -> UIImage? {\n    // Check if the image orientation is already .up and don't need any rotation.\n    guard imageOrientation != UIImage.Orientation.up else {\n      // No rotation needed so return a copy of this image.\n      return self.copy() as? UIImage\n    }\n\n    // Make sure that this image has an CGImage attached.\n    guard let cgImage = self.cgImage else { return nil }\n\n    // Create a CGContext to draw the rotated image to.\n    guard let colorSpace = cgImage.colorSpace,\n      let context = CGContext(\n        data: nil,\n        width: Int(size.width),\n        height: Int(size.height),\n        bitsPerComponent: cgImage.bitsPerComponent,\n        bytesPerRow: 0,\n        space: colorSpace,\n        bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue\n      )\n    else { return nil }\n\n    var transform: CGAffineTransform = CGAffineTransform.identity\n\n    // Calculate the transformation matrix that needed to bring the image orientation to .up\n    switch imageOrientation {\n    case .down, .downMirrored:\n      transform = transform.translatedBy(x: size.width, y: size.height)\n      transform = transform.rotated(by: CGFloat.pi)\n      break\n    case .left, .leftMirrored:\n      transform = transform.translatedBy(x: size.width, y: 0)\n      transform = transform.rotated(by: CGFloat.pi / 2.0)\n      break\n    case .right, .rightMirrored:\n      transform = transform.translatedBy(x: 0, y: size.height)\n      transform = transform.rotated(by: CGFloat.pi / -2.0)\n      break\n    case .up, .upMirrored:\n      break\n    @unknown default:\n      break\n    }\n\n    // If the image is mirrored then flip it.\n    switch imageOrientation {\n    case .upMirrored, .downMirrored:\n      transform = transform.translatedBy(x: size.width, y: 0)\n      transform = transform.scaledBy(x: -1, y: 1)\n      break\n    case .leftMirrored, .rightMirrored:\n      transform = transform.translatedBy(x: size.height, y: 0)\n      transform = transform.scaledBy(x: -1, y: 1)\n    case .up, .down, .left, .right:\n      break\n    @unknown default:\n      break\n    }\n\n    // Apply transformation matrix to the CGContext.\n    context.concatenate(transform)\n\n    switch imageOrientation {\n    case .left, .leftMirrored, .right, .rightMirrored:\n      context.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.height, height: size.width))\n    default:\n      context.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))\n      break\n    }\n\n    // Create a CGImage from the context.\n    guard let newCGImage = context.makeImage() else { return nil }\n\n    // Convert it to UIImage.\n    return UIImage.init(cgImage: newCGImage, scale: 1, orientation: .up)\n  }\n\n  // MARK: - Private\n\n  /// The PNG or JPEG data representation of the image or `nil` if the conversion failed.\n  private var data: Data? {\n    return self.pngData() ?? self.jpegData(compressionQuality: Constant.jpegCompressionQuality)\n  }\n\n  static func normalizeImage(_ image: CGImage, resizingTo size: CGSize) -> CGImage? {\n    // The TF Lite model expects images in the RGB color space.\n    // Device-specific RGB color spaces should have the same number of colors as the standard\n    // RGB color space so we probably don't have to redraw them.\n    let colorSpace = CGColorSpaceCreateDeviceRGB()\n    let cgImageSize = CGSize(width: image.width, height: image.height)\n    if cgImageSize == size,\n      (image.colorSpace?.name == colorSpace.name || image.colorSpace?.name == CGColorSpace.sRGB) {\n      // Return the image if it is in the right format\n      // to save a redraw operation.\n      return image\n    }\n    let bitmapInfo = CGBitmapInfo(\n      rawValue: CGBitmapInfo.byteOrder32Big.rawValue | CGImageAlphaInfo.premultipliedLast.rawValue\n    )\n    let width = Int(size.width)\n    let scaledBytesPerRow = (image.bytesPerRow / image.width) * width\n    guard\n      let context = CGContext(\n        data: nil,\n        width: width,\n        height: Int(size.height),\n        bitsPerComponent: image.bitsPerComponent,\n        bytesPerRow: scaledBytesPerRow,\n        space: colorSpace,\n        bitmapInfo: bitmapInfo.rawValue)\n    else {\n      return nil\n    }\n    context.draw(image, in: CGRect(origin: .zero, size: size))\n    return context.makeImage()\n  }\n\n  static func normalizedData<T>(from image: CGImage,\n                                resizingTo size: CGSize,\n                                quantum: T.Type) -> Data? where T: FloatingPoint {\n    guard let normalizedImage = normalizeImage(image, resizingTo: size) else {\n      return nil\n    }\n    guard let data = normalizedImage.dataProvider?.data as Data? else { return nil }\n    // TF Lite expects an array of pixels in the form of floats normalized between 0 and 1.\n    var floatArray: [T]\n\n    // A full list of pixel formats is listed in this document under Table 2-1:\n    // https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-CJBHBFFE\n    // This code only handles pixel formats supported on iOS in the RGB color space.\n    // If you're targeting macOS or macOS via Catalyst, you should support the macOS\n    // pixel formats as well.\n    switch normalizedImage.bitsPerPixel {\n\n    // 16-bit pixel with no alpha channel. On iOS, this must have 5 bits per channel and\n    // no alpha channel. The most significant bits are skipped.\n    case 16:\n      guard normalizedImage.bitsPerComponent == 5 else { return nil }\n      guard normalizedImage.alphaInfo.rawValue & CGImageAlphaInfo.noneSkipFirst.rawValue != 0 else {\n        return nil\n      }\n\n      // If this bool is false, assume little endian byte order.\n      let bigEndian: Bool = {\n        // Sometimes images have both littleEndian and bigEndian flags set. In this case, use the\n        // non-default endianness because it seems to work best in empirical testing.\n        let hasLittleEndian = normalizedImage.bitmapInfo.contains(.byteOrder16Little)\n        let hasBigEndian = normalizedImage.bitmapInfo.contains(.byteOrder16Big)\n        if !(hasLittleEndian && hasBigEndian) {\n          return hasBigEndian\n        }\n        let currentByteOrder = CFByteOrderGetCurrent()\n        switch currentByteOrder {\n        case Int(CFByteOrderLittleEndian.rawValue):\n          return true\n        case Int(CFByteOrderBigEndian.rawValue):\n          return false\n        case _:\n          // For unknown endianness, assume little endian since it's how most\n          // Apple platforms are laid out nowadays.\n          return false\n        }\n      }()\n\n      let initializer: (inout UnsafeMutableBufferPointer<T>, inout Int) -> () =\n      { bufferPointer, initializedCount in\n        let redMask: UInt16   = UInt16(0b0111110000000000)\n        let greenMask: UInt16 = UInt16(0b0000001111100000)\n        let blueMask: UInt16  = UInt16(0b0000000000011111)\n\n        for byteIndex in stride(from: 0, to: data.count, by: 2) {\n          // pixels are two bytes wide\n          let pixelRange = byteIndex ..< byteIndex + 2\n          let pixelData = data[pixelRange]\n          let rawPixel = pixelData.withUnsafeBytes { $0.load(as: UInt16.self) }\n          let pixel: UInt16\n          if bigEndian {\n            pixel = rawPixel.bigEndian\n          } else {\n            pixel = rawPixel.littleEndian\n          }\n          let redChannel   = ((pixel & redMask) &>> 10)\n          let greenChannel = ((pixel & greenMask) &>> 5)\n          let blueChannel  = ((pixel & blueMask) &>> 0)\n\n          let maximumChannelValue = T(31) // 2 ^ 5 - 1\n          let red   = T(redChannel) / maximumChannelValue\n          let green = T(greenChannel) / maximumChannelValue\n          let blue  = T(blueChannel) / maximumChannelValue\n\n          let pixelIndex = byteIndex / 2\n          let floatIndex = pixelIndex * 3\n          bufferPointer[floatIndex] = red\n          bufferPointer[floatIndex + 1] = green\n          bufferPointer[floatIndex + 2] = blue\n        }\n\n        initializedCount = data.count / 2 * 3\n      }\n      floatArray = [T](unsafeUninitializedCapacity: data.count / 2 * 3,\n                       initializingWith: initializer)\n\n    // We discard the image's alpha channel before running the TF Lite model, so we can treat\n    // alpha and non-alpha images identically.\n    case 32:\n      guard normalizedImage.bitsPerComponent == 8 else { return nil }\n      let alphaFirst =\n          normalizedImage.alphaInfo == CGImageAlphaInfo.noneSkipFirst ||\n          normalizedImage.alphaInfo == CGImageAlphaInfo.premultipliedFirst\n      let alphaLast =\n          normalizedImage.alphaInfo == CGImageAlphaInfo.noneSkipLast ||\n          normalizedImage.alphaInfo == CGImageAlphaInfo.premultipliedLast\n      let bigEndian = normalizedImage.bitmapInfo.contains(.byteOrder32Big)\n      let littleEndian = normalizedImage.bitmapInfo.contains(.byteOrder32Little)\n      guard alphaFirst || alphaLast else { return nil }\n      guard bigEndian || littleEndian else { return nil }\n\n      // Iterate over channels individually. Since the order of the channels in memory\n      // may vary, we cannot add channels to the float buffer we pass to TF Lite in the\n      // order that they are iterated over.\n      let initializer: (inout UnsafeMutableBufferPointer<T>, inout Int) -> () =\n      { bufferPointer, initializedCount in\n        let numberOfChannels = 4\n        let alphaOffset: UInt8 = {\n          if bigEndian {\n            return alphaFirst ? 0 : 3\n          } else {\n            return alphaFirst ? 3 : 0\n          }\n        }()\n        let redOffset: UInt8 = {\n          if bigEndian {\n            return alphaFirst ? 1 : 0\n          } else {\n            return alphaFirst ? 2 : 3\n          }\n        }()\n        let greenOffset: UInt8 = {\n          if bigEndian {\n            return alphaFirst ? 2 : 1\n          } else {\n            return alphaFirst ? 1 : 2\n          }\n        }()\n        let blueOffset: UInt8 = {\n          if bigEndian {\n            return alphaFirst ? 3 : 2\n          } else {\n            return alphaFirst ? 0 : 1\n          }\n        }()\n\n        // Make sure we add the pixel components to the float array in the right\n        // order regardless of pixel endianness.\n        var rgbHolder: (red: T?, green: T?, blue: T?) = (nil, nil, nil)\n        var floatIndex = 0\n\n        func flushRGBs(_ rgbs: (red: T?, green: T?, blue: T?),\n                       to array: inout UnsafeMutableBufferPointer<T>,\n                       at index: Int) {\n          guard let red = rgbs.red, let green = rgbs.green, let blue = rgbs.blue else { return }\n          array[index] = red\n          array[index + 1] = green\n          array[index + 2] = blue\n          floatIndex += 3\n        }\n\n        let maximumChannelValue: T = 255 // 2 ^ 8 - 1\n\n        func normalizeChannel(_ channel: UInt8) -> T {\n          return T(\n            bigEndian ? channel.bigEndian : channel.littleEndian\n          ) / maximumChannelValue\n        }\n\n        for component in data.enumerated() {\n          switch UInt8(component.offset % numberOfChannels) {\n          case alphaOffset:\n            // Ignore alpha channels\n            break // Breaks from the switch, not the loop\n\n          case redOffset:\n            rgbHolder.red = normalizeChannel(component.element)\n          case greenOffset:\n            rgbHolder.green = normalizeChannel(component.element)\n          case blueOffset:\n            rgbHolder.blue = normalizeChannel(component.element)\n\n          case _:\n            fatalError(\"Unhandled offset: \\(component.offset)\")\n          }\n\n          // After every 4th channel (one full pixel), write the RGBs to\n          // the float buffer in the correct order.\n          if component.offset % 4 == 3 {\n            flushRGBs(rgbHolder, to: &bufferPointer, at: floatIndex)\n            rgbHolder.red = nil; rgbHolder.green = nil; rgbHolder.blue = nil\n          }\n        }\n\n        initializedCount = floatIndex\n      }\n\n      floatArray = [T](unsafeUninitializedCapacity: data.count / 4 * 3,\n                       initializingWith: initializer)\n\n    case _:\n      print(\"Unsupported format from image: \\(normalizedImage)\")\n      return nil\n    }\n\n    return Data(copyingBufferOf: floatArray)\n  }\n\n}\n\n// MARK: - Data\n\nextension Data {\n  /// Creates a new buffer by copying the buffer pointer of the given array.\n  ///\n  /// - Warning: The given array's element type `T` must be trivial in that it can be copied bit\n  ///     for bit with no indirection or reference-counting operations; otherwise, reinterpreting\n  ///     data from the resulting buffer has undefined behavior.\n  /// - Parameter array: An array with elements of type `T`.\n  init<T>(copyingBufferOf array: [T]) {\n    self = array.withUnsafeBufferPointer(Data.init)\n  }\n\n  /// Convert a Data instance to Array representation.\n  func toArray<T>(type: T.Type) -> [T] where T: AdditiveArithmetic {\n    var array = [T](repeating: T.zero, count: self.count/MemoryLayout<T>.stride)\n    _ = array.withUnsafeMutableBytes { copyBytes(to: $0) }\n    return array\n  }\n}\n\n// MARK: - Constants\n\nprivate enum Constant {\n  static let jpegCompressionQuality: CGFloat = 0.8\n}\n"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/UIKitExtension.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n/// Helper functions for the UIImage class that is useful for this sample app.\nextension UIImage {\n\n  /// Helper function to center-crop image.\n  /// - Returns: Center-cropped copy of this image\n  func cropCenter() -> UIImage? {\n    // Don't do anything if the image is already square.\n    guard size.height != size.width else {\n      return self\n    }\n    let isPortrait = size.height > size.width\n    let smallestDimension = min(size.width, size.height)\n    let croppedSize = CGSize(width: smallestDimension, height: smallestDimension)\n    let croppedRect = CGRect(origin: .zero, size: croppedSize)\n\n    UIGraphicsBeginImageContextWithOptions(croppedSize, false, scale)\n    let croppingOrigin = CGPoint(\n      x: isPortrait ? 0 : floor((size.width - size.height) / 2),\n      y: isPortrait ? floor((size.height - size.width) / 2) : 0\n    )\n    guard let cgImage = cgImage?.cropping(to: CGRect(origin: croppingOrigin, size: croppedSize))\n    else { return nil }\n    UIImage(cgImage: cgImage).draw(in: croppedRect)\n    let croppedImage = UIGraphicsGetImageFromCurrentImageContext()\n    UIGraphicsEndImageContext()\n\n    return croppedImage\n  }\n\n}\n"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer/ViewController.swift",
    "content": "// Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\nimport os\n\nclass ViewController: UIViewController {\n\n  /// Image picker for accessing the photo library or camera.\n  private var imagePicker = UIImagePickerController()\n\n  /// Style transferer instance reponsible for running the TF model. Uses a Int8-based model and\n  /// runs inference on the CPU.\n  private var cpuStyleTransferer: StyleTransferer?\n\n  /// Style transferer instance reponsible for running the TF model. Uses a Float16-based model and\n  /// runs inference on the GPU.\n  private var gpuStyleTransferer: StyleTransferer?\n\n  /// Target image to transfer a style onto.\n  private var targetImage: UIImage?\n\n  /// Style-representative image applied to the input image to create a pastiche.\n  private var styleImage: UIImage?\n\n  /// Style transfer result.\n  private var styleTransferResult: StyleTransferResult?\n\n  // UI elements\n  @IBOutlet weak var imageView: UIImageView!\n  @IBOutlet weak var photoCameraButton: UIButton!\n  @IBOutlet weak var segmentedControl: UISegmentedControl!\n  @IBOutlet weak var cropSwitch: UISwitch!\n  @IBOutlet weak var useGPUSwitch: UISwitch!\n  @IBOutlet weak var inferenceStatusLabel: UILabel!\n  @IBOutlet weak var legendLabel: UILabel!\n  @IBOutlet weak var styleImageView: UIImageView!\n  @IBOutlet weak var runButton: UIButton!\n  @IBOutlet weak var pasteImageButton: UIButton!\n\n  override func viewDidLoad() {\n    super.viewDidLoad()\n\n    imageView.contentMode = .scaleAspectFill\n\n    // Setup image picker.\n    imagePicker.delegate = self\n    imagePicker.sourceType = .photoLibrary\n\n    // Set default style image.\n    styleImage = StylePickerDataSource.defaultStyle()\n    styleImageView.image = styleImage\n\n    // Enable camera option only if current device has camera.\n    let isCameraAvailable = UIImagePickerController.isCameraDeviceAvailable(.front)\n      || UIImagePickerController.isCameraDeviceAvailable(.rear)\n    if isCameraAvailable {\n      photoCameraButton.isEnabled = true\n    }\n\n    // MetalDelegate is not available on iOS Simulator in Xcode versions below 11.\n    // If you're not able to run GPU-based inference in iOS simulator, please check\n    // your Xcode version.\n    useGPUSwitch.isOn = true\n\n    // Initialize new style transferer instances.\n    StyleTransferer.newCPUStyleTransferer { result in\n      switch result {\n      case .success(let transferer):\n        self.cpuStyleTransferer = transferer\n      case .error(let wrappedError):\n        print(\"Failed to initialize: \\(wrappedError)\")\n      }\n    }\n    StyleTransferer.newGPUStyleTransferer { result in\n      switch result {\n      case .success(let transferer):\n        self.gpuStyleTransferer = transferer\n      case .error(let wrappedError):\n        print(\"Failed to initialize: \\(wrappedError)\")\n      }\n    }\n  }\n\n  override func viewDidAppear(_ animated: Bool) {\n    super.viewDidAppear(animated)\n    // Observe foregrounding events for pasteboard access.\n    addForegroundEventHandler()\n    pasteImageButton.isEnabled = imageFromPasteboard() != nil\n  }\n\n  override func viewDidDisappear(_ animated: Bool) {\n    super.viewDidDisappear(animated)\n    NotificationCenter.default.removeObserver(self)\n  }\n\n  @IBAction func onTapPasteImage(_ sender: Any) {\n    guard let image = imageFromPasteboard() else { return }\n    let actionSheet = imageRoleSelectionAlert(image: image)\n    present(actionSheet, animated: true, completion: nil)\n  }\n\n  @IBAction func onTapRunButton(_ sender: Any) {\n    // Make sure that the cached target image is available.\n    guard targetImage != nil else {\n      self.inferenceStatusLabel.text = \"Error: Input image is nil.\"\n      return\n    }\n\n    runStyleTransfer(targetImage!)\n  }\n\n  @IBAction func onTapChangeStyleButton(_ sender: Any) {\n    let pickerController = StylePickerViewController.fromStoryboard()\n    pickerController.delegate = self\n    present(pickerController, animated: true, completion: nil)\n  }\n\n  /// Open camera to allow user taking photo.\n  @IBAction func onTapOpenCamera(_ sender: Any) {\n    guard\n      UIImagePickerController.isCameraDeviceAvailable(.front)\n        || UIImagePickerController.isCameraDeviceAvailable(.rear)\n    else {\n      return\n    }\n\n    imagePicker.sourceType = .camera\n    present(imagePicker, animated: true)\n  }\n\n  /// Open photo library for user to choose an image from.\n  @IBAction func onTapPhotoLibrary(_ sender: Any) {\n    imagePicker.sourceType = .photoLibrary\n    present(imagePicker, animated: true)\n  }\n\n  /// Handle tapping on different display mode: Input, Style, Result\n  @IBAction func onSegmentChanged(_ sender: Any) {\n    switch segmentedControl.selectedSegmentIndex {\n    case 0:\n      // Mode 0: Show input image\n      imageView.image = targetImage\n    case 1:\n      // Mode 1: Show style image\n      imageView.image = styleImage\n    case 2:\n      // Mode 2: Show style transfer result.\n      imageView.image = styleTransferResult?.resultImage\n    default:\n      break\n    }\n  }\n\n  /// Handle changing center crop setting.\n  @IBAction func onCropSwitchValueChanged(_ sender: Any) {\n    // Make sure that the cached target image is available.\n    guard targetImage != nil else {\n      self.inferenceStatusLabel.text = \"Error: Input image is nil.\"\n      return\n    }\n\n    // Re-run style transfer upon center-crop setting changed.\n    runStyleTransfer(targetImage!)\n  }\n}\n\n// MARK: - Style Transfer\n\nextension ViewController {\n  /// Run style transfer on the given image, and show result on screen.\n  ///  - Parameter image: The target image for style transfer.\n  func runStyleTransfer(_ image: UIImage) {\n    clearResults()\n\n    let shouldUseQuantizedFloat16 = useGPUSwitch.isOn\n    let transferer = shouldUseQuantizedFloat16 ? gpuStyleTransferer : cpuStyleTransferer\n\n    // Make sure that the style transferer is initialized.\n    guard let styleTransferer = transferer else {\n      inferenceStatusLabel.text = \"ERROR: Interpreter is not ready.\"\n      return\n    }\n\n    guard let targetImage = self.targetImage else {\n      inferenceStatusLabel.text = \"ERROR: Select a target image.\"\n      return\n    }\n\n    // Center-crop the target image if the user has enabled the option.\n    let willCenterCrop = cropSwitch.isOn\n    let image = willCenterCrop ? targetImage.cropCenter() : targetImage\n\n    // Cache the potentially cropped image.\n    self.targetImage = image\n\n    // Show the potentially cropped image on screen.\n    imageView.image = image\n\n    // Make sure that the image is ready before running style transfer.\n    guard image != nil else {\n      inferenceStatusLabel.text = \"ERROR: Image could not be cropped.\"\n      return\n    }\n\n    guard let styleImage = styleImage else {\n      inferenceStatusLabel.text = \"ERROR: Select a style image.\"\n      return\n    }\n\n    // Lock the crop switch and run buttons while style transfer is running.\n    cropSwitch.isEnabled = false\n    runButton.isEnabled = false\n\n    // Run style transfer.\n    styleTransferer.runStyleTransfer(\n      style: styleImage,\n      image: image!,\n      completion: { result in\n        // Show the result on screen\n        switch result {\n        case let .success(styleTransferResult):\n          self.styleTransferResult = styleTransferResult\n\n          // Change to show style transfer result\n          self.segmentedControl.selectedSegmentIndex = 2\n          self.onSegmentChanged(self)\n\n          // Show result metadata\n          self.showInferenceTime(styleTransferResult)\n        case let .error(error):\n          self.inferenceStatusLabel.text = error.localizedDescription\n        }\n\n        // Regardless of the result, re-enable switching between different display modes\n        self.segmentedControl.isEnabled = true\n        self.cropSwitch.isEnabled = true\n        self.runButton.isEnabled = true\n      })\n  }\n\n  /// Clear result from previous run to prepare for new style transfer.\n  private func clearResults() {\n    inferenceStatusLabel.text = \"Running inference with TensorFlow Lite...\"\n    legendLabel.text = nil\n    segmentedControl.isEnabled = false\n    segmentedControl.selectedSegmentIndex = 0\n  }\n\n  /// Show processing time on screen.\n  private func showInferenceTime(_ result: StyleTransferResult) {\n    let timeString = \"Preprocessing: \\(Int(result.preprocessingTime * 1000))ms.\\n\"\n      + \"Style prediction: \\(Int(result.stylePredictTime * 1000))ms.\\n\"\n      + \"Style transfer: \\(Int(result.styleTransferTime * 1000))ms.\\n\"\n      + \"Post-processing: \\(Int(result.postprocessingTime * 1000))ms.\\n\"\n\n    inferenceStatusLabel.text = timeString\n  }\n\n}\n\n// MARK: - UIImagePickerControllerDelegate\n\nextension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {\n\n  func imagePickerController(\n    _ picker: UIImagePickerController,\n    didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]\n  ) {\n\n    if let pickedImage = info[.originalImage] as? UIImage {\n      // Rotate target image to .up orientation to avoid potential orientation misalignment.\n      guard let targetImage = pickedImage.transformOrientationToUp() else {\n        inferenceStatusLabel.text = \"ERROR: Image orientation couldn't be fixed.\"\n        return\n      }\n\n      self.targetImage = targetImage\n\n      if styleImage != nil {\n        runStyleTransfer(targetImage)\n      } else {\n        imageView.image = targetImage\n      }\n    }\n\n    dismiss(animated: true)\n  }\n}\n\n// MARK: StylePickerViewControllerDelegate\n\nextension ViewController: StylePickerViewControllerDelegate {\n\n  func picker(_: StylePickerViewController, didSelectStyle image: UIImage) {\n    styleImage = image\n    styleImageView.image = image\n\n    if let targetImage = targetImage {\n      runStyleTransfer(targetImage)\n    }\n  }\n\n}\n\n// MARK: Pasteboard images\n\nextension ViewController {\n\n  fileprivate func imageFromPasteboard() -> UIImage? {\n    return UIPasteboard.general.images?.first\n  }\n\n  fileprivate func imageRoleSelectionAlert(image: UIImage) -> UIAlertController {\n    let controller = UIAlertController(title: \"Paste Image\",\n                                       message: nil,\n                                       preferredStyle: .actionSheet)\n    controller.popoverPresentationController?.sourceView = view\n    let setInputAction = UIAlertAction(title: \"Set input image\", style: .default) { _ in\n      // Rotate target image to .up orientation to avoid potential orientation misalignment.\n      guard let targetImage = image.transformOrientationToUp() else {\n        self.inferenceStatusLabel.text = \"ERROR: Image orientation couldn't be fixed.\"\n        return\n      }\n\n      self.targetImage = targetImage\n      self.imageView.image = targetImage\n    }\n    let setStyleAction = UIAlertAction(title: \"Set style image\", style: .default) { _ in\n      guard let croppedImage = image.cropCenter() else {\n        self.inferenceStatusLabel.text = \"ERROR: Unable to crop style image.\"\n        return\n      }\n\n      self.styleImage = croppedImage\n      self.styleImageView.image = croppedImage\n    }\n    let cancelAction = UIAlertAction(title: \"Cancel\", style: .cancel) { _ in\n      controller.dismiss(animated: true, completion: nil)\n    }\n    controller.addAction(setInputAction)\n    controller.addAction(setStyleAction)\n    controller.addAction(cancelAction)\n\n    return controller\n  }\n\n  fileprivate func addForegroundEventHandler() {\n    NotificationCenter.default.addObserver(self,\n                                           selector: #selector(onForeground(_:)),\n                                           name: UIApplication.willEnterForegroundNotification,\n                                           object: nil)\n  }\n\n  @objc fileprivate func onForeground(_ sender: Any) {\n    self.pasteImageButton.isEnabled = self.imageFromPasteboard() != nil\n  }\n\n}\n"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t47FF5F7580893FFA591078AA /* Pods_StyleTransfer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E1EEEF9C06A44B2D6BC04C0 /* Pods_StyleTransfer.framework */; };\n\t\t7F67811F23405195002A02F2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F67811E23405195002A02F2 /* AppDelegate.swift */; };\n\t\t7F67812323405195002A02F2 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F67812223405195002A02F2 /* ViewController.swift */; };\n\t\t7F67812623405195002A02F2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7F67812423405195002A02F2 /* Main.storyboard */; };\n\t\t7F67812823405196002A02F2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7F67812723405196002A02F2 /* Assets.xcassets */; };\n\t\t7F67812B23405196002A02F2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7F67812923405196002A02F2 /* LaunchScreen.storyboard */; };\n\t\t7F9738DD234071180058C202 /* StyleTransferer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F9738DC234071180058C202 /* StyleTransferer.swift */; };\n\t\t7F9738E22341EFDD0058C202 /* TFLiteExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F9738E12341EFDD0058C202 /* TFLiteExtension.swift */; };\n\t\t7F9738E72343CE9F0058C202 /* UIKitExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F9738E62343CE9F0058C202 /* UIKitExtension.swift */; };\n\t\t8D3572E823FC81B600DBD36C /* StylePickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D3572E723FC81B600DBD36C /* StylePickerViewController.swift */; };\n\t\t8D3572E923FDB85100DBD36C /* style_predict_quantized_256.tflite in Resources */ = {isa = PBXBuildFile; fileRef = 8D3572E623FC818500DBD36C /* style_predict_quantized_256.tflite */; };\n\t\t8D3572EA23FDB85400DBD36C /* style_transfer_quantized_384.tflite in Resources */ = {isa = PBXBuildFile; fileRef = 8D3572E423FC818500DBD36C /* style_transfer_quantized_384.tflite */; };\n\t\t8DEEDCEC245245C6008007CF /* style_predict_f16_256.tflite in Resources */ = {isa = PBXBuildFile; fileRef = 8D159EC2244917840060668E /* style_predict_f16_256.tflite */; };\n\t\t8DEEDCED245245CC008007CF /* style_transfer_f16_384.tflite in Resources */ = {isa = PBXBuildFile; fileRef = 8D159EC1244917830060668E /* style_transfer_f16_384.tflite */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\t11E508C91B476032C6E9C79D /* Pods-StyleTransfer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-StyleTransfer.release.xcconfig\"; path = \"Target Support Files/Pods-StyleTransfer/Pods-StyleTransfer.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t151FD01147858B3D1B64E667 /* Pods-StyleTransfer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-StyleTransfer.debug.xcconfig\"; path = \"Target Support Files/Pods-StyleTransfer/Pods-StyleTransfer.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t1E1EEEF9C06A44B2D6BC04C0 /* Pods_StyleTransfer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_StyleTransfer.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t7F67811B23405195002A02F2 /* TFL Style Transfer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = \"TFL Style Transfer.app\"; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t7F67811E23405195002A02F2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t7F67812223405195002A02F2 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = \"<group>\"; };\n\t\t7F67812523405195002A02F2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t7F67812723405196002A02F2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t7F67812A23405196002A02F2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t7F67812C23405196002A02F2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t7F9738DC234071180058C202 /* StyleTransferer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StyleTransferer.swift; sourceTree = \"<group>\"; };\n\t\t7F9738E12341EFDD0058C202 /* TFLiteExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TFLiteExtension.swift; sourceTree = \"<group>\"; };\n\t\t7F9738E62343CE9F0058C202 /* UIKitExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitExtension.swift; sourceTree = \"<group>\"; };\n\t\t8D159EC1244917830060668E /* style_transfer_f16_384.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = style_transfer_f16_384.tflite; sourceTree = \"<group>\"; };\n\t\t8D159EC2244917840060668E /* style_predict_f16_256.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = style_predict_f16_256.tflite; sourceTree = \"<group>\"; };\n\t\t8D3572E423FC818500DBD36C /* style_transfer_quantized_384.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = style_transfer_quantized_384.tflite; sourceTree = \"<group>\"; };\n\t\t8D3572E623FC818500DBD36C /* style_predict_quantized_256.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = style_predict_quantized_256.tflite; sourceTree = \"<group>\"; };\n\t\t8D3572E723FC81B600DBD36C /* StylePickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StylePickerViewController.swift; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t7F67811823405195002A02F2 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t47FF5F7580893FFA591078AA /* Pods_StyleTransfer.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t7F67811223405195002A02F2 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t7F67811D23405195002A02F2 /* StyleTransfer */,\n\t\t\t\t7F67811C23405195002A02F2 /* Products */,\n\t\t\t\tAB21E30E2A763328F8A613FD /* Pods */,\n\t\t\t\tCBE38C7FA00D2736D6CDB4D7 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7F67811C23405195002A02F2 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t7F67811B23405195002A02F2 /* TFL Style Transfer.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7F67811D23405195002A02F2 /* StyleTransfer */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t7F67811E23405195002A02F2 /* AppDelegate.swift */,\n\t\t\t\t7F67812223405195002A02F2 /* ViewController.swift */,\n\t\t\t\t8D3572E723FC81B600DBD36C /* StylePickerViewController.swift */,\n\t\t\t\t7F9738DC234071180058C202 /* StyleTransferer.swift */,\n\t\t\t\t7F9738E12341EFDD0058C202 /* TFLiteExtension.swift */,\n\t\t\t\t7F9738E62343CE9F0058C202 /* UIKitExtension.swift */,\n\t\t\t\t7F9738E823448E750058C202 /* model */,\n\t\t\t\t7F67812423405195002A02F2 /* Main.storyboard */,\n\t\t\t\t7F67812723405196002A02F2 /* Assets.xcassets */,\n\t\t\t\t7F67812923405196002A02F2 /* LaunchScreen.storyboard */,\n\t\t\t\t7F67812C23405196002A02F2 /* Info.plist */,\n\t\t\t);\n\t\t\tpath = StyleTransfer;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7F9738E823448E750058C202 /* model */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t8D3572E623FC818500DBD36C /* style_predict_quantized_256.tflite */,\n\t\t\t\t8D3572E423FC818500DBD36C /* style_transfer_quantized_384.tflite */,\n\t\t\t\t8D159EC2244917840060668E /* style_predict_f16_256.tflite */,\n\t\t\t\t8D159EC1244917830060668E /* style_transfer_f16_384.tflite */,\n\t\t\t);\n\t\t\tpath = model;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAB21E30E2A763328F8A613FD /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t151FD01147858B3D1B64E667 /* Pods-StyleTransfer.debug.xcconfig */,\n\t\t\t\t11E508C91B476032C6E9C79D /* Pods-StyleTransfer.release.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tCBE38C7FA00D2736D6CDB4D7 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t1E1EEEF9C06A44B2D6BC04C0 /* Pods_StyleTransfer.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t7F67811A23405195002A02F2 /* StyleTransfer */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 7F67812F23405196002A02F2 /* Build configuration list for PBXNativeTarget \"StyleTransfer\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t93981806F6786B7C45C56C58 /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t7FA4B3692345B949005784A9 /* Download TensorFlow Lite model */,\n\t\t\t\t7F67811723405195002A02F2 /* Sources */,\n\t\t\t\t7F67811823405195002A02F2 /* Frameworks */,\n\t\t\t\t7F67811923405195002A02F2 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = StyleTransfer;\n\t\t\tproductName = StyleTransfer;\n\t\t\tproductReference = 7F67811B23405195002A02F2 /* TFL Style Transfer.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t7F67811323405195002A02F2 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 1100;\n\t\t\t\tLastUpgradeCheck = 1100;\n\t\t\t\tORGANIZATIONNAME = \"TensorFlow Authors\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t7F67811A23405195002A02F2 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 11.0;\n\t\t\t\t\t\tLastSwiftMigration = 1140;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 7F67811623405195002A02F2 /* Build configuration list for PBXProject \"StyleTransfer\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 7F67811223405195002A02F2;\n\t\t\tproductRefGroup = 7F67811C23405195002A02F2 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t7F67811A23405195002A02F2 /* StyleTransfer */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t7F67811923405195002A02F2 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t7F67812B23405196002A02F2 /* LaunchScreen.storyboard in Resources */,\n\t\t\t\t8D3572EA23FDB85400DBD36C /* style_transfer_quantized_384.tflite in Resources */,\n\t\t\t\t7F67812823405196002A02F2 /* Assets.xcassets in Resources */,\n\t\t\t\t8DEEDCEC245245C6008007CF /* style_predict_f16_256.tflite in Resources */,\n\t\t\t\t7F67812623405195002A02F2 /* Main.storyboard in Resources */,\n\t\t\t\t8DEEDCED245245CC008007CF /* style_transfer_f16_384.tflite in Resources */,\n\t\t\t\t8D3572E923FDB85100DBD36C /* style_predict_quantized_256.tflite in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t7FA4B3692345B949005784A9 /* Download TensorFlow Lite model */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Download TensorFlow Lite model\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"# Download TF Lite model from the internet if it does not exist.\\nDOWNLOAD_SCRIPT=${SRCROOT}/download_tflite_models.sh\\nsh ${DOWNLOAD_SCRIPT}\\n\";\n\t\t};\n\t\t93981806F6786B7C45C56C58 /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-StyleTransfer-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t7F67811723405195002A02F2 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t7F67812323405195002A02F2 /* ViewController.swift in Sources */,\n\t\t\t\t7F9738DD234071180058C202 /* StyleTransferer.swift in Sources */,\n\t\t\t\t7F9738E22341EFDD0058C202 /* TFLiteExtension.swift in Sources */,\n\t\t\t\t7F9738E72343CE9F0058C202 /* UIKitExtension.swift in Sources */,\n\t\t\t\t8D3572E823FC81B600DBD36C /* StylePickerViewController.swift in Sources */,\n\t\t\t\t7F67811F23405195002A02F2 /* AppDelegate.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\t7F67812423405195002A02F2 /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t7F67812523405195002A02F2 /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7F67812923405196002A02F2 /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t7F67812A23405196002A02F2 /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t7F67812D23405196002A02F2 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 10.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t7F67812E23405196002A02F2 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = s;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 10.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t7F67813023405196002A02F2 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 151FD01147858B3D1B64E667 /* Pods-StyleTransfer.debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"Apple Development\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tINFOPLIST_FILE = StyleTransfer/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 11.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.tensorflow.examples.StyleTransfer;\n\t\t\t\tPRODUCT_NAME = \"TFL Style Transfer\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_INSTALL_OBJC_HEADER = NO;\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t7F67813123405196002A02F2 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 11E508C91B476032C6E9C79D /* Pods-StyleTransfer.release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"Apple Development\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tINFOPLIST_FILE = StyleTransfer/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 11.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.tensorflow.examples.StyleTransfer;\n\t\t\t\tPRODUCT_NAME = \"TFL Style Transfer\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_INSTALL_OBJC_HEADER = NO;\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t7F67811623405195002A02F2 /* Build configuration list for PBXProject \"StyleTransfer\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t7F67812D23405196002A02F2 /* Debug */,\n\t\t\t\t7F67812E23405196002A02F2 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t7F67812F23405196002A02F2 /* Build configuration list for PBXNativeTarget \"StyleTransfer\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t7F67813023405196002A02F2 /* Debug */,\n\t\t\t\t7F67813123405196002A02F2 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 7F67811323405195002A02F2 /* Project object */;\n}\n"
  },
  {
    "path": "lite/examples/style_transfer/ios/StyleTransfer.xcodeproj/xcshareddata/xcschemes/StyleTransfer.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1130\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"7F67811A23405195002A02F2\"\n               BuildableName = \"TFL Style Transfer.app\"\n               BlueprintName = \"StyleTransfer\"\n               ReferencedContainer = \"container:StyleTransfer.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"7F67811A23405195002A02F2\"\n            BuildableName = \"TFL Style Transfer.app\"\n            BlueprintName = \"StyleTransfer\"\n            ReferencedContainer = \"container:StyleTransfer.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n      <EnvironmentVariables>\n         <EnvironmentVariable\n            key = \"CGBITMAP_CONTEXT_LOG_ERRORS\"\n            value = \"1\"\n            isEnabled = \"YES\">\n         </EnvironmentVariable>\n      </EnvironmentVariables>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"7F67811A23405195002A02F2\"\n            BuildableName = \"TFL Style Transfer.app\"\n            BlueprintName = \"StyleTransfer\"\n            ReferencedContainer = \"container:StyleTransfer.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "lite/examples/style_transfer/ios/download_tflite_models.sh",
    "content": "# Download TF Lite models from the internet if it does not exist.\nMODEL_FOLDER=$(dirname \"$0\")/StyleTransfer/model\nPREDICT_INT8_MODEL=${MODEL_FOLDER}/style_predict_quantized_256.tflite\nTRANSFORM_INT8_MODEL=${MODEL_FOLDER}/style_transfer_quantized_384.tflite\nPREDICT_F16_MODEL=${MODEL_FOLDER}/style_predict_f16_256.tflite\nTRANSFORM_F16_MODEL=${MODEL_FOLDER}/style_transfer_f16_384.tflite\n\nif [[ -f \"$PREDICT_INT8_MODEL\" &&\n      -f \"$TRANSFORM_INT8_MODEL\" &&\n      -f \"$PREDICT_F16_MODEL\" &&\n      -f \"$TRANSFORM_F16_MODEL\" ]]; then\n  echo \"INFO: TF Lite model already exists. Skip downloading and use the local model.\"\nelse\n  mkdir -p ${MODEL_FOLDER}\n  curl -o ${PREDICT_INT8_MODEL} -L https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/style_transfer/ios/magenta_arbitrary-image-stylization-v1-256_int8_prediction_1.tflite\n  curl -o ${TRANSFORM_INT8_MODEL} -L https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/style_transfer/ios/magenta_arbitrary-image-stylization-v1-256_int8_transfer_1.tflite\n  curl -o ${PREDICT_F16_MODEL} -L https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/style_transfer/ios/magenta_arbitrary-image-stylization-v1-256_fp16_prediction_1.tflite\n  curl -o ${TRANSFORM_F16_MODEL} -L https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/style_transfer/ios/magenta_arbitrary-image-stylization-v1-256_fp16_transfer_1.tflite\n  echo \"INFO: Downloaded TensorFlow Lite model to $MODEL_FOLDER .\"\nfi\n"
  },
  {
    "path": "lite/examples/super_resolution/android/README.md",
    "content": "# Super Resolution Android sample.\n\nThe task of recovering a high resolution (HR) image from its low resolution\ncounterpart is commonly referred to as Single Image Super Resolution (SISR).\n\nThe model used here is ESRGAN\n([ESRGAN: Enhanced Super-Resolution Generative Adversarial Networks](https://arxiv.org/abs/1809.00219)).\nThe TFLite model is converted from this\n[implementation](https://tfhub.dev/captain-pool/esrgan-tf2/1) hosted on TF Hub.\nDemo images are from [DIV2K dataset](https://data.vision.ee.ethz.ch/cvl/DIV2K/).\n\nThis sample automatically downloads TFLite JAR files and uses TFLite C API\nthrough Android NDK.\n\n![SCREENSHOT](screenshot.jpg)\n\n## Requirements\n\n*   Android Studio 4.2 or above (installed on a Linux, Mac or Windows machine)\n*   An Android device, or an Android Emulator\n\n## Build and run\n\n### Step 1. Clone the TensorFlow examples source code\n\nClone the TensorFlow examples GitHub repository to your computer to get the demo\napplication.\n\n```\ngit clone https://github.com/tensorflow/examples\n```\n\n### Step 2. Import the sample app to Android Studio\n\nOpen the TensorFlow source code in Android Studio. To do this, open Android\nStudio and select `Import Projects (Gradle, Eclipse ADT, etc.)`, setting the\nfolder to `examples/lite/examples/super_resolution/android`\n\n### Step 3. Download TFLite library\n\nOpen your terminal and go to the sample folder. Type './gradlew fetchTFLiteLibs'\nto run the download tasks. Use 'gradlew.bat' on Windows.\n\n### Step 4. Run the Android app\n\nConnect the Android device to the computer and be sure to approve any ADB\npermission prompts that appear on your phone. Select `Run -> Run app.` Select\nthe deployment target in the connected devices to the device on which the app\nwill be installed. This will install the app on the device.\n\nTo test the app, open the app called `TFL Super Resolution` on your device.\nRe-installing the app may require you to uninstall the previous installations.\n\n## Future work:\n\n*   Use a distilled version to do video super resolution\n\n## Resources used:\n\n*   [TensorFlow Lite](https://www.tensorflow.org/lite)\n*   [ESRGAN: Enhanced Super-Resolution Generative Adversarial Networks](https://arxiv.org/abs/1809.00219)\n"
  },
  {
    "path": "lite/examples/super_resolution/android/app/build.gradle",
    "content": "apply plugin: 'com.android.application'\napply plugin: 'de.undercouch.download'\n\nandroid {\n    compileSdkVersion 28\n\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.superresolution\"\n        minSdkVersion 21\n        targetSdkVersion 28\n        versionCode 1\n        versionName \"1.0\"\n        externalNativeBuild {\n            cmake {\n                arguments '-DANDROID_STL=c++_shared'\n            }\n        }\n        ndk {\n            abiFilters 'armeabi-v7a','arm64-v8a'\n        }\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n        debug {\n            debuggable true\n            jniDebuggable true\n            packagingOptions { doNotStrip \"**//.so\" }\n        }\n    }\n\n    aaptOptions {\n        noCompress \"tflite\"\n    }\n    compileOptions {\n        sourceCompatibility = '1.8'\n        targetCompatibility = '1.8'\n    }\n    lintOptions {\n        abortOnError false\n    }\n\n    sourceSets {\n        main {\n            // let gradle pack the shared library into apk\n            jniLibs.srcDirs = ['../libraries/tensorflowlite/jni',\n                               '../libraries/tensorflowlite-gpu/jni']\n        }\n    }\n\n    externalNativeBuild {\n        cmake {\n            path \"src/main/cc/CMakeLists.txt\"\n            version \"3.6.0\"\n        }\n    }\n}\n\n// import download tasks\nproject.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'\n\napply from: 'download.gradle'\n\ndependencies {\n    implementation 'androidx.appcompat:appcompat:1.1.0'\n    implementation 'com.google.guava:guava:30.0-android'\n    implementation 'com.android.support:design:23.0.1'\n}"
  },
  {
    "path": "lite/examples/super_resolution/android/app/download.gradle",
    "content": "task downloadESRGANModelFile() {\n    download {\n        src 'https://storage.googleapis.com/download.tensorflow.org/models/tflite/esrgan/ESRGAN.tflite'\n        dest project.ext.ASSET_DIR + '/ESRGAN.tflite'\n        overwrite false\n    }\n}\n\ntask downloadTFLiteJARFile() {\n    download {\n        src \"https://repo1.maven.org/maven2/org/tensorflow/tensorflow-lite/2.3.0/tensorflow-lite-2.3.0.aar\"\n        dest \"${project.rootDir}/libraries/tensorflow-lite-2.3.0.aar\"\n        overwrite false\n        retries 5\n    }\n}\n\ntask downloadTFLiteGPUDelegateJARFile() {\n    download {\n        src \"https://repo1.maven.org/maven2/org/tensorflow/tensorflow-lite-gpu/2.3.0/tensorflow-lite-gpu-2.3.0.aar\"\n        dest \"${project.rootDir}/libraries/tensorflow-lite-gpu-2.3.0.aar\"\n        overwrite false\n        retries 5\n    }\n}\n\ntask fetchTFLiteLibs() {\n    copy {\n        from zipTree(\"${project.rootDir}/libraries/tensorflow-lite-2.3.0.aar\")\n        into \"${project.rootDir}/libraries/tensorflowlite/\"\n        include \"headers/tensorflow/lite/c/*h\"\n        include \"headers/tensorflow/lite/*h\"\n        include \"jni/**/libtensorflowlite_jni.so\"\n    }\n    copy {\n        from zipTree(\"${project.rootDir}/libraries/tensorflow-lite-gpu-2.3.0.aar\")\n        into \"${project.rootDir}/libraries/tensorflowlite-gpu/\"\n        include \"headers/tensorflow/lite/delegates/gpu/*h\"\n        include \"jni/**/libtensorflowlite_gpu_jni.so\"\n    }\n}"
  },
  {
    "path": "lite/examples/super_resolution/android/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "lite/examples/super_resolution/android/app/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.tensorflow.lite.examples.superresolution\">\n\n    <uses-sdk />\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme.SuperResolution\">\n        <activity\n            android:name=\".MainActivity\"\n            android:label=\"@string/app_name\"\n            android:screenOrientation=\"portrait\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "lite/examples/super_resolution/android/app/src/main/cc/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.4.1)\n\nset(TFLITE_LIBPATH \"${CMAKE_CURRENT_SOURCE_DIR}/../../../../libraries/tensorflowlite/jni\")\nset(TFLITE_INCLUDE \"${CMAKE_CURRENT_SOURCE_DIR}/../../../../libraries/tensorflowlite/headers\")\nset(TFLITE_GPU_LIBPATH \"${CMAKE_CURRENT_SOURCE_DIR}/../../../../libraries/tensorflowlite-gpu/jni\")\nset(TFLITE_GPU_INCLUDE \"${CMAKE_CURRENT_SOURCE_DIR}/../../../../libraries/tensorflowlite-gpu/headers\")\n\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -std=gnu++14\")\nset(CMAKE_CXX_STANDARD 14)\n\nadd_library(SuperResolution SHARED SuperResolution_jni.cpp SuperResolution.cpp)\n\nadd_library(lib_tensorflowlite SHARED IMPORTED)\nset_target_properties(lib_tensorflowlite PROPERTIES IMPORTED_LOCATION\n        ${TFLITE_LIBPATH}/${ANDROID_ABI}/libtensorflowlite_jni.so)\n\nadd_library(lib_tensorflowlite_gpu SHARED IMPORTED)\nset_target_properties(lib_tensorflowlite_gpu PROPERTIES IMPORTED_LOCATION\n        ${TFLITE_GPU_LIBPATH}/${ANDROID_ABI}/libtensorflowlite_gpu_jni.so)\n\nfind_library(log-lib log)\n\ninclude_directories(${TFLITE_INCLUDE})\ntarget_include_directories(SuperResolution PRIVATE\n        ${TFLITE_INCLUDE})\n\ninclude_directories(${TFLITE_GPU_INCLUDE})\n\ntarget_include_directories(SuperResolution PRIVATE\n        ${TFLITE_GPU_INCLUDE})\n\ntarget_link_libraries(SuperResolution\n                      android\n                      lib_tensorflowlite\n                      lib_tensorflowlite_gpu\n                      # Links the target library to the log library\n                      # included in the NDK.\n                      ${log-lib})\n"
  },
  {
    "path": "lite/examples/super_resolution/android/app/src/main/cc/SuperResolution.cpp",
    "content": "/*\n * Copyright 2020 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"SuperResolution.h\"\n\n#include <android/log.h>\n#include <math.h>\n\n#include <fstream>\n#include <iostream>\n#include <memory>\n#include <string>\n#include <vector>\n\nnamespace tflite {\nnamespace examples {\nnamespace superresolution {\n\n// TODO: make it changeable in the UI\nconstexpr int kThreadNum = 4;\n\nSuperResolution::SuperResolution(const void* model_data, size_t model_size,\n                                 bool use_gpu) {\n  // Load the model\n  model_ = TfLiteModelCreate(model_data, model_size);\n  if (!model_) {\n    LOGE(\"Failed to create TFLite model\");\n    return;\n  }\n\n  // Create the interpreter options\n  options_ = TfLiteInterpreterOptionsCreate();\n\n  // Choose CPU or GPU\n  if (use_gpu) {\n    delegate_ = TfLiteGpuDelegateV2Create(/*default options=*/nullptr);\n    TfLiteInterpreterOptionsAddDelegate(options_, delegate_);\n  } else {\n    TfLiteInterpreterOptionsSetNumThreads(options_, kThreadNum);\n  }\n\n  // Create the interpreter\n  interpreter_ = TfLiteInterpreterCreate(model_, options_);\n  if (!interpreter_) {\n    LOGE(\"Failed to create TFLite interpreter\");\n    return;\n  }\n}\n\nSuperResolution::~SuperResolution() {\n  // Dispose of the model and interpreter objects\n  if (interpreter_) {\n    TfLiteInterpreterDelete(interpreter_);\n  }\n  if (delegate_) {\n    TfLiteGpuDelegateV2Delete(delegate_);\n  }\n  if (options_) {\n    TfLiteInterpreterOptionsDelete(options_);\n  }\n  if (model_) {\n    TfLiteModelDelete(model_);\n  }\n}\n\nbool SuperResolution::IsInterpreterCreated() {\n  if (!interpreter_) {\n    return false;\n  } else {\n    return true;\n  }\n}\n\nstd::unique_ptr<int[]> SuperResolution::DoSuperResolution(int* lr_img_rgb) {\n  // Allocate tensors and populate the input tensor data\n  TfLiteStatus status = TfLiteInterpreterAllocateTensors(interpreter_);\n  if (status != kTfLiteOk) {\n    LOGE(\"Something went wrong when allocating tensors\");\n    return nullptr;\n  }\n\n  TfLiteTensor* input_tensor =\n      TfLiteInterpreterGetInputTensor(interpreter_, 0);\n\n  // Extract RGB values from each pixel\n  float input_buffer[kNumberOfInputPixels * kImageChannels];\n  for (int i = 0, j = 0; i < kNumberOfInputPixels; i++) {\n    // Alpha is ignored\n    input_buffer[j++] = static_cast<float>((lr_img_rgb[i] >> 16) & 0xff);\n    input_buffer[j++] = static_cast<float>((lr_img_rgb[i] >> 8) & 0xff);\n    input_buffer[j++] = static_cast<float>((lr_img_rgb[i]) & 0xff);\n  }\n\n  // Feed input into model\n  status = TfLiteTensorCopyFromBuffer(\n      input_tensor, input_buffer,\n      kNumberOfInputPixels * kImageChannels * sizeof(float));\n  if (status != kTfLiteOk) {\n    LOGE(\"Something went wrong when copying input buffer to input tensor\");\n    return nullptr;\n  }\n\n  // Run the interpreter\n  status = TfLiteInterpreterInvoke(interpreter_);\n  if (status != kTfLiteOk) {\n    LOGE(\"Something went wrong when running the TFLite model\");\n    return nullptr;\n  }\n\n  // Extract the output tensor data\n  const TfLiteTensor* output_tensor =\n      TfLiteInterpreterGetOutputTensor(interpreter_, 0);\n  float output_buffer[kNumberOfOutputPixels * kImageChannels];\n  status = TfLiteTensorCopyToBuffer(\n      output_tensor, output_buffer,\n      kNumberOfOutputPixels * kImageChannels * sizeof(float));\n  if (status != kTfLiteOk) {\n    LOGE(\"Something went wrong when copying output tensor to output buffer\");\n    return nullptr;\n  }\n\n  // Postprocess the output from TFLite\n  int clipped_output[kImageChannels];\n  auto rgb_colors = std::make_unique<int[]>(kNumberOfOutputPixels);\n  for (int i = 0; i < kNumberOfOutputPixels; i++) {\n    for (int j = 0; j < kImageChannels; j++) {\n      clipped_output[j] = std::max<float>(\n          0, std::min<float>(255, output_buffer[i * kImageChannels + j]));\n    }\n    // When we have RGB values, we pack them into a single pixel.\n    // Alpha is set to 255.\n    rgb_colors[i] = (255u & 0xff) << 24 | (clipped_output[0] & 0xff) << 16 |\n                    (clipped_output[1] & 0xff) << 8 |\n                    (clipped_output[2] & 0xff);\n  }\n\n  return rgb_colors;\n}\n\n}  // namespace superresolution\n}  // namespace examples\n}  // namespace tflite\n"
  },
  {
    "path": "lite/examples/super_resolution/android/app/src/main/cc/SuperResolution.h",
    "content": "/*\n * Copyright 2020 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef NATIVE_LIBS_SUPERRESOLUTION_H\n#define NATIVE_LIBS_SUPERRESOLUTION_H\n\n#include <string>\n\n#include \"tensorflow/lite/c/c_api.h\"\n#include \"tensorflow/lite/delegates/gpu/delegate.h\"\n\n#define LOG_TAG \"super_resolution::\"\n#define LOGI(...) \\\n  ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))\n#define LOGE(...) \\\n  ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))\n\nnamespace tflite {\nnamespace examples {\nnamespace superresolution {\n\nconst int kInputImageHeight = 50;\nconst int kInputImageWidth = 50;\nconst int kImageChannels = 3;\nconst int kNumberOfInputPixels = kInputImageHeight * kInputImageWidth;\nconst int kUpscaleFactor = 4;\nconst int kOutputImageHeight = kInputImageHeight * kUpscaleFactor;\nconst int kOutputImageWidth = kInputImageWidth * kUpscaleFactor;\nconst int kNumberOfOutputPixels = kOutputImageHeight * kOutputImageWidth;\n\nclass SuperResolution {\n public:\n  SuperResolution(const void* model_data, size_t model_size, bool use_gpu);\n  ~SuperResolution();\n  bool IsInterpreterCreated();\n  // DoSuperResolution() performs super resolution on a low resolution image. It\n  // returns a valid pointer if successful and nullptr if unsuccessful.\n  // lr_img_rgb: the pointer to the RGB array extracted from low resolution\n  // image\n  std::unique_ptr<int[]> DoSuperResolution(int* lr_img_rgb);\n\n private:\n  // TODO: use unique_ptr\n  TfLiteInterpreter* interpreter_;\n  TfLiteModel* model_ = nullptr;\n  TfLiteInterpreterOptions* options_ = nullptr;\n  TfLiteDelegate* delegate_ = nullptr;\n};\n\n}  // namespace superresolution\n}  // namespace examples\n}  // namespace tflite\n#endif\n"
  },
  {
    "path": "lite/examples/super_resolution/android/app/src/main/cc/SuperResolution_jni.cpp",
    "content": "/*\n * Copyright 2020 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android/log.h>\n#include <jni.h>\n\n#include <cinttypes>\n#include <cstring>\n#include <string>\n\n#include \"SuperResolution.h\"\n\nnamespace tflite {\nnamespace examples {\nnamespace superresolution {\n\nextern \"C\" JNIEXPORT jintArray JNICALL\nJava_org_tensorflow_lite_examples_superresolution_MainActivity_superResolutionFromJNI(\n    JNIEnv *env, jobject thiz, jlong native_handle, jintArray low_res_rgb) {\n  jint *lr_img_rgb = env->GetIntArrayElements(low_res_rgb, NULL);\n\n  if (!reinterpret_cast<SuperResolution *>(native_handle)\n           ->IsInterpreterCreated()) {\n    return nullptr;\n  }\n\n  // Generate super resolution image\n  auto sr_rgb_colors = reinterpret_cast<SuperResolution *>(native_handle)\n                           ->DoSuperResolution(static_cast<int *>(lr_img_rgb));\n  if (!sr_rgb_colors) {\n    return nullptr;  // super resolution failed\n  }\n  jintArray sr_img_rgb = env->NewIntArray(kNumberOfOutputPixels);\n  env->SetIntArrayRegion(sr_img_rgb, 0, kNumberOfOutputPixels,\n                         sr_rgb_colors.get());\n\n  // Clean up before we return\n  env->ReleaseIntArrayElements(low_res_rgb, lr_img_rgb, JNI_COMMIT);\n\n  return sr_img_rgb;\n}\n\nextern \"C\" JNIEXPORT jlong JNICALL\nJava_org_tensorflow_lite_examples_superresolution_MainActivity_initWithByteBufferFromJNI(\n    JNIEnv *env, jobject thiz, jobject model_buffer, jboolean use_gpu) {\n  const void *model_data =\n      static_cast<void *>(env->GetDirectBufferAddress(model_buffer));\n  jlong model_size_bytes = env->GetDirectBufferCapacity(model_buffer);\n  SuperResolution *super_resolution = new SuperResolution(\n      model_data, static_cast<size_t>(model_size_bytes), use_gpu);\n  if (super_resolution->IsInterpreterCreated()) {\n    LOGI(\"Interpreter is created successfully\");\n    return reinterpret_cast<jlong>(super_resolution);\n  } else {\n    delete super_resolution;\n    return 0;\n  }\n}\n\nextern \"C\" JNIEXPORT void JNICALL\nJava_org_tensorflow_lite_examples_superresolution_MainActivity_deinitFromJNI(\n    JNIEnv *env, jobject thiz, jlong native_handle) {\n  delete reinterpret_cast<SuperResolution*>(native_handle);\n}\n\n}  // namespace superresolution\n}  // namespace examples\n}  // namespace tflite\n"
  },
  {
    "path": "lite/examples/super_resolution/android/app/src/main/java/org/tensorflow/lite/examples/superresolution/AssetsUtil.java",
    "content": "/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\npackage org.tensorflow.lite.examples.superresolution;\n\nimport static android.content.res.AssetManager.ACCESS_BUFFER;\nimport static android.os.ParcelFileDescriptor.MODE_READ_ONLY;\n\nimport android.content.Context;\nimport android.content.res.AssetFileDescriptor;\nimport android.os.ParcelFileDescriptor;\nimport com.google.common.io.ByteStreams;\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\n/** Helper to load assets. */\npublic class AssetsUtil {\n\n  private AssetsUtil() {}\n\n  /**\n   * Gets AssetFileDescriptor directly for given a path, or returns its copy by caching for the\n   * compressed one.\n   */\n  public static AssetFileDescriptor getAssetFileDescriptorOrCached(\n      Context context, String assetPath) throws IOException {\n    try {\n      return context.getAssets().openFd(assetPath);\n    } catch (FileNotFoundException e) {\n      // If it cannot read from asset file (probably compressed), try copying to cache folder and\n      // reloading.\n      File cacheFile = new File(context.getCacheDir(), assetPath);\n      cacheFile.getParentFile().mkdirs();\n      copyToCacheFile(context, assetPath, cacheFile);\n      ParcelFileDescriptor cachedFd = ParcelFileDescriptor.open(cacheFile, MODE_READ_ONLY);\n      return new AssetFileDescriptor(cachedFd, 0, cacheFile.length());\n    }\n  }\n\n  private static void copyToCacheFile(Context context, String assetPath, File cacheFile)\n      throws IOException {\n    try (InputStream inputStream = context.getAssets().open(assetPath, ACCESS_BUFFER);\n        FileOutputStream fileOutputStream = new FileOutputStream(cacheFile, false)) {\n      ByteStreams.copy(inputStream, fileOutputStream);\n    }\n  }\n}\n"
  },
  {
    "path": "lite/examples/super_resolution/android/app/src/main/java/org/tensorflow/lite/examples/superresolution/MainActivity.java",
    "content": "/*\n * Copyright 2020 The TensorFlow Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.superresolution;\n\nimport android.content.res.AssetFileDescriptor;\nimport android.content.res.AssetManager;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.os.Bundle;\nimport android.os.SystemClock;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.util.Log;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.Switch;\nimport android.widget.TextView;\nimport android.widget.Toast;\nimport androidx.annotation.WorkerThread;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.MappedByteBuffer;\nimport java.nio.channels.FileChannel;\n\n/** A super resolution class to generate super resolution images from low resolution images * */\npublic class MainActivity extends AppCompatActivity {\n  static {\n    System.loadLibrary(\"SuperResolution\");\n  }\n\n  private static final String TAG = \"SuperResolution\";\n  private static final String MODEL_NAME = \"ESRGAN.tflite\";\n  private static final int LR_IMAGE_HEIGHT = 50;\n  private static final int LR_IMAGE_WIDTH = 50;\n  private static final int UPSCALE_FACTOR = 4;\n  private static final int SR_IMAGE_HEIGHT = LR_IMAGE_HEIGHT * UPSCALE_FACTOR;\n  private static final int SR_IMAGE_WIDTH = LR_IMAGE_WIDTH * UPSCALE_FACTOR;\n  private static final String LR_IMG_1 = \"lr-1.jpg\";\n  private static final String LR_IMG_2 = \"lr-2.jpg\";\n  private static final String LR_IMG_3 = \"lr-3.jpg\";\n\n  private MappedByteBuffer model;\n  private long superResolutionNativeHandle = 0;\n  private Bitmap selectedLRBitmap = null;\n  private boolean useGPU = false;\n\n  private ImageView lowResImageView1;\n  private ImageView lowResImageView2;\n  private ImageView lowResImageView3;\n  private TextView selectedImageTextView;\n  private Switch gpuSwitch;\n\n  @Override\n  protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    setContentView(R.layout.activity_main);\n\n    final Button superResolutionButton = findViewById(R.id.upsample_button);\n    lowResImageView1 = findViewById(R.id.low_resolution_image_1);\n    lowResImageView2 = findViewById(R.id.low_resolution_image_2);\n    lowResImageView3 = findViewById(R.id.low_resolution_image_3);\n    selectedImageTextView = findViewById(R.id.chosen_image_tv);\n    gpuSwitch = findViewById(R.id.switch_use_gpu);\n\n    ImageView[] lowResImageViews = {lowResImageView1, lowResImageView2, lowResImageView3};\n\n    AssetManager assetManager = getAssets();\n    try {\n      InputStream inputStream1 = assetManager.open(LR_IMG_1);\n      Bitmap bitmap1 = BitmapFactory.decodeStream(inputStream1);\n      lowResImageView1.setImageBitmap(bitmap1);\n\n      InputStream inputStream2 = assetManager.open(LR_IMG_2);\n      Bitmap bitmap2 = BitmapFactory.decodeStream(inputStream2);\n      lowResImageView2.setImageBitmap(bitmap2);\n\n      InputStream inputStream3 = assetManager.open(LR_IMG_3);\n      Bitmap bitmap3 = BitmapFactory.decodeStream(inputStream3);\n      lowResImageView3.setImageBitmap(bitmap3);\n    } catch (IOException e) {\n      Log.e(TAG, \"Failed to open an low resolution image\");\n    }\n\n    for (ImageView iv : lowResImageViews) {\n      setLRImageViewListener(iv);\n    }\n\n    superResolutionButton.setOnClickListener(\n        new View.OnClickListener() {\n          @Override\n          public void onClick(View view) {\n            if (selectedLRBitmap == null) {\n              Toast.makeText(\n                      getApplicationContext(),\n                      \"Please choose one low resolution image\",\n                      Toast.LENGTH_LONG)\n                  .show();\n              return;\n            }\n\n            if (superResolutionNativeHandle == 0) {\n                superResolutionNativeHandle = initTFLiteInterpreter(gpuSwitch.isChecked());\n            } else if (useGPU != gpuSwitch.isChecked()) {\n              // We need to reinitialize interpreter when execution hardware is changed\n              deinit();\n              superResolutionNativeHandle = initTFLiteInterpreter(gpuSwitch.isChecked());\n            }\n            useGPU = gpuSwitch.isChecked();\n            if (superResolutionNativeHandle == 0) {\n              showToast(\"TFLite interpreter failed to create!\");\n              return;\n            }\n\n            int[] lowResRGB = new int[LR_IMAGE_HEIGHT * LR_IMAGE_WIDTH];\n            selectedLRBitmap.getPixels(\n                lowResRGB, 0, LR_IMAGE_WIDTH, 0, 0, LR_IMAGE_WIDTH, LR_IMAGE_HEIGHT);\n\n            final long startTime = SystemClock.uptimeMillis();\n            int[] superResRGB = doSuperResolution(lowResRGB);\n            final long processingTimeMs = SystemClock.uptimeMillis() - startTime;\n            if (superResRGB == null) {\n              showToast(\"Super resolution failed!\");\n              return;\n            }\n\n            final LinearLayout resultLayout = findViewById(R.id.result_layout);\n            final ImageView superResolutionImageView = findViewById(R.id.super_resolution_image);\n            final ImageView nativelyScaledImageView = findViewById(R.id.natively_scaled_image);\n            final TextView superResolutionTextView = findViewById(R.id.super_resolution_tv);\n            final TextView nativelyScaledImageTextView =\n                findViewById(R.id.natively_scaled_image_tv);\n            final TextView logTextView = findViewById(R.id.log_view);\n\n            // Force refreshing the ImageView\n            superResolutionImageView.setImageDrawable(null);\n            Bitmap srImgBitmap =\n                Bitmap.createBitmap(\n                    superResRGB, SR_IMAGE_WIDTH, SR_IMAGE_HEIGHT, Bitmap.Config.ARGB_8888);\n            superResolutionImageView.setImageBitmap(srImgBitmap);\n            nativelyScaledImageView.setImageBitmap(selectedLRBitmap);\n            resultLayout.setVisibility(View.VISIBLE);\n            logTextView.setText(\"Inference time: \" + processingTimeMs + \"ms\");\n          }\n        });\n  }\n\n  @Override\n  public void onDestroy() {\n    super.onDestroy();\n    deinit();\n  }\n\n  private void setLRImageViewListener(ImageView iv) {\n    iv.setOnTouchListener(\n        new View.OnTouchListener() {\n          @Override\n          public boolean onTouch(View v, MotionEvent event) {\n            if (v.equals(lowResImageView1)) {\n              selectedLRBitmap = ((BitmapDrawable) lowResImageView1.getDrawable()).getBitmap();\n              selectedImageTextView.setText(\n                  \"You are using low resolution image: 1 (\"\n                      + getResources().getString(R.string.low_resolution_1)\n                      + \")\");\n            } else if (v.equals(lowResImageView2)) {\n              selectedLRBitmap = ((BitmapDrawable) lowResImageView2.getDrawable()).getBitmap();\n              selectedImageTextView.setText(\n                  \"You are using low resolution image: 2 (\"\n                      + getResources().getString(R.string.low_resolution_2)\n                      + \")\");\n            } else if (v.equals(lowResImageView3)) {\n              selectedLRBitmap = ((BitmapDrawable) lowResImageView3.getDrawable()).getBitmap();\n              selectedImageTextView.setText(\n                  \"You are using low resolution image: 3 (\"\n                      + getResources().getString(R.string.low_resolution_3)\n                      + \")\");\n            }\n            return false;\n          }\n        });\n  }\n\n  @WorkerThread\n  public synchronized int[] doSuperResolution(int[] lowResRGB) {\n    return superResolutionFromJNI(superResolutionNativeHandle, lowResRGB);\n  }\n\n  private MappedByteBuffer loadModelFile() throws IOException {\n    try (AssetFileDescriptor fileDescriptor =\n            AssetsUtil.getAssetFileDescriptorOrCached(getApplicationContext(), MODEL_NAME);\n        FileInputStream inputStream = new FileInputStream(fileDescriptor.getFileDescriptor())) {\n      FileChannel fileChannel = inputStream.getChannel();\n      long startOffset = fileDescriptor.getStartOffset();\n      long declaredLength = fileDescriptor.getDeclaredLength();\n      return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength);\n    }\n  }\n\n  private void showToast(String str) {\n    Toast.makeText(getApplicationContext(), str, Toast.LENGTH_LONG).show();\n  }\n\n  private long initTFLiteInterpreter(boolean useGPU) {\n    try {\n      model = loadModelFile();\n    } catch (IOException e) {\n      Log.e(TAG, \"Fail to load model\", e);\n    }\n    return initWithByteBufferFromJNI(model, useGPU);\n  }\n\n  private void deinit() {\n    deinitFromJNI(superResolutionNativeHandle);\n  }\n\n  private native int[] superResolutionFromJNI(long superResolutionNativeHandle, int[] lowResRGB);\n\n  private native long initWithByteBufferFromJNI(MappedByteBuffer modelBuffer, boolean useGPU);\n\n  private native void deinitFromJNI(long superResolutionNativeHandle);\n}\n"
  },
  {
    "path": "lite/examples/super_resolution/android/app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillColor=\"#26A69A\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n</vector>\n"
  },
  {
    "path": "lite/examples/super_resolution/android/app/src/main/res/drawable/rounded_edge.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <solid\n        android:color=\"@color/tfe_light_gray\">\n    </solid>\n    <stroke\n        android:width=\"0dp\"\n        android:color=\"#424242\">\n    </stroke>\n    <corners\n        android:topLeftRadius=\"30dp\"\n        android:topRightRadius=\"30dp\"\n        android:bottomLeftRadius=\"0dp\"\n        android:bottomRightRadius=\"0dp\">\n    </corners>\n\n</shape>\n"
  },
  {
    "path": "lite/examples/super_resolution/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n  <path\n      android:fillType=\"evenOdd\"\n      android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\">\n    <aapt:attr name=\"android:fillColor\">\n      <gradient\n          android:endX=\"78.5885\"\n          android:endY=\"90.9159\"\n          android:startX=\"48.7653\"\n          android:startY=\"61.0927\"\n          android:type=\"linear\">\n        <item\n            android:color=\"#44000000\"\n            android:offset=\"0.0\"/>\n        <item\n            android:color=\"#00000000\"\n            android:offset=\"1.0\"/>\n      </gradient>\n    </aapt:attr>\n  </path>\n  <path\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"nonZero\"\n      android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/super_resolution/android/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"#00000000\">\n\n    <RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        xmlns:tools=\"http://schemas.android.com/tools\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        tools:context=\".MainActivity\">\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:layout_alignParentTop=\"true\"\n            android:background=\"@android:color/white\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/tfl2_logo_dark\" />\n        </androidx.appcompat.widget.Toolbar>\n\n        <ScrollView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:paddingTop=\"?attr/actionBarSize\">\n\n            <LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:orientation=\"vertical\">\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/choose_image\"\n                    android:textSize=\"14sp\" />\n\n                <LinearLayout\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:orientation=\"horizontal\">\n\n                    <ImageView\n                        android:id=\"@+id/low_resolution_image_1\"\n                        android:layout_width=\"50dp\"\n                        android:layout_height=\"50dp\"\n                        android:layout_marginRight=\"5dp\"\n                        android:contentDescription=\"@string/low_resolution_1\" />\n\n                    <ImageView\n                        android:id=\"@+id/low_resolution_image_2\"\n                        android:layout_width=\"50dp\"\n                        android:layout_height=\"50dp\"\n                        android:layout_marginRight=\"5dp\"\n                        android:contentDescription=\"@string/low_resolution_2\" />\n\n                    <ImageView\n                        android:id=\"@+id/low_resolution_image_3\"\n                        android:layout_width=\"50dp\"\n                        android:layout_height=\"50dp\"\n                        android:contentDescription=\"@string/low_resolution_3\" />\n                </LinearLayout>\n\n                <TextView\n                    android:id=\"@+id/chosen_image_tv\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/chosen_image_text\"\n                    android:textSize=\"14sp\" />\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/simple_explanation\"\n                    android:textSize=\"12sp\" />\n\n                <Button\n                    android:id=\"@+id/upsample_button\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/upsample\" />\n\n                <LinearLayout\n                    android:id=\"@+id/result_layout\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:orientation=\"vertical\"\n                    android:visibility=\"invisible\">\n                    <TextView\n                        android:id=\"@+id/super_resolution_tv\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/super_resolution_image\"\n                        android:textSize=\"14sp\" />\n\n                    <ImageView\n                        android:id=\"@+id/super_resolution_image\"\n                        android:layout_width=\"200dp\"\n                        android:layout_height=\"200dp\"\n                        android:contentDescription=\"@string/super_resolution_image\" />\n\n                    <TextView\n                        android:id=\"@+id/natively_scaled_image_tv\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:paddingTop=\"10dp\"\n                        android:text=\"@string/natively_scaled_image\"\n                        android:textSize=\"14sp\" />\n\n                    <ImageView\n                        android:id=\"@+id/natively_scaled_image\"\n                        android:layout_width=\"200dp\"\n                        android:layout_height=\"200dp\"\n                        android:contentDescription=\"@string/natively_scaled_image\" />\n\n                    <Space\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"80dp\" />\n                </LinearLayout>\n            </LinearLayout>\n        </ScrollView>\n    </RelativeLayout>\n\n    <include layout=\"@layout/bottom_sheet_layout\" />\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lite/examples/super_resolution/android/app/src/main/res/layout/bottom_sheet_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/bottom_sheet\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_margin=\"8dp\"\n    android:background=\"@drawable/rounded_edge\"\n    android:orientation=\"vertical\"\n    android:padding=\"16dp\"\n    app:behavior_hideable=\"false\"\n    app:behavior_peekHeight=\"36dp\"\n    app:layout_behavior=\"com.google.android.material.bottomsheet.BottomSheetBehavior\"\n    tools:showIn=\"@layout/activity_main\">\n\n    <ImageView\n        android:id=\"@+id/bottom_sheet_arrow\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:src=\"@drawable/icn_chevron_up\" />\n\n    <Switch\n        android:id=\"@+id/switch_use_gpu\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"8dp\"\n        android:layout_marginEnd=\"8dp\"\n        android:layout_marginBottom=\"36dp\"\n        android:text=\"@string/gpu\" />\n\n    <TextView\n        android:id=\"@+id/log_view\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Medium\"\n        android:textColor=\"@android:color/black\" />\n</LinearLayout>\n"
  },
  {
    "path": "lite/examples/super_resolution/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/examples/super_resolution/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/examples/super_resolution/android/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"tfe_color_primary\">#ffa800</color>\n    <color name=\"tfe_color_primary_dark\">#ff6f00</color>\n    <color name=\"tfe_color_accent\">#425066</color>\n\n    <color name=\"tfe_semi_transparent\">#66000000</color>\n    <color name=\"tfe_light_gray\">#E4E4E4</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/super_resolution/android/app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\" translatable=\"false\">TFL Super Resolution</string>\n    <string name=\"choose_image\" translatable=\"false\">Choose a low resolution image below:</string>\n    <string name=\"low_resolution_1\" translatable=\"false\">insect head</string>\n    <string name=\"low_resolution_2\" translatable=\"false\">lights</string>\n    <string name=\"low_resolution_3\" translatable=\"false\">cat ear</string>\n    <string name=\"chosen_image_text\" translatable=\"false\">You are using low resolution image: None.</string>\n    <string name=\"simple_explanation\" translatable=\"false\">(Clicking UPSAMPLE button below will use TFLite to generate a corresponding high resolution image based on your chosen image.)</string>\n    <string name=\"super_resolution_image\" translatable=\"false\">Super resolution image:</string>\n    <string name=\"natively_scaled_image\" translatable=\"false\">Android ImageView-scaled image:</string>\n    <string name=\"upsample\" translatable=\"false\">Upsample</string>\n    <string name=\"gpu\" translatable=\"false\">GPU</string>\n</resources>\n"
  },
  {
    "path": "lite/examples/super_resolution/android/app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme.SuperResolution\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/tfe_color_primary</item>\n        <item name=\"colorPrimaryDark\">@color/tfe_color_primary_dark</item>\n        <item name=\"colorAccent\">@color/tfe_color_accent</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "lite/examples/super_resolution/android/build.gradle",
    "content": "\n// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n\n    repositories {\n        google()\n        mavenCentral()\n\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:4.2.0'\n        classpath 'de.undercouch:gradle-download-task:4.1.1'\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n        maven {\n            name 'ossrh-snapshot'\n            url 'https://oss.sonatype.org/content/repositories/snapshots'\n        }\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}"
  },
  {
    "path": "lite/examples/super_resolution/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/super_resolution/android/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\nandroid.useAndroidX=true\nandroid.enableJetifier=true\n"
  },
  {
    "path": "lite/examples/super_resolution/android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/super_resolution/android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/super_resolution/android/settings.gradle",
    "content": "rootProject.name = 'TFLite Super Resolution Demo App'\ninclude ':app'"
  },
  {
    "path": "lite/examples/super_resolution/ml/super_resolution.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"JfOIB1KdkbYW\"\n      },\n      \"source\": [\n        \"##### Copyright 2020 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": 1,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"Ojb0aXCmBgo7\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"M9Y4JZ0ZGoE4\"\n      },\n      \"source\": [\n        \"# Super resolution with TensorFlow Lite\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"-uF3N4BbaMvA\"\n      },\n      \"source\": [\n        \"## Overview\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"isbXET4vVHfu\"\n      },\n      \"source\": [\n        \"The task of recovering a high resolution (HR) image from its low resolution counterpart is commonly referred to as Single Image Super Resolution (SISR). \\n\",\n        \"\\n\",\n        \"The model used here is ESRGAN\\n\",\n        \"([ESRGAN: Enhanced Super-Resolution Generative Adversarial Networks](https://arxiv.org/abs/1809.00219)). And we are going to use TensorFlow Lite to run inference on the pretrained model.\\n\",\n        \"\\n\",\n        \"The TFLite model is converted from this\\n\",\n        \"[implementation](https://tfhub.dev/captain-pool/esrgan-tf2/1) hosted on TF Hub. Note that the model we converted upsamples a 50x50 low resolution image to a 200x200 high resolution image (scale factor=4). If you want a different input size or scale factor, you need to re-convert or re-train the original model.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"2dQlTqiffuoU\"\n      },\n      \"source\": [\n        \"## Setup\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"qKyMtsGqu3zH\"\n      },\n      \"source\": [\n        \"Since we are going to need to do some color space transformation, let's install OpenCV first.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"7YTT1Rxsw3A9\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!pip install opencv-python matplotlib tensorflow\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Clz5Kl97FswD\"\n      },\n      \"source\": [\n        \"Import dependencies.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": 3,\n      \"metadata\": {\n        \"id\": \"2xh1kvGEBjuP\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"2.3.0\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"import tensorflow as tf\\n\",\n        \"import tensorflow_hub as hub\\n\",\n        \"import cv2\\n\",\n        \"import matplotlib.pyplot as plt\\n\",\n        \"print(tf.__version__)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"i5miVfL4kxTA\"\n      },\n      \"source\": [\n        \"Download and convert the ESRGAN model\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": 4,\n      \"metadata\": {\n        \"id\": \"X5PvXIXRwvHj\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"model = hub.load(\\\"https://tfhub.dev/captain-pool/esrgan-tf2/1\\\")\\n\",\n        \"concrete_func = model.signatures[tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY]\\n\",\n        \"concrete_func.inputs[0].set_shape([1, 50, 50, 3])\\n\",\n        \"converter = tf.lite.TFLiteConverter.from_concrete_functions([concrete_func], model)\\n\",\n        \"converter.optimizations = [tf.lite.Optimize.DEFAULT]\\n\",\n        \"tflite_model = converter.convert()\\n\",\n        \"\\n\",\n        \"# Save the TF Lite model.\\n\",\n        \"with tf.io.gfile.GFile('ESRGAN.tflite', 'wb') as f:\\n\",\n        \"  f.write(tflite_model)\\n\",\n        \"\\n\",\n        \"esrgan_model_path = './ESRGAN.tflite'\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"jH5-xPkyUEqt\"\n      },\n      \"source\": [\n        \"Download a test image (insect head).\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": 5,\n      \"metadata\": {\n        \"id\": \"suWiStTWgK6e\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"test_img_path = tf.keras.utils.get_file('lr.jpg', 'https://raw.githubusercontent.com/tensorflow/examples/master/lite/examples/super_resolution/android/app/src/main/assets/lr-1.jpg')\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"rgQ4qRuFNpyW\"\n      },\n      \"source\": [\n        \"## Generate a super resolution image using TensorFlow Lite\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": 6,\n      \"metadata\": {\n        \"id\": \"J9FV4btf02-2\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"lr = cv2.imread(test_img_path)\\n\",\n        \"lr = cv2.cvtColor(lr, cv2.COLOR_BGR2RGB)\\n\",\n        \"lr = tf.expand_dims(lr, axis=0)\\n\",\n        \"lr = tf.cast(lr, tf.float32)\\n\",\n        \"\\n\",\n        \"# Load TFLite model and allocate tensors.\\n\",\n        \"interpreter = tf.lite.Interpreter(model_path=esrgan_model_path)\\n\",\n        \"interpreter.allocate_tensors()\\n\",\n        \"\\n\",\n        \"# Get input and output tensors.\\n\",\n        \"input_details = interpreter.get_input_details()\\n\",\n        \"output_details = interpreter.get_output_details()\\n\",\n        \"\\n\",\n        \"# Run the model\\n\",\n        \"interpreter.set_tensor(input_details[0]['index'], lr)\\n\",\n        \"interpreter.invoke()\\n\",\n        \"\\n\",\n        \"# Extract the output and postprocess it\\n\",\n        \"output_data = interpreter.get_tensor(output_details[0]['index'])\\n\",\n        \"sr = tf.squeeze(output_data, axis=0)\\n\",\n        \"sr = tf.clip_by_value(sr, 0, 255)\\n\",\n        \"sr = tf.round(sr)\\n\",\n        \"sr = tf.cast(sr, tf.uint8)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"EwddQrDUNQGO\"\n      },\n      \"source\": [\n        \"## Visualize the result\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": 7,\n      \"metadata\": {\n        \"id\": \"aasKuozt1gNd\"\n      },\n      \"outputs\": [\n        {\n          \"data\": {\n            \"image/png\": \"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\\n\",\n            \"text/plain\": [\n              \"<Figure size 72x72 with 1 Axes>\"\n            ]\n          },\n          \"metadata\": {\n            \"needs_background\": \"light\"\n          },\n          \"output_type\": \"display_data\"\n        },\n        {\n          \"data\": {\n            \"image/png\": \"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\\n\",\n            \"text/plain\": [\n              \"<Figure size 720x288 with 2 Axes>\"\n            ]\n          },\n          \"metadata\": {\n            \"needs_background\": \"light\"\n          },\n          \"output_type\": \"display_data\"\n        }\n      ],\n      \"source\": [\n        \"lr = tf.cast(tf.squeeze(lr, axis=0), tf.uint8)\\n\",\n        \"plt.figure(figsize = (1, 1))\\n\",\n        \"plt.title('LR')\\n\",\n        \"plt.imshow(lr.numpy());\\n\",\n        \"\\n\",\n        \"plt.figure(figsize=(10, 4))\\n\",\n        \"plt.subplot(1, 2, 1)        \\n\",\n        \"plt.title(f'ESRGAN (x4)')\\n\",\n        \"plt.imshow(sr.numpy());\\n\",\n        \"\\n\",\n        \"bicubic = tf.image.resize(lr, [200, 200], tf.image.ResizeMethod.BICUBIC)\\n\",\n        \"bicubic = tf.cast(bicubic, tf.uint8)\\n\",\n        \"plt.subplot(1, 2, 2)   \\n\",\n        \"plt.title('Bicubic')\\n\",\n        \"plt.imshow(bicubic.numpy());\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"0kb-fkogObjq\"\n      },\n      \"source\": [\n        \"## Performance Benchmarks\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"tNzdgpqTy5P3\"\n      },\n      \"source\": [\n        \"Performance benchmark numbers are generated with the tool\\n\",\n        \"[described here](https://www.tensorflow.org/lite/performance/benchmarks).\\n\",\n        \"\\n\",\n        \"<table>\\n\",\n        \"  <thead>\\n\",\n        \"    <tr>\\n\",\n        \"      <th>Model Name</th>\\n\",\n        \"      <th>Model Size </th>\\n\",\n        \"      <th>Device </th>\\n\",\n        \"      <th>CPU</th>\\n\",\n        \"      <th>GPU</th>\\n\",\n        \"    </tr>\\n\",\n        \"  </thead>\\n\",\n        \"  <tr>\\n\",\n        \"    <td rowspan = 3>\\n\",\n        \"      super resolution (ESRGAN)\\n\",\n        \"    </td>\\n\",\n        \"    <td rowspan = 3>\\n\",\n        \"      4.8 Mb\\n\",\n        \"    </td>\\n\",\n        \"    <td>Pixel 3</td>\\n\",\n        \"    <td>586.8ms*</td>\\n\",\n        \"      <td>128.6ms</td>\\n\",\n        \"  </tr>\\n\",\n        \"  <tr>\\n\",\n        \"     <td>Pixel 4</td>\\n\",\n        \"    <td>385.1ms*</td>\\n\",\n        \"      <td>130.3ms</td>\\n\",\n        \"  </tr>\\n\",\n        \"\\n\",\n        \"</table>\\n\",\n        \"\\n\",\n        \"**4 threads used*\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"name\": \"super_resolution.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "lite/examples/text_classification/android/README.md",
    "content": "\n# TensorFlow Lite Text Classification Android Demo\n\n### Overview\n\nThis sample will accept text entered into a field and classify it as either\npositive or negative with a provided confidence score. The supported\nclassification models include Word Vector and MobileBERT, both of which are\ngenerated using\n[TensorFlow's Model Maker](https://www.tensorflow.org/lite/models/modify/model_maker/text_classification).\nThese instructions walk you through building and running the demo on an Android\ndevice.\n\nThe model files are downloaded via Gradle scripts when you build and run the\napp. You don't need to do any steps to download TFLite models into the project\nexplicitly.\n\n## Build the demo using Android Studio\n\n### Prerequisites\n\n* The **[Android Studio](https://developer.android.com/studio/index.html)** IDE.\n  This sample has been tested on Android Studio Chipmunk.\n\n* A physical or emulated Android device with a minimum OS version of SDK 21\n  (Android 5.0) with developer mode enabled. The process of enabling\n  developer mode may vary by device.\n\n### Building\n\n* Open Android Studio. From the Welcome screen, select Open an existing\n    Android Studio project.\n\n* From the Open File or Project window that appears, navigate to and select\n    the tensorflow-lite/examples/text_classification/android directory.\n    Click OK.\n\n* If it asks you to do a Gradle Sync, click OK.\n\n* With your Android device connected developer mode\n    enabled, click on the green Run arrow in Android Studio.\n\n### Models used\n\nDownloading, extraction, and placing the models into the assets folder is\nmanaged automatically by the download_model.gradle file."
  },
  {
    "path": "lite/examples/text_classification/android/app/build.gradle",
    "content": "plugins {\n    id 'com.android.application'\n    id 'org.jetbrains.kotlin.android'\n    id 'de.undercouch.download'\n}\n\nandroid {\n    compileSdk 32\n\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.textclassification\"\n        minSdk 21\n        targetSdk 32\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    buildFeatures {\n        viewBinding true\n    }\n    androidResources {\n        noCompress 'tflite'\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n    kotlinOptions {\n        jvmTarget = '1.8'\n    }\n}\n\n// import DownloadModels task\nproject.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'\nproject.ext.TEST_ASSET_DIR = projectDir.toString() + '/src/androidTest/assets'\n\napply from:'download_model.gradle'\n\ndependencies {\n    implementation 'androidx.core:core-ktx:1.7.0'\n    implementation 'androidx.appcompat:appcompat:1.4.2'\n    implementation 'com.google.android.material:material:1.6.1'\n    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'\n    testImplementation 'junit:junit:4.13.2'\n    androidTestImplementation 'androidx.test.ext:junit:1.1.3'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n\n    implementation 'org.tensorflow:tensorflow-lite-task-text:0.4.0'\n    implementation 'org.tensorflow:tensorflow-lite-gpu-delegate-plugin:0.4.0'\n    implementation 'org.tensorflow:tensorflow-lite-gpu:2.9.0'\n}"
  },
  {
    "path": "lite/examples/text_classification/android/app/download_model.gradle",
    "content": "// Both models may be generated by https://www.tensorflow.org/lite/\n// models/modify/model_maker/text_classification\ntask downloadMobileBERTTextClassifierModel(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow' +\n            '.org/models/tflite/task_library/text_classification/android/mobilebert.tflite'\n    dest project.ext.ASSET_DIR + '/mobilebert.tflite'\n    overwrite false\n}\n\ntask downloadWorkVecTextClassifierModel(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow' +\n            '.org/models/tflite/task_library/text_classification/android/text_classification_v2.tflite'\n    dest project.ext.ASSET_DIR + '/wordvec.tflite'\n    overwrite false\n}\n\ntask downloadTestWorkVecTextClassifierModel(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow' +\n            '.org/models/tflite/task_library/text_classification/android/text_classification_v2.tflite'\n    dest project.ext.TEST_ASSET_DIR + '/wordvec.tflite'\n    overwrite false\n}\n\ntask downloadTestMobileBERTTextClassifierModel(type: Download) {\n    src 'https://storage.googleapis.com/download.tensorflow' +\n            '.org/models/tflite/task_library/text_classification/android/mobilebert.tflite'\n    dest project.ext.TEST_ASSET_DIR + '/mobilebert.tflite'\n    overwrite false\n}\n\npreBuild.dependsOn downloadMobileBERTTextClassifierModel, downloadWorkVecTextClassifierModel,\n        downloadTestWorkVecTextClassifierModel, downloadTestMobileBERTTextClassifierModel\n\n"
  },
  {
    "path": "lite/examples/text_classification/android/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "lite/examples/text_classification/android/app/src/androidTest/java/org/tensorflow/lite/examples/textclassification/TextClassifierInstrumentationTest.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.textclassification\n\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.ext.junit.runners.AndroidJUnit4\n\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\nimport org.junit.Assert.*\nimport org.tensorflow.lite.support.label.Category\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass TextClassifierInstrumentationTest {\n\n    val testText = \"This was a triumph. I\\\\'m making a note here, HUGE SUCCESS. It\\\\'s hard to \" +\n            \"overstate my satisfaction.\"\n\n    val nlClassifierPositiveConfidence = 0.5736692f\n    val nlClassifierNegativeConfidence = 0.4263308f\n\n    @Test\n    fun TextClassificationHelperReturnsConsistentConfidenceResults() {\n        val textClassificationHelper =\n            TextClassificationHelper(\n                context = InstrumentationRegistry.getInstrumentation().context,\n                listener = object : TextClassificationHelper.TextResultsListener {\n                    override fun onError(error: String) {\n                        // no op\n                    }\n\n                    override fun onResult(results: List<Category>, inferenceTime: Long) {\n                        assertEquals(results[0].score, nlClassifierNegativeConfidence)\n                        assertEquals(results[1].score, nlClassifierPositiveConfidence)\n                    }\n                }\n            )\n\n        textClassificationHelper.classify(testText)\n    }\n}"
  },
  {
    "path": "lite/examples/text_classification/android/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.tensorflow.lite.examples.textclassification\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:taskAffinity=\"\"\n        android:theme=\"@style/AppTheme\">\n        <activity\n            android:name=\".MainActivity\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "lite/examples/text_classification/android/app/src/main/java/org/tensorflow/lite/examples/textclassification/MainActivity.kt",
    "content": "\n/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.textclassification\n\nimport android.os.Build\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.AdapterView\nimport android.widget.RadioGroup\nimport android.widget.Toast\nimport androidx.appcompat.app.AppCompatActivity\nimport com.google.android.material.bottomsheet.BottomSheetBehavior\nimport org.tensorflow.lite.examples.textclassification.databinding.ActivityMainBinding\nimport org.tensorflow.lite.support.label.Category\n\nclass MainActivity : AppCompatActivity() {\n\n    private var _activityMainBinding: ActivityMainBinding? = null\n    private val activityMainBinding get() = _activityMainBinding!!\n    private lateinit var classifierHelper: TextClassificationHelper\n    private val adapter by lazy {\n        ResultsAdapter()\n    }\n\n    private val listener = object : TextClassificationHelper.TextResultsListener {\n        override fun onResult(results: List<Category>, inferenceTime: Long) {\n            runOnUiThread {\n                activityMainBinding.bottomSheetLayout.inferenceTimeVal.text =\n                    String.format(\"%d ms\", inferenceTime)\n\n                adapter.resultsList = results.sortedByDescending {\n                    it.score\n                }\n\n                adapter.notifyDataSetChanged()\n            }\n        }\n\n        override fun onError(error: String) {\n            Toast.makeText(this@MainActivity, error, Toast.LENGTH_SHORT).show()\n        }\n    }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        _activityMainBinding = ActivityMainBinding.inflate(layoutInflater)\n        setContentView(activityMainBinding.root)\n\n        // Create the classification helper that will do the heavy lifting\n        classifierHelper = TextClassificationHelper(\n            context = this@MainActivity,\n            listener = listener)\n\n        // Classify the text in the TextEdit box (or the default if nothing is added)\n        // on button click.\n        activityMainBinding.classifyBtn.setOnClickListener {\n            if (activityMainBinding.inputText.text.isNullOrEmpty()) {\n                classifierHelper.classify(getString(R.string.default_edit_text))\n            }\n            else {\n                classifierHelper.classify(activityMainBinding.inputText.text.toString())\n            }\n        }\n\n        activityMainBinding.results.adapter = adapter\n        initBottomeSheetControls()\n    }\n\n    private fun initBottomeSheetControls() {\n        val behavior = BottomSheetBehavior.from(activityMainBinding.bottomSheetLayout.bottomSheetLayout)\n        behavior.state = BottomSheetBehavior.STATE_EXPANDED\n        // When clicked, change the underlying hardware used for inference. Current options are CPU\n        // and NNAPI. GPU is another available option, but when using this option you will need\n        // to initialize the classifier on the thread that does the classifying.\n        activityMainBinding.bottomSheetLayout.spinnerDelegate.onItemSelectedListener =\n            object : AdapterView.OnItemSelectedListener {\n                override fun onItemSelected(\n                    parent: AdapterView<*>?,\n                    view: View?,\n                    position: Int,\n                    id: Long\n                ) {\n                    classifierHelper.currentDelegate = position\n                    classifierHelper.initClassifier()\n                }\n\n                override fun onNothingSelected(p0: AdapterView<*>?) {\n                    /* no op */\n                }\n            }\n\n        activityMainBinding.bottomSheetLayout.spinnerDelegate.setSelection(\n            0,\n            false\n        )\n\n        // Allows the user to switch between the classification models that are available.\n        activityMainBinding.bottomSheetLayout.modelSelector.setOnCheckedChangeListener(\n            object : RadioGroup.OnCheckedChangeListener {\n                override fun onCheckedChanged(group: RadioGroup?, checkedId: Int) {\n                    when (checkedId) {\n                        R.id.wordvec -> {\n                            classifierHelper.currentModel = TextClassificationHelper.WORD_VEC\n                            classifierHelper.initClassifier()\n                        }\n                        R.id.mobilebert -> {\n                            classifierHelper.currentModel = TextClassificationHelper.MOBILEBERT\n                            classifierHelper.initClassifier()\n                        }\n                    }\n                }\n            }\n        )\n    }\n\n    override fun onBackPressed() {\n        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {\n            // Workaround for Android Q memory leak issue in IRequestFinishCallback$Stub.\n            // (https://issuetracker.google.com/issues/139738913)\n            finishAfterTransition()\n        } else {\n            super.onBackPressed()\n        }\n    }\n}"
  },
  {
    "path": "lite/examples/text_classification/android/app/src/main/java/org/tensorflow/lite/examples/textclassification/ResultsAdapter.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.textclassification\n\nimport android.view.LayoutInflater\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport org.tensorflow.lite.examples.textclassification.databinding.ItemClassificationBinding\nimport org.tensorflow.lite.support.label.Category\n\nclass ResultsAdapter : RecyclerView.Adapter<ResultsAdapter.ViewHolder>() {\n\n    var resultsList: List<Category> = emptyList()\n\n    class ViewHolder(private val binding: ItemClassificationBinding) :\n        RecyclerView.ViewHolder(binding.root) {\n        fun bind(label: String, score: Float) {\n            with(binding) {\n                result.text = binding.root.context.getString(\n                    R.string.result_display_text,\n                    label,\n                    score)\n            }\n        }\n    }\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {\n        val binding =\n            ItemClassificationBinding.inflate(\n                LayoutInflater.from(parent.context),\n                parent,\n                false)\n        return ViewHolder(binding)\n    }\n\n    override fun onBindViewHolder(holder: ViewHolder, position: Int) {\n        val category = resultsList[position]\n        holder.bind(category.label, category.score)\n    }\n\n    override fun getItemCount() = resultsList.size\n}"
  },
  {
    "path": "lite/examples/text_classification/android/app/src/main/java/org/tensorflow/lite/examples/textclassification/TextClassificationHelper.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *             http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.tensorflow.lite.examples.textclassification\n\nimport android.content.Context\nimport android.os.SystemClock\nimport org.tensorflow.lite.support.label.Category\nimport org.tensorflow.lite.task.core.BaseOptions\nimport org.tensorflow.lite.task.text.nlclassifier.BertNLClassifier\nimport org.tensorflow.lite.task.text.nlclassifier.NLClassifier\nimport java.util.concurrent.ScheduledThreadPoolExecutor\n\nclass TextClassificationHelper(\n    var currentDelegate: Int = 0,\n    var currentModel: String = WORD_VEC,\n    val context: Context,\n    val listener: TextResultsListener,\n) {\n    // There are two different classifiers here to support both the Average Word Vector\n    // model (NLClassifier) and the MobileBERT model (BertNLClassifier). Model selection\n    // can be changed from the UI bottom sheet.\n    private lateinit var bertClassifier: BertNLClassifier\n    private lateinit var nlClassifier: NLClassifier\n\n    private lateinit var executor: ScheduledThreadPoolExecutor\n\n    init {\n        initClassifier()\n    }\n\n    fun initClassifier() {\n        val baseOptionsBuilder = BaseOptions.builder()\n\n        // Use the specified hardware for running the model. Default to CPU.\n        // Possible to also use a GPU delegate, but this requires that the classifier be created\n        // on the same thread that is using the classifier, which is outside of the scope of this\n        // sample's design.\n        when (currentDelegate) {\n            DELEGATE_CPU -> {\n                // Default\n            }\n            DELEGATE_NNAPI -> {\n                baseOptionsBuilder.useNnapi()\n            }\n        }\n\n        val baseOptions = baseOptionsBuilder.build()\n\n        // Directions for generating both models can be found at\n        // https://www.tensorflow.org/lite/models/modify/model_maker/text_classification\n        if( currentModel == MOBILEBERT ) {\n            val options = BertNLClassifier.BertNLClassifierOptions\n                .builder()\n                .setBaseOptions(baseOptions)\n                .build()\n\n            bertClassifier = BertNLClassifier.createFromFileAndOptions(\n                context,\n                MOBILEBERT,\n                options)\n        } else if (currentModel == WORD_VEC) {\n            val options = NLClassifier.NLClassifierOptions.builder()\n                .setBaseOptions(baseOptions)\n                .build()\n\n            nlClassifier = NLClassifier.createFromFileAndOptions(\n                context,\n                WORD_VEC,\n                options)\n        }\n    }\n\n    fun classify(text: String) {\n        executor = ScheduledThreadPoolExecutor(1)\n\n        executor.execute {\n            val results: List<Category>\n            // inferenceTime is the amount of time, in milliseconds, that it takes to\n            // classify the input text.\n            var inferenceTime = SystemClock.uptimeMillis()\n\n            // Use the appropriate classifier based on the selected model\n            if(currentModel == MOBILEBERT) {\n                results = bertClassifier.classify(text)\n            } else {\n                results = nlClassifier.classify(text)\n            }\n\n            inferenceTime = SystemClock.uptimeMillis() - inferenceTime\n\n            listener.onResult(results, inferenceTime)\n        }\n    }\n\n    interface TextResultsListener {\n        fun onError(error: String)\n        fun onResult(results: List<Category>, inferenceTime: Long)\n    }\n\n    companion object {\n        const val DELEGATE_CPU = 0\n        const val DELEGATE_NNAPI = 1\n        const val WORD_VEC = \"wordvec.tflite\"\n        const val MOBILEBERT = \"mobilebert.tflite\"\n    }\n}"
  },
  {
    "path": "lite/examples/text_classification/android/app/src/main/res/drawable/ic_minus.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13H5v-2h14v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/text_classification/android/app/src/main/res/drawable/ic_plus.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/text_classification/android/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:background=\"@android:color/transparent\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <androidx.appcompat.widget.LinearLayoutCompat\n            android:layout_height=\"match_parent\"\n            android:layout_width=\"match_parent\"\n            android:layout_margin=\"@dimen/default_margin\"\n            android:layout_below=\"@id/toolbar\"\n            android:orientation=\"vertical\">\n\n            <com.google.android.material.textfield.TextInputEditText\n                android:id=\"@+id/input_text\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/text_input_height\"\n                android:gravity=\"top\"\n                android:hint=\"@string/default_edit_text\"/>\n\n            <com.google.android.material.button.MaterialButton\n                android:id=\"@+id/classify_btn\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/classify_btn_text\" />\n\n            <androidx.recyclerview.widget.RecyclerView\n                android:id=\"@+id/results\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"@dimen/default_margin\"\n                app:layoutManager=\"androidx.recyclerview.widget.LinearLayoutManager\"\n                tools:listitem=\"@layout/item_classification\"/>\n\n        </androidx.appcompat.widget.LinearLayoutCompat>\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:layout_alignParentTop=\"true\"\n            android:background=\"@color/toolbar_background\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/tfl_logo\" />\n        </androidx.appcompat.widget.Toolbar>\n\n    </RelativeLayout>\n\n    <include\n        android:id=\"@+id/bottom_sheet_layout\"\n        layout=\"@layout/info_bottom_sheet\" />\n</androidx.coordinatorlayout.widget.CoordinatorLayout>"
  },
  {
    "path": "lite/examples/text_classification/android/app/src/main/res/layout/info_bottom_sheet.xml",
    "content": "<!--\n    ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n    ~\n    ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n    ~ you may not use this file except in compliance with the License.\n    ~ You may obtain a copy of the License at\n    ~\n    ~       http://www.apache.org/licenses/LICENSE-2.0\n    ~\n    ~ Unless required by applicable law or agreed to in writing, software\n    ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n    ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    ~ See the License for the specific language governing permissions and\n    ~ limitations under the License.\n-->\n<androidx.core.widget.NestedScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/bottom_sheet_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_gravity=\"center_horizontal\"\n    android:background=\"@color/bottom_sheet_background\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:padding=\"@dimen/bottom_sheet_padding\"\n    android:layout_height=\"wrap_content\"\n    app:behavior_hideable=\"false\"\n    app:behavior_peekHeight=\"@dimen/bottom_sheet_peek_height\"\n    app:layout_behavior=\"com.google.android.material.bottomsheet.BottomSheetBehavior\">\n\n    <androidx.appcompat.widget.LinearLayoutCompat\n        android:orientation=\"vertical\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <!-- Chevron indicating that the bottom sheet is expandable -->\n        <RelativeLayout\n            android:layout_height=\"wrap_content\"\n            android:layout_width=\"match_parent\">\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerHorizontal=\"true\"\n                android:layout_centerVertical=\"true\"\n                android:src=\"@drawable/icn_chevron_up\"\n                android:contentDescription=\"@string/alt_bottom_sheet_chevron\" />\n        </RelativeLayout>\n\n        <!-- Inference time row -->\n        <androidx.appcompat.widget.LinearLayoutCompat\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/default_margin\"\n            android:orientation=\"horizontal\">\n            <TextView\n                android:id=\"@+id/inference_time_label\"\n                android:text=\"@string/label_interence_time\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n            <TextView\n                android:id=\"@+id/inference_time_val\"\n                android:text=\"0ms\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"end\"\n                android:layout_centerVertical=\"true\"\n                android:textColor=\"@color/bottom_sheet_text_color\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\" />\n        </androidx.appcompat.widget.LinearLayoutCompat>\n\n        <!-- Delegate selection row -->\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/default_margin\">\n\n            <TextView\n                android:text=\"@string/label_delegate\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textSize=\"@dimen/bottom_sheet_text_size\"\n                android:textColor=\"@color/bottom_sheet_text_color\" />\n\n            <androidx.appcompat.widget.AppCompatSpinner\n                android:id=\"@+id/spinner_delegate\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:minWidth=\"@dimen/bottom_sheet_spinner_delegate_min_width\"\n                android:spinnerMode=\"dropdown\"\n                android:theme=\"@style/BottomSheetSpinnerItemStyle\"\n                android:layout_alignParentRight=\"true\"\n                android:entries=\"@array/delegate_spinner_titles\"/>\n\n        </RelativeLayout>\n\n        <!-- Model selection section -->\n        <RadioGroup\n            android:id=\"@+id/model_selector\"\n            android:layout_marginTop=\"@dimen/default_margin\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\">\n            <RadioButton\n                android:id=\"@+id/wordvec\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:checked=\"true\"\n                android:text=\"@string/model_wordvec\"/>\n\n            <RadioButton\n                android:id=\"@+id/mobilebert\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/model_mobilebert\"/>\n        </RadioGroup>\n    </androidx.appcompat.widget.LinearLayoutCompat>\n</androidx.core.widget.NestedScrollView>"
  },
  {
    "path": "lite/examples/text_classification/android/app/src/main/res/layout/item_classification.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<androidx.constraintlayout.widget.ConstraintLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <TextView\n        android:id=\"@+id/result\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"16dp\"\n        android:textColor=\"@color/black\"\n        android:textSize=\"16sp\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        android:textStyle=\"bold\"\n        />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "lite/examples/text_classification/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/text_classification/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "lite/examples/text_classification/android/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"purple_200\">#FFBB86FC</color>\n    <color name=\"purple_500\">#FF6200EE</color>\n    <color name=\"purple_700\">#FF3700B3</color>\n    <color name=\"teal_200\">#FF03DAC5</color>\n    <color name=\"teal_700\">#FF018786</color>\n    <color name=\"black\">#FF000000</color>\n    <color name=\"white\">#FFFFFFFF</color>\n    <color name=\"toolbar_background\">#EEEEEE</color>\n    <color name=\"ic_launcher_background\">#FFFFFF</color>\n\n    <color name=\"bottom_sheet_background\">#EEEEEE</color>\n    <color name=\"bottom_sheet_text_color\">@android:color/black</color>\n</resources>"
  },
  {
    "path": "lite/examples/text_classification/android/app/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Bottom Sheet -->\n    <dimen name=\"default_margin\">16dp</dimen>\n    <dimen name=\"bottom_sheet_text_size\">20sp</dimen>\n    <dimen name=\"bottom_sheet_padding\">16dp</dimen>\n    <dimen name=\"bottom_sheet_peek_height\">50dp</dimen>\n    <dimen name=\"bottom_sheet_control_btn_size\">48dp</dimen>\n    <dimen name=\"bottom_sheet_control_text_side_margin\">10dp</dimen>\n    <dimen name=\"bottom_sheet_spinner_delegate_min_width\">160dp</dimen>\n    <dimen name=\"bottom_sheet_spinner_model_min_width\">240dp</dimen>\n\n    <integer name=\"bottom_sheet_control_text_min_ems\">3</integer>\n\n    <dimen name=\"default_text_size\">20sp</dimen>\n    <dimen name=\"text_input_height\">240dp</dimen>\n</resources>"
  },
  {
    "path": "lite/examples/text_classification/android/app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">TFLite Text Classification</string>\n\n    <string name=\"classify_btn_text\">Classify</string>\n    <string name=\"default_edit_text\">Google has released 24 versions of the Android operating\n        system since 2008 and continues to make substantial investments to develop, grow,\n        and improve the OS.</string>\n    <string name=\"alt_bottom_sheet_chevron\">Bottom sheet expandable indicator</string>\n\n    <string name=\"label_interence_time\">Inference Time</string>\n    <string name=\"label_delegate\">Delegate</string>\n    <string name=\"label_models\">ML Model</string>\n\n    <string-array name=\"delegate_spinner_titles\">\n        <item>CPU</item>\n        <item>NNAPI</item>\n    </string-array>\n\n    <string name=\"model_wordvec\">AverageWordVec</string>\n    <string name=\"model_mobilebert\">MobileBERT</string>\n\n    <string name=\"result_display_text\">%1$s (%2$f)</string>\n</resources>"
  },
  {
    "path": "lite/examples/text_classification/android/app/src/main/res/values/themes.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.Material3.Light.NoActionBar\" />\n\n    <style name=\"BottomSheetSpinnerItemStyle\" parent=\"Widget.AppCompat.DropDownItem.Spinner\">\n        <item name=\"android:textSize\">@dimen/bottom_sheet_text_size</item>\n    </style>\n</resources>"
  },
  {
    "path": "lite/examples/text_classification/android/app/src/main/res/values-night/themes.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <!-- Base application theme. -->\n    <style name=\"Theme.TextClassification\" parent=\"Theme.MaterialComponents.DayNight.DarkActionBar\">\n        <!-- Primary brand color. -->\n        <item name=\"colorPrimary\">@color/purple_200</item>\n        <item name=\"colorPrimaryVariant\">@color/purple_700</item>\n        <item name=\"colorOnPrimary\">@color/black</item>\n        <!-- Secondary brand color. -->\n        <item name=\"colorSecondary\">@color/teal_200</item>\n        <item name=\"colorSecondaryVariant\">@color/teal_200</item>\n        <item name=\"colorOnSecondary\">@color/black</item>\n        <!-- Status bar color. -->\n        <item name=\"android:statusBarColor\" tools:targetApi=\"l\">?attr/colorPrimaryVariant</item>\n        <!-- Customize your theme here. -->\n    </style>\n</resources>"
  },
  {
    "path": "lite/examples/text_classification/android/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nplugins {\n    id 'com.android.application' version '7.1.2' apply false\n    id 'com.android.library' version '7.1.2' apply false\n    id 'org.jetbrains.kotlin.android' version '1.5.30' apply false\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}"
  },
  {
    "path": "lite/examples/text_classification/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/text_classification/android/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app\"s APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official\n# Enables namespacing of each library's R class so that its R class includes only the\n# resources declared in the library itself and none from the library's dependencies,\n# thereby reducing the size of the R class for that library\nandroid.nonTransitiveRClass=true"
  },
  {
    "path": "lite/examples/text_classification/android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/text_classification/android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/text_classification/android/settings.gradle",
    "content": "pluginManagement {\n    repositories {\n        gradlePluginPortal()\n        google()\n        mavenCentral()\n    }\n}\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\nrootProject.name = \"Text Classification\"\ninclude ':app'\n"
  },
  {
    "path": "lite/examples/text_classification/ios/Podfile",
    "content": "platform :ios, '12.0'\n\ntarget 'TextClassification' do\n  use_frameworks!\n\n  pod 'TensorFlowLiteTaskText'\n\nend\n"
  },
  {
    "path": "lite/examples/text_classification/ios/README.md",
    "content": "# TensorFlow Lite text classification iOS sample\n\n<img src=\"screenshot.png\" />\n\n## Requirements\n\n*  Xcode 10.3 (installed on a Mac machine)\n*  An iOS Simulator running iOS 12 or above\n*  Xcode command-line tools (run ```xcode-select --install```)\n*  CocoaPods (run ```sudo gem install cocoapods```)\n\n## Build and run\n\n1. Clone the TensorFlow examples GitHub repository to your computer to get the\ndemo\napplication.<br/>\n```git clone https://github.com/tensorflow/examples```\n1. Install the pod to generate the workspace file:<br/>\n```cd examples/lite/examples/text_classification/ios && pod install```<br/>\nNote: If you have installed this pod before and that command doesn't work, try ```pod update```.<br/>\nAt the end of this step you should have a directory called ```TextClassification.xcworkspace```.\n1. Open the project in Xcode with the following command:<br/>\n```open TextClassification.xcworkspace```<br/>\nThis launches Xcode and opens the ```TextClassification``` project.\n1. Select `Product -> Run` to install the app on an iOS Simulator or a physical\ndevice.\n"
  },
  {
    "path": "lite/examples/text_classification/ios/TextClassification/AppDelegate.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n  var window: UIWindow?\n\n}\n\n"
  },
  {
    "path": "lite/examples/text_classification/ios/TextClassification/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\"images\":[{\"size\":\"60x60\",\"expected-size\":\"180\",\"filename\":\"180.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"40x40\",\"expected-size\":\"80\",\"filename\":\"80.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"40x40\",\"expected-size\":\"120\",\"filename\":\"120.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"60x60\",\"expected-size\":\"120\",\"filename\":\"120.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"57x57\",\"expected-size\":\"57\",\"filename\":\"57.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"58\",\"filename\":\"58.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"29x29\",\"expected-size\":\"29\",\"filename\":\"29.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"87\",\"filename\":\"87.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"57x57\",\"expected-size\":\"114\",\"filename\":\"114.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"60\",\"filename\":\"60.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"iphone\",\"scale\":\"3x\"},{\"size\":\"1024x1024\",\"filename\":\"1024.png\",\"expected-size\":\"1024\",\"idiom\":\"ios-marketing\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"scale\":\"1x\"},{\"size\":\"40x40\",\"expected-size\":\"80\",\"filename\":\"80.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"72x72\",\"expected-size\":\"72\",\"filename\":\"72.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"76x76\",\"expected-size\":\"152\",\"filename\":\"152.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"50x50\",\"expected-size\":\"100\",\"filename\":\"100.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"29x29\",\"expected-size\":\"58\",\"filename\":\"58.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"76x76\",\"expected-size\":\"76\",\"filename\":\"76.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"29x29\",\"expected-size\":\"29\",\"filename\":\"29.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"50x50\",\"expected-size\":\"50\",\"filename\":\"50.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"72x72\",\"expected-size\":\"144\",\"filename\":\"144.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"40x40\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"83.5x83.5\",\"expected-size\":\"167\",\"filename\":\"167.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"},{\"size\":\"20x20\",\"expected-size\":\"20\",\"filename\":\"20.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"1x\"},{\"size\":\"20x20\",\"expected-size\":\"40\",\"filename\":\"40.png\",\"folder\":\"Assets.xcassets/AppIcon.appiconset/\",\"idiom\":\"ipad\",\"scale\":\"2x\"}]}"
  },
  {
    "path": "lite/examples/text_classification/ios/TextClassification/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "lite/examples/text_classification/ios/TextClassification/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"13122.16\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"13104.12\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "lite/examples/text_classification/ios/TextClassification/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"18122\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"FY1-QU-hgX\">\n    <device id=\"retina6_1\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"18093\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"System colors in document resources\" minToolsVersion=\"11.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--TFL Text Classification-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"ViewController\" customModule=\"TFL_TextClassification\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"414\" height=\"896\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <textField opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"249\" contentHorizontalAlignment=\"left\" contentVerticalAlignment=\"center\" borderStyle=\"roundedRect\" placeholder=\"Classify text\" textAlignment=\"natural\" minimumFontSize=\"17\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"nZM-FA-Zjl\">\n                                <rect key=\"frame\" x=\"8\" y=\"836\" width=\"336\" height=\"34\"/>\n                                <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"14\"/>\n                                <textInputTraits key=\"textInputTraits\" returnKeyType=\"done\"/>\n                            </textField>\n                            <tableView clipsSubviews=\"YES\" contentMode=\"scaleToFill\" alwaysBounceVertical=\"YES\" dataMode=\"prototypes\" style=\"plain\" separatorStyle=\"default\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"28\" sectionFooterHeight=\"28\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"pUW-WU-S9u\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"88\" width=\"414\" height=\"740\"/>\n                                <color key=\"backgroundColor\" systemColor=\"systemBackgroundColor\"/>\n                            </tableView>\n                            <button opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" buttonType=\"system\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"8rU-8g-wT5\">\n                                <rect key=\"frame\" x=\"352\" y=\"836\" width=\"54\" height=\"34\"/>\n                                <state key=\"normal\" title=\"Classify\"/>\n                                <connections>\n                                    <action selector=\"tapClassify:\" destination=\"BYZ-38-t0r\" eventType=\"touchUpInside\" id=\"0Yf-qU-uwF\"/>\n                                </connections>\n                            </button>\n                        </subviews>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <constraints>\n                            <constraint firstItem=\"8rU-8g-wT5\" firstAttribute=\"top\" secondItem=\"nZM-FA-Zjl\" secondAttribute=\"top\" id=\"9oH-Aa-2l0\"/>\n                            <constraint firstItem=\"nZM-FA-Zjl\" firstAttribute=\"top\" secondItem=\"pUW-WU-S9u\" secondAttribute=\"bottom\" constant=\"8\" id=\"Ex0-jj-Wkf\"/>\n                            <constraint firstItem=\"nZM-FA-Zjl\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" constant=\"8\" id=\"I6r-au-B4U\"/>\n                            <constraint firstItem=\"nZM-FA-Zjl\" firstAttribute=\"bottom\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"bottom\" constant=\"8\" id=\"Lu6-pn-6nH\"/>\n                            <constraint firstItem=\"pUW-WU-S9u\" firstAttribute=\"leading\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"leading\" id=\"aHW-JJ-UOs\"/>\n                            <constraint firstItem=\"6Tk-OE-BBY\" firstAttribute=\"trailing\" secondItem=\"8rU-8g-wT5\" secondAttribute=\"trailing\" constant=\"8\" id=\"apR-ET-XFX\"/>\n                            <constraint firstItem=\"8rU-8g-wT5\" firstAttribute=\"leading\" secondItem=\"nZM-FA-Zjl\" secondAttribute=\"trailing\" constant=\"8\" symbolic=\"YES\" id=\"gVV-u1-1dP\"/>\n                            <constraint firstItem=\"pUW-WU-S9u\" firstAttribute=\"top\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"top\" id=\"rcQ-pQ-RMI\"/>\n                            <constraint firstItem=\"pUW-WU-S9u\" firstAttribute=\"trailing\" secondItem=\"6Tk-OE-BBY\" secondAttribute=\"trailing\" id=\"sJY-qg-kMR\"/>\n                            <constraint firstItem=\"8rU-8g-wT5\" firstAttribute=\"bottom\" secondItem=\"nZM-FA-Zjl\" secondAttribute=\"bottom\" id=\"xZS-UQ-3Bm\"/>\n                        </constraints>\n                    </view>\n                    <navigationItem key=\"navigationItem\" title=\"TFL Text Classification\" id=\"oaa-bg-hKo\"/>\n                    <connections>\n                        <outlet property=\"tableView\" destination=\"pUW-WU-S9u\" id=\"yBB-Od-M0K\"/>\n                        <outlet property=\"textField\" destination=\"nZM-FA-Zjl\" id=\"2AA-GC-miW\"/>\n                        <outlet property=\"textFieldBottomConstraint\" destination=\"Lu6-pn-6nH\" id=\"kfQ-x5-oRK\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"1042.0289855072465\" y=\"138.61607142857142\"/>\n        </scene>\n        <!--Navigation Controller-->\n        <scene sceneID=\"gSB-fK-87g\">\n            <objects>\n                <navigationController automaticallyAdjustsScrollViewInsets=\"NO\" id=\"FY1-QU-hgX\" sceneMemberID=\"viewController\">\n                    <toolbarItems/>\n                    <navigationBar key=\"navigationBar\" contentMode=\"scaleToFill\" insetsLayoutMarginsFromSafeArea=\"NO\" id=\"c5o-X8-QDu\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"44\" width=\"414\" height=\"44\"/>\n                        <autoresizingMask key=\"autoresizingMask\"/>\n                    </navigationBar>\n                    <nil name=\"viewControllers\"/>\n                    <connections>\n                        <segue destination=\"BYZ-38-t0r\" kind=\"relationship\" relationship=\"rootViewController\" id=\"RJb-Gv-YNb\"/>\n                    </connections>\n                </navigationController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"RPB-y3-xT7\" userLabel=\"First Responder\" customClass=\"UIResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"131.8840579710145\" y=\"138.61607142857142\"/>\n        </scene>\n    </scenes>\n    <color key=\"tintColor\" red=\"0.93044394249999995\" green=\"0.47058212760000001\" blue=\"0.18419533969999999\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"displayP3\"/>\n    <resources>\n        <systemColor name=\"systemBackgroundColor\">\n            <color white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n        </systemColor>\n    </resources>\n</document>\n"
  },
  {
    "path": "lite/examples/text_classification/ios/TextClassification/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>UIUserInterfaceStyle</key>\n\t<string>Light</string>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "lite/examples/text_classification/ios/TextClassification/ViewController.swift",
    "content": "// Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport UIKit\nimport TensorFlowLiteTaskText\n\nclass ViewController: UIViewController {\n\n  @IBOutlet weak var tableView: UITableView!\n  @IBOutlet weak var textField: UITextField!\n  @IBOutlet weak var textFieldBottomConstraint: NSLayoutConstraint!\n\n    private var classifier: TFLNLClassifier?\n\n    func loadModel() {\n      guard let modelPath = Bundle.main.path(\n        forResource: \"text_classification\", ofType: \"tflite\") else { return }\n      let options = TFLNLClassifierOptions()\n      self.classifier = TFLNLClassifier.nlClassifier(modelPath: modelPath, options: options)\n    }\n\n  private var results: [ClassificationResult] = []\n\n  override func viewDidLoad() {\n    super.viewDidLoad()\n\n    navigationController?.navigationBar.barTintColor =\n        UIColor(red: 1, green: 0x6F / 0xFF, blue: 0, alpha: 1)\n    navigationController?.navigationBar.titleTextAttributes = [\n      NSAttributedString.Key.foregroundColor: UIColor.white\n    ]\n    navigationController?.navigationBar.isTranslucent = false\n\n    tableView.register(UITableViewCell.self, forCellReuseIdentifier: \"UITableViewCell\")\n    tableView.dataSource = self\n    textField.delegate = self\n\n    // Initialize a TextClassification instance\n    loadModel()\n  }\n\n  override func viewDidAppear(_ animated: Bool) {\n    super.viewDidAppear(animated)\n    NotificationCenter.default.addObserver(self,\n                                           selector: #selector(keyboardWillShow(_:)),\n                                           name: UIResponder.keyboardWillShowNotification,\n                                           object: nil)\n    NotificationCenter.default.addObserver(self,\n                                           selector: #selector(keyboardWillHide(_:)),\n                                           name: UIResponder.keyboardWillHideNotification,\n                                           object: nil)\n  }\n\n  override func viewDidDisappear(_ animated: Bool) {\n    super.viewDidDisappear(animated)\n    NotificationCenter.default.removeObserver(self)\n  }\n  \n  /// Action when user tap the \"Classify\" button.\n  @IBAction func tapClassify(_ sender: Any) {\n    guard let text = textField.text else { return }\n    if text.count == 0 { return }\n    classify(text: text)\n  }\n    \n  /// Classify the text and display the result.\n  private func classify(text: String) {\n    guard let classifier = self.classifier else { return }\n    \n    // Run TF Lite inference in a background thread to avoid blocking app UI\n    DispatchQueue.global(qos: .userInitiated).async {\n        let classifierResults = classifier.classify(text: text)\n        let result = ClassificationResult(text: text, results: classifierResults)\n        self.results.append(result)\n\n        DispatchQueue.main.async {\n            // Return to main thread to update the UI.\n            self.textField.text = nil\n            self.tableView.reloadData()\n        }\n    }\n  }\n\n}\n\nextension ViewController: UITableViewDataSource {\n\n  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {\n    return results.count\n  }\n\n  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {\n    let result = results[indexPath.row]\n    let cell = tableView.dequeueReusableCell(withIdentifier: \"UITableViewCell\", for: indexPath)\n    var displayText = \"Input: \\(result.text)\\nOutput:\\n\"\n    for (key, value) in result.results {\n      displayText.append(\"\\t\\(key): \\(value)\\n\")\n    }\n    cell.textLabel?.text = displayText.trimmingCharacters(in: .whitespacesAndNewlines)\n    cell.textLabel?.numberOfLines = 0\n    return cell\n  }\n\n}\n\nextension ViewController: UITextFieldDelegate {\n\n  func textFieldShouldReturn(_ textField: UITextField) -> Bool {\n    if let text = textField.text?.trimmingCharacters(in: .whitespacesAndNewlines), !text.isEmpty {\n      classify(text: text)\n    }\n    textField.resignFirstResponder()\n    return true\n  }\n\n  @objc func keyboardWillShow(_ notification: Notification) {\n    if let keyboardFrame =\n        notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {\n      let keyboardRectangle = keyboardFrame.cgRectValue\n      let keyboardHeight = keyboardRectangle.height\n      textFieldBottomConstraint.constant = -keyboardHeight\n      UIView.animate(withDuration: 0.2) {\n        self.view.layoutIfNeeded()\n      }\n    }\n  }\n\n  @objc func keyboardWillHide(_ notification: Notification) {\n    textFieldBottomConstraint.constant = 0\n    UIView.animate(withDuration: 0.2) {\n      self.view.layoutIfNeeded()\n    }\n  }\n\n}\n\nstruct ClassificationResult {\n\n  var text: String\n  var results: [String: NSNumber]\n\n}\n"
  },
  {
    "path": "lite/examples/text_classification/ios/TextClassification.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 51;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t7F2AAA5122FAB86900C66F75 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F2AAA5022FAB86900C66F75 /* AppDelegate.swift */; };\n\t\t7F2AAA5322FAB86900C66F75 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F2AAA5222FAB86900C66F75 /* ViewController.swift */; };\n\t\t7F2AAA5622FAB86900C66F75 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7F2AAA5422FAB86900C66F75 /* Main.storyboard */; };\n\t\t7F2AAA5822FAB86C00C66F75 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7F2AAA5722FAB86C00C66F75 /* Assets.xcassets */; };\n\t\t7F2AAA5B22FAB86C00C66F75 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7F2AAA5922FAB86C00C66F75 /* LaunchScreen.storyboard */; };\n\t\t8D7F3B3B252E430D0092CF30 /* text_classification.tflite in Resources */ = {isa = PBXBuildFile; fileRef = 8D7F3B3A252E41D60092CF30 /* text_classification.tflite */; };\n\t\t988E024BBDD5EDF43E0F931D /* Pods_TextClassification.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F7AD92C95AE1E2AE57F64D86 /* Pods_TextClassification.framework */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\t2AFC10A451D1067367ED5F89 /* Pods-TextClassification.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-TextClassification.release.xcconfig\"; path = \"Target Support Files/Pods-TextClassification/Pods-TextClassification.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t3145779E2698069303666999 /* Pods-TextClassification.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-TextClassification.debug.xcconfig\"; path = \"Target Support Files/Pods-TextClassification/Pods-TextClassification.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t7F2AAA4D22FAB86900C66F75 /* TFL TextClassification.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = \"TFL TextClassification.app\"; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t7F2AAA5022FAB86900C66F75 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t7F2AAA5222FAB86900C66F75 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = \"<group>\"; };\n\t\t7F2AAA5522FAB86900C66F75 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t7F2AAA5722FAB86C00C66F75 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t7F2AAA5A22FAB86C00C66F75 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t7F2AAA5C22FAB86C00C66F75 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t8D7F3B3A252E41D60092CF30 /* text_classification.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; path = text_classification.tflite; sourceTree = \"<group>\"; };\n\t\tF7AD92C95AE1E2AE57F64D86 /* Pods_TextClassification.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TextClassification.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t7F2AAA4A22FAB86900C66F75 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t988E024BBDD5EDF43E0F931D /* Pods_TextClassification.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t1D1C753F1A21986AD6BC22C4 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF7AD92C95AE1E2AE57F64D86 /* Pods_TextClassification.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7F2AAA4422FAB86900C66F75 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t7F2AAA4F22FAB86900C66F75 /* TextClassification */,\n\t\t\t\t7F2AAA4E22FAB86900C66F75 /* Products */,\n\t\t\t\tE31751D2CA91CF060D46ED54 /* Pods */,\n\t\t\t\t1D1C753F1A21986AD6BC22C4 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7F2AAA4E22FAB86900C66F75 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t7F2AAA4D22FAB86900C66F75 /* TFL TextClassification.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7F2AAA4F22FAB86900C66F75 /* TextClassification */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t7F2AAA5022FAB86900C66F75 /* AppDelegate.swift */,\n\t\t\t\t7F2AAA5222FAB86900C66F75 /* ViewController.swift */,\n\t\t\t\t7F2AAA5422FAB86900C66F75 /* Main.storyboard */,\n\t\t\t\t7F2AAA5722FAB86C00C66F75 /* Assets.xcassets */,\n\t\t\t\t7F2AAA5922FAB86C00C66F75 /* LaunchScreen.storyboard */,\n\t\t\t\t7F2AAA5C22FAB86C00C66F75 /* Info.plist */,\n\t\t\t\t8D7F3B3A252E41D60092CF30 /* text_classification.tflite */,\n\t\t\t);\n\t\t\tpath = TextClassification;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tE31751D2CA91CF060D46ED54 /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t3145779E2698069303666999 /* Pods-TextClassification.debug.xcconfig */,\n\t\t\t\t2AFC10A451D1067367ED5F89 /* Pods-TextClassification.release.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t7F2AAA4C22FAB86900C66F75 /* TextClassification */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 7F2AAA5F22FAB86C00C66F75 /* Build configuration list for PBXNativeTarget \"TextClassification\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t40E115FAA050A10A89659263 /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t7F5EEAE82333630A00A72F0E /* Download TF Lite model */,\n\t\t\t\t7F2AAA4922FAB86900C66F75 /* Sources */,\n\t\t\t\t7F2AAA4A22FAB86900C66F75 /* Frameworks */,\n\t\t\t\t7F2AAA4B22FAB86900C66F75 /* Resources */,\n\t\t\t\tACA70D4179FEEDAAF7830155 /* [CP] Embed Pods Frameworks */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = TextClassification;\n\t\t\tproductName = TextClassification;\n\t\t\tproductReference = 7F2AAA4D22FAB86900C66F75 /* TFL TextClassification.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t7F2AAA4522FAB86900C66F75 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 1030;\n\t\t\t\tLastUpgradeCheck = 1030;\n\t\t\t\tORGANIZATIONNAME = \"Google Inc.\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t7F2AAA4C22FAB86900C66F75 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 10.3;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 7F2AAA4822FAB86900C66F75 /* Build configuration list for PBXProject \"TextClassification\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 7F2AAA4422FAB86900C66F75;\n\t\t\tproductRefGroup = 7F2AAA4E22FAB86900C66F75 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t7F2AAA4C22FAB86900C66F75 /* TextClassification */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t7F2AAA4B22FAB86900C66F75 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t8D7F3B3B252E430D0092CF30 /* text_classification.tflite in Resources */,\n\t\t\t\t7F2AAA5B22FAB86C00C66F75 /* LaunchScreen.storyboard in Resources */,\n\t\t\t\t7F2AAA5822FAB86C00C66F75 /* Assets.xcassets in Resources */,\n\t\t\t\t7F2AAA5622FAB86900C66F75 /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t40E115FAA050A10A89659263 /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-TextClassification-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t7F5EEAE82333630A00A72F0E /* Download TF Lite model */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Download TF Lite model\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"# Download mnist.tflite from the internet if it's not exist.\\nTFLITE_FILE=${SRCROOT}/TextClassification/text_classification.tflite\\nif test -f \\\"$TFLITE_FILE\\\"; then\\n    echo \\\"INFO: text_classification.tflite existed. Skip downloading and use the local model.\\\"\\nelse\\n    curl -o ${TFLITE_FILE} https://storage.googleapis.com/download.tensorflow.org/models/tflite/task_library/text_classification/ios/text_classification_v2.tflite\\n    echo \\\"INFO: Downloaded text_classification.tflite to $TFLITE_FILE .\\\"\\nfi\\n\\n\";\n\t\t};\n\t\tACA70D4179FEEDAAF7830155 /* [CP] Embed Pods Frameworks */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-TextClassification/Pods-TextClassification-frameworks-${CONFIGURATION}-input-files.xcfilelist\",\n\t\t\t);\n\t\t\tname = \"[CP] Embed Pods Frameworks\";\n\t\t\toutputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-TextClassification/Pods-TextClassification-frameworks-${CONFIGURATION}-output-files.xcfilelist\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"${PODS_ROOT}/Target Support Files/Pods-TextClassification/Pods-TextClassification-frameworks.sh\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t7F2AAA4922FAB86900C66F75 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t7F2AAA5322FAB86900C66F75 /* ViewController.swift in Sources */,\n\t\t\t\t7F2AAA5122FAB86900C66F75 /* AppDelegate.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\t7F2AAA5422FAB86900C66F75 /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t7F2AAA5522FAB86900C66F75 /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t7F2AAA5922FAB86C00C66F75 /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t7F2AAA5A22FAB86C00C66F75 /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t7F2AAA5D22FAB86C00C66F75 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.4;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t7F2AAA5E22FAB86C00C66F75 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.4;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t7F2AAA6022FAB86C00C66F75 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 3145779E2698069303666999 /* Pods-TextClassification.debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tDEVELOPMENT_TEAM = PNR9X9TVFG;\n\t\t\t\tINFOPLIST_FILE = TextClassification/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.google.examples.TextClassification;\n\t\t\t\tPRODUCT_NAME = \"TFL TextClassification\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = Wildcard;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t7F2AAA6122FAB86C00C66F75 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 2AFC10A451D1067367ED5F89 /* Pods-TextClassification.release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tDEVELOPMENT_TEAM = PNR9X9TVFG;\n\t\t\t\tINFOPLIST_FILE = TextClassification/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.google.examples.TextClassification;\n\t\t\t\tPRODUCT_NAME = \"TFL TextClassification\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = Wildcard;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t7F2AAA4822FAB86900C66F75 /* Build configuration list for PBXProject \"TextClassification\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t7F2AAA5D22FAB86C00C66F75 /* Debug */,\n\t\t\t\t7F2AAA5E22FAB86C00C66F75 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t7F2AAA5F22FAB86C00C66F75 /* Build configuration list for PBXNativeTarget \"TextClassification\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t7F2AAA6022FAB86C00C66F75 /* Debug */,\n\t\t\t\t7F2AAA6122FAB86C00C66F75 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 7F2AAA4522FAB86900C66F75 /* Project object */;\n}\n"
  },
  {
    "path": "lite/examples/text_searcher/android/README.md",
    "content": "# Text Searcher Android sample\n\nThis sample app demonstrates how to use the TensorFlow Lite Task Library's Text\nSearcher API on Android. It works by using a model to embed the search query\ninto a high-dimensional vector representing the semantic meaning of the query.\nThen it uses\n[ScaNN (Scalable Nearest Neighbors)](https://github.com/google-research/google-research/tree/master/scann)\nto search for similar items from a predefined database.\n\n## Requirements\n\n*   Android Studio 4.2 or above (installed on a Linux, Mac or Windows machine)\n*   An Android device, or an Android Emulator\n\n## Build and run\n\n### Step 1. Clone the TensorFlow examples source code\n\nClone the TensorFlow examples GitHub repository to your computer to get the demo\napplication.\n\n```\ngit clone https://github.com/tensorflow/examples\n```\n\n### Step 2. Import the sample app to Android Studio\n\nOpen the TensorFlow source code in Android Studio. To do this, open Android\nStudio and select `Import Projects (Gradle, Eclipse ADT, etc.)`, setting the\nfolder to `examples/lite/examples/style_transfer/android`\n\n### Step 3. Run the Android app\n\nConnect the Android device to the computer and be sure to approve any ADB\npermission prompts that appear on your phone. Select `Run -> Run app.` Select\nthe deployment target in the connected devices to the device on which the app\nwill be installed. This will install the app on the device.\n\nTo test the app, open the app called `TFL Style Transfer` on your device.\nRe-installing the app may require you to uninstall the previous installations.\n\n## Resources used:\n\n*   [TensorFlow Lite](https://www.tensorflow.org/lite)\n*   [TensorFlow Task Library](https://www.tensorflow.org/lite/inference_with_metadata/task_library/overview)\n*   [CNN/DailyMail non-anonymized summarization dataset](https://github.com/abisee/cnn-dailymail)\n*   [TensorFlow Lite Model Maker](https://www.tensorflow.org/lite/guide/model_maker)\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/build.gradle",
    "content": "plugins {\n    id 'com.android.application'\n    id 'kotlin-android'\n    id 'de.undercouch.download'\n}\n\nandroid {\n    compileSdk 31\n\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.textsearcher\"\n        minSdk 23\n        targetSdk 31\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n    kotlinOptions {\n        jvmTarget = '1.8'\n    }\n}\n\ndependencies {\n    implementation 'androidx.core:core-ktx:1.7.0'\n    implementation 'androidx.appcompat:appcompat:1.4.1'\n    implementation 'com.google.android.material:material:1.5.0'\n    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'\n    implementation 'org.tensorflow:tensorflow-lite-task-text:0.4.0'\n    testImplementation 'junit:junit:4.+'\n    androidTestImplementation 'androidx.test.ext:junit:1.1.3'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n}\n\n// Download default models; if you wish to use your own models then\n// place them in the \"assets\" directory and comment out this line.\nproject.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'\napply from: 'download.gradle'\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/download.gradle",
    "content": "def textSearcherModelFile = \"text_searcher.tflite\"\ndef modelDownloadUrl = \"https://storage.googleapis.com/download.tensorflow.org/models/tflite_support/searcher/text_to_image_blogpost/cnn_daily_text_searcher.tflite\"\n\ntask downloadTextSearcherModel(type: Download) {\n    src \"${modelDownloadUrl}\"\n    dest project.ext.ASSET_DIR + \"/${textSearcherModelFile}\"\n    overwrite false\n}\n\npreBuild.dependsOn downloadTextSearcherModel\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/androidTest/java/org/tensorflow/lite/examples/textsearcher/TextSearcherClientTest.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.textsearcher\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.platform.app.InstrumentationRegistry\nimport org.junit.Assert.assertEquals\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.tensorflow.lite.examples.textsearcher.tflite.TextSearcherClient\n\n@RunWith(AndroidJUnit4::class)\nclass TextSearcherClientTest {\n  private lateinit var textSearcherClient: TextSearcherClient\n\n  @Before\n  fun setUp() {\n    textSearcherClient =\n      TextSearcherClient.create(InstrumentationRegistry.getInstrumentation().targetContext)\n  }\n\n  @Test\n  fun testSearchResult() {\n    val results =\n      textSearcherClient.search(\n        \"Shuttle will deliver final U.S. portion of the international space station\"\n      )\n\n    assert(results.isNotEmpty())\n    assertEquals(\n      \"http://edition.cnn.com:80/2010/US/11/02/space.station.anniversary/index.html\",\n      results[0].url\n    )\n    assertEquals(\n      \"http://www.dailymail.co.uk/news/article-2155446/The-Space-Shuttle-lands-Manhattan-Enterprise-arrives-Intrepid-begin-new-life-New-York-tourist-attraction.html\",\n      results[1].url\n    )\n    assertEquals(\n      \"http://www.dailymail.co.uk/sciencetech/article-2276943/NASA-prepare-Landsat-satellite-launch-reveal-hidden-beauty-Earths-landscapes-space.html\",\n      results[2].url\n    )\n    assertEquals(\n      \"http://edition.cnn.com/2010/TECH/space/01/28/space.shuttle.endeavour/index.html\",\n      results[3].url\n    )\n    assertEquals(\n      \"http://www.dailymail.co.uk/sciencetech/article-2642917/Virgin-FAA-sign-agreement-Spaceport-flights.html\",\n      results[4].url\n    )\n  }\n}\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.tensorflow.lite.examples.textsearcher\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n\n    <application\n        android:taskAffinity=\"\"\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/tfe_app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/Theme.TFLTextSearcher\"\n        android:usesCleartextTraffic=\"true\">\n        <activity\n            android:name=\".ui.WebviewActivity\"\n            android:exported=\"false\" />\n        <activity\n            android:name=\".ui.TextSearcherResultActivity\"\n            android:exported=\"false\" />\n        <activity\n            android:name=\".ui.MainActivity\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/java/org/tensorflow/lite/examples/textsearcher/tflite/TextSearcherClient.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.textsearcher.tflite\n\nimport android.content.Context\nimport java.io.Serializable\nimport org.tensorflow.lite.task.core.BaseOptions\nimport org.tensorflow.lite.task.processor.SearcherOptions\nimport org.tensorflow.lite.task.text.searcher.TextSearcher\n\nclass TextSearcherClient(private var textSearcher: TextSearcher) {\n\n  companion object {\n    private const val MODEL_PATH = \"text_searcher.tflite\"\n    private const val NUM_THREADS = 4\n    private const val MAX_RESULTS = 10\n    private const val IS_NORMALIZE = true\n\n    fun create(context: Context): TextSearcherClient {\n      val baseOptions = BaseOptions.builder().setNumThreads(NUM_THREADS).build()\n      val searchOptions =\n        SearcherOptions.builder().setMaxResults(MAX_RESULTS).setL2Normalize(IS_NORMALIZE).build()\n      val options =\n        TextSearcher.TextSearcherOptions.builder()\n          .setBaseOptions(baseOptions)\n          .setSearcherOptions(searchOptions)\n          .build()\n      val textSearcher = TextSearcher.createFromFileAndOptions(context, MODEL_PATH, options)\n      return TextSearcherClient(textSearcher)\n    }\n  }\n\n  fun search(query: String): List<Result> {\n    val results = mutableListOf<Result>()\n    val modelResults = textSearcher.search(query)\n\n    // Postprocess the model output to human readable class names\n    modelResults.forEach { results.add(Result(it.distance, String(it.metadata.array()))) }\n    return results\n  }\n\n  fun close() {\n    textSearcher.close()\n  }\n}\n\ndata class Result(val distance: Float, val url: String) : Serializable\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/java/org/tensorflow/lite/examples/textsearcher/ui/TextSearcherActivity.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.textsearcher.ui\n\nimport android.content.Intent\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.ImageButton\nimport android.widget.TextView\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.core.widget.doOnTextChanged\nimport androidx.recyclerview.widget.DividerItemDecoration\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport com.google.android.material.textfield.TextInputLayout\nimport java.io.Serializable\nimport org.tensorflow.lite.examples.textsearcher.R\nimport org.tensorflow.lite.examples.textsearcher.tflite.Result\nimport org.tensorflow.lite.examples.textsearcher.tflite.TextSearcherClient\n\nclass MainActivity : AppCompatActivity() {\n  private lateinit var presetQueryRecyclerView: RecyclerView\n  private lateinit var searchButton: ImageButton\n  private lateinit var searchQueryTextInput: TextInputLayout\n  private lateinit var presetQueries: Array<String>\n  private lateinit var presetQueryAdapter: PresetQueryAdapter\n  private lateinit var textSearcherClient: TextSearcherClient\n\n  override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n    setContentView(R.layout.activity_text_searcher)\n\n    presetQueryRecyclerView = findViewById(R.id.preset_query_recycler_view)\n    searchButton = findViewById(R.id.search_button)\n    searchQueryTextInput = findViewById(R.id.search_query_text_input)\n    textSearcherClient = TextSearcherClient.create(this)\n\n    // Load the preset queries into an array.\n    presetQueries = resources.getStringArray(R.array.tfe_preset_queries)\n\n    presetQueryAdapter = PresetQueryAdapter(presetQueries) { startSearch(presetQueries[it]) }\n    searchButton.setOnClickListener {\n      val query = searchQueryTextInput.editText?.text.toString()\n      startSearch(query)\n    }\n    initPresetQueryRecyclerView()\n    updateSearchButton()\n  }\n\n  // Start the text search logic.\n  private fun startSearch(queryContent: String) {\n    val results = textSearcherClient.search(queryContent)\n    openTextSearcherScreen(queryContent, results)\n  }\n\n  // Send search result to result activity.\n  private fun openTextSearcherScreen(queryContent: String, results: List<Result>) {\n    startActivity(\n      Intent(this, TextSearcherResultActivity::class.java).apply {\n        putExtra(getString(R.string.tfe_search_query_content), queryContent)\n        putExtra(getString(R.string.tfe_search_result_id), results as Serializable)\n      }\n    )\n  }\n\n  // Set up the RecyclerView that shows the preset search queries.\n  private fun initPresetQueryRecyclerView() {\n    presetQueryRecyclerView.layoutManager = LinearLayoutManager(this)\n    presetQueryRecyclerView.addItemDecoration(\n      DividerItemDecoration(this, DividerItemDecoration.VERTICAL)\n    )\n    presetQueryRecyclerView.adapter = presetQueryAdapter\n  }\n\n  // Update the search button status.\n  private fun updateSearchButton() {\n    searchButton.isEnabled = false\n    searchQueryTextInput.editText?.doOnTextChanged { text, _, _, _ ->\n      // Only allow search if the search query isn't empty.\n      searchButton.isEnabled = text?.trim()?.length ?: 0 > 0\n    }\n  }\n\n  override fun onDestroy() {\n    super.onDestroy()\n    textSearcherClient.close()\n  }\n}\n\nclass PresetQueryAdapter(private val preset: Array<String>, private val onClick: (Int) -> Unit) :\n  RecyclerView.Adapter<PresetQueryAdapter.PresetQueryViewHolder>() {\n\n  inner class PresetQueryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {\n    private val tvPreset: TextView = itemView.findViewById(R.id.tvPreset)\n\n    init {\n      itemView.setOnClickListener {\n        if (adapterPosition == RecyclerView.NO_POSITION) return@setOnClickListener\n        onClick.invoke(adapterPosition)\n      }\n    }\n\n    fun bind(content: String) {\n      tvPreset.text = content\n    }\n  }\n\n  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PresetQueryViewHolder {\n    val view =\n      LayoutInflater.from(parent.context).inflate(R.layout.preset_query_item_layout, parent, false)\n\n    return PresetQueryViewHolder(view)\n  }\n\n  override fun onBindViewHolder(holder: PresetQueryViewHolder, position: Int) {\n    holder.bind(preset[position])\n  }\n\n  override fun getItemCount(): Int {\n    return preset.size\n  }\n}\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/java/org/tensorflow/lite/examples/textsearcher/ui/TextSearcherResultActivity.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.textsearcher.ui\n\nimport android.content.Intent\nimport android.os.Bundle\nimport android.view.*\nimport android.widget.TextView\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.recyclerview.widget.DividerItemDecoration\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport org.tensorflow.lite.examples.textsearcher.R\nimport org.tensorflow.lite.examples.textsearcher.tflite.Result\n\nclass TextSearcherResultActivity : AppCompatActivity() {\n  private lateinit var searchQueryTextView: TextView\n  private lateinit var searchResultRecyclerView: RecyclerView\n  private lateinit var searchResultAdapter: SearchResultAdapter\n\n  override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n    setContentView(R.layout.activity_text_searcher_result)\n    searchQueryTextView = findViewById(R.id.tvQueryContent)\n    searchResultRecyclerView = findViewById(R.id.search_result_recycler_view)\n\n    val results =\n      intent.getSerializableExtra(getString(R.string.tfe_search_result_id)) as List<Result>\n    val queryContent = intent.getStringExtra(getString(R.string.tfe_search_query_content))\n\n    searchQueryTextView.text = queryContent\n    searchResultAdapter =\n      SearchResultAdapter(results) { position -> openSearchResultUrl(results[position].url) }\n    initSearchResultRecyclerView()\n  }\n\n  // Open a search result's metadata URL in an webview.\n  private fun openSearchResultUrl(url: String) {\n    startActivity(\n      Intent(this, WebviewActivity::class.java).apply {\n        putExtra(getString(R.string.tfe_target_url), url)\n      }\n    )\n  }\n\n  // Initialize the search result RecyclerView.\n  private fun initSearchResultRecyclerView() {\n    searchResultRecyclerView.layoutManager = LinearLayoutManager(this)\n    searchResultRecyclerView.addItemDecoration(\n      DividerItemDecoration(this, DividerItemDecoration.VERTICAL)\n    )\n    searchResultRecyclerView.adapter = searchResultAdapter\n  }\n\n  override fun onCreateOptionsMenu(menu: Menu?): Boolean {\n    menuInflater.inflate(R.menu.action_bar_menu, menu)\n    return true\n  }\n\n  override fun onOptionsItemSelected(item: MenuItem): Boolean =\n    when (item.itemId) {\n      R.id.action_close -> {\n        // The user chose the \"Close\" action, so close this activity.\n        finish()\n        true\n      }\n      else -> {\n        // If we got here, the user's action was not recognized.\n        // Invoke the superclass to handle it.\n        super.onOptionsItemSelected(item)\n      }\n    }\n}\n\nclass SearchResultAdapter(private val results: List<Result>, private val onClick: (Int) -> Unit) :\n  RecyclerView.Adapter<SearchResultAdapter.SearchResultViewHolder>() {\n\n  inner class SearchResultViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {\n    private val distanceTextView: TextView = itemView.findViewById(R.id.distance_text_view)\n    private val urlTextView: TextView = itemView.findViewById(R.id.url_text_view)\n\n    init {\n      itemView.setOnClickListener {\n        if (adapterPosition == RecyclerView.NO_POSITION) return@setOnClickListener\n        onClick.invoke(adapterPosition)\n      }\n    }\n\n    fun bind(result: Result) {\n      distanceTextView.text =\n        itemView.context.getString(R.string.tfe_tv_distance, result.distance.toString())\n      urlTextView.text = result.url\n    }\n  }\n\n  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchResultViewHolder {\n    val view =\n      LayoutInflater.from(parent.context)\n        .inflate(R.layout.searcher_result_item_layout, parent, false)\n    return SearchResultViewHolder(view)\n  }\n\n  override fun onBindViewHolder(holder: SearchResultViewHolder, position: Int) {\n    holder.bind(results[position])\n  }\n\n  override fun getItemCount(): Int {\n    return results.size\n  }\n}\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/java/org/tensorflow/lite/examples/textsearcher/ui/WebviewActivity.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.textsearcher.ui\n\nimport android.os.Bundle\nimport android.view.Menu\nimport android.view.MenuItem\nimport android.webkit.WebView\nimport android.webkit.WebViewClient\nimport androidx.appcompat.app.AppCompatActivity\nimport org.tensorflow.lite.examples.textsearcher.R\n\nclass WebviewActivity : AppCompatActivity() {\n  private lateinit var webview: WebView\n  override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n    setContentView(R.layout.activity_webview)\n\n    webview = findViewById(R.id.webview)\n    webview.webViewClient = WebViewClient()\n    intent.getStringExtra(getString(R.string.tfe_target_url))?.let { url -> webview.loadUrl(url) }\n  }\n\n  override fun onCreateOptionsMenu(menu: Menu?): Boolean {\n    menuInflater.inflate(R.menu.action_bar_menu, menu)\n    return true\n  }\n\n  override fun onOptionsItemSelected(item: MenuItem): Boolean =\n    when (item.itemId) {\n      R.id.action_close -> {\n        // The user chose the \"Close\" action, so close this activity.\n        finish()\n        true\n      }\n      else -> {\n        // If we got here, the user's action was not recognized.\n        // Invoke the superclass to handle it.\n        super.onOptionsItemSelected(item)\n      }\n    }\n}\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/res/drawable/ic_baseline_close_48.xml",
    "content": "<vector android:height=\"48dp\" android:tint=\"#FFFFFF\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"48dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/res/drawable/ic_baseline_search_48.xml",
    "content": "<vector android:height=\"48dp\" android:tint=\"?attr/colorControlNormal\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"48dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/res/drawable/ic_baseline_web_48.xml",
    "content": "<vector android:height=\"48dp\" android:tint=\"?attr/colorControlNormal\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"48dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM15,18L4,18v-4h11v4zM15,13L4,13L4,9h11v4zM20,18h-4L16,9h4v9z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path\n        android:fillColor=\"#3DDC84\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n</vector>\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path android:pathData=\"M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"85.84757\"\n                android:endY=\"92.4963\"\n                android:startX=\"42.9492\"\n                android:startY=\"49.59793\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/res/layout/activity_text_searcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".ui.MainActivity\">\n\n    <com.google.android.material.textfield.TextInputLayout\n        android:id=\"@+id/search_query_text_input\"\n        style=\"@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:hint=\"@string/tfe_tv_enter_your_search_query\"\n        android:paddingStart=\"@dimen/tfe_default_padding\"\n        android:paddingTop=\"@dimen/tfe_default_padding\"\n        android:paddingEnd=\"0dp\"\n        android:paddingBottom=\"@dimen/tfe_default_padding\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toLeftOf=\"@id/search_button\"\n        app:layout_constraintTop_toTopOf=\"parent\">\n\n        <com.google.android.material.textfield.TextInputEditText\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\" />\n\n    </com.google.android.material.textfield.TextInputLayout>\n\n    <ImageButton\n        android:id=\"@+id/search_button\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"0dp\"\n        android:background=\"@android:color/transparent\"\n        android:contentDescription=\"@null\"\n        android:paddingStart=\"@dimen/tfe_default_padding\"\n        android:paddingEnd=\"@dimen/tfe_default_padding\"\n        android:src=\"@drawable/ic_baseline_search_48\"\n        app:layout_constraintBottom_toBottomOf=\"@id/search_query_text_input\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"@id/search_query_text_input\" />\n\n    <TextView\n        android:id=\"@+id/tvNote\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:padding=\"@dimen/tfe_default_padding\"\n        android:text=\"@string/tfe_tv_note\"\n        android:textStyle=\"italic\"\n        app:layout_constraintTop_toBottomOf=\"@id/search_query_text_input\" />\n\n    <TextView\n        android:id=\"@+id/tvSuggestPreset\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:padding=\"@dimen/tfe_default_padding\"\n        android:text=\"@string/tfe_tv_suggest_preset\"\n        android:textStyle=\"bold\"\n        app:layout_constraintTop_toBottomOf=\"@id/tvNote\" />\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:background=\"?android:attr/listDivider\"\n        app:layout_constraintTop_toBottomOf=\"@id/tvSuggestPreset\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/preset_query_recycler_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/tvSuggestPreset\" />\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/res/layout/activity_text_searcher_result.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".ui.TextSearcherResultActivity\">\n\n    <TextView\n        android:id=\"@+id/tvSearchTitle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:padding=\"@dimen/tfe_default_padding\"\n        android:text=\"@string/tfe_tv_search_query\"\n        android:textStyle=\"bold\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <TextView\n        android:id=\"@+id/tvQueryContent\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:paddingStart=\"@dimen/tfe_default_padding\"\n        android:paddingEnd=\"@dimen/tfe_default_padding\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/tvSearchTitle\" />\n\n    <TextView\n        android:id=\"@+id/tvSearchResultTitle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:padding=\"@dimen/tfe_default_padding\"\n        android:text=\"@string/tfe_tv_search_result\"\n        android:textStyle=\"bold\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/tvQueryContent\" />\n\n    <TextView\n        android:id=\"@+id/tvSearchResultDescription\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:paddingStart=\"@dimen/tfe_default_padding\"\n        android:paddingEnd=\"@dimen/tfe_default_padding\"\n        android:paddingBottom=\"@dimen/tfe_default_padding\"\n        android:text=\"@string/tfe_tv_search_result_description\"\n        android:textStyle=\"italic\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/tvSearchResultTitle\" />\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:background=\"?android:attr/listDivider\"\n        app:layout_constraintTop_toBottomOf=\"@id/tvSearchResultDescription\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/search_result_recycler_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/tvSearchResultDescription\" />\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/res/layout/activity_webview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<WebView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/webview\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".ui.WebviewActivity\" />\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/res/layout/preset_query_item_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <TextView\n        android:padding=\"@dimen/tfe_default_padding\"\n        android:id=\"@+id/tvPreset\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/res/layout/searcher_result_item_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:padding=\"@dimen/tfe_default_padding\">\n\n    <ImageView\n        android:id=\"@+id/imgOpenUrl\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"0dp\"\n        android:contentDescription=\"@null\"\n        android:src=\"@drawable/ic_baseline_web_48\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <TextView\n        android:id=\"@+id/distance_text_view\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:textSize=\"@dimen/tfe_tv_distance\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toLeftOf=\"@id/imgOpenUrl\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <TextView\n        android:id=\"@+id/url_text_view\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"@dimen/tfe_default_padding\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toLeftOf=\"@id/imgOpenUrl\"\n        app:layout_constraintTop_toBottomOf=\"@id/distance_text_view\" />\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/res/menu/action_bar_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <item\n        android:id=\"@+id/action_close\"\n        android:icon=\"@drawable/ic_baseline_close_48\"\n        android:title=\"@string/tfe_menu_close\"\n        app:showAsAction=\"ifRoom\" />\n\n</menu>\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"tfe_color_primary\">#ffa800</color>\n    <color name=\"tfe_color_primary_dark\">#ff6f00</color>\n    <color name=\"tfe_color_accent\">#425066</color>\n    <color name=\"tfe_black\">#FF000000</color>\n    <color name=\"tfe_white\">#FFFFFFFF</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <dimen name=\"tfe_default_padding\">10dp</dimen>\n    <dimen name=\"tfe_tv_distance\">16sp</dimen>\n</resources>\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"tfe_app_name\">TFL Text Searcher</string>\n    <string name=\"tfe_tv_enter_your_search_query\">Enter your search query</string>\n    <string name=\"tfe_tv_note\">Note: Enter your query as a sentence. It works better than just keywords.</string>\n    <string name=\"tfe_tv_suggest_preset\">or select a preset query:</string>\n    <string name=\"tfe_tv_search_query\">Search query</string>\n    <string name=\"tfe_tv_search_result\">Search results</string>\n    <string name=\"tfe_tv_search_result_description\">Note: Most relevant results come first.</string>\n    <string name=\"tfe_tv_distance\">Distance: %1$s</string>\n    <string name=\"tfe_menu_close\">Close</string>\n    <string name=\"tfe_search_result_id\">search_result</string>\n    <string name=\"tfe_search_query_content\">search_query_content</string>\n    <string name=\"tfe_target_url\">target_url</string>\n    <string-array name=\"tfe_preset_queries\">\n        <item>The Airline Quality Rankings Report looks at the 14 largest U.S. airlines.</item>\n        <item>Valentine\\'s Day has evolved to incorporate more than just romantic interests.</item>\n        <item>Digital game distribution continues to grow, thanks to new titles and smartphone apps.</item>\n        <item>The incident is the latest in a spate of fires attributed to e-cigarettes.</item>\n        <item>Liverpool have received heavy criticism over the last week.</item>\n        <item>Space Shuttle Enterprise floated past New York City landmarks Wednesday as it made its way north.</item>\n        <item>Tips on how to recognise eating habits and understand real food needs.</item>\n        <item>Lung cancer is the second most common cancer in the UK.</item>\n        <item>Elephants were pictured enjoying a mud bath in Southern Mozambique.</item>\n        <item>Scientists suggest short bursts of daily exercise more effective than long infrequent sessions.</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "lite/examples/text_searcher/android/app/src/main/res/values/themes.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <!-- Base application theme. -->\n    <style name=\"Theme.TFLTextSearcher\" parent=\"Theme.MaterialComponents.DayNight.DarkActionBar\">\n        <!-- Primary brand color. -->\n        <item name=\"colorPrimary\">@color/tfe_color_primary</item>\n        <item name=\"colorPrimaryVariant\">@color/tfe_color_primary_dark</item>\n        <item name=\"colorOnPrimary\">@color/tfe_white</item>\n        <!-- Status bar color. -->\n        <item name=\"android:statusBarColor\" tools:targetApi=\"l\">?attr/colorPrimaryVariant</item>\n        <!-- Customize your theme here. -->\n        <item name=\"android:textColor\">@color/tfe_black</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "lite/examples/text_searcher/android/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath \"com.android.tools.build:gradle:7.0.4\"\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20\"\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "lite/examples/text_searcher/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/examples/text_searcher/android/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app\"s APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Automatically convert third-party libraries to use AndroidX\nandroid.enableJetifier=true\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official"
  },
  {
    "path": "lite/examples/text_searcher/android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/examples/text_searcher/android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/examples/text_searcher/android/settings.gradle",
    "content": "dependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        google()\n        mavenCentral()\n        jcenter() // Warning: this repository is going to shut down soon\n    }\n}\nrootProject.name = \"TFL Text Searcher\"\ninclude ':app'\n"
  },
  {
    "path": "lite/examples/video_classification/android/README.md",
    "content": "# TensorFlow Lite video classification Android example application\n\n## Overview\n\nThis is an example application for\n[TensorFlow Lite](https://tensorflow.org/lite) on Android. It uses\nvideo classification to continuously classify whatever it sees from the device's back camera.\nInference is performed using the TensorFlow Lite Java API. The demo app\nclassifies frames in real-time, displaying the top most probable\nclassifications. It allows the user to choose between multiple variants of the [MoviNet](https://tfhub.dev/s?deployment-format=lite&q=movinet)\nvideo classification model.\n\nThese instructions walk you through building and running the demo on an Android device.\n\n### Model\n\nWe provide 3 integer-only quantized models bundled in this app:\n* MoviNet-A0\n* MoviNet-A1\n* MoviNet-A2\n\nMoviNet-A0 is the smallest and fastest model but less accurate than the A1 and A2. On the other \nhand, MoviNet-A2 is the most accurate one but also larger and slower.\n\nDownloading, extracting, and placing the model in the assets folder is managed\nautomatically by download.gradle.\n\n## Requirements\n\n*   Android Studio Bumblebee | 2021.1.1 or newer (installed on a Linux, Mac or Windows machine)\n\n*   Android device in\n    [developer mode](https://developer.android.com/studio/debug/dev-options)\n    with USB debugging enabled\n\n*   USB cable (to connect Android device to your computer)\n\n## Build and run\n\n### Step 1. Clone the TensorFlow examples source code\n\nClone the TensorFlow examples GitHub repository to your computer to get the demo\napplication.\n\n```\ngit clone https://github.com/tensorflow/examples\n```\n\nOpen the TensorFlow source code in Android Studio. To do this, open Android\nStudio and select `Open an existing project`, setting the folder to\n`examples/lite/examples/video_classification/android`\n\n### Step 2. Build the Android Studio project\n\nSelect `Build -> Make Project` and check that the project builds successfully.\nYou will need Android SDK configured in the settings. You'll need at least SDK\nversion 23. The `build.gradle` file will prompt you to download any missing\nlibraries.\n\nThe file `download.gradle` directs gradle to download the three models used in the\nexample, placing them into `assets`.\n\n<aside class=\"note\"><b>Note:</b><p>`build.gradle` is configured to use\nTensorFlow Lite's nightly build.</p><p>If you see a build error related to\ncompatibility with Tensorflow Lite's Java API (for example, `method X is\nundefined for type Interpreter`), there has likely been a backwards compatible\nchange to the API. You will need to run `git pull` in the examples repo to\nobtain a version that is compatible with the nightly build.</p></aside>\n\n### Step 3. Install and run the app\n\nConnect the Android device to the computer and be sure to approve any ADB\npermission prompts that appear on your phone. Select `Run -> Run app.` Select\nthe deployment target in the connected devices to the device on which the app\nwill be installed. This will install the app on the device.\n\nTo test the app, open the app called `TFL Video Classification` on your device. When you run\nthe app the first time, the app will request permission to access the camera.\nRe-installing the app may require you to uninstall the previous installations.\n\n## Assets folder\n\n_Do not delete the assets folder content_. If you explicitly deleted the files,\nchoose `Build -> Rebuild` to re-download the deleted model files into the assets\nfolder.\n"
  },
  {
    "path": "lite/examples/video_classification/android/app/build.gradle",
    "content": "plugins {\n    id 'com.android.application'\n    id 'kotlin-android'\n    id 'de.undercouch.download'\n}\n\nandroid {\n    compileSdk 31\n\n    defaultConfig {\n        applicationId \"org.tensorflow.lite.examples.videoclassification\"\n        minSdk 21\n        targetSdk 31\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n    buildFeatures {\n        viewBinding true\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n    kotlinOptions {\n        jvmTarget = '1.8'\n    }\n    androidResources {\n        noCompress 'tflite'\n    }\n}\n\n\ndependencies {\n    // General Android dependencies\n    implementation 'androidx.core:core-ktx:1.7.0'\n    implementation 'androidx.appcompat:appcompat:1.4.1'\n    implementation 'com.google.android.material:material:1.5.0'\n    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'\n    androidTestImplementation 'androidx.test.ext:junit:1.1.3'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n\n    // CameraX core library using the Camera2 implementation\n    def camerax_version = \"1.1.0-beta01\"\n    implementation \"androidx.camera:camera-camera2:${camerax_version}\"\n    implementation \"androidx.camera:camera-lifecycle:${camerax_version}\"\n    implementation \"androidx.camera:camera-view:${camerax_version}\"\n\n    // TensorFlow Lite\n    implementation 'org.tensorflow:tensorflow-lite:0.0.0-nightly-SNAPSHOT'\n    implementation 'org.tensorflow:tensorflow-lite-support:0.0.0-nightly-SNAPSHOT'\n\n}\nproject.ext.ASSET_DIR = projectDir.toString() + \"/src/main/assets\"\napply from: 'download.gradle'"
  },
  {
    "path": "lite/examples/video_classification/android/app/download.gradle",
    "content": "def modelMovinetA0Url = \"https://tfhub.dev/tensorflow/lite-model/movinet/a0/stream/kinetics-600/classification/tflite/int8/1?lite-format=tflite\"\ndef modelMovinetA0File = \"movinet_a0_stream_int8.tflite\"\ndef modelMovinetA1Url = \"https://tfhub.dev/tensorflow/lite-model/movinet/a1/stream/kinetics-600/classification/tflite/int8/1?lite-format=tflite\"\ndef modelMovinetA1File = \"movinet_a1_stream_int8.tflite\"\ndef modelMovinetA2Url = \"https://tfhub.dev/tensorflow/lite-model/movinet/a2/stream/kinetics-600/classification/tflite/int8/1?lite-format=tflite\"\ndef modelMovinetA2File = \"movinet_a2_stream_int8.tflite\"\n\ntask downloadMovinetA0(type: Download) {\n    src \"${modelMovinetA0Url}\"\n    dest project.ext.ASSET_DIR + \"/${modelMovinetA0File}\"\n    overwrite false\n}\n\ntask downloadMovinetA1(type: Download) {\n    src \"${modelMovinetA1Url}\"\n    dest project.ext.ASSET_DIR + \"/${modelMovinetA1File}\"\n    overwrite false\n}\n\ntask downloadMovinetA2(type: Download) {\n    src \"${modelMovinetA2Url}\"\n    dest project.ext.ASSET_DIR + \"/${modelMovinetA2File}\"\n    overwrite false\n}\n\npreBuild.dependsOn downloadMovinetA0\npreBuild.dependsOn downloadMovinetA1\npreBuild.dependsOn downloadMovinetA2\n"
  },
  {
    "path": "lite/examples/video_classification/android/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "lite/examples/video_classification/android/app/src/androidTest/java/org/tensorflow/lite/examples/videoclassification/VideoClassifierTest.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.videoclassification\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.media.MediaMetadataRetriever\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.test.platform.app.InstrumentationRegistry\nimport org.hamcrest.MatcherAssert.assertThat\nimport org.hamcrest.Matchers.greaterThan\nimport org.junit.Assert.assertEquals\nimport org.junit.Assert.assertTrue\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.tensorflow.lite.examples.videoclassification.ml.VideoClassifier\nimport org.tensorflow.lite.support.label.Category\nimport java.io.*\n\n@RunWith(AndroidJUnit4::class)\nclass VideoClassificationTest {\n\n    companion object {\n        private const val INPUT_FILE_NAME = \"carving_ice.mp4\"\n    }\n\n    private lateinit var videoClassifier: VideoClassifier\n    private lateinit var appContext: Context\n    private lateinit var testContext: Context\n\n    @Before\n    fun setup() {\n        appContext = InstrumentationRegistry.getInstrumentation().targetContext\n        testContext = InstrumentationRegistry.getInstrumentation().context\n        val options =\n            VideoClassifier.VideoClassifierOptions.builder().setMaxResult(5)\n                .setNumThreads(1)\n                .build()\n        videoClassifier = VideoClassifier.createFromFileAndLabelsAndOptions(\n            appContext,\n            \"movinet_a0_stream_int8.tflite\",\n            \"kinetics600_label_map.txt\",\n            options\n        )\n    }\n\n    @Test\n    fun testVideoClassifierWithExampleVideo() {\n        // Load the test video.\n        val inputStream = testContext.assets.open(INPUT_FILE_NAME)\n        val file = createFileFromInputStream(inputStream)\n        val media = MediaMetadataRetriever()\n        media.setDataSource(file.path)\n\n        // Run classification on all frames in the video\n        var categories: List<Category> = listOf()\n        val frameCount = media.extractMetadata(\n            MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT)!!.toInt()\n        for (i in 0 until frameCount) {\n            val inputFrame = media\n                .getFrameAtIndex(i)\n                ?.copy(Bitmap.Config.ARGB_8888, true)\n            inputFrame?.let {\n                categories = videoClassifier.classify(it)\n            }\n        }\n        media.release()\n\n        // Assert if the top-1 classification result matches with ground truth.\n        assertTrue(\"Classification result isn't empty.\", categories.isNotEmpty())\n        assertEquals(\"Top1 category matches.\", \"carving ice\", categories[0].label)\n        assertThat(\"Score is larger than threshold.\", categories[0].score, greaterThan(0.6f))\n    }\n\n    private fun createFileFromInputStream(inputStream: InputStream): File {\n        val f = File(\"${appContext.cacheDir}/$INPUT_FILE_NAME\")\n        val outputStream: OutputStream = FileOutputStream(f)\n        val buffer = ByteArray(1024)\n        var length: Int\n        while (inputStream.read(buffer).also { length = it } > 0) {\n            outputStream.write(buffer, 0, length)\n        }\n        outputStream.close()\n        inputStream.close()\n        return f\n    }\n}\n"
  },
  {
    "path": "lite/examples/video_classification/android/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.tensorflow.lite.examples.videoclassification\">\n\n    <uses-feature android:name=\"android.hardware.camera.any\" />\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/Theme.VideoClassification\">\n        <activity\n            android:name=\".MainActivity\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "lite/examples/video_classification/android/app/src/main/assets/kinetics600_label_map.txt",
    "content": "abseiling\nacting in play\nadjusting glasses\nair drumming\nalligator wrestling\nanswering questions\napplauding\napplying cream\narchaeological excavation\narchery\narguing\narm wrestling\narranging flowers\nassembling bicycle\nassembling computer\nattending conference\nauctioning\nbackflip (human)\nbaking cookies\nbandaging\nbarbequing\nbartending\nbase jumping\nbathing dog\nbattle rope training\nbeatboxing\nbee keeping\nbelly dancing\nbench pressing\nbending back\nbending metal\nbiking through snow\nblasting sand\nblowdrying hair\nblowing bubble gum\nblowing glass\nblowing leaves\nblowing nose\nblowing out candles\nbobsledding\nbodysurfing\nbookbinding\nbottling\nbouncing on bouncy castle\nbouncing on trampoline\nbowling\nbraiding hair\nbreading or breadcrumbing\nbreakdancing\nbreaking boards\nbreathing fire\nbrush painting\nbrushing hair\nbrushing teeth\nbuilding cabinet\nbuilding lego\nbuilding sandcastle\nbuilding shed\nbull fighting\nbulldozing\nbungee jumping\nburping\nbusking\ncalculating\ncalligraphy\ncanoeing or kayaking\ncapoeira\ncapsizing\ncard stacking\ncard throwing\ncarrying baby\ncartwheeling\ncarving ice\ncarving pumpkin\ncasting fishing line\ncatching fish\ncatching or throwing baseball\ncatching or throwing frisbee\ncatching or throwing softball\ncelebrating\nchanging gear in car\nchanging oil\nchanging wheel (not on bike)\nchecking tires\ncheerleading\nchewing gum\nchiseling stone\nchiseling wood\nchopping meat\nchopping vegetables\nchopping wood\nclam digging\nclapping\nclay pottery making\nclean and jerk\ncleaning gutters\ncleaning pool\ncleaning shoes\ncleaning toilet\ncleaning windows\nclimbing a rope\nclimbing ladder\nclimbing tree\ncoloring in\ncombing hair\ncontact juggling\ncontorting\ncooking egg\ncooking on campfire\ncooking sausages (not on barbeque)\ncooking scallops\ncosplaying\ncounting money\ncountry line dancing\ncracking back\ncracking knuckles\ncracking neck\ncrawling baby\ncrossing eyes\ncrossing river\ncrying\ncumbia\ncurling (sport)\ncurling hair\ncutting apple\ncutting nails\ncutting orange\ncutting pineapple\ncutting watermelon\ndancing ballet\ndancing charleston\ndancing gangnam style\ndancing macarena\ndeadlifting\ndecorating the christmas tree\ndelivering mail\ndining\ndirecting traffic\ndisc golfing\ndiving cliff\ndocking boat\ndodgeball\ndoing aerobics\ndoing jigsaw puzzle\ndoing laundry\ndoing nails\ndrawing\ndribbling basketball\ndrinking shots\ndriving car\ndriving tractor\ndrooling\ndrop kicking\ndrumming fingers\ndumpster diving\ndunking basketball\ndyeing eyebrows\ndyeing hair\neating burger\neating cake\neating carrots\neating chips\neating doughnuts\neating hotdog\neating ice cream\neating spaghetti\neating watermelon\negg hunting\nembroidering\nexercising with an exercise ball\nextinguishing fire\nfaceplanting\nfalling off bike\nfalling off chair\nfeeding birds\nfeeding fish\nfeeding goats\nfencing (sport)\nfidgeting\nfinger snapping\nfixing bicycle\nfixing hair\nflint knapping\nflipping pancake\nfly tying\nflying kite\nfolding clothes\nfolding napkins\nfolding paper\nfront raises\nfrying vegetables\ngeocaching\ngetting a haircut\ngetting a piercing\ngetting a tattoo\ngiving or receiving award\ngold panning\ngolf chipping\ngolf driving\ngolf putting\ngospel singing in church\ngrinding meat\ngrooming dog\ngrooming horse\ngymnastics tumbling\nhammer throw\nhand washing clothes\nhead stand\nheadbanging\nheadbutting\nhigh jump\nhigh kick\nhistorical reenactment\nhitting baseball\nhockey stop\nholding snake\nhome roasting coffee\nhopscotch\nhoverboarding\nhuddling\nhugging (not baby)\nhugging baby\nhula hooping\nhurdling\nhurling (sport)\nice climbing\nice fishing\nice skating\nice swimming\ninflating balloons\ninstalling carpet\nironing\nironing hair\njavelin throw\njaywalking\njetskiing\njogging\njuggling balls\njuggling fire\njuggling soccer ball\njumping bicycle\njumping into pool\njumping jacks\njumpstyle dancing\nkaraoke\nkicking field goal\nkicking soccer ball\nkissing\nkitesurfing\nknitting\nkrumping\nland sailing\nlaughing\nlawn mower racing\nlaying bricks\nlaying concrete\nlaying stone\nlaying tiles\nleatherworking\nlicking\nlifting hat\nlighting fire\nlock picking\nlong jump\nlongboarding\nlooking at phone\nluge\nlunge\nmaking a cake\nmaking a sandwich\nmaking balloon shapes\nmaking bubbles\nmaking cheese\nmaking horseshoes\nmaking jewelry\nmaking paper aeroplanes\nmaking pizza\nmaking snowman\nmaking sushi\nmaking tea\nmaking the bed\nmarching\nmarriage proposal\nmassaging back\nmassaging feet\nmassaging legs\nmassaging neck\nmassaging person's head\nmilking cow\nmoon walking\nmopping floor\nmosh pit dancing\nmotorcycling\nmountain climber (exercise)\nmoving furniture\nmowing lawn\nmushroom foraging\nneedle felting\nnews anchoring\nopening bottle (not wine)\nopening door\nopening present\nopening refrigerator\nopening wine bottle\npacking\nparagliding\nparasailing\nparkour\npassing American football (in game)\npassing american football (not in game)\npassing soccer ball\npeeling apples\npeeling potatoes\nperson collecting garbage\npetting animal (not cat)\npetting cat\nphotobombing\nphotocopying\npicking fruit\npillow fight\npinching\npirouetting\nplaning wood\nplanting trees\nplastering\nplaying accordion\nplaying badminton\nplaying bagpipes\nplaying basketball\nplaying bass guitar\nplaying beer pong\nplaying blackjack\nplaying cello\nplaying chess\nplaying clarinet\nplaying controller\nplaying cricket\nplaying cymbals\nplaying darts\nplaying didgeridoo\nplaying dominoes\nplaying drums\nplaying field hockey\nplaying flute\nplaying gong\nplaying guitar\nplaying hand clapping games\nplaying harmonica\nplaying harp\nplaying ice hockey\nplaying keyboard\nplaying kickball\nplaying laser tag\nplaying lute\nplaying maracas\nplaying marbles\nplaying monopoly\nplaying netball\nplaying ocarina\nplaying organ\nplaying paintball\nplaying pan pipes\nplaying piano\nplaying pinball\nplaying ping pong\nplaying poker\nplaying polo\nplaying recorder\nplaying rubiks cube\nplaying saxophone\nplaying scrabble\nplaying squash or racquetball\nplaying tennis\nplaying trombone\nplaying trumpet\nplaying ukulele\nplaying violin\nplaying volleyball\nplaying with trains\nplaying xylophone\npoking bellybutton\npole vault\npolishing metal\npopping balloons\npouring beer\npreparing salad\npresenting weather forecast\npull ups\npumping fist\npumping gas\npunching bag\npunching person (boxing)\npush up\npushing car\npushing cart\npushing wheelbarrow\npushing wheelchair\nputting in contact lenses\nputting on eyeliner\nputting on foundation\nputting on lipstick\nputting on mascara\nputting on sari\nputting on shoes\nraising eyebrows\nreading book\nreading newspaper\nrecording music\nrepairing puncture\nriding a bike\nriding camel\nriding elephant\nriding mechanical bull\nriding mule\nriding or walking with horse\nriding scooter\nriding snow blower\nriding unicycle\nripping paper\nroasting marshmallows\nroasting pig\nrobot dancing\nrock climbing\nrock scissors paper\nroller skating\nrolling pastry\nrope pushdown\nrunning on treadmill\nsailing\nsalsa dancing\nsanding floor\nsausage making\nsawing wood\nscrambling eggs\nscrapbooking\nscrubbing face\nscuba diving\nseparating eggs\nsetting table\nsewing\nshaking hands\nshaking head\nshaping bread dough\nsharpening knives\nsharpening pencil\nshaving head\nshaving legs\nshearing sheep\nshining flashlight\nshining shoes\nshooting basketball\nshooting goal (soccer)\nshopping\nshot put\nshoveling snow\nshucking oysters\nshuffling cards\nshuffling feet\nside kick\nsign language interpreting\nsinging\nsipping cup\nsitup\nskateboarding\nski jumping\nskiing crosscountry\nskiing mono\nskiing slalom\nskipping rope\nskipping stone\nskydiving\nslacklining\nslapping\nsled dog racing\nsleeping\nsmashing\nsmelling feet\nsmoking\nsmoking hookah\nsmoking pipe\nsnatch weight lifting\nsneezing\nsnorkeling\nsnowboarding\nsnowkiting\nsnowmobiling\nsomersaulting\nspelunking\nspinning poi\nspray painting\nspringboard diving\nsquare dancing\nsquat\nstanding on hands\nstaring\nsteer roping\nsticking tongue out\nstomping grapes\nstretching arm\nstretching leg\nsucking lolly\nsurfing crowd\nsurfing water\nsweeping floor\nswimming backstroke\nswimming breast stroke\nswimming butterfly stroke\nswimming front crawl\nswing dancing\nswinging baseball bat\nswinging on something\nsword fighting\nsword swallowing\ntackling\ntagging graffiti\ntai chi\ntalking on cell phone\ntango dancing\ntap dancing\ntapping guitar\ntapping pen\ntasting beer\ntasting food\ntasting wine\ntestifying\ntexting\nthreading needle\nthrowing axe\nthrowing ball (not baseball or American football)\nthrowing discus\nthrowing knife\nthrowing snowballs\nthrowing tantrum\nthrowing water balloon\ntickling\ntie dying\ntightrope walking\ntiptoeing\ntobogganing\ntossing coin\ntraining dog\ntrapezing\ntrimming or shaving beard\ntrimming shrubs\ntrimming trees\ntriple jump\ntwiddling fingers\ntying bow tie\ntying knot (not on a tie)\ntying necktie\ntying shoe laces\nunboxing\nunloading truck\nusing a microscope\nusing a paint roller\nusing a power drill\nusing a sledge hammer\nusing a wrench\nusing atm\nusing bagging machine\nusing circular saw\nusing inhaler\nusing puppets\nusing remote controller (not gaming)\nusing segway\nvacuuming floor\nvisiting the zoo\nwading through mud\nwading through water\nwaiting in line\nwaking up\nwalking the dog\nwalking through snow\nwashing dishes\nwashing feet\nwashing hair\nwashing hands\nwatching tv\nwater skiing\nwater sliding\nwatering plants\nwaving hand\nwaxing back\nwaxing chest\nwaxing eyebrows\nwaxing legs\nweaving basket\nweaving fabric\nwelding\nwhistling\nwindsurfing\nwinking\nwood burning (art)\nwrapping present\nwrestling\nwriting\nyarn spinning\nyawning\nyoga\nzumba\n"
  },
  {
    "path": "lite/examples/video_classification/android/app/src/main/java/org/tensorflow/lite/examples/videoclassification/CalculateUtils.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.videoclassification\n\nimport android.graphics.*\nimport android.media.Image\nimport java.io.ByteArrayOutputStream\nimport kotlin.math.exp\n\nobject CalculateUtils {\n\n    /**\n     * Softmax function\n     */\n    fun softmax(floatArray: FloatArray): FloatArray {\n        var total = 0f\n        val result = FloatArray(floatArray.size)\n        for (i in floatArray.indices) {\n            result[i] = exp(floatArray[i])\n            total += result[i]\n        }\n\n        for (i in result.indices) {\n            result[i] /= total\n        }\n        return result\n    }\n\n    /**\n     * Convert ImageProxy to Bitmap\n     * Input format YUV420\n     */\n    fun yuvToRgb(image: Image, output: Bitmap) {\n        val yBuffer = image.planes[0].buffer // Y\n        val uBuffer = image.planes[1].buffer // U\n        val vBuffer = image.planes[2].buffer // V\n\n        val ySize = yBuffer.remaining()\n        val uSize = uBuffer.remaining()\n        val vSize = vBuffer.remaining()\n\n        val nv21 = ByteArray(ySize + uSize + vSize)\n\n        yBuffer.get(nv21, 0, ySize)\n        vBuffer.get(nv21, ySize, vSize)\n        uBuffer.get(nv21, ySize + vSize, uSize)\n\n        val yuvImage = YuvImage(nv21, ImageFormat.NV21, image.width, image.height, null)\n        val out = ByteArrayOutputStream()\n        yuvImage.compressToJpeg(Rect(0, 0, yuvImage.width, yuvImage.height), 100, out)\n        val imageBytes = out.toByteArray()\n        val bitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)\n        val canvas = Canvas(output)\n        canvas.drawBitmap(bitmap, 0f, 0f, null)\n    }\n}\n"
  },
  {
    "path": "lite/examples/video_classification/android/app/src/main/java/org/tensorflow/lite/examples/videoclassification/MainActivity.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.videoclassification\n\nimport android.Manifest\nimport android.content.pm.PackageManager\nimport android.graphics.Bitmap\nimport android.graphics.Matrix\nimport android.hardware.camera2.CaptureRequest\nimport android.os.Bundle\nimport android.os.SystemClock\nimport android.util.Log\nimport android.util.Range\nimport android.view.View\nimport android.view.ViewTreeObserver.OnGlobalLayoutListener\nimport android.view.WindowManager\nimport android.widget.AdapterView\nimport android.widget.ArrayAdapter\nimport android.widget.LinearLayout\nimport android.widget.Toast\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.camera.camera2.interop.Camera2Interop\nimport androidx.camera.core.*\nimport androidx.camera.lifecycle.ProcessCameraProvider\nimport androidx.core.app.ActivityCompat\nimport androidx.core.content.ContextCompat\nimport com.google.android.material.bottomsheet.BottomSheetBehavior\nimport org.tensorflow.lite.examples.videoclassification.databinding.ActivityMainBinding\nimport org.tensorflow.lite.examples.videoclassification.ml.VideoClassifier\nimport org.tensorflow.lite.support.label.Category\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Executors\n\n@androidx.camera.core.ExperimentalGetImage\n@androidx.camera.camera2.interop.ExperimentalCamera2Interop\nclass MainActivity : AppCompatActivity() {\n    companion object {\n        private const val REQUEST_CODE_PERMISSIONS = 10\n        private const val TAG = \"TFLite-VidClassify\"\n        private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)\n        private const val MAX_RESULT = 3\n        private const val MODEL_MOVINET_A0_FILE = \"movinet_a0_stream_int8.tflite\"\n        private const val MODEL_MOVINET_A1_FILE = \"movinet_a1_stream_int8.tflite\"\n        private const val MODEL_MOVINET_A2_FILE = \"movinet_a2_stream_int8.tflite\"\n        private const val MODEL_LABEL_FILE = \"kinetics600_label_map.txt\"\n        private const val MODEL_FPS = 5 // Ensure the input images are fed to the model at this fps.\n        private const val MODEL_FPS_ERROR_RANGE = 0.1 // Acceptable error range in fps.\n        private const val MAX_CAPTURE_FPS = 20\n    }\n\n    private val lock = Any()\n    private lateinit var binding: ActivityMainBinding\n    private lateinit var executor: ExecutorService\n    private lateinit var sheetBehavior: BottomSheetBehavior<LinearLayout>\n\n    private var videoClassifier: VideoClassifier? = null\n    private var numThread = 1\n    private var modelPosition = 0\n    private var lastInferenceStartTime: Long = 0\n    private var changeModelListener = object : AdapterView.OnItemSelectedListener {\n        override fun onNothingSelected(parent: AdapterView<*>?) {\n            // do nothing\n        }\n\n        override fun onItemSelected(\n            parent: AdapterView<*>?,\n            view: View?,\n            position: Int,\n            id: Long\n        ) {\n            modelPosition = position\n            createClassifier()\n        }\n    }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)\n\n        // Initialize the view layout.\n        binding = ActivityMainBinding.inflate(layoutInflater)\n        setContentView(binding.root)\n\n        // Initialize the bottom sheet.\n        sheetBehavior = BottomSheetBehavior.from(binding.bottomSheet.bottomSheetLayout)\n        binding.bottomSheet.gestureLayout.viewTreeObserver.addOnGlobalLayoutListener(object :\n            OnGlobalLayoutListener {\n            override fun onGlobalLayout() {\n                binding.bottomSheet.gestureLayout.viewTreeObserver.removeOnGlobalLayoutListener(this)\n                val height = binding.bottomSheet.gestureLayout.measuredHeight\n                sheetBehavior.peekHeight = height\n            }\n        })\n        sheetBehavior.isHideable = false\n        sheetBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {\n            override fun onStateChanged(bottomSheet: View, newState: Int) {\n                when (newState) {\n                    BottomSheetBehavior.STATE_EXPANDED -> {\n                        binding.bottomSheet.bottomSheetArrow.setImageResource(R.drawable.icn_chevron_down)\n                    }\n                    BottomSheetBehavior.STATE_COLLAPSED, BottomSheetBehavior.STATE_SETTLING -> {\n                        binding.bottomSheet.bottomSheetArrow.setImageResource(R.drawable.icn_chevron_up)\n                    }\n                    else -> {\n                        // do nothing.\n                    }\n                }\n            }\n\n            override fun onSlide(bottomSheet: View, slideOffset: Float) {\n                // no func\n            }\n\n        })\n        binding.bottomSheet.threads.text = numThread.toString()\n        binding.bottomSheet.minus.setOnClickListener {\n            if (numThread <= 1) return@setOnClickListener\n            numThread--\n            binding.bottomSheet.threads.text = numThread.toString()\n            createClassifier()\n        }\n        binding.bottomSheet.plus.setOnClickListener {\n            if (numThread >= 4) return@setOnClickListener\n            numThread++\n            binding.bottomSheet.threads.text = numThread.toString()\n            createClassifier()\n        }\n        binding.bottomSheet.btnClearModelState.setOnClickListener {\n            videoClassifier?.reset()\n        }\n        initSpinner()\n\n        // Start the camera.\n        if (allPermissionsGranted()) {\n            startCamera()\n        } else {\n            ActivityCompat.requestPermissions(\n                this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS\n            )\n        }\n    }\n\n    /**\n     * Initialize the spinner to let users change the TFLite model.\n     */\n    private fun initSpinner() {\n        ArrayAdapter.createFromResource(\n            this,\n            R.array.tfe_pe_models_array,\n            android.R.layout.simple_spinner_item\n        ).also { adapter ->\n            // Specify the layout to use when the list of choices appears\n            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)\n            // Apply the adapter to the spinner\n            binding.bottomSheet.spnSelectModel.adapter = adapter\n            binding.bottomSheet.spnSelectModel.setSelection(modelPosition)\n        }\n        binding.bottomSheet.spnSelectModel.onItemSelectedListener = changeModelListener\n    }\n\n    /**\n     * Start the image capturing pipeline.\n     */\n    private fun startCamera() {\n        executor = Executors.newSingleThreadExecutor()\n        val cameraProviderFuture = ProcessCameraProvider.getInstance(this)\n\n        cameraProviderFuture.addListener({\n            // Used to bind the lifecycle of cameras to the lifecycle owner\n            val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()\n\n            // Select back camera as a default\n            val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA\n\n            // Create a Preview to show the image captured by the camera on screen.\n            val preview = Preview.Builder()\n                .build()\n                .also {\n                    it.setSurfaceProvider(binding.preview.surfaceProvider)\n                }\n\n            try {\n                // Unbind use cases before rebinding.\n                cameraProvider.unbindAll()\n\n                // Create an ImageAnalysis to continuously capture still images using the camera,\n                // and feed them to the TFLite model. We set the capturing frame rate to a multiply\n                // of the TFLite model's desired FPS to keep the preview smooth, then drop\n                // unnecessary frames during image analysis.\n                val targetFpsMultiplier = MAX_CAPTURE_FPS.div(MODEL_FPS)\n                val targetCaptureFps = MODEL_FPS * targetFpsMultiplier\n                val builder = ImageAnalysis.Builder()\n                    .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)\n                val extender: Camera2Interop.Extender<*> = Camera2Interop.Extender(builder)\n                extender.setCaptureRequestOption(\n                    CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,\n                    Range(targetCaptureFps, targetCaptureFps)\n                )\n                val imageAnalysis = builder.build()\n\n                imageAnalysis.setAnalyzer(executor) { imageProxy ->\n                    processImage(imageProxy)\n                }\n\n                // Combine the ImageAnalysis and Preview into a use case group.\n                val useCaseGroup = UseCaseGroup.Builder()\n                    .addUseCase(preview)\n                    .addUseCase(imageAnalysis)\n                    .setViewPort(binding.preview.viewPort!!)\n                    .build()\n\n                // Bind use cases to camera.\n                cameraProvider.bindToLifecycle(\n                    this, cameraSelector, useCaseGroup\n                )\n\n            } catch (e: Exception) {\n                Log.e(TAG, \"Use case binding failed.\", e)\n            }\n\n        }, ContextCompat.getMainExecutor(this))\n    }\n\n    /**\n     * Run a frames received from the camera through the TFLite video classification pipeline.\n     */\n    private fun processImage(imageProxy: ImageProxy) {\n        // Ensure that only one frame is processed at any given moment.\n        synchronized(lock) {\n            val currentTime = SystemClock.uptimeMillis()\n            val diff = currentTime - lastInferenceStartTime\n\n            // Check to ensure that we only run inference at a frequency required by the\n            // model, within an acceptable error range (e.g. 10%). Discard the frames\n            // that comes too early.\n            if (diff * MODEL_FPS >= 1000 /* milliseconds */ * (1 - MODEL_FPS_ERROR_RANGE)) {\n                lastInferenceStartTime = currentTime\n\n                val image = imageProxy.image\n                image?.let {\n                    videoClassifier?.let { classifier ->\n                        // Convert the captured frame to Bitmap.\n                        val imageBitmap = Bitmap.createBitmap(\n                            it.width,\n                            it.height,\n                            Bitmap.Config.ARGB_8888\n                        )\n                        CalculateUtils.yuvToRgb(image, imageBitmap)\n\n                        // Rotate the image to the correct orientation.\n                        val rotateMatrix = Matrix()\n                        rotateMatrix.postRotate(\n                            imageProxy.imageInfo.rotationDegrees.toFloat()\n                        )\n                        val rotatedBitmap = Bitmap.createBitmap(\n                            imageBitmap, 0, 0, it.width, it.height,\n                            rotateMatrix, false\n                        )\n\n                        // Run inference using the TFLite model.\n                        val startTimeForReference = SystemClock.uptimeMillis()\n                        val results = classifier.classify(rotatedBitmap)\n                        val endTimeForReference =\n                            SystemClock.uptimeMillis() - startTimeForReference\n                        val inputFps = 1000f / diff\n                        showResults(results, endTimeForReference, inputFps)\n\n                        if (inputFps < MODEL_FPS * (1 - MODEL_FPS_ERROR_RANGE)) {\n                            Log.w(\n                                TAG, \"Current input FPS ($inputFps) is \" +\n                                        \"significantly lower than the TFLite model's \" +\n                                        \"expected FPS ($MODEL_FPS). It's likely because \" +\n                                        \"model inference takes too long on this device.\"\n                            )\n                        }\n                    }\n                }\n            }\n            imageProxy.close()\n        }\n    }\n\n    /**\n     * Check whether camera permission is already granted.\n     */\n    private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {\n        ContextCompat.checkSelfPermission(\n            baseContext, it\n        ) == PackageManager.PERMISSION_GRANTED\n    }\n\n    override fun onRequestPermissionsResult(\n        requestCode: Int,\n        permissions: Array<out String>,\n        grantResults: IntArray\n    ) {\n        super.onRequestPermissionsResult(requestCode, permissions, grantResults)\n        if (requestCode == REQUEST_CODE_PERMISSIONS) {\n            if (allPermissionsGranted()) {\n                startCamera()\n            } else {\n                Toast.makeText(this, \"Permissions not granted by the user.\", Toast.LENGTH_LONG)\n                    .show()\n            }\n        }\n    }\n\n    /**\n     * Initialize the TFLite video classifier.\n     */\n    private fun createClassifier() {\n        synchronized(lock) {\n            if (videoClassifier != null) {\n                videoClassifier?.close()\n                videoClassifier = null\n            }\n            val options =\n                VideoClassifier.VideoClassifierOptions.builder()\n                    .setMaxResult(MAX_RESULT)\n                    .setNumThreads(numThread)\n                    .build()\n            val modelFile = when (modelPosition) {\n                0 -> MODEL_MOVINET_A0_FILE\n                1 -> MODEL_MOVINET_A1_FILE\n                else -> MODEL_MOVINET_A2_FILE\n            }\n\n            videoClassifier = VideoClassifier.createFromFileAndLabelsAndOptions(\n                this,\n                modelFile,\n                MODEL_LABEL_FILE,\n                options\n            )\n\n            // show input size of video classification\n            videoClassifier?.getInputSize()?.let {\n                binding.bottomSheet.inputSizeInfo.text =\n                    getString(R.string.frame_size, it.width, it.height)\n            }\n            Log.d(TAG, \"Classifier created.\")\n        }\n    }\n\n    /**\n     * Show the video classification results on the screen.\n     */\n    private fun showResults(labels: List<Category>, inferenceTime: Long, inputFps: Float) {\n        runOnUiThread {\n            binding.bottomSheet.tvDetectedItem0.text = labels[0].label\n            binding.bottomSheet.tvDetectedItem1.text = labels[1].label\n            binding.bottomSheet.tvDetectedItem2.text = labels[2].label\n            binding.bottomSheet.tvDetectedItem0Value.text = labels[0].score.toString()\n            binding.bottomSheet.tvDetectedItem1Value.text = labels[1].score.toString()\n            binding.bottomSheet.tvDetectedItem2Value.text = labels[2].score.toString()\n            binding.bottomSheet.inferenceInfo.text =\n                getString(R.string.inference_time, inferenceTime)\n            binding.bottomSheet.inputFpsInfo.text = String.format(\"%.1f\", inputFps)\n        }\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        videoClassifier?.close()\n        executor.shutdown()\n    }\n}\n"
  },
  {
    "path": "lite/examples/video_classification/android/app/src/main/java/org/tensorflow/lite/examples/videoclassification/ml/VideoClassifier.kt",
    "content": "/*\n * Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.tensorflow.lite.examples.videoclassification.ml\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.util.Size\nimport org.tensorflow.lite.DataType\nimport org.tensorflow.lite.Interpreter\nimport org.tensorflow.lite.examples.videoclassification.CalculateUtils\nimport org.tensorflow.lite.support.common.FileUtil\nimport org.tensorflow.lite.support.common.ops.NormalizeOp\nimport org.tensorflow.lite.support.image.ImageProcessor\nimport org.tensorflow.lite.support.image.TensorImage\nimport org.tensorflow.lite.support.image.ops.ResizeOp\nimport org.tensorflow.lite.support.image.ops.ResizeWithCropOrPadOp\nimport org.tensorflow.lite.support.label.Category\nimport java.nio.ByteBuffer\nimport java.nio.ByteOrder\nimport kotlin.math.max\nimport kotlin.math.min\n\nclass VideoClassifier private constructor(\n    private val interpreter: Interpreter,\n    private val labels: List<String>,\n    private val maxResults: Int?\n) {\n    private val inputShape = interpreter\n        .getInputTensorFromSignature(IMAGE_INPUT_NAME, SIGNATURE_KEY)\n        .shape()\n    private val outputCategoryCount = interpreter\n        .getOutputTensorFromSignature(LOGITS_OUTPUT_NAME, SIGNATURE_KEY)\n        .shape()[1]\n    private val inputHeight = inputShape[2]\n    private val inputWidth = inputShape[3]\n    private var inputState = HashMap<String, Any>()\n    private val lock = Any()\n\n    companion object {\n        private const val IMAGE_INPUT_NAME = \"image\"\n        private const val LOGITS_OUTPUT_NAME = \"logits\"\n        private const val SIGNATURE_KEY = \"serving_default\"\n        private const val INPUT_MEAN = 0f\n        private const val INPUT_STD = 255f\n\n        fun createFromFileAndLabelsAndOptions(\n            context: Context,\n            modelFile: String,\n            labelFile: String,\n            options: VideoClassifierOptions\n        ): VideoClassifier {\n            // Create a TFLite interpreter from the TFLite model file.\n            val interpreterOptions = Interpreter.Options()\n            interpreterOptions.setNumThreads(options.numThreads)\n            val interpreter =\n                Interpreter(FileUtil.loadMappedFile(context, modelFile))\n\n            // Load the label file.\n            val labels = FileUtil.loadLabels(context, labelFile)\n\n            // Save the max result option.\n            val maxResults = if (options.maxResults > 0 && options.maxResults <= labels.size)\n                options.maxResults else null\n\n            return VideoClassifier(interpreter, labels, maxResults)\n        }\n    }\n\n    init {\n        if (outputCategoryCount != labels.size)\n            throw java.lang.IllegalArgumentException(\n                \"Label list size doesn't match with model output shape \" +\n                        \"(${labels.size} != $outputCategoryCount\"\n            )\n        inputState = initializeInput()\n    }\n\n    /**\n     * Initialize the input objects and fill them with zeros.\n     */\n    private fun initializeInput(): HashMap<String, Any> {\n        val inputs = HashMap<String, Any>()\n        for (inputName in interpreter.getSignatureInputs(SIGNATURE_KEY)) {\n            // Skip the input image tensor as it'll be fed in later.\n            if (inputName.equals(IMAGE_INPUT_NAME))\n                continue\n\n            // Initialize a ByteBuffer filled with zeros as an initial input of the TFLite model.\n            val tensor = interpreter.getInputTensorFromSignature(inputName, SIGNATURE_KEY)\n            val byteBuffer = ByteBuffer.allocateDirect(tensor.numBytes())\n            byteBuffer.order(ByteOrder.nativeOrder())\n            inputs[inputName] = byteBuffer\n        }\n\n        return inputs\n    }\n\n    /**\n     * Initialize the output objects to store the TFLite model outputs.\n     */\n    private fun initializeOutput(): HashMap<String, Any> {\n        val outputs = HashMap<String, Any>()\n        for (outputName in interpreter.getSignatureOutputs(SIGNATURE_KEY)) {\n            // Initialize a ByteBuffer to store the output of the TFLite model.\n            val tensor = interpreter.getOutputTensorFromSignature(outputName, SIGNATURE_KEY)\n            val byteBuffer = ByteBuffer.allocateDirect(tensor.numBytes())\n            byteBuffer.order(ByteOrder.nativeOrder())\n            outputs[outputName] = byteBuffer\n        }\n\n        return outputs\n    }\n\n    /**\n     * Run classify and return a list include action and score.\n     */\n    fun classify(inputBitmap: Bitmap): List<Category> {\n        // As this model is stateful, ensure there's only one inference going on at once.\n        synchronized(lock) {\n            // Prepare inputs.\n            val tensorImage = preprocessInputImage(inputBitmap)\n            inputState[IMAGE_INPUT_NAME] = tensorImage.buffer\n\n            // Initialize a placeholder to store the output objects.\n            val outputs = initializeOutput()\n\n            // Run inference using the TFLite model.\n            interpreter.runSignature(inputState, outputs)\n\n            // Post-process the outputs.\n            var categories = postprocessOutputLogits(outputs[LOGITS_OUTPUT_NAME] as ByteBuffer)\n\n            // Store the output states to feed as input for the next frame.\n            outputs.remove(LOGITS_OUTPUT_NAME)\n            inputState = outputs\n\n            // Sort the output and return only the top K results.\n            categories.sortByDescending { it.score }\n\n            // Take only maxResults number of result.\n            maxResults?.let {\n                categories = categories.subList(0, max(maxResults, categories.size))\n            }\n            return categories\n        }\n    }\n\n    /**\n     * Return the input size required by the model.\n     */\n    fun getInputSize(): Size {\n        return Size(inputWidth, inputHeight)\n    }\n\n    /**\n     * Convert input bitmap to TensorImage and normalize.\n     */\n    private fun preprocessInputImage(bitmap: Bitmap): TensorImage {\n        val size = min(bitmap.width, bitmap.height)\n        val imageProcessor = ImageProcessor.Builder().apply {\n            add(ResizeWithCropOrPadOp(size, size))\n            add(ResizeOp(inputHeight, inputWidth, ResizeOp.ResizeMethod.BILINEAR))\n            add(NormalizeOp(INPUT_MEAN, INPUT_STD))\n        }.build()\n        val tensorImage = TensorImage(DataType.FLOAT32)\n        tensorImage.load(bitmap)\n        return imageProcessor.process(tensorImage)\n    }\n\n    /**\n     * Convert output logits of the model to a list of Category objects.\n     */\n    private fun postprocessOutputLogits(logitsByteBuffer: ByteBuffer): MutableList<Category> {\n        // Convert ByteBuffer to FloatArray.\n        val logits = FloatArray(outputCategoryCount)\n        logitsByteBuffer.rewind()\n        logitsByteBuffer.asFloatBuffer().get(logits)\n\n        // Convert logits into probability list.\n        val probabilities = CalculateUtils.softmax(logits)\n\n        // Append label name to form a list of Category objects.\n        val categories = mutableListOf<Category>()\n        probabilities.forEachIndexed { index, probability ->\n            categories.add(Category(labels[index], probability))\n        }\n        return categories\n    }\n\n    /**\n     * Close the interpreter when it's no longer needed.\n     */\n    fun close() {\n        interpreter.close()\n    }\n\n    /**\n     * Clear the internal state of the model.\n     *\n     * Call this function if the future inputs is unrelated to the past inputs. (e.g. when changing\n     * to a new video sequence)\n     */\n    fun reset() {\n        // Ensure that no inference is running when the state is being cleared.\n        synchronized(lock) {\n            inputState = initializeInput()\n        }\n    }\n\n    class VideoClassifierOptions private constructor(\n        val numThreads: Int,\n        val maxResults: Int\n    ) {\n        companion object {\n            fun builder() = Builder()\n        }\n\n        class Builder {\n            private var numThreads: Int = -1\n            private var maxResult: Int = -1\n\n            fun setNumThreads(numThreads: Int): Builder {\n                this.numThreads = numThreads\n                return this\n            }\n\n            fun setMaxResult(maxResults: Int): Builder {\n                if ((maxResults <= 0) && (maxResults != -1)) {\n                    throw IllegalArgumentException(\"maxResults must be positive or -1.\")\n                }\n                this.maxResult = maxResults\n                return this\n            }\n\n            fun build(): VideoClassifierOptions {\n                return VideoClassifierOptions(this.numThreads, this.maxResult)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lite/examples/video_classification/android/app/src/main/res/drawable/bg_bottom_sheet.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <corners\n        android:topLeftRadius=\"@dimen/tfe_bottom_sheet_corner_radius\"\n        android:topRightRadius=\"@dimen/tfe_bottom_sheet_corner_radius\" />\n    <padding android:top=\"@dimen/tfe_bottom_sheet_top_padding\" />\n    <solid android:color=\"@android:color/white\" />\n</shape>\n"
  },
  {
    "path": "lite/examples/video_classification/android/app/src/main/res/drawable/bg_rectangle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/listview_background_shape\">\n    <stroke\n        android:width=\"1dp\"\n        android:color=\"@android:color/darker_gray\" />\n    <padding\n        android:bottom=\"2dp\"\n        android:left=\"2dp\"\n        android:right=\"2dp\"\n        android:top=\"2dp\" />\n    <solid android:color=\"#ffffffff\" />\n</shape>\n"
  },
  {
    "path": "lite/examples/video_classification/android/app/src/main/res/drawable/ic_baseline_add.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/video_classification/android/app/src/main/res/drawable/ic_baseline_remove.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n  <path\n      android:fillColor=\"#FF000000\"\n      android:pathData=\"M19,13H5v-2h14v2z\"/>\n</vector>\n"
  },
  {
    "path": "lite/examples/video_classification/android/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/black\"\n    tools:context=\".MainActivity\">\n\n    <androidx.camera.view.PreviewView\n        android:id=\"@+id/preview\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintDimensionRatio=\"1:1\"\n        app:scaleType=\"fillCenter\" />\n\n    <androidx.coordinatorlayout.widget.CoordinatorLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintBottom_toBottomOf=\"parent\">\n\n        <include\n            android:id=\"@+id/bottomSheet\"\n            layout=\"@layout/layout_bottom_sheet\" />\n\n    </androidx.coordinatorlayout.widget.CoordinatorLayout>\n\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "lite/examples/video_classification/android/app/src/main/res/layout/layout_bottom_sheet.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/bottom_sheet_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@drawable/bg_bottom_sheet\"\n    android:orientation=\"vertical\"\n    android:padding=\"8dp\"\n    app:behavior_hideable=\"true\"\n    app:layout_behavior=\"com.google.android.material.bottomsheet.BottomSheetBehavior\">\n\n    <LinearLayout\n        android:id=\"@+id/gesture_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:paddingTop=\"@dimen/tfe_bottom_sheet_margin\"\n        android:paddingBottom=\"@dimen/tfe_bottom_sheet_margin\"\n        app:behavior_hideable=\"false\">\n\n        <ImageView\n            android:id=\"@+id/bottom_sheet_arrow\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:contentDescription=\"@null\"\n            android:src=\"@drawable/icn_chevron_up\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/tfe_bottom_sheet_margin\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/tv_detected_item0\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"@color/black\" />\n\n            <TextView\n                android:id=\"@+id/tv_detected_item0_value\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:textAlignment=\"textEnd\"\n                android:textColor=\"@color/black\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/tfe_bottom_sheet_margin\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/tv_detected_item1\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"@color/black\" />\n\n            <TextView\n                android:id=\"@+id/tv_detected_item1_value\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:textAlignment=\"textEnd\"\n                android:textColor=\"@color/black\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/tfe_bottom_sheet_margin\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/tv_detected_item2\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"@color/black\" />\n\n            <TextView\n                android:id=\"@+id/tv_detected_item2_value\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:textAlignment=\"textEnd\"\n                android:textColor=\"@color/black\" />\n        </LinearLayout>\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"1px\"\n            android:layout_marginTop=\"@dimen/tfe_bottom_sheet_margin\"\n            android:background=\"@android:color/darker_gray\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:id=\"@+id/input_size\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/tfe_bottom_sheet_margin\"\n            android:text=\"@string/tfe_tv_frame\"\n            android:textColor=\"@android:color/black\" />\n\n        <TextView\n            android:id=\"@+id/input_size_info\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/tfe_bottom_sheet_margin\"\n            android:textAlignment=\"textEnd\"\n            android:textColor=\"@android:color/black\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:id=\"@+id/inference\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/tfe_bottom_sheet_margin\"\n            android:text=\"@string/tfe_tv_inference_time\"\n            android:textColor=\"@android:color/black\" />\n\n        <TextView\n            android:id=\"@+id/inference_info\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/tfe_bottom_sheet_margin\"\n            android:textAlignment=\"textEnd\"\n            android:textColor=\"@android:color/black\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:id=\"@+id/input_fps\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/tfe_bottom_sheet_margin\"\n            android:text=\"@string/tfe_tv_input_fps\"\n            android:textColor=\"@android:color/black\" />\n\n        <TextView\n            android:id=\"@+id/input_fps_info\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/tfe_bottom_sheet_margin\"\n            android:textAlignment=\"textEnd\"\n            android:textColor=\"@android:color/black\" />\n    </LinearLayout>\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1px\"\n        android:layout_marginTop=\"@dimen/tfe_bottom_sheet_margin\"\n        android:background=\"@android:color/darker_gray\" />\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"@dimen/tfe_bottom_sheet_margin\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/tfe_bottom_sheet_margin\"\n            android:layout_toStartOf=\"@+id/llChangeThreads\"\n            android:text=\"@string/tfe_tv_threads\"\n            android:textColor=\"@android:color/black\" />\n\n        <LinearLayout\n            android:id=\"@+id/llChangeThreads\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentEnd=\"true\"\n            android:background=\"@drawable/bg_rectangle\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\"\n            android:padding=\"4dp\">\n\n            <ImageView\n                android:id=\"@+id/minus\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:contentDescription=\"@null\"\n                android:src=\"@drawable/ic_baseline_remove\" />\n\n            <TextView\n                android:id=\"@+id/threads\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"@dimen/tfe_bottom_sheet_margin\"\n                android:layout_marginRight=\"@dimen/tfe_bottom_sheet_margin\"\n                android:textColor=\"@android:color/black\"\n                android:textSize=\"14sp\" />\n\n            <ImageView\n                android:id=\"@+id/plus\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:contentDescription=\"@null\"\n                android:src=\"@drawable/ic_baseline_add\" />\n        </LinearLayout>\n    </RelativeLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/tfe_bottom_sheet_margin\"\n            android:text=\"@string/tfe_tv_model\"\n            android:textColor=\"@android:color/black\" />\n\n        <Spinner\n            android:id=\"@+id/spnSelectModel\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/tfe_bottom_sheet_margin\"\n            android:textAlignment=\"textEnd\" />\n    </LinearLayout>\n\n    <Button\n        android:id=\"@+id/btnClearModelState\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"end\"\n        android:text=\"@string/tfe_btn_clear_model_state\" />\n</LinearLayout>\n"
  },
  {
    "path": "lite/examples/video_classification/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/examples/video_classification/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/examples/video_classification/android/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"tfe_color_primary\">#ffa800</color>\n    <color name=\"tfe_color_primary_dark\">#ff6f00</color>\n    <color name=\"tfe_color_accent\">#425066</color>\n    <color name=\"black\">#FF000000</color>\n</resources>\n"
  },
  {
    "path": "lite/examples/video_classification/android/app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <dimen name=\"tfe_bottom_sheet_corner_radius\">15dp</dimen>\n    <dimen name=\"tfe_bottom_sheet_top_padding\">8dp</dimen>\n    <dimen name=\"tfe_bottom_sheet_margin\">10dp</dimen>\n</resources>\n"
  },
  {
    "path": "lite/examples/video_classification/android/app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">TFL Video Classification</string>\n    <string name=\"tfe_tv_frame\">Input Size</string>\n    <string name=\"tfe_tv_inference_time\">Inference Time</string>\n    <string name=\"tfe_tv_input_fps\">Input FPS</string>\n    <string name=\"tfe_tv_threads\">Threads</string>\n    <string name=\"inference_time\">%dms</string>\n    <string name=\"frame_size\">%d*%d</string>\n    <string name=\"tfe_btn_clear_model_state\">Clear model state</string>\n    <string name=\"tfe_tv_model\">Model:</string>\n    <string-array name=\"tfe_pe_models_array\">\n        <item>MoviNet-A0</item>\n        <item>MoviNet-A1</item>\n        <item>MoviNet-A2</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "lite/examples/video_classification/android/app/src/main/res/values/themes.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <!-- Base application theme. -->\n    <style name=\"Theme.VideoClassification\" parent=\"Theme.MaterialComponents.DayNight.NoActionBar\">\n        <!-- Primary brand color. -->\n        <item name=\"colorPrimary\">@color/tfe_color_primary</item>\n        <item name=\"colorPrimaryVariant\">@color/tfe_color_primary_dark</item>\n        <item name=\"colorAccent\">@color/tfe_color_accent</item>\n        <!-- Status bar color. -->\n        <item name=\"android:statusBarColor\" tools:targetApi=\"l\">?attr/colorPrimaryVariant</item>\n        <!-- Customize your theme here. -->\n    </style>\n</resources>\n"
  },
  {
    "path": "lite/examples/video_classification/android/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n    repositories {\n        google()\n        mavenCentral()\n        maven {\n            name 'ossrh-snapshot'\n            url 'https://oss.sonatype.org/content/repositories/snapshots'\n        }\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:7.0.4'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10\"\n        classpath 'de.undercouch:gradle-download-task:4.1.2'\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}"
  },
  {
    "path": "lite/examples/video_classification/android/gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app\"s APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Automatically convert third-party libraries to use AndroidX\nandroid.enableJetifier=true\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official"
  },
  {
    "path": "lite/examples/video_classification/android/settings.gradle",
    "content": "dependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        google()\n        mavenCentral()\n        jcenter() // Warning: this repository is going to shut down soon\n    }\n}\nrootProject.name = \"TFL Video Classification\"\ninclude ':app'\n"
  },
  {
    "path": "lite/examples/video_classification/raspberry_pi/README.md",
    "content": "# TensorFlow Lite Python video classification example with Raspberry Pi.\n\nThis example uses [TensorFlow Lite](https://tensorflow.org/lite) with Python on\na Raspberry Pi to perform real-time video classification using images streamed\nfrom the camera.\n\n## Set up your hardware\n\nBefore you begin, you need to\n[set up your Raspberry Pi](https://projects.raspberrypi.org/en/projects/raspberry-pi-setting-up)\nwith Raspberry Pi OS (preferably updated to Buster).\n\nYou also need to\n[connect and configure the Pi Camera](https://www.raspberrypi.org/documentation/configuration/camera.md)\nif you use the Pi Camera. This code also works with USB camera connect to the\nRaspberry Pi.\n\nAnd to see the results from the camera, you need a monitor connected to the\nRaspberry Pi. It's okay if you're using SSH to access the Pi shell (you don't\nneed to use a keyboard connected to the Pi)—you only need a monitor attached to\nthe Pi to see the camera stream.\n\n## Install the TensorFlow Lite runtime\n\nIn this project, all you need from the TensorFlow Lite API is the `Interpreter`\nclass. So instead of installing the large `tensorflow` package, we're using the\nmuch smaller `tflite_runtime` package.\n\nTo install this on your Raspberry Pi, follow the instructions in the\n[Python quickstart](https://www.tensorflow.org/lite/guide/python#install_tensorflow_lite_for_python).\n\nYou can install the TFLite runtime using this script.\n\n```\nsh setup.sh\n```\n\n## Download the example files\n\nFirst, clone this Git repo onto your Raspberry Pi like this:\n\n```\ngit clone https://github.com/tensorflow/examples --depth 1\n```\n\nThen use our script to install a couple Python packages:\n\n```\ncd examples/lite/examples/video_classification/raspberry_pi\n\n# The script install the required dependencies and download the TFLite models.\nsh setup.sh\n```\n\n## Run the example\n\n```\npython3 classify.py\n```\n\n*   You can optionally specify the `model` parameter to set the TensorFlow Lite\n    model to be used:\n    *   The default value is `movinet_a0_int8.tflite`\n    *   This sample currently uses MoviNet-A0, but it supports all TensorFlow\n        Lite\n        [MoviNet video classification](https://tfhub.dev/s?deployment-format=lite&q=movinet)\n        models that are available on TensorFlow Hub. You can use the larger\n        variant of MoviNet if you need higher accuracy.\n*   You can optionally specify the `maxResults` parameter to limit the list of\n    classification results:\n    *   Supported value: A positive integer.\n    *   Default value: `3`.\n*   Example usage: `python3 classify.py \\ --model movinet_a0_int8.tflite \\\n    --maxResults 5`\n\nFor more information about executing inferences with TensorFlow Lite, read\n[TensorFlow Lite inference](https://www.tensorflow.org/lite/guide/inference).\n"
  },
  {
    "path": "lite/examples/video_classification/raspberry_pi/classify.py",
    "content": "# Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Main script to run video classification.\"\"\"\n\nimport argparse\nimport sys\nimport time\n\nimport cv2\n\nfrom video_classifier import VideoClassifier\nfrom video_classifier import VideoClassifierOptions\n\n# Visualization parameters\n_ROW_SIZE = 20  # pixels\n_LEFT_MARGIN = 24  # pixels\n_TEXT_COLOR = (0, 0, 255)  # red\n_FONT_SIZE = 1\n_FONT_THICKNESS = 1\n_MODEL_FPS = 5  # Ensure the input images are fed to the model at this fps.\n_MODEL_FPS_ERROR_RANGE = 0.1  # Acceptable error range in fps.\n\n\ndef run(model: str, label: str, max_results: int, num_threads: int,\n        camera_id: int, width: int, height: int) -> None:\n  \"\"\"Continuously run inference on images acquired from the camera.\n\n  Args:\n      model: Name of the TFLite video classification model.\n      label: Name of the video classification label.\n      max_results: Max of classification results.\n      num_threads: Number of CPU threads to run the model.\n      camera_id: The camera id to be passed to OpenCV.\n      width: The width of the frame captured from the camera.\n      height: The height of the frame captured from the camera.\n  \"\"\"\n  # Initialize the video classification model\n  options = VideoClassifierOptions(\n      num_threads=num_threads, max_results=max_results)\n  classifier = VideoClassifier(model, label, options)\n\n  # Variables to calculate FPS\n  counter, fps, last_inference_start_time, time_per_infer = 0, 0, 0, 0\n  categories = []\n\n  # Start capturing video input from the camera\n  cap = cv2.VideoCapture(camera_id)\n  cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)\n  cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)\n\n  # Continuously capture images from the camera and run inference\n  while cap.isOpened():\n    success, image = cap.read()\n    if not success:\n      sys.exit(\n          'ERROR: Unable to read from webcam. Please verify your webcam settings.'\n      )\n    counter += 1\n\n    # Mirror the image\n    image = cv2.flip(image, 1)\n\n    # Ensure that frames are feed to the model at {_MODEL_FPS} frames per second\n    # as required in the model specs.\n    current_frame_start_time = time.time()\n    diff = current_frame_start_time - last_inference_start_time\n    if diff * _MODEL_FPS >= (1 - _MODEL_FPS_ERROR_RANGE):\n      # Store the time when inference starts.\n      last_inference_start_time = current_frame_start_time\n\n      # Calculate the inference FPS\n      fps = 1.0 / diff\n\n      # Convert the frame to RGB as required by the TFLite model.\n      frame_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n\n      # Feed the frame to the video classification model.\n      categories = classifier.classify(frame_rgb)\n\n      # Calculate time required per inference.\n      time_per_infer = time.time() - current_frame_start_time\n\n    # Notes: Frames that aren't fed to the model are still displayed to make the\n    # video look smooth. We'll show classification results from the latest\n    # classification run on the screen.\n    # Show the FPS .\n    fps_text = 'Current FPS = {0:.1f}. Expect: {1}'.format(fps, _MODEL_FPS)\n    text_location = (_LEFT_MARGIN, _ROW_SIZE)\n    cv2.putText(image, fps_text, text_location, cv2.FONT_HERSHEY_PLAIN,\n                _FONT_SIZE, _TEXT_COLOR, _FONT_THICKNESS)\n\n    # Show the time per inference.\n    time_per_infer_text = 'Time per inference: {0}ms'.format(\n        int(time_per_infer * 1000))\n    text_location = (_LEFT_MARGIN, _ROW_SIZE * 2)\n    cv2.putText(image, time_per_infer_text, text_location,\n                cv2.FONT_HERSHEY_PLAIN, _FONT_SIZE, _TEXT_COLOR,\n                _FONT_THICKNESS)\n\n    # Show classification results on the image.\n    for idx, category in enumerate(categories):\n      class_name = category.label\n      probability = round(category.score, 2)\n      result_text = class_name + ' (' + str(probability) + ')'\n      # Skip the first 2 lines occupied by the fps and time per inference.\n      text_location = (_LEFT_MARGIN, (idx + 3) * _ROW_SIZE)\n      cv2.putText(image, result_text, text_location, cv2.FONT_HERSHEY_PLAIN,\n                  _FONT_SIZE, _TEXT_COLOR, _FONT_THICKNESS)\n\n    # Stop the program if the ESC key is pressed.\n    if cv2.waitKey(1) == 27:\n      break\n    cv2.imshow('video_classification', image)\n\n  cap.release()\n  cv2.destroyAllWindows()\n\n\ndef main():\n  parser = argparse.ArgumentParser(\n      formatter_class=argparse.ArgumentDefaultsHelpFormatter)\n  parser.add_argument(\n      '--model',\n      help='Name of video classification model.',\n      required=False,\n      default='movinet_a0_int8.tflite')\n  parser.add_argument(\n      '--label',\n      help='Name of video classification label.',\n      required=False,\n      default='kinetics600_label_map.txt')\n  parser.add_argument(\n      '--maxResults',\n      help='Max of classification results.',\n      required=False,\n      default=3)\n  parser.add_argument(\n      '--numThreads',\n      help='Number of CPU threads to run the model.',\n      required=False,\n      default=4)\n  parser.add_argument(\n      '--cameraId', help='Id of camera.', required=False, default=0)\n  parser.add_argument(\n      '--frameWidth',\n      help='Width of frame to capture from camera.',\n      required=False,\n      default=640)\n  parser.add_argument(\n      '--frameHeight',\n      help='Height of frame to capture from camera.',\n      required=False,\n      default=480)\n  args = parser.parse_args()\n\n  run(args.model, args.label, int(args.maxResults), int(args.numThreads),\n      int(args.cameraId), args.frameWidth, args.frameHeight)\n\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "lite/examples/video_classification/raspberry_pi/kinetics600_label_map.txt",
    "content": "abseiling\nacting in play\nadjusting glasses\nair drumming\nalligator wrestling\nanswering questions\napplauding\napplying cream\narchaeological excavation\narchery\narguing\narm wrestling\narranging flowers\nassembling bicycle\nassembling computer\nattending conference\nauctioning\nbackflip (human)\nbaking cookies\nbandaging\nbarbequing\nbartending\nbase jumping\nbathing dog\nbattle rope training\nbeatboxing\nbee keeping\nbelly dancing\nbench pressing\nbending back\nbending metal\nbiking through snow\nblasting sand\nblowdrying hair\nblowing bubble gum\nblowing glass\nblowing leaves\nblowing nose\nblowing out candles\nbobsledding\nbodysurfing\nbookbinding\nbottling\nbouncing on bouncy castle\nbouncing on trampoline\nbowling\nbraiding hair\nbreading or breadcrumbing\nbreakdancing\nbreaking boards\nbreathing fire\nbrush painting\nbrushing hair\nbrushing teeth\nbuilding cabinet\nbuilding lego\nbuilding sandcastle\nbuilding shed\nbull fighting\nbulldozing\nbungee jumping\nburping\nbusking\ncalculating\ncalligraphy\ncanoeing or kayaking\ncapoeira\ncapsizing\ncard stacking\ncard throwing\ncarrying baby\ncartwheeling\ncarving ice\ncarving pumpkin\ncasting fishing line\ncatching fish\ncatching or throwing baseball\ncatching or throwing frisbee\ncatching or throwing softball\ncelebrating\nchanging gear in car\nchanging oil\nchanging wheel (not on bike)\nchecking tires\ncheerleading\nchewing gum\nchiseling stone\nchiseling wood\nchopping meat\nchopping vegetables\nchopping wood\nclam digging\nclapping\nclay pottery making\nclean and jerk\ncleaning gutters\ncleaning pool\ncleaning shoes\ncleaning toilet\ncleaning windows\nclimbing a rope\nclimbing ladder\nclimbing tree\ncoloring in\ncombing hair\ncontact juggling\ncontorting\ncooking egg\ncooking on campfire\ncooking sausages (not on barbeque)\ncooking scallops\ncosplaying\ncounting money\ncountry line dancing\ncracking back\ncracking knuckles\ncracking neck\ncrawling baby\ncrossing eyes\ncrossing river\ncrying\ncumbia\ncurling (sport)\ncurling hair\ncutting apple\ncutting nails\ncutting orange\ncutting pineapple\ncutting watermelon\ndancing ballet\ndancing charleston\ndancing gangnam style\ndancing macarena\ndeadlifting\ndecorating the christmas tree\ndelivering mail\ndining\ndirecting traffic\ndisc golfing\ndiving cliff\ndocking boat\ndodgeball\ndoing aerobics\ndoing jigsaw puzzle\ndoing laundry\ndoing nails\ndrawing\ndribbling basketball\ndrinking shots\ndriving car\ndriving tractor\ndrooling\ndrop kicking\ndrumming fingers\ndumpster diving\ndunking basketball\ndyeing eyebrows\ndyeing hair\neating burger\neating cake\neating carrots\neating chips\neating doughnuts\neating hotdog\neating ice cream\neating spaghetti\neating watermelon\negg hunting\nembroidering\nexercising with an exercise ball\nextinguishing fire\nfaceplanting\nfalling off bike\nfalling off chair\nfeeding birds\nfeeding fish\nfeeding goats\nfencing (sport)\nfidgeting\nfinger snapping\nfixing bicycle\nfixing hair\nflint knapping\nflipping pancake\nfly tying\nflying kite\nfolding clothes\nfolding napkins\nfolding paper\nfront raises\nfrying vegetables\ngeocaching\ngetting a haircut\ngetting a piercing\ngetting a tattoo\ngiving or receiving award\ngold panning\ngolf chipping\ngolf driving\ngolf putting\ngospel singing in church\ngrinding meat\ngrooming dog\ngrooming horse\ngymnastics tumbling\nhammer throw\nhand washing clothes\nhead stand\nheadbanging\nheadbutting\nhigh jump\nhigh kick\nhistorical reenactment\nhitting baseball\nhockey stop\nholding snake\nhome roasting coffee\nhopscotch\nhoverboarding\nhuddling\nhugging (not baby)\nhugging baby\nhula hooping\nhurdling\nhurling (sport)\nice climbing\nice fishing\nice skating\nice swimming\ninflating balloons\ninstalling carpet\nironing\nironing hair\njavelin throw\njaywalking\njetskiing\njogging\njuggling balls\njuggling fire\njuggling soccer ball\njumping bicycle\njumping into pool\njumping jacks\njumpstyle dancing\nkaraoke\nkicking field goal\nkicking soccer ball\nkissing\nkitesurfing\nknitting\nkrumping\nland sailing\nlaughing\nlawn mower racing\nlaying bricks\nlaying concrete\nlaying stone\nlaying tiles\nleatherworking\nlicking\nlifting hat\nlighting fire\nlock picking\nlong jump\nlongboarding\nlooking at phone\nluge\nlunge\nmaking a cake\nmaking a sandwich\nmaking balloon shapes\nmaking bubbles\nmaking cheese\nmaking horseshoes\nmaking jewelry\nmaking paper aeroplanes\nmaking pizza\nmaking snowman\nmaking sushi\nmaking tea\nmaking the bed\nmarching\nmarriage proposal\nmassaging back\nmassaging feet\nmassaging legs\nmassaging neck\nmassaging person's head\nmilking cow\nmoon walking\nmopping floor\nmosh pit dancing\nmotorcycling\nmountain climber (exercise)\nmoving furniture\nmowing lawn\nmushroom foraging\nneedle felting\nnews anchoring\nopening bottle (not wine)\nopening door\nopening present\nopening refrigerator\nopening wine bottle\npacking\nparagliding\nparasailing\nparkour\npassing American football (in game)\npassing american football (not in game)\npassing soccer ball\npeeling apples\npeeling potatoes\nperson collecting garbage\npetting animal (not cat)\npetting cat\nphotobombing\nphotocopying\npicking fruit\npillow fight\npinching\npirouetting\nplaning wood\nplanting trees\nplastering\nplaying accordion\nplaying badminton\nplaying bagpipes\nplaying basketball\nplaying bass guitar\nplaying beer pong\nplaying blackjack\nplaying cello\nplaying chess\nplaying clarinet\nplaying controller\nplaying cricket\nplaying cymbals\nplaying darts\nplaying didgeridoo\nplaying dominoes\nplaying drums\nplaying field hockey\nplaying flute\nplaying gong\nplaying guitar\nplaying hand clapping games\nplaying harmonica\nplaying harp\nplaying ice hockey\nplaying keyboard\nplaying kickball\nplaying laser tag\nplaying lute\nplaying maracas\nplaying marbles\nplaying monopoly\nplaying netball\nplaying ocarina\nplaying organ\nplaying paintball\nplaying pan pipes\nplaying piano\nplaying pinball\nplaying ping pong\nplaying poker\nplaying polo\nplaying recorder\nplaying rubiks cube\nplaying saxophone\nplaying scrabble\nplaying squash or racquetball\nplaying tennis\nplaying trombone\nplaying trumpet\nplaying ukulele\nplaying violin\nplaying volleyball\nplaying with trains\nplaying xylophone\npoking bellybutton\npole vault\npolishing metal\npopping balloons\npouring beer\npreparing salad\npresenting weather forecast\npull ups\npumping fist\npumping gas\npunching bag\npunching person (boxing)\npush up\npushing car\npushing cart\npushing wheelbarrow\npushing wheelchair\nputting in contact lenses\nputting on eyeliner\nputting on foundation\nputting on lipstick\nputting on mascara\nputting on sari\nputting on shoes\nraising eyebrows\nreading book\nreading newspaper\nrecording music\nrepairing puncture\nriding a bike\nriding camel\nriding elephant\nriding mechanical bull\nriding mule\nriding or walking with horse\nriding scooter\nriding snow blower\nriding unicycle\nripping paper\nroasting marshmallows\nroasting pig\nrobot dancing\nrock climbing\nrock scissors paper\nroller skating\nrolling pastry\nrope pushdown\nrunning on treadmill\nsailing\nsalsa dancing\nsanding floor\nsausage making\nsawing wood\nscrambling eggs\nscrapbooking\nscrubbing face\nscuba diving\nseparating eggs\nsetting table\nsewing\nshaking hands\nshaking head\nshaping bread dough\nsharpening knives\nsharpening pencil\nshaving head\nshaving legs\nshearing sheep\nshining flashlight\nshining shoes\nshooting basketball\nshooting goal (soccer)\nshopping\nshot put\nshoveling snow\nshucking oysters\nshuffling cards\nshuffling feet\nside kick\nsign language interpreting\nsinging\nsipping cup\nsitup\nskateboarding\nski jumping\nskiing crosscountry\nskiing mono\nskiing slalom\nskipping rope\nskipping stone\nskydiving\nslacklining\nslapping\nsled dog racing\nsleeping\nsmashing\nsmelling feet\nsmoking\nsmoking hookah\nsmoking pipe\nsnatch weight lifting\nsneezing\nsnorkeling\nsnowboarding\nsnowkiting\nsnowmobiling\nsomersaulting\nspelunking\nspinning poi\nspray painting\nspringboard diving\nsquare dancing\nsquat\nstanding on hands\nstaring\nsteer roping\nsticking tongue out\nstomping grapes\nstretching arm\nstretching leg\nsucking lolly\nsurfing crowd\nsurfing water\nsweeping floor\nswimming backstroke\nswimming breast stroke\nswimming butterfly stroke\nswimming front crawl\nswing dancing\nswinging baseball bat\nswinging on something\nsword fighting\nsword swallowing\ntackling\ntagging graffiti\ntai chi\ntalking on cell phone\ntango dancing\ntap dancing\ntapping guitar\ntapping pen\ntasting beer\ntasting food\ntasting wine\ntestifying\ntexting\nthreading needle\nthrowing axe\nthrowing ball (not baseball or American football)\nthrowing discus\nthrowing knife\nthrowing snowballs\nthrowing tantrum\nthrowing water balloon\ntickling\ntie dying\ntightrope walking\ntiptoeing\ntobogganing\ntossing coin\ntraining dog\ntrapezing\ntrimming or shaving beard\ntrimming shrubs\ntrimming trees\ntriple jump\ntwiddling fingers\ntying bow tie\ntying knot (not on a tie)\ntying necktie\ntying shoe laces\nunboxing\nunloading truck\nusing a microscope\nusing a paint roller\nusing a power drill\nusing a sledge hammer\nusing a wrench\nusing atm\nusing bagging machine\nusing circular saw\nusing inhaler\nusing puppets\nusing remote controller (not gaming)\nusing segway\nvacuuming floor\nvisiting the zoo\nwading through mud\nwading through water\nwaiting in line\nwaking up\nwalking the dog\nwalking through snow\nwashing dishes\nwashing feet\nwashing hair\nwashing hands\nwatching tv\nwater skiing\nwater sliding\nwatering plants\nwaving hand\nwaxing back\nwaxing chest\nwaxing eyebrows\nwaxing legs\nweaving basket\nweaving fabric\nwelding\nwhistling\nwindsurfing\nwinking\nwood burning (art)\nwrapping present\nwrestling\nwriting\nyarn spinning\nyawning\nyoga\nzumba\n"
  },
  {
    "path": "lite/examples/video_classification/raspberry_pi/requirements.txt",
    "content": "argparse\nnumpy>=1.20.0  # To ensure compatibility with OpenCV on Raspberry Pi.\nopencv-python~=4.5.3.56\ntflite-runtime>=2.7.0"
  },
  {
    "path": "lite/examples/video_classification/raspberry_pi/setup.sh",
    "content": "#!/bin/bash\n\nif [ $# -eq 0 ]; then\n  DATA_DIR=\"./\"\nelse\n  DATA_DIR=\"$1\"\nfi\n\n# Install Python dependencies.\npython3 -m pip install pip --upgrade\npython3 -m pip install -r requirements.txt\n\n# Download TF Lite models\nFILE=${DATA_DIR}/movinet_a0_int8.tflite\nif [ ! -f \"$FILE\" ]; then\n  curl \\\n    -L 'https://tfhub.dev/tensorflow/lite-model/movinet/a0/stream/kinetics-600/classification/tflite/int8/1?lite-format=tflite' \\\n    -o ${FILE}\nfi\n\necho -e \"Downloaded files are in ${DATA_DIR}\""
  },
  {
    "path": "lite/examples/video_classification/raspberry_pi/video_classifier.py",
    "content": "# Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"A wrapper for TensorFlow Lite video classification models.\"\"\"\n\nfrom typing import List, NamedTuple\n\nimport cv2\nimport numpy as np\n\n# pylint: disable=g-import-not-at-top\ntry:\n  # Import TFLite interpreter from tflite_runtime package if it's available.\n  from ai_edge_litert.interpreter import Interpreter\nexcept ImportError:\n  # If not, fallback to use the TFLite interpreter from the full TF package.\n  import tensorflow as tf\n  Interpreter = tf.lite.Interpreter\n# pylint: enable=g-import-not-at-top\n\n\nclass VideoClassifierOptions(NamedTuple):\n  \"\"\"A config to initialize an video classifier.\"\"\"\n\n  label_allow_list: List[str] = None\n  \"\"\"The optional allow list of labels.\"\"\"\n\n  label_deny_list: List[str] = None\n  \"\"\"The optional deny list of labels.\"\"\"\n\n  max_results: int = 5\n  \"\"\"The maximum number of top-scored classification results to return.\"\"\"\n\n  num_threads: int = 1\n  \"\"\"The number of CPU threads to be used.\"\"\"\n\n  score_threshold: float = 0.0\n  \"\"\"The score threshold of classification results to return.\"\"\"\n\n\nclass Category(NamedTuple):\n  \"\"\"A result of a video classification.\"\"\"\n  label: str\n  score: float\n\n\nclass VideoClassifier(object):\n  \"\"\"A wrapper class for a TFLite video classification model.\"\"\"\n\n  _MODEL_INPUT_SIGNATURE_NAME = 'image'\n  _MODEL_OUTPUT_SIGNATURE_NAME = 'logits'\n  _MODEL_INPUT_MEAN = 0\n  _MODEL_INPUT_STD = 255\n\n  def __init__(\n      self,\n      model_path: str,\n      label_file: str,\n      options: VideoClassifierOptions = VideoClassifierOptions()\n  ) -> None:\n    \"\"\"Initialize a video classification model.\n\n    Args:\n        model_path: Path of the TFLite video classification model.\n        label_file: Path of the video classification label list.\n        options: The config to initialize an video classifier. (Optional)\n\n    Raises:\n        ValueError: If the TFLite model is invalid.\n    \"\"\"\n\n    interpreter = Interpreter(\n        model_path=model_path, num_threads=options.num_threads)\n    signature = interpreter.get_signature_runner()\n\n    # Load the label list.\n    with open(label_file, 'r') as f:\n      lines = f.readlines()\n      label_list = [line.replace('\\n', '') for line in lines]\n      self._label_list = label_list\n\n    # Remove the batch dimension to get the real input shape.\n    input_shape = signature.get_input_details()[\n        self._MODEL_INPUT_SIGNATURE_NAME]['shape']\n    input_shape = np.delete(input_shape, np.where(input_shape == 1))\n    self._input_height = input_shape[0]\n    self._input_width = input_shape[1]\n\n    # Store the signature runner and model options for later use.\n    self._signature = signature\n    self._options = options\n\n    # Set the initial state for the model.\n    self._internal_states = {}\n    self.clear()\n\n  def clear(self):\n    \"\"\"Clear the internal state of the model to start classifying a new scene.\"\"\"\n    # Create the initial (zero) states\n    init_states = {\n        name: np.zeros(signature['shape'], dtype=signature['dtype'])\n        for name, signature in self._signature.get_input_details().items()\n    }\n\n    # Remove the holder for the input image as it'll be fed by the caller.\n    init_states.pop(self._MODEL_INPUT_SIGNATURE_NAME)\n\n    # Store the model's internal state.\n    self._internal_states = init_states\n\n  def _preprocess(self, image: np.ndarray) -> np.ndarray:\n    \"\"\"Preprocess the image as required by the TFLite model.\"\"\"\n    input_tensor = cv2.resize(image, (self._input_width, self._input_height))\n    input_tensor = input_tensor[np.newaxis, np.newaxis]\n    input_tensor = np.float32(input_tensor -\n                              self._MODEL_INPUT_MEAN) / self._MODEL_INPUT_STD\n\n    return input_tensor\n\n  def classify(self, frame: np.ndarray) -> List[Category]:\n    \"\"\"Classify an input frame.\n\n    Frames from the target video should be fed to the model in sequence.\n\n    Args:\n        frame: A [height, width, 3] RGB image representing a frame in a video.\n\n    Returns:\n        A list of prediction result. Sorted by probability descending.\n    \"\"\"\n    # Preprocess the input frame.\n    frame = self._preprocess(frame)\n\n    # Feed the input frame and the model internal states to the TFLite model.\n    outputs = self._signature(**self._internal_states, image=frame)\n\n    # Take the model output and store the internal states for subsequence\n    # frames.\n    logits = outputs.pop(self._MODEL_OUTPUT_SIGNATURE_NAME)\n    self._internal_states = outputs\n\n    return self._postprocess(logits)\n\n  def _postprocess(self, logits: np.ndarray) -> List[Category]:\n    \"\"\"Post-process the logits into a list of Category objects.\n\n    Args:\n        logits: Raw logits output of the TFLite model.\n\n    Returns:\n        A list of classification results.\n    \"\"\"\n    # Convert from logits to probabilities using softmax function.\n    exp_logits = np.exp(np.squeeze(logits, axis=0))\n    probabilities = exp_logits / np.sum(exp_logits)\n\n    # Sort the labels so that the more likely categories come first.\n    prob_descending = sorted(\n        range(len(probabilities)), key=lambda k: probabilities[k], reverse=True)\n    categories = [\n        Category(label=self._label_list[idx], score=probabilities[idx])\n        for idx in prob_descending\n    ]\n\n    # Filter out categories in the deny list.\n    filtered_results = categories\n    if self._options.label_deny_list is not None:\n      filtered_results = list(\n          filter(\n              lambda category: category.label not in self._options.\n              label_deny_list, filtered_results))\n\n    # Keep only categories in the allow list.\n    if self._options.label_allow_list is not None:\n      filtered_results = list(\n          filter(\n              lambda category: category.label in self._options.label_allow_list,\n              filtered_results))\n\n    # Filter out categories with score lower than the score threshold.\n    if self._options.score_threshold is not None:\n      filtered_results = list(\n          filter(\n              lambda category: category.score >= self._options.score_threshold,\n              filtered_results))\n\n    # Only return maximum of max_results categories.\n    if self._options.max_results > 0:\n      result_count = min(len(filtered_results), self._options.max_results)\n      filtered_results = filtered_results[:result_count]\n\n    return filtered_results\n"
  },
  {
    "path": "lite/examples/video_classification/raspberry_pi/video_classifier_test.py",
    "content": "# Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Unit tests for the VideoClassifier wrapper.\"\"\"\n\nfrom typing import List\nimport unittest\n\nimport cv2\nimport numpy as np\nfrom video_classifier import Category\nfrom video_classifier import VideoClassifier\nfrom video_classifier import VideoClassifierOptions\n\n_MODEL_FILE = 'movinet_a0_int8.tflite'\n_LABEL_FILE = 'kinetics600_label_map.txt'\n_GROUND_TRUTH_LABEL = 'carving ice'\n_GROUND_TRUTH_MIN_SCORE = 0.5\n_VIDEO_FILE = 'test_data/carving_ice.mp4'\n_ALLOW_LIST = ['carving ice', 'sawing wood']\n_DENY_LIST = ['chiseling stone']\n_SCORE_THRESHOLD = 0.2\n_MAX_RESULTS = 3\n_ACCEPTABLE_ERROR_RANGE = 0.01\n\n\nclass VideoClassifierTest(unittest.TestCase):\n\n  def setUp(self):\n    \"\"\"Initialize the shared variables.\"\"\"\n    super().setUp()\n\n    # Load frames from the test video.\n    cap = cv2.VideoCapture(_VIDEO_FILE)\n    frames = []\n    for _ in range(int(cap.get(cv2.CAP_PROP_FRAME_COUNT))):\n      _, frame = cap.read()\n      frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)\n      frames.append(frame)\n    self._frames = frames\n\n  def _run_classification_with_frames(self, classifier: VideoClassifier,\n                                      frames: List[np.ndarray]) -> [Category]:\n    \"\"\"Run video classification with all the given frames and return the final result.\"\"\"\n    categories = None\n    for frame in frames:\n      categories = classifier.classify(frame)\n\n    return categories\n\n  def test_default_option(self):\n    \"\"\"Check if the default option works correctly.\"\"\"\n    classifier = VideoClassifier(_MODEL_FILE, _LABEL_FILE)\n\n    # Run classification with frames from the test video.\n    categories = self._run_classification_with_frames(classifier, self._frames)\n\n    # Check if TOP-1 result match.\n    top_1_category = categories[0]\n    self.assertEqual(\n        _GROUND_TRUTH_LABEL, top_1_category.label,\n        'Label {0} does not match with ground truth {1}'.format(\n            top_1_category.label, _GROUND_TRUTH_LABEL))\n    self.assertLessEqual(\n        _GROUND_TRUTH_MIN_SCORE, top_1_category.score,\n        'Classification score {0} is smaller than threshold {1}'.format(\n            top_1_category.score, _GROUND_TRUTH_MIN_SCORE))\n\n  def test_allow_list(self):\n    \"\"\"Test the label_allow_list option.\"\"\"\n    option = VideoClassifierOptions(label_allow_list=_ALLOW_LIST)\n    classifier = VideoClassifier(_MODEL_FILE, _LABEL_FILE, option)\n\n    # Run classification with frames from the test video.\n    categories = self._run_classification_with_frames(classifier, self._frames)\n\n    for category in categories:\n      label = category.label\n      self.assertIn(\n          label, _ALLOW_LIST,\n          'Label \"{0}\" found but not in label allow list'.format(label))\n\n  def test_deny_list(self):\n    \"\"\"Test the label_deny_list option.\"\"\"\n    option = VideoClassifierOptions(label_deny_list=_DENY_LIST)\n    classifier = VideoClassifier(_MODEL_FILE, _LABEL_FILE, options=option)\n\n    # Run classification with frames from the test video.\n    categories = self._run_classification_with_frames(classifier, self._frames)\n\n    for category in categories:\n      label = category.label\n      self.assertNotIn(label, _DENY_LIST,\n                       'Label \"{0}\" found but in deny list.'.format(label))\n\n  def test_score_threshold_option(self):\n    \"\"\"Test the score_threshold option.\"\"\"\n    option = VideoClassifierOptions(score_threshold=_SCORE_THRESHOLD)\n    classifier = VideoClassifier(_MODEL_FILE, _LABEL_FILE, options=option)\n\n    # Run classification with frames from the test video.\n    categories = self._run_classification_with_frames(classifier, self._frames)\n\n    for category in categories:\n      score = category.score\n      self.assertGreaterEqual(\n          score, _SCORE_THRESHOLD,\n          'Classification with score lower than threshold found. {0}'.format(\n              category))\n\n  def test_max_results_option(self):\n    \"\"\"Test the max_results option.\"\"\"\n    option = VideoClassifierOptions(max_results=_MAX_RESULTS)\n    classifier = VideoClassifier(_MODEL_FILE, _LABEL_FILE, options=option)\n\n    # Run classification with frames from the test video.\n    categories = self._run_classification_with_frames(classifier, self._frames)\n\n    self.assertLessEqual(\n        len(categories), _MAX_RESULTS, 'Too many results returned.')\n\n\nif __name__ == '__main__':\n  unittest.main()\n"
  },
  {
    "path": "lite/tools/build_all_android.sh",
    "content": "#!/bin/bash\n# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\nset -e  # Exit immediately when one of the commands fails.\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" >/dev/null 2>&1 && pwd)\"\nEXAMPLES_DIR=\"$(realpath \"${SCRIPT_DIR}/../examples\")\"\nNUM_PROCESSES=16   # Run tests in parallel. Adjust this to your own machine.\n\n# Finds all <example_name>/android directories under lite/examples.\n# Runs the android build script for each of those directories.\n#\n# If two number arguments are provided, interpret them as \"Part $1 of $2\".\n# That is, split the app_dir_list into $2 parts, and only run the $1-th part.\nfunction build_android_examples {\n  # NOTE: This script breaks if there are any spaces in the full path.\n  app_dir_list=( $(find \"${EXAMPLES_DIR}\" -mindepth 2 -maxdepth 2 -type d -name android | sort) )\n  target_list=( \"${app_dir_list[@]}\" )\n\n  num_re=\"^[0-9]+$\"\n  if [[ \"$1\" =~ $num_re ]] && [[ \"$2\" =~ $num_re ]] && (( $1 <= $2 )); then\n    total=${#app_dir_list[@]}\n    batch_size=$(( $total / $2 ))\n    start=$(( $batch_size * ($1 - 1) ))\n    length=$batch_size\n\n    # Make sure to process all the remainder if this is the last batch.\n    if (( $1 == $2 )); then\n      length=$(( $length + $total % $2 ))\n    fi\n\n    # Take the sublist.\n    target_list=( \"${app_dir_list[@]:$start:$length}\" )\n  fi\n\n  # Run the builds in parallel.\n  for app_dir in \"${target_list[@]}\"; do\n    echo ${app_dir}\n  done | xargs -0 -n 1 -P ${NUM_PROCESSES?} -I{} \\\n         ${SCRIPT_DIR}/build_android_app.sh {}\n}\n\nbuild_android_examples \"$@\"\n"
  },
  {
    "path": "lite/tools/build_all_ios.sh",
    "content": "#!/bin/bash\n# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\nset -e  # Exit immediately when one of the commands fails.\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" >/dev/null 2>&1 && pwd)\"\nEXAMPLES_DIR=\"$(realpath \"${SCRIPT_DIR}/../examples\")\"\nNUM_PROCESSES=4   # Run tests in parallel. Adjust this to your own machine.\n\n# Finds all <example_name>/ios* directories under lite/examples.\n# Runs the xcode build script for each of those directories.\nfunction build_ios_examples {\n  # In case the one of the build fails, overwrite the exit code to 255, in order\n  # to make xargs to terminate early and stop processing the remaining builds.\n  find \"${EXAMPLES_DIR}\" -mindepth 2 -maxdepth 2 -type d -name \"ios*\" -print0 \\\n    | xargs -0 -n 1 -P ${NUM_PROCESSES} -I{} bash -c \\\n        \"${SCRIPT_DIR}/build_ios_app.sh \\\"{}\\\" || exit 255\"\n}\n\nfunction install_helper_tools {\n  if ! [ -x \"$(command -v jq)\" ]; then\n    brew install jq\n  fi\n\n  if ! [ -x \"$(command -v xcpretty)\" ]; then\n    sudo gem install xcpretty\n  fi\n}\n\ninstall_helper_tools\nbuild_ios_examples\n"
  },
  {
    "path": "lite/tools/build_android_app.sh",
    "content": "#!/bin/bash\n# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\nset -e  # Exit immediately when one of the commands fails.\nset -x  # Verbose\n\n# Prerequisites: The following envvars should be set when running this script.\n#  - ANDROID_HOME: Android SDK location (tested with Android SDK 29)\n#  - JAVA_HOME:    Java SDK location (tested with Open JDK 8)\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" >/dev/null 2>&1 && pwd)\"\nEXAMPLES_DIR=\"$(realpath \"${SCRIPT_DIR}/../examples\")\"\n\n# Keep a list of android apps which should be excluded from the CI builds.\nSKIPPED_BUILDS=\"\"\n\nfunction build_android_example {\n  # Check if this directory appears in the skipped builds list.\n  RELATIVE_DIR=\"${1#\"${EXAMPLES_DIR}/\"}\"\n  if echo \"${SKIPPED_BUILDS}\" | grep -qx \"${RELATIVE_DIR}\"; then\n    echo \"WARNING: Skipping build for ${RELATIVE_DIR}.\"\n    return 0\n  fi\n\n  echo \"=== BUILD STARTED: ${RELATIVE_DIR} ===\"\n\n  pushd \"$1\" > /dev/null\n\n  # Check if the directory contains a gradle wrapper.\n  if [[ -x \"$1/gradlew\" ]]; then\n    # Run the \"build\" task with the gradle wrapper.\n    ./gradlew clean assembleRelease --stacktrace\n  elif [[ -x \"$1/finish/gradlew\" ]]; then\n    # Accommodate codelab directory\n    ./finish/gradlew clean assembleRelease --stacktrace\n  else\n    echo \"ERROR: Gradle wrapper could not be found under ${RELATIVE_DIR}.\"\n    exit 1\n  fi\n\n  popd > /dev/null\n\n  echo \"=== BUILD FINISHED: ${RELATIVE_DIR} ===\"\n  echo\n  echo\n}\n\n\nfunction build_smartreply_aar {\n  # Builds once only after Smart Reply Android app.\n  # It is to use bazel to build and create AAR for custom ops in cc.\n  # TODO(tianlin): To generalize as pre-/post-build.\n  RELATIVE_DIR=\"${1#\"${EXAMPLES_DIR}/\"}\"\n\n  # Run this only for smart_reply/android.\n  if [[ \"${RELATIVE_DIR}\" != \"smart_reply/android\" ]]; then\n    return 0\n  fi\n  WORKSPACE_DIR=\"${EXAMPLES_DIR}/smart_reply/android/app/libs\"\n  echo \"=== BUILD STARTED: ${RELATIVE_DIR} :: build_smartreply_aar ===\"\n\n  pushd \"$1\" > /dev/null\n\n  cd \"${WORKSPACE_DIR}\"\n  echo \"-- Building in directory: ${WORKSPACE_DIR} --\"\n  /usr/bin/gcc -v\n  bazel version  # Get bazel version info.\n  # Add --sandbox_debug to provide more info for testing.\n  bazel build --sandbox_debug //cc/... //cc:smartreply_runtime_aar\n  bazel test //cc/...\n\n  popd > /dev/null\n\n  echo \"=== BUILD STARTED: ${RELATIVE_DIR} :: build_smartreply_aar ===\"\n  echo\n  echo\n}\n\ntime build_android_example \"$1\"\n\ntime build_smartreply_aar \"$1\"\n"
  },
  {
    "path": "lite/tools/build_ios_app.sh",
    "content": "#!/bin/bash\n# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\nset -e  # Exit immediately when one of the commands fails.\nset -x  # Verbose\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" >/dev/null 2>&1 && pwd)\"\nEXAMPLES_DIR=\"$(realpath \"${SCRIPT_DIR}/../examples\")\"\n\nPROJECT_EXT=\".xcodeproj\"\nWORKSPACE_EXT=\".xcworkspace\"\n\n# Keep a list of iOS apps which should be excluded from the CI builds.\nSKIPPED_BUILDS=\"\ngesture_classification/ios\nclassification_by_retrieval/ios\n\"\n\nINSTALL_LATEST_NIGHTLY_VERSION=\"${2:-false}\"\n\necho \"Example path: $1\"\necho \"Install latest nightly version: $2\"\n\nfunction build_ios_example {\n  # Check if this directory appears in the skipped builds list.\n  RELATIVE_DIR=\"${1#\"${EXAMPLES_DIR}/\"}\"\n  if echo \"${SKIPPED_BUILDS}\" | grep -qx \"${RELATIVE_DIR}\"; then\n    echo \"WARNING: Skipping build for ${RELATIVE_DIR}.\"\n    return 0\n  fi\n\n  echo \"=== BUILD STARTED: ${RELATIVE_DIR} ===\"\n\n  pushd \"$1\" > /dev/null\n\n  # Cleanly install the dependencies\n  # Retry a few times to workaround intermittent download errors.\n  MAX_RETRY=3\n  INSTALLED=false\n  for i in $(seq 1 ${MAX_RETRY})\n  do\n    echo \"Trying to install dependencies... (trial $i)\"\n    if \"$INSTALL_LATEST_NIGHTLY_VERSION\"; then\n      if pod update --verbose --repo-update --clean-install; then\n        INSTALLED=true\n        break\n      fi\n    else\n      if pod install --verbose --repo-update --clean-install; then\n        INSTALLED=true\n        break\n      fi\n    fi\n  done\n\n  if [[ \"${INSTALLED}\" == false ]]; then\n    echo \"Exceeded the max retry limit (${MAX_RETRY}) of pod install command.\"\n    exit 1\n  fi\n\n  # Extract the scheme names.\n  PROJECT_NAME=\"$(find * -maxdepth 0 -type d -name \"*${PROJECT_EXT}\")\"\n  WORKSPACE_NAME=\"$(find * -type d -name \"*${WORKSPACE_EXT}\")\"\n  SCHEMES=\"$(xcodebuild -list -project \"${PROJECT_NAME}\" -json | jq -r \".project.schemes[]\")\"\n\n  # Build each scheme without code signing.\n  for scheme in ${SCHEMES}; do\n    # Due to an unknown issue prior to Xcode 11.4, a non-existing test scheme\n    # might appear in the list of project schemes. For now, if a scheme name\n    # contains the word \"Tests\", skip the build for that particular scheme.\n    if [[ \"${scheme}\" == *\"Tests\"* ]]; then\n      continue\n    fi\n\n    echo \"--- BUILDING SCHEME ${scheme} FOR PROJECT ${RELATIVE_DIR} ---\"\n    set -o pipefail && xcodebuild \\\n        CODE_SIGN_IDENTITY=\"\" \\\n        CODE_SIGNING_REQUIRED=\"NO\" \\\n        CODE_SIGN_ENTITLEMENTS=\"\" \\\n        CODE_SIGNING_ALLOWED=\"NO\" \\\n        ARCHS=\"arm64\" \\\n        -scheme \"${scheme}\" \\\n        -workspace \"${WORKSPACE_NAME}\" \\\n        | xcpretty  # Pretty print the build output.\n    echo \"--- FINISHED BUILDING SCHEME ${scheme} FOR PROJECT ${RELATIVE_DIR} ---\"\n  done\n\n  popd > /dev/null\n\n  echo \"=== BUILD FINISHED: ${RELATIVE_DIR} ===\"\n  echo\n  echo\n}\n\nbuild_ios_example \"$1\"\n"
  },
  {
    "path": "lite/tools/build_model_maker_api_docs.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\nr\"\"\"Generate python docs for tf.lite.\n\n# How to run\n\n```\npython build_docs.py --output_dir=/path/to/output\n```\n\n\"\"\"\nimport pathlib\nfrom typing import Any, Sequence\n\nfrom absl import app\nfrom absl import flags\n\nfrom tensorflow_docs.api_generator import generate_lib\nfrom tensorflow_docs.api_generator import public_api\nfrom tensorflow_docs.api_generator.public_api import Children\n\nimport tensorflow_examples\nimport tflite_model_maker\n\nimport yaml\n\n\nclass OrderedDumper(yaml.dumper.Dumper):\n  pass\n\n\ndef _dict_representer(dumper, data):\n  \"\"\"Force yaml to output dictionaries in order, not alphabetically.\"\"\"\n  return dumper.represent_dict(data.items())\n\n\nOrderedDumper.add_representer(dict, _dict_representer)\n\nflags.DEFINE_string('output_dir', '/tmp/mm_api/',\n                    'The path to output the files to')\n\nflags.DEFINE_string(\n    'code_url_prefix',\n    'https://github.com/tensorflow/examples/blob/master/tensorflow_examples/lite/model_maker/public',\n    'The url prefix for links to code.')\n\nflags.DEFINE_bool('search_hints', True,\n                  'Include metadata search hints in the generated files')\n\nflags.DEFINE_string('site_path', '/lite/api_docs/python',\n                    'Path prefix in the _toc.yaml')\n\nFLAGS = flags.FLAGS\n\n\nclass DeprecatedAPIFilter(object):\n  \"\"\"Filter deprecated APIs.\"\"\"\n\n  def __init__(self):\n    \"\"\"Constructor.\"\"\"\n    self._deprecated = [\n        'tflite_model_maker.configs',\n        'tflite_model_maker.ExportFormat',\n        'tflite_model_maker.ImageClassifierDataLoader',\n    ]\n\n  def _is_deprecated(self, path: Sequence[str], child_name: str) -> bool:\n    full_name = list(path) + [child_name]\n    full_name = '.'.join(full_name)\n\n    for d in self._deprecated:\n      if full_name == d:\n        return True\n    return False\n\n  def __call__(self, path: Sequence[str], parent: Any,\n               children: Children) -> Children:\n    return [(child_name, child_obj)\n            for child_name, child_obj in list(children)\n            if not self._is_deprecated(path, child_name)]\n\n\ndef main(_):\n  doc_generator = generate_lib.DocGenerator(\n      root_title='TensorFlow Lite Model Maker',\n      py_modules=[('tflite_model_maker', tflite_model_maker)],\n      base_dir=[\n          pathlib.Path(tflite_model_maker.__file__).parent,\n          pathlib.Path(tensorflow_examples.__file__).parent,\n      ],\n      code_url_prefix=[\n          FLAGS.code_url_prefix,\n          'https://github.com/tensorflow/examples/blob/master/tensorflow_examples/'\n      ],\n      search_hints=FLAGS.search_hints,\n      site_path=FLAGS.site_path,\n      callbacks=[\n          public_api.explicit_package_contents_filter,\n          DeprecatedAPIFilter()\n      ])\n\n  doc_generator.build(output_dir=FLAGS.output_dir)\n\n  toc_file = pathlib.Path(FLAGS.output_dir) / 'tflite_model_maker/_toc.yaml'\n  toc = yaml.safe_load(toc_file.read_text())\n\n  ## Nest all sub-modules under the root module instead of beside it.\n  #\n  # Before:\n  #\n  #  mm\n  #  mm.compat\n  #  mm.configs\n  #\n  # After:\n  #\n  #  mm\n  #    compat\n  #    configs\n\n  # The first item of the toc is the root module.\n  mm = toc['toc'][0]\n  mm['status'] = 'experimental'\n  # Shorten the title, and insert each sub-modules into the root module's\n  # \"section\"\n  sub_sections = mm['section']\n  # The remaining items are the submodules\n  for section in toc['toc'][1:]:\n    section['title'] = section['title'].replace('tflite_model_maker.', '')\n    sub_sections.append(section)\n  # replace the list of (sub)modules with the root module.\n  toc['toc'] = [mm]\n\n  with toc_file.open('w') as f:\n    yaml.dump(toc, f, Dumper=OrderedDumper)\n\n\nif __name__ == '__main__':\n  app.run(main)\n"
  },
  {
    "path": "lite/tools/shared/android/app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillColor=\"#26A69A\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n</vector>\n"
  },
  {
    "path": "lite/tools/shared/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n  <path\n      android:fillType=\"evenOdd\"\n      android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\">\n    <aapt:attr name=\"android:fillColor\">\n      <gradient\n          android:endX=\"78.5885\"\n          android:endY=\"90.9159\"\n          android:startX=\"48.7653\"\n          android:startY=\"61.0927\"\n          android:type=\"linear\">\n        <item\n            android:color=\"#44000000\"\n            android:offset=\"0.0\"/>\n        <item\n            android:color=\"#00000000\"\n            android:offset=\"1.0\"/>\n      </gradient>\n    </aapt:attr>\n  </path>\n  <path\n      android:fillColor=\"#FFFFFF\"\n      android:fillType=\"nonZero\"\n      android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n      android:strokeColor=\"#00000000\"\n      android:strokeWidth=\"1\"/>\n</vector>\n"
  },
  {
    "path": "lite/tools/shared/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/tools/shared/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@android:color/white\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "lite/tools/shared/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.4-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "lite/tools/shared/android/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=\"\\\\\\\"\\\\\\\"\"\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "lite/tools/shared/android/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lite/tools/test_pip_setup.sh",
    "content": "#!/bin/bash\n# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\n# The script is to pip install tensorflow-examples and test the package.\nset -e  # Exit immediately when one of the commands fails.\nset -x  # Verbose\n\nsudo apt-get install -y libsndfile1  # Library required for audio processing.\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" >/dev/null 2>&1 && pwd)\"\nWORKSPACE_DIR=\"$(realpath \"${SCRIPT_DIR}/../..\")\"\n\nTEST_SCRIPT=\"${WORKSPACE_DIR}/tensorflow_examples/lite/model_maker/pip_package/test_pip_package.sh\"\n\n# Test model maker's pip package.\n\"${TEST_SCRIPT}\" $1\n"
  },
  {
    "path": "setup.py",
    "content": "# Copyright 2015 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"tensorflow_examples is a package of tensorflow example code.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport datetime\nimport subprocess\nimport sys\n\nfrom setuptools import find_packages\nfrom setuptools import setup\n\nnightly = False\nif '--nightly' in sys.argv:\n  nightly = True\n  sys.argv.remove('--nightly')\n\nproject_name = 'tensorflow-examples'\n# Get the current commit hash and timestamp\n# The timestamp is used so that newer commits have a higher version number\n# The hash is used so that the specific commit can be identified\n# The hash integer can be converted back to the hash string with:\n# '%032x' % commit_hash_int\ncommit_hash_int = int(\n    subprocess.check_output(['git', 'rev-parse', 'HEAD'])\n    .decode('utf-8')\n    .strip(),\n    16,\n)\ncommit_timestamp = (\n    subprocess.check_output(['git', 'show', '-s', '--format=%ct', 'HEAD'])\n    .decode('utf-8')\n    .strip()\n)\nversion = f'0.{commit_timestamp}.{commit_hash_int}'\n\nif nightly:\n  project_name = 'tensorflow-examples-nightly'\n  datestring = datetime.datetime.now().strftime('%Y%m%d%H%M')\n  version = '%s-dev%s' % (version, datestring)\n\nDOCLINES = __doc__.split('\\n')\n\nREQUIRED_PKGS = [\n    'absl-py',\n    'six',\n]\n\nTESTS_REQUIRE = [\n    'jupyter',\n]\n\nif sys.version_info.major == 3:\n  # Packages only for Python 3\n  pass\nelse:\n  # Packages only for Python 2\n  TESTS_REQUIRE.append('mock')\n  REQUIRED_PKGS.append('futures')  # concurrent.futures\n\nif sys.version_info < (3, 4):\n  # enum introduced in Python 3.4\n  REQUIRED_PKGS.append('enum34')\n\nsetup(\n    name=project_name,\n    version=version,\n    description=DOCLINES[0],\n    long_description='\\n'.join(DOCLINES[2:]),\n    author='Google Inc.',\n    author_email='packages@tensorflow.org',\n    url='http://github.com/tensorflow/examples',\n    download_url='https://github.com/tensorflow/examples/tags',\n    license='Apache 2.0',\n    packages=find_packages(),\n    scripts=[],\n    install_requires=REQUIRED_PKGS,\n    extras_require={\n        'tests': TESTS_REQUIRE,\n    },\n    classifiers=[\n        'Development Status :: 4 - Beta',\n        'Intended Audience :: Developers',\n        'License :: OSI Approved :: Apache Software License',\n        'Topic :: Scientific/Engineering :: Artificial Intelligence',\n    ],\n    keywords='tensorflow examples',\n)\n"
  },
  {
    "path": "templates/notebook.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Tce3stUlHN0L\"\n      },\n      \"source\": [\n        \"##### Copyright 2018 The TensorFlow Authors.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"tuOe1ymfHZPu\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"MfBg1C5NB3X0\"\n      },\n      \"source\": [\n        \"# Title\\n\",\n        \"\\n\",\n        \"_Notebook orignially contributed by: {link to you}_\\n\",\n        \"\\n\",\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/template/notebook.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/template/notebook.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"r6P32iYYV27b\"\n      },\n      \"source\": [\n        \"{**Fix these links**}\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"xHxb-dlhMIzW\"\n      },\n      \"source\": [\n        \"## Overview\\n\",\n        \"\\n\",\n        \"{Include a paragraph or two explaining what this example demonstrates, who should be interested in it, and what you need to know before you get started.}\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"MUXex9ctTuDB\"\n      },\n      \"source\": [\n        \"## Setup\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"IqR2PQG4ZaZ0\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import tensorflow as tf\\n\",\n        \"\\n\",\n        \"import numpy as np\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"1Eh-iCRVBm0p\"\n      },\n      \"source\": [\n        \"{Put all your imports and installs up into a setup section.}\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"UhNtHfuxCGVy\"\n      },\n      \"source\": [\n        \"## Notes\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"kKhmFeraTdEI\"\n      },\n      \"source\": [\n        \"For general instructions on how to write docs for Tensorflow see [Writing TensorFlow Documentation](https://www.tensorflow.org/community/contribute/docs).\\n\",\n        \"\\n\",\n        \"The tips below are specific to notebooks for tensorflow.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"2V22fKegUtF9\"\n      },\n      \"source\": [\n        \"### General\\n\",\n        \"\\n\",\n        \"* Include the collapsed license at the top (this uses Colab's \\\"Form\\\" mode to hide the cells).\\n\",\n        \"* Only include a single `H1` title.\\n\",\n        \"* Include the button-bar immediately under the `H1`.\\n\",\n        \"* Include an overview section before any code.\\n\",\n        \"* Put all your installs and imports in a setup section.\\n\",\n        \"* Always include the three `__future__` imports.\\n\",\n        \"* Save the notebook with the Table of Contents open.\\n\",\n        \"* Write python3 compatible code.\\n\",\n        \"* Keep cells small (~max 20 lines).\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"YrsKXcPRUvK9\"\n      },\n      \"source\": [\n        \"### Working in GitHub\\n\",\n        \"\\n\",\n        \"* Be consistent about how you save your notebooks, otherwise the JSON-diffs will be a mess.\\n\",\n        \"\\n\",\n        \"* This notebook has the \\\"Omit code cell output when saving this notebook\\\" option set. GitHub refuses to diff notebooks with large diffs (inline images).\\n\",\n        \"\\n\",\n        \"* [reviewnb.com](http://reviewnb.com) may help. You can access it using this bookmarklet:\\n\",\n        \"\\n\",\n        \"  ```\\n\",\n        \"javascript:(function(){ window.open(window.location.toString().replace(/github\\\\.com/, 'app.reviewnb.com').replace(/files$/,\\\"\\\")); })()\\n\",\n        \" ```\\n\",\n        \" \\n\",\n        \"* To open a GitHub notebook in Colab use the [Open in Colab](https://chrome.google.com/webstore/detail/open-in-colab/iogfkhleblhcpcekbiedikdehleodpjo) extension (or make a bookmarklet).\\n\",\n        \"  \\n\",\n        \"* The easiest way to edit a notebook in GitHub is to open it with Colab from the branch you want to edit. Then use File --> Save a copy in GitHub, which will save it back to the branch you opened it from.\\n\",\n        \"\\n\",\n        \"* For PRs it's helpful to post a direct Colab link to the PR head: https://colab.research.google.com/github/{user}/{repo}/blob/{branch}/{path}.ipynb\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"QKp40qS-DGEZ\"\n      },\n      \"source\": [\n        \"### Code Style\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"* Notebooks are for people. Write code optimized for clarity.\\n\",\n        \"\\n\",\n        \"* Demonstrate small parts before combining them into something more complex. Like below:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"KtylpxOmceaC\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#Build the model\\n\",\n        \"model = tf.keras.Sequential([\\n\",\n        \"    tf.keras.layers.Dense(10, activation='relu', input_shape=(None, 5)),\\n\",\n        \"    tf.keras.layers.Dense(3)\\n\",\n        \"])\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"mMOeXVmbdilM\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Run the model on a single batch of data, and inspect the output.\\n\",\n        \"result = model(tf.constant(np.random.randn(10,5), dtype = tf.float32)).numpy()\\n\",\n        \"\\n\",\n        \"print(\\\"min:\\\", result.min())\\n\",\n        \"print(\\\"max:\\\", result.max())\\n\",\n        \"print(\\\"mean:\\\", result.mean())\\n\",\n        \"print(\\\"shape:\\\", result.shape)\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"U82B_tH2d294\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"# Compile the model for training\\n\",\n        \"model.compile(optimizer=tf.keras.optimizers.Adam(),\\n\",\n        \"              loss=tf.keras.losses.CategoricalCrossentropy(from_logits=True))\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"g3-lzxbCZi-H\"\n      },\n      \"source\": [\n        \"* Keep examples quick. Use small datasets, or small slices of datasets. You don't need to train to convergence, train until it's obvious it's making progress.\\n\",\n        \"\\n\",\n        \"* For a large example, don't try to fit all the code in the notebook. Add python files to tensorflow examples, and in the noptebook run: `!pip install git+https://github.com/tensorflow/examples`\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"TJdqBNBbS78n\"\n      },\n      \"source\": [\n        \"### Code content\\n\",\n        \"\\n\",\n        \"Use the highest level API that gets the job done (unless the goal is to demonstrate the low level API).\\n\",\n        \"\\n\",\n        \"Use `keras.Sequential` > keras functional api > keras model subclassing > ...\\n\",\n        \"\\n\",\n        \"Use  `model.fit` > `model.train_on_batch` > manual `GradientTapes`.\\n\",\n        \"\\n\",\n        \"Use eager-style code.\\n\",\n        \"\\n\",\n        \"Use `tensorflow_datasets` and `tf.data` where possible.\\n\",\n        \"\\n\",\n        \"Avoid `compat.v1`.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"78HBT9cQXJko\"\n      },\n      \"source\": [\n        \"### Text\\n\",\n        \"\\n\",\n        \"* Use an imperative style. \\\"Run a batch of images through the model.\\\"\\n\",\n        \"\\n\",\n        \"* Use sentence case in titles/headings. \\n\",\n        \"\\n\",\n        \"* Use short titles/headings: \\\"Download the data\\\", \\\"Build the Model\\\", \\\"Train the model\\\".\\n\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"collapsed_sections\": [\n        \"Tce3stUlHN0L\"\n      ],\n      \"name\": \"notebook.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "tensorflow_examples/__init__.py",
    "content": "# Copyright 2015 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n"
  },
  {
    "path": "tensorflow_examples/lite/__init__.py",
    "content": "# Copyright 2015 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/README.md",
    "content": "# TFLite Model Maker\n\n## Overview\n\nThe TFLite Model Maker library simplifies the process of adapting and converting\na TensorFlow neural-network model to particular input data when deploying this\nmodel for on-device ML applications.\n\n## Requirements\n\n*   Refer to\n    [requirements.txt](https://github.com/tensorflow/examples/blob/master/tensorflow_examples/lite/model_maker/requirements.txt)\n    for dependent libraries that're needed to use the library and run the demo\n    code.\n*   Note that you might also need to install `sndfile` for Audio tasks.\nOn Debian/Ubuntu, you can do so by `sudo apt-get install libsndfile1`\n\n## Installation\n\nThere are two ways to install Model Maker.\n\n*   Install a prebuilt pip package:\n    [`tflite-model-maker`](https://pypi.org/project/tflite-model-maker/).\n\n```shell\npip install tflite-model-maker\n```\n\nIf you want to install nightly version\n[`tflite-model-maker-nightly`](https://pypi.org/project/tflite-model-maker-nightly/),\nplease follow the command:\n\n```shell\npip install tflite-model-maker-nightly\n```\n\n*   Clone the source code from GitHub and install.\n\n```shell\ngit clone https://github.com/tensorflow/examples\ncd examples/tensorflow_examples/lite/model_maker/pip_package\npip install -e .\n```\n\nTensorFlow Lite Model Maker depends on TensorFlow\n[pip package](https://www.tensorflow.org/install/pip). For GPU support, please\nrefer to TensorFlow's [GPU guide](https://www.tensorflow.org/install/gpu) or\n[installation guide](https://www.tensorflow.org/install).\n\n## End-to-End Example\n\nFor instance, it could have an end-to-end image classification example that\nutilizes this library with just 4 lines of code, each of which representing one\nstep of the overall process. For more detail, you could refer to\n[Colab for image classification](https://colab.research.google.com/github/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/tutorials/model_maker_image_classification.ipynb).\n\n*   Step 1. Import the required modules.\n\n```python\nfrom tflite_model_maker import image_classifier\nfrom tflite_model_maker.image_classifier import DataLoader\n```\n\n*   Step 2. Load input data specific to an on-device ML app.\n\n```python\ndata = DataLoader.from_folder('flower_photos/')\n```\n\n*   Step 3. Customize the TensorFlow model.\n\n```python\nmodel = image_classifier.create(data)\n```\n\n*   Step 4. Evaluate the model.\n\n```python\nloss, accuracy = model.evaluate()\n```\n\n*   Step 5. Export to Tensorflow Lite model and label file in `export_dir`.\n\n```python\nmodel.export(export_dir='/tmp/')\n```\n\n## Notebook\n\nCurrently, we support image classification, text classification and question\nanswer tasks. Meanwhile, we provide demo code for each of them in demo folder.\n\n*   [Overview for TensorFlow Lite Model Maker](https://www.tensorflow.org/lite/guide/model_maker)\n*   [Python API Reference](https://www.tensorflow.org/lite/api_docs/python/tflite_model_maker)\n*   [Colab for image classification](https://colab.research.google.com/github/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/tutorials/model_maker_image_classification.ipynb)\n*   [Colab for text classification](https://colab.research.google.com/github/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/tutorials/model_maker_text_classification.ipynb)\n*   [Colab for BERT question answer](https://colab.research.google.com/github/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/tutorials/model_maker_question_answer.ipynb)\n*   [Colab for object detection](https://www.tensorflow.org/lite/tutorials/model_maker_object_detection)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/RELEASE.md",
    "content": "# Release 0.4\n## 0.4.3\n\n*   Fix the requirement deps issues.\n\n## 0.4.2\n\n*   Fix the version of tflite-support and flatbuffers to be consistent with\n    other packages.\n\n## 0.4.1\n\n*   Fix the version of scann to 1.2.6 to solve the import failure.\n\n## 0.4.0\n\n*   Add one new task: Searcher that support Image / Text as the inputs.\n\n# Release 0.3\n\n## 0.3.4\n\n*   Minor fix: remove tensorflow-estimator version requirements to avoid the\n    incompatible version error message during pip installation.\n\n## 0.3.3\n\n*   Minor fix: update tensorflow==2.6 and fix the failure of `evaluate_tflite`\n    for the object detection task.\n\n## 0.3.2\n\n*   Minor fix: Move librosa to an optional library for audio task.\n\n## 0.3.1\n\n*   Polish document showing in\n    https://www.tensorflow.org/lite/api_docs/python/tflite_model_maker.\n*   Add some 0.2.x APIs for backward compatibility.\n\n## 0.3.0\n\n*   Export TFLite Model Maker public APIs, and adapt colabs as demo code.\n*   Add three new tasks: Object Detection, Audio Classification and\n    Recommendation.\n\n# Release 0.2\n\n## 0.2.5\n\n*   Refactor the code to be more flexible and extentable without changing API.\n*   Add executable test script for pip package.\n*   Update tensorflow-hub version in requirements.txt to fix incompatible error\n    when installing pip package.\n\n## 0.2.4\n\n*   Update stable tensorflow version in requirements.txt.\n*   Support exporting to TFJS models.\n*   Refine Code: refactor the `gen_dataset` internal API in `DataLoader` and\n    remove redundant methods.\n*   Split requirements to stable version and nightly version.\n\n## 0.2.3\n\n*   Fix a bug for missing `default_batch_size`.\n*   Add a demo of customized task.\n*   Refine Code: Refactor `model_spec` to split different model specifications\n    into image/text and add `ClassificationDataLoader`.\n\n## 0.2.2\n\n*   Update tf-nightly version in requirements.txt.\n*   Refine Code: Change pre-defined model specification to function and extract\n    Keras callback logic into a reusable function.\n\n## 0.2.1\n\n*   Set the required package version in requirements.txt.\n*   Refine Code: Move `load_from_tfds` into `ImageDataLoader`, refactor the\n    `DataLoader` code, unify the predict method and provide a default\n    implementation for TFLite expo.\n\n## 0.2.0\n\n*   Setup virtualenv and test pip package.\n\n# Release 0.1\n\nInitial version for\n[TFLite Model Maker](https://www.tensorflow.org/lite/guide/model_maker): a model\ncustomization library for on-device applications.\n\nSupport tasks:\n\n*   [image classification](https://www.tensorflow.org/lite/tutorials/model_maker_image_classification)\n*   [text classification](https://www.tensorflow.org/lite/tutorials/model_maker_text_classification)\n*   [question answer](https://www.tensorflow.org/lite/tutorials/model_maker_question_answer)\n\n## 0.1.2\n\n*   Add `metadata_writer` for nlp tasks and set `with_metadata=True` by default\n    when exporting to TFLite.\n*   Update `model_info` in metadata for image classifier.\n\n## 0.1.1\n\n*   Update README.\n*   Add `mobilebert_qa_squad` model_spec, a mobilebert model pre-trained with\n    Squad1.1 dataset.\n\n## 0.1.0\n\n*   Initial version.\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/__init__.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"A transfer learning library to train custom TFLite models.\"\"\"\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/cli/__init__.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/cli/cli.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"CLI tool for model maker.\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport fire\n\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core.task import model_spec\nfrom tensorflow_examples.lite.model_maker.demo import image_classification_demo\nfrom tensorflow_examples.lite.model_maker.demo import question_answer_demo\nfrom tensorflow_examples.lite.model_maker.demo import text_classification_demo\n\n_IMAGE_MODELS = model_spec.IMAGE_CLASSIFICATION_MODELS\n_TEXT_MODELS = model_spec.TEXT_CLASSIFICATION_MODELS\n_QA_MODELS = model_spec.QUESTION_ANSWER_MODELS\n\n\nclass FormatDoc(object):\n  \"\"\"Decorator to format functionn doc with given parameters.\"\"\"\n\n  def __init__(self, *format_args, **format_kwargs):\n    self.args = format_args\n    self.kwargs = format_kwargs\n\n  def __call__(self, fn):\n    fn.__doc__ = fn.__doc__.format(*self.args, **self.kwargs)\n    return fn\n\n\nclass ModelMakerCLI(object):\n  \"\"\"Model Maker Command Line Interface.\n\n  Flags:\n    --tf: int, version of TF behavior. Valid: [1, 2], default: 2.\n  \"\"\"\n\n  def __init__(self, tf=2):\n    compat.setup_tf_behavior(tf)\n\n  @FormatDoc(MODELS=_IMAGE_MODELS)\n  def image_classification(self,\n                           data_dir,\n                           export_dir,\n                           spec='efficientnet_lite0',\n                           **kwargs):\n    \"\"\"Run Image classification.\n\n    Args:\n      data_dir: str, input directory of training data. (required)\n      export_dir: str, output directory to export files. (required)\n      spec: str, model_name. Valid: {MODELS}, default: efficientnet_lite0.\n      **kwargs: --epochs: int, epoch num to run. More: see `create` function.\n    \"\"\"\n    # Convert types\n    data_dir = str(data_dir)\n    export_dir = str(export_dir)\n\n    image_classification_demo.run(data_dir, export_dir, spec, **kwargs)\n\n  @FormatDoc(MODELS=_TEXT_MODELS)\n  def text_classification(self,\n                          data_dir,\n                          export_dir,\n                          spec='mobilebert_classifier',\n                          **kwargs):\n    r\"\"\"Run text classification.\n\n    Args:\n      data_dir: str, input directory of training data. (required)\n      export_dir: str, output directory to export files. (required)\n      spec: str, model_name. Valid: {MODELS}, default: mobilebert_classifier.\n      **kwargs: --epochs: int, epoch num to run. More: see `create` function.\n    \"\"\"\n    # Convert types\n    data_dir = str(data_dir)\n    export_dir = str(export_dir)\n    text_classification_demo.run(data_dir, export_dir, spec, **kwargs)\n\n  @FormatDoc(MODELS=_QA_MODELS)\n  def question_answer(self,\n                      train_data_path,\n                      validation_data_path,\n                      export_dir,\n                      spec='mobilebert_qa_squad',\n                      **kwargs):\n    r\"\"\"Run question answer.\n\n    Args:\n      train_data_path: str, input path of training data. (required)\n      validation_data_path: str, input path of training data. (required)\n      export_dir: str, output directory to export files. (required)\n      spec: str, model_name. Valid: {MODELS}, default: mobilebert_qa.\n      **kwargs: --epochs: int, epoch num to run. More: see `create` function.\n    \"\"\"\n    # Convert types\n    train_data_path = str(train_data_path)\n    validation_data_path = str(validation_data_path)\n    question_answer_demo.run(train_data_path, validation_data_path, export_dir,\n                             spec, **kwargs)\n\n\ndef main():\n  fire.Fire(ModelMakerCLI)\n\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/cli/cli_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport sys\nfrom unittest.mock import patch\n\nfrom absl.testing import absltest\nfrom absl.testing import parameterized\nimport fire\n\nfrom tensorflow_examples.lite.model_maker.cli import cli\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.demo import image_classification_demo\nfrom tensorflow_examples.lite.model_maker.demo import question_answer_demo\nfrom tensorflow_examples.lite.model_maker.demo import text_classification_demo\n\nBIN = 'model_maker'  # Whatever binary name.\n\n\ndef patch_image():\n  \"\"\"Patch image classification demo run.\"\"\"\n  return patch.object(image_classification_demo, 'run')\n\n\ndef patch_text():\n  \"\"\"Patch text classification demo run.\"\"\"\n  return patch.object(text_classification_demo, 'run')\n\n\ndef patch_qa():\n  \"\"\"Patch question answer demo run.\"\"\"\n  return patch.object(question_answer_demo, 'run')\n\n\ndef patch_setup():\n  \"\"\"Patch image classification demo run.\"\"\"\n  return patch.object(compat, 'setup_tf_behavior')\n\n\nclass CLITest(parameterized.TestCase):\n\n  @parameterized.parameters(\n      (['--tf=1'], 1),\n      (['--tf=2'], 2),\n      ([], 2),  # No extra flag, default\n  )\n  def test_init(self, tf_opt, expected_tf):\n    sys.argv = [BIN, 'image_classification', 'data', 'export'] + tf_opt\n    with patch_image() as run, patch_setup() as setup:\n      cli.main()\n      setup.assert_called_once_with(expected_tf)\n      run.assert_called_once_with('data', 'export', 'efficientnet_lite0')\n\n  @parameterized.parameters(\n      ([], ['efficientnet_lite0'], {}),\n      (['--spec=mobilenet_v2'], ['mobilenet_v2'], {}),\n      (['--spec=mobilenet_v2', '--epochs=1'], ['mobilenet_v2'], dict(epochs=1)),\n  )\n  def test_image_classification_demo(self, opt, args, kwargs):\n    sys.argv = [BIN, 'image_classification', 'data', 'export'] + opt\n    with patch_image() as run:\n      cli.main()\n      run.assert_called_once_with('data', 'export', *args, **kwargs)\n\n  def test_image_classification_demo_lack_param(self):\n    sys.argv = [BIN, 'image_classification', 'data']\n    with patch_image() as run:\n      with self.assertRaisesRegex(fire.core.FireExit, '2'):\n        cli.main()\n      run.assert_not_called()\n\n  @parameterized.parameters(\n      ([], ['mobilebert_classifier'], {}),\n      (['--spec=average_word_vec'], ['average_word_vec'], {}),\n      (['--epochs=1'], ['mobilebert_classifier'], dict(epochs=1)),\n  )\n  def test_text_classification_demo(self, opt, args, kwargs):\n    sys.argv = [BIN, 'text_classification', 'data', 'export'] + opt\n    with patch_text() as run:\n      cli.main()\n      run.assert_called_once_with('data', 'export', *args, **kwargs)\n\n  def test_text_classification_demo_lack_param(self):\n    sys.argv = [BIN, 'text_classification', 'data']\n    with patch_text() as run:\n      with self.assertRaisesRegex(fire.core.FireExit, '2'):\n        cli.main()\n      run.assert_not_called()\n\n  @parameterized.parameters(\n      ([], ['mobilebert_qa_squad'], {}),\n      (['--spec=bert_qa'], ['bert_qa'], {}),\n      (['--epochs=1'], ['mobilebert_qa_squad'], dict(epochs=1)),\n  )\n  def test_question_answer_demo(self, opt, args, kwargs):\n    sys.argv = [\n        BIN, 'question_answer', 'train_data', 'validation_data', 'export'\n    ] + opt\n    with patch_qa() as run:\n      cli.main()\n      run.assert_called_once_with('train_data', 'validation_data', 'export',\n                                  *args, **kwargs)\n\n  def test_question_answer_lack_param(self):\n    sys.argv = [BIN, 'question_answer', 'train_data', 'validation_data']\n    with patch_qa() as run:\n      with self.assertRaisesRegex(fire.core.FireExit, '2'):\n        cli.main()\n      run.assert_not_called()\n\n  def test_invalid_command(self):\n    sys.argv = [BIN, 'invalid_command']\n    with self.assertRaisesRegex(fire.core.FireExit, '2'):\n      cli.main()\n\n  @parameterized.parameters(\n      ([BIN, '--', '--help'],),\n      ([BIN, 'image_classification', '--', '--help'],),\n      ([BIN, 'text_classification', '--', '--help'],),\n      ([BIN, 'question_answer', '--', '--help'],),\n  )\n  def test_help(self, opt):\n    sys.argv = opt\n    with self.assertRaisesRegex(fire.core.FireExit, '0'):\n      cli.main()\n\n\nif __name__ == '__main__':\n  absltest.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/__init__.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/api/__init__.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Package to export APIs for Model Maker.\"\"\"\nfrom tensorflow_examples.lite.model_maker.core.api.api_util import mm_export\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/api/api_gen.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nr\"\"\"CLI utility to generate APIs.\n\nUsage:\npython api_gen.py --output_dir=<path to output> --version=0.x.x \\\n  --base_package=tflite_model_maker\n\"\"\"\n\nimport argparse\nimport json\nimport pathlib\nfrom typing import Dict, Sequence\n\nfrom tensorflow_examples.lite.model_maker.core.api import api_util\nfrom tensorflow_examples.lite.model_maker.core.api import deprecated_api\nfrom tensorflow_examples.lite.model_maker.core.api import golden_api_doc\n\n\ndef parse_arguments():\n  \"\"\"Parse arguments for API gen.\"\"\"\n  parser = argparse.ArgumentParser()\n  parser.add_argument(\n      '-o', '--output_dir', type=str, help='Base dir to output generated APIs.')\n  parser.add_argument(\n      '-v',\n      '--version',\n      type=str,\n      default='0.0.0dev',\n      help='Version of the package.')\n  parser.add_argument(\n      '-b',\n      '--base_package',\n      type=str,\n      default='tflite_model_maker',\n      help='Version of the package.')\n  return parser.parse_args()\n\n\ndef _read_golden_text(json_file: str) -> str:\n  \"\"\"Reads Golden file as text.\"\"\"\n  path = pathlib.Path(__file__).with_name(json_file)\n  with open(path) as f:\n    return f.read()\n\n\ndef load_golden(json_file: str) -> Dict[str, Sequence[str]]:\n  \"\"\"Loads Golden APIs.\"\"\"\n  content = _read_golden_text(json_file)\n  return json.loads(content)\n\n\ndef run(output_dir: str, base_package: str, version: str) -> None:\n  \"\"\"Runs main.\"\"\"\n  imports = load_golden('golden_api.json')\n  imports_doc = golden_api_doc.DOCS\n  deprecated_imports = deprecated_api.IMPORTS\n  api_util.write_packages(\n      output_dir,\n      imports,\n      imports_doc,\n      base_package,\n      version,\n      deprecated_imports=deprecated_imports)\n\n\ndef main() -> None:\n  args = parse_arguments()\n  run(args.output_dir, args.base_package, args.version)\n\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/api/api_gen_test.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Test for API generation.\"\"\"\n\nimport json\n\nimport tensorflow.compat.v2 as tf\n\nfrom tensorflow_examples.lite.model_maker.core.api import api_gen\nfrom tensorflow_examples.lite.model_maker.core.api import api_util\nfrom tensorflow_examples.lite.model_maker.core.api import golden_api_doc\nfrom tensorflow_examples.lite.model_maker.core.api import include  # pylint: disable=unused-import\n\n\nclass ApiGenTest(tf.test.TestCase):\n\n  def test_golden_api(self):\n    golden = api_gen.load_golden('golden_api.json')\n    imports = api_util.generate_imports()\n\n    imports_json = json.dumps(imports, indent=2, sort_keys=True)\n    golden_content = api_gen._read_golden_text('golden_api.json')\n    msg = ('Exported APIs do not match `golden_api.json`. Please check it.\\n\\n'\n           'Imports in json format: \\n{}\\n\\n\\n'\n           'Golden file content:\\n{}\\n\\n').format(imports_json, golden_content)\n    self.assertDictEqual(imports, golden, msg)\n\n  def test_golden_api_doc(self):\n    golden = api_gen.load_golden('golden_api.json')\n    golden_doc = golden_api_doc.DOCS\n\n    api_keys = list(golden.keys())\n    doc_keys = list(golden_doc.keys())\n    msg = ('Expect package keys are matched: \\n'\n           'In `golden_api.json`: \\n{}\\n\\n'\n           'In `golden_api_doc.py`: \\n{}\\n\\n').format(api_keys, doc_keys)\n    self.assertListEqual(api_keys, doc_keys, msg)\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/api/api_util.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Utilities for exporting TFLite Model Maker symbols as API.\n\nUsage:\n\nTo export a function or a class, use mm_export decorator. e.g.:\n```python\n@mm_export('bar.foo')\ndef foo(...):\n  ...\n```\n\nIf a function is assigned to a variable, you can export it by calling\nmm_export explicitly. e.g.:\n```python\ndef get_foo(...):\n  ...\nmm_export('bar.foo')(get_foo)\n```\n\nExporting a constant.\n```python\nFOO = 1\nmm_export('bar.FOO').export_constant(__name__, 'FOO')\n```\nwhere __name__ gets the current module name, and 'FOO' is the constant.\n\"\"\"\nimport collections\nfrom collections.abc import Callable  # pylint: disable=g-importing-member\nimport inspect\nimport os\nimport pathlib\nimport re\nfrom typing import Dict, List, Tuple, Sequence, Optional, Union\n\nimport dataclasses\n\n# Model Maker API.\nNAME_TO_SYMBOL = {}\n\nLICENSE = \"\"\"# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# pylint: disable=g-bad-import-order,redefined-builtin\n\"\"\"\n\n# Package prefix name for model maker.\nPACKAGE_PLACEHOLDER = '$PKG'\nROOT_PACKAGE_KEY = ''\n\n\n@dataclasses.dataclass\nclass Symbol:\n  \"\"\"Symbol to export.\"\"\"\n\n  exported_name: str  # Exported name of the symbol\n  exported_parts: List[str]  # Parts after splitting.\n  func: Optional[Callable]  # Function or class.\n  imported_module: str  # Imported module name.\n  imported_name: str  # Imported symbol name.\n\n  @classmethod\n  def from_callable(cls, exported_name: str, func: Callable) -> 'Symbol':\n    \"\"\"Creates a symbol from a callable (function or class).\"\"\"\n    if func is None:\n      raise ValueError('func should not be None: {}'.format(func))\n    imported_module, imported_name = _get_module_and_name(func)\n\n    exported_parts = split_name(exported_name)\n    return cls(\n        exported_name=exported_name,\n        exported_parts=exported_parts,\n        func=func,\n        imported_module=imported_module,\n        imported_name=imported_name)\n\n  @classmethod\n  def from_constant(cls, exported_name: str, module: str,\n                    name: str) -> 'Symbol':\n    \"\"\"Creates a symbol from a constant.\"\"\"\n    exported_parts = split_name(exported_name)\n    return cls(\n        exported_name=exported_name,\n        exported_parts=exported_parts,\n        func=None,\n        imported_module=module,\n        imported_name=name)\n\n  def get_package_name(self) -> str:\n    \"\"\"Generated path.\"\"\"\n    return as_package(self.exported_parts[:-1])\n\n  def gen_import(self) -> str:\n    \"\"\"Generates import text line.\"\"\"\n    as_name = self.exported_parts[-1]\n    if as_name == self.imported_name:\n      import_line = 'from {} import {}'.format(self.imported_module,\n                                               self.imported_name)\n    else:\n      import_line = 'from {} import {} as {}'.format(self.imported_module,\n                                                     self.imported_name,\n                                                     as_name)\n    return import_line\n\n  def gen_parents_import(self) -> Dict[str, Sequence[str]]:\n    \"\"\"Generates parents import.\"\"\"\n    length = len(self.exported_parts)\n    parents_import = {}\n    for i in range(length - 1):\n      parts = self.exported_parts[:i]\n      import_name = self.exported_parts[i]\n      parent = as_package(parts)\n      abs_package = split_name(PACKAGE_PLACEHOLDER) + parts\n      abs_package = as_package(abs_package)\n      import_line = 'from {} import {}'.format(abs_package, import_name)\n      parents_import[parent] = [import_line]\n    return parents_import\n\n\ndef split_name(name: str) -> List[str]:\n  \"\"\"Splits name and returns a list of segments.\n\n  Args:\n    name: str.\n\n  Returns:\n    list of str: package segments\n  \"\"\"\n  parts = name.split('.')\n  return list(filter(lambda n: n, parts))\n\n\ndef as_package(names: List[str]) -> str:\n  \"\"\"Joins names as a package name.\"\"\"\n  return '.'.join(names)\n\n\ndef as_path(names: List[str]) -> str:\n  \"\"\"Joins names as a file path.\"\"\"\n  if names:\n    return os.path.join(*names)\n  else:\n    return ''\n\n\ndef _get_module_and_name(func: Callable) -> Tuple[str, str]:\n  \"\"\"Gets module and name, or raise error if not a function.\"\"\"\n  if not inspect.isfunction(func) and not inspect.isclass(func):\n    raise ValueError('Expect a function or class, but got: {}'.format(func))\n  return func.__module__, func.__name__\n\n\nclass mm_export:  # pylint: disable=invalid-name\n  \"\"\"Exports model maker APIs.\"\"\"\n\n  def __init__(self, name: str):\n    if name in NAME_TO_SYMBOL:\n      raise ValueError('API already exists: `{}`.'.format(name))\n    self._exported_name = name  # API name.\n\n  def __call__(self, func: Callable) -> Callable:\n    \"\"\"Exports function or class.\"\"\"\n    NAME_TO_SYMBOL[self._exported_name] = Symbol.from_callable(\n        self._exported_name, func)\n    return func\n\n  def export_constant(self, module: str, name: str) -> None:\n    \"\"\"Exports constants.\"\"\"\n    NAME_TO_SYMBOL[self._exported_name] = Symbol.from_constant(\n        self._exported_name, module, name)\n\n\ndef _reset_apis():\n  \"\"\"Resets all APIs.\"\"\"\n  global NAME_TO_SYMBOL\n  NAME_TO_SYMBOL = {}\n\n\ndef _case_insensitive(s: str):\n  \"\"\"To sort with case insensitive.\"\"\"\n  return s.lower()\n\n\ndef generate_imports() -> Dict[str, Sequence[str]]:\n  \"\"\"Generates imports.\"\"\"\n  import_dict = collections.defaultdict(set)\n  for _, symbol in NAME_TO_SYMBOL.items():\n    package_name = symbol.get_package_name()\n    import_line = symbol.gen_import()\n    import_dict[package_name].add(import_line)\n\n    for k, line in symbol.gen_parents_import().items():\n      import_dict[k].update(line)\n\n  # Add prefix and sort import values.\n  abs_import_dict = {}\n  for package_name, value_set in import_dict.items():\n    parts = split_name(package_name)\n    abs_package = as_package(parts)\n    abs_import_dict[abs_package] = list(\n        sorted(value_set, key=_case_insensitive))\n  return abs_import_dict\n\n\ndef generate_package_doc(package_name):\n  \"\"\"Generates package doc.\"\"\"\n  return '\"\"\"Generated API for package: {}.\"\"\"'.format(package_name)\n\n\ndef write_packages(\n    base_dir: str,\n    imports: Dict[str, Sequence[str]],\n    doc_dict: Dict[str, str],\n    base_package: str,\n    version: str,\n    deprecated_imports: Optional[Dict[str, Sequence[str]]] = None) -> None:\n  \"\"\"Writes packages as init files.\n\n  Args:\n    base_dir: str, base directory to write packages.\n    imports: dict, pairs of (namespace, list of imports).\n    doc_dict: dict, pairs of (namespace, package_doc).\n    base_package: str, the base package name. (e.g. 'tflite_model_maker')\n    version: str, version string. (e.g., 0.x.x).\n    deprecated_imports: optinal dict, pairs of (namespace, list of imports).\n  \"\"\"\n  if not deprecated_imports:\n    deprecated_imports = {}\n\n  for package_name, import_lines in imports.items():\n    # Create parent dir.\n    parts = as_path(split_name(package_name))\n    parent_dir = os.path.join(base_dir, parts)\n    make_dirs_or_not(parent_dir)\n\n    # Write header and import lines.\n    full_path = os.path.join(parent_dir, '__init__.py')\n\n    lines = [\n        line.replace(PACKAGE_PLACEHOLDER, base_package) for line in import_lines\n    ]\n\n    # Add deprecated imports for backward compatiblity..\n    if package_name in deprecated_imports:\n      lines.append(deprecated_imports[package_name])\n\n    # For base package add __version__.\n    if package_name == ROOT_PACKAGE_KEY:\n      lines.append(_version_line(version))\n\n    full_package_name = as_package(\n        split_name(base_package) + split_name(package_name))\n\n    # Add package doc.\n    if package_name in doc_dict:\n      doc = '\"\"\"{}\"\"\"'.format(doc_dict[package_name])\n    else:\n      doc = generate_package_doc(full_package_name)\n\n    write_python_file(full_path, doc, lines)\n\n\nPathOrStrType = Union[pathlib.Path, str]\n\n\ndef make_dirs_or_not(dirpath: Union[PathOrStrType]):\n  \"\"\"Make dirs if not exists.\"\"\"\n  if not os.path.exists(dirpath):\n    os.makedirs(dirpath)\n\n\ndef write_python_file(filepath: PathOrStrType, package_doc: Optional[str],\n                      lines: Optional[Sequence[str]]):\n  \"\"\"Writes python file.\"\"\"\n  with open(filepath, 'w') as f:\n    f.write(LICENSE)\n    if package_doc:\n      f.write(package_doc + '\\n\\n')\n    if lines:\n      for line in lines:\n        f.write(line + '\\n')\n\n\ndef _version_line(version: str):\n  \"\"\"Gets a version statement.\"\"\"\n  return \"__version__ = '{}'\".format(version)\n\n\ndef overwrite_version_in_package(base_dir: PathOrStrType, version: str):\n  \"\"\"Overwrites __version__ in package.\"\"\"\n  base_init = os.path.join(str(base_dir), '__init__.py')\n  with open(base_init, 'r+') as f:\n    content = f.read()\n    version_regex = re.compile(r'^__version__ = .+$', flags=re.MULTILINE)\n    new_content = version_regex.sub(_version_line(version), content)\n    f.seek(0)\n    f.write(new_content)\n    f.truncate()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/api/api_util_test.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Tests for api_util.\"\"\"\n\nimport os\nimport tempfile\n\nimport tensorflow.compat.v2 as tf\n\nfrom tensorflow_examples.lite.model_maker.core.api import api_util\n\n\nclass MMExportTest(tf.test.TestCase):\n\n  def setUp(self):\n    super(MMExportTest, self).setUp()\n    api_util._reset_apis()\n\n  def test_call(self):\n\n    @api_util.mm_export('foo.a')\n    def a():\n      pass\n\n    self.assertLen(api_util.NAME_TO_SYMBOL, 1)\n    expected = api_util.Symbol('foo.a', ['foo', 'a'], a, '__main__', 'a')\n    self.assertEqual(api_util.NAME_TO_SYMBOL['foo.a'], expected)\n\n  def test_call_class(self):\n\n    @api_util.mm_export('foo.A')\n    class A():\n      pass\n\n    self.assertLen(api_util.NAME_TO_SYMBOL, 1)\n    expected = api_util.Symbol('foo.A', ['foo', 'A'], A, '__main__', 'A')\n    self.assertEqual(api_util.NAME_TO_SYMBOL['foo.A'], expected)\n\n  def test_call_duplicated(self):\n    with self.assertRaisesRegex(ValueError, 'API already exists'):\n\n      @api_util.mm_export('foo.a')\n      def a():  # pylint: disable=unused-variable\n        pass\n\n      @api_util.mm_export('foo.a')\n      def b():  # pylint: disable=unused-variable\n        pass\n\n  def test_call_global_function(self):\n\n    def test_func():\n      \"\"\"Func to test export.\"\"\"\n      pass\n\n    exportor = api_util.mm_export('foo.bar.test_func')\n    ret_func = exportor(test_func)\n    self.assertEqual(ret_func, test_func)\n    func = api_util.NAME_TO_SYMBOL['foo.bar.test_func']\n    self.assertEqual(func.gen_import(), 'from __main__ import test_func')\n    self.assertEqual(func.get_package_name(), 'foo.bar')\n\n    exportor = api_util.mm_export('fn')\n    exportor(test_func)\n    func = api_util.NAME_TO_SYMBOL['fn']\n    self.assertEqual(func.gen_import(), 'from __main__ import test_func as fn')\n    self.assertEqual(func.get_package_name(), '')\n\n  def test_export_constant(self):\n\n    FOO = 1  # pylint: disable=invalid-name,unused-variable\n    api_util.mm_export('foo.FOO').export_constant(__name__, 'FOO')\n\n    self.assertLen(api_util.NAME_TO_SYMBOL, 1)\n    expected = api_util.Symbol('foo.FOO', ['foo', 'FOO'], None, '__main__',\n                               'FOO')\n    self.assertEqual(api_util.NAME_TO_SYMBOL['foo.FOO'], expected)\n\n\nclass ApiUtilTest(tf.test.TestCase):\n\n  def setUp(self):\n    super(ApiUtilTest, self).setUp()\n    api_util._reset_apis()\n\n  def test_get_module_and_name(self):\n\n    def a():\n      pass\n\n    module_and_name = api_util._get_module_and_name(a)\n    self.assertTupleEqual(module_and_name, ('__main__', 'a'))\n\n    expected = (\n        'tensorflow_examples.lite.model_maker.core.api.api_util',\n        'generate_imports',\n    )\n    module_and_name = api_util._get_module_and_name(api_util.generate_imports)\n    self.assertTupleEqual(module_and_name, expected)\n\n    class A:\n      pass\n\n    module_and_name = api_util._get_module_and_name(A)\n    self.assertTupleEqual(module_and_name, ('__main__', 'A'))\n\n  def test_generate_imports(self):\n\n    @api_util.mm_export('foo.a')\n    def a():  # pylint: disable=unused-variable\n      pass\n\n    @api_util.mm_export('foo.b')\n    def b():  # pylint: disable=unused-variable\n      pass\n\n    @api_util.mm_export('bar.sub.c')\n    def c():  # pylint: disable=unused-variable\n      pass\n\n    @api_util.mm_export('bar.sub.aaa')\n    def aa():  # pylint: disable=unused-variable\n      pass\n\n    imports = api_util.generate_imports()\n    expected = {\n        '': [\n            'from $PKG import bar',\n            'from $PKG import foo',\n        ],\n        'foo': [\n            'from __main__ import a',\n            'from __main__ import b',\n        ],\n        'bar': ['from $PKG.bar import sub',],\n        'bar.sub': [\n            'from __main__ import aa as aaa',\n            'from __main__ import c',\n        ],\n    }\n    self.assertDictEqual(imports, expected)\n\n  def test_write_packages(self):\n\n    @api_util.mm_export('foo.a')\n    def a():  # pylint: disable=unused-variable\n      pass\n\n    @api_util.mm_export('bar.sub.b')\n    def b():  # pylint: disable=unused-variable\n      pass\n\n    imports = api_util.generate_imports()\n    expected_imports = {\n        '': [\n            'from $PKG import bar',\n            'from $PKG import foo',\n        ],\n        'foo': ['from __main__ import a',],\n        'bar': ['from $PKG.bar import sub',],\n        'bar.sub': ['from __main__ import b',],\n    }\n    self.assertDictEqual(imports, expected_imports)\n\n    version = '0.0.0-test'\n    with tempfile.TemporaryDirectory() as tmp_dir:\n      api_util.write_packages(tmp_dir, imports, {},\n                              api_util.PACKAGE_PLACEHOLDER, version)\n\n      # Checks existence of __init__ file and its content.\n      for package_name, symbols in expected_imports.items():\n        path = api_util.as_path(api_util.split_name(package_name))\n        init_file = os.path.join(tmp_dir, path, '__init__.py')\n        self.assertTrue(os.path.exists(init_file))\n        self.assertGreater(os.path.getsize(init_file), 0)\n        with tf.io.gfile.GFile(init_file) as f:\n          content = f.read()\n          self.assertIn(api_util.LICENSE, content)\n          self.assertIn('Generated API for package:', content)\n          for symbol in symbols:\n            self.assertIn(symbol, content)\n\n          if package_name == api_util.PACKAGE_PLACEHOLDER:\n            self.assertIn(\"\"\"__version__ = '0.0.0-test'\"\"\", content)\n\n      # Overwrite version.\n      new_version = '0.0.0-new'\n      api_util.overwrite_version_in_package(tmp_dir, new_version)\n      init_file = os.path.join(tmp_dir, '__init__.py')\n      with tf.io.gfile.GFile(init_file) as f:\n        new_content = f.read()\n        self.assertIn(\"\"\"__version__ = '0.0.0-new'\"\"\", new_content)\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/api/deprecated_api.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Deprecated APIs.\"\"\"\n\nIMPORTS = {}\n\n# pylint: disable=line-too-long\nIMPORTS[''] = \"\"\"\n# Deprecated imports are kept for backward compatiblity and to be removed in\n# future versions. Please refer to public APIs for replacement:\n# https://www.tensorflow.org/lite/api_docs/python/tflite_model_maker\n# pylint: disable=g-bad-import-order\nfrom tensorflow_examples.lite.model_maker.core.data_util.image_dataloader import ImageClassifierDataLoader\nfrom tensorflow_examples.lite.model_maker.core.export_format import ExportFormat\nfrom tensorflow_examples.lite.model_maker.core.task import configs\n# pylint: enable=g-bad-import-order\n\"\"\"\n# pylint: enable=line-too-long\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/api/golden_api.json",
    "content": "{\n  \"\": [\n    \"from $PKG import audio_classifier\",\n    \"from $PKG import config\",\n    \"from $PKG import image_classifier\",\n    \"from $PKG import model_spec\",\n    \"from $PKG import object_detector\",\n    \"from $PKG import question_answer\",\n    \"from $PKG import recommendation\",\n    \"from $PKG import searcher\",\n    \"from $PKG import text_classifier\"\n  ],\n  \"audio_classifier\": [\n    \"from tensorflow_examples.lite.model_maker.core.data_util.audio_dataloader import DataLoader\",\n    \"from tensorflow_examples.lite.model_maker.core.task.audio_classifier import AudioClassifier\",\n    \"from tensorflow_examples.lite.model_maker.core.task.audio_classifier import create\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.audio_spec import BrowserFFTSpec as BrowserFftSpec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.audio_spec import YAMNetSpec as YamNetSpec\"\n  ],\n  \"config\": [\n    \"from tensorflow_examples.lite.model_maker.core.export_format import ExportFormat\",\n    \"from tensorflow_examples.lite.model_maker.core.task.configs import QuantizationConfig\"\n  ],\n  \"image_classifier\": [\n    \"from tensorflow_examples.lite.model_maker.core.data_util.image_dataloader import ImageClassifierDataLoader as DataLoader\",\n    \"from tensorflow_examples.lite.model_maker.core.task.image_classifier import create\",\n    \"from tensorflow_examples.lite.model_maker.core.task.image_classifier import ImageClassifier\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.image_spec import efficientnet_lite0_spec as EfficientNetLite0Spec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.image_spec import efficientnet_lite1_spec as EfficientNetLite1Spec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.image_spec import efficientnet_lite2_spec as EfficientNetLite2Spec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.image_spec import efficientnet_lite3_spec as EfficientNetLite3Spec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.image_spec import efficientnet_lite4_spec as EfficientNetLite4Spec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.image_spec import ImageModelSpec as ModelSpec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.image_spec import mobilenet_v2_spec as MobileNetV2Spec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.image_spec import resnet_50_spec as Resnet50Spec\"\n  ],\n  \"model_spec\": [\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec import AUDIO_CLASSIFICATION_MODELS\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec import get\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec import IMAGE_CLASSIFICATION_MODELS\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec import OBJECT_DETECTION_MODELS\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec import QUESTION_ANSWER_MODELS\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec import RECOMMENDATION_MODELS\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec import TEXT_CLASSIFICATION_MODELS\"\n  ],\n  \"object_detector\": [\n    \"from tensorflow_examples.lite.model_maker.core.data_util.object_detector_dataloader import DataLoader\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.object_detector_spec import efficientdet_lite0_spec as EfficientDetLite0Spec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.object_detector_spec import efficientdet_lite1_spec as EfficientDetLite1Spec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.object_detector_spec import efficientdet_lite2_spec as EfficientDetLite2Spec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.object_detector_spec import efficientdet_lite3_spec as EfficientDetLite3Spec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.object_detector_spec import efficientdet_lite4_spec as EfficientDetLite4Spec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.object_detector_spec import EfficientDetModelSpec as EfficientDetSpec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.object_detector import create\",\n    \"from tensorflow_examples.lite.model_maker.core.task.object_detector import ObjectDetector\"\n  ],\n  \"question_answer\": [\n    \"from tensorflow_examples.lite.model_maker.core.data_util.text_dataloader import QuestionAnswerDataLoader as DataLoader\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.text_spec import BertQAModelSpec as BertQaSpec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.text_spec import mobilebert_qa_spec as MobileBertQaSpec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.text_spec import mobilebert_qa_squad_spec as MobileBertQaSquadSpec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.question_answer import create\",\n    \"from tensorflow_examples.lite.model_maker.core.task.question_answer import QuestionAnswer\"\n  ],\n  \"recommendation\": [\n    \"from $PKG.recommendation import spec\",\n    \"from tensorflow_examples.lite.model_maker.core.data_util.recommendation_dataloader import RecommendationDataLoader as DataLoader\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.recommendation_spec import RecommendationSpec as ModelSpec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.recommendation import create\",\n    \"from tensorflow_examples.lite.model_maker.core.task.recommendation import Recommendation\"\n  ],\n  \"recommendation.spec\": [\n    \"from tensorflow_examples.lite.model_maker.core.data_util.recommendation_config import EncoderType\",\n    \"from tensorflow_examples.lite.model_maker.core.data_util.recommendation_config import Feature\",\n    \"from tensorflow_examples.lite.model_maker.core.data_util.recommendation_config import FeatureGroup\",\n    \"from tensorflow_examples.lite.model_maker.core.data_util.recommendation_config import FeatureType\",\n    \"from tensorflow_examples.lite.model_maker.core.data_util.recommendation_config import InputSpec\",\n    \"from tensorflow_examples.lite.model_maker.core.data_util.recommendation_config import ModelHParams\"\n  ],\n  \"searcher\": [\n    \"from tensorflow_examples.lite.model_maker.core.data_util.image_searcher_dataloader import DataLoader as ImageDataLoader\",\n    \"from tensorflow_examples.lite.model_maker.core.data_util.metadata_loader import MetadataType\",\n    \"from tensorflow_examples.lite.model_maker.core.data_util.searcher_dataloader import DataLoader\",\n    \"from tensorflow_examples.lite.model_maker.core.data_util.text_searcher_dataloader import DataLoader as TextDataLoader\",\n    \"from tensorflow_examples.lite.model_maker.core.task.searcher import ExportFormat\",\n    \"from tensorflow_examples.lite.model_maker.core.task.searcher import ScaNNOptions\",\n    \"from tensorflow_examples.lite.model_maker.core.task.searcher import ScoreAH\",\n    \"from tensorflow_examples.lite.model_maker.core.task.searcher import ScoreBruteForce\",\n    \"from tensorflow_examples.lite.model_maker.core.task.searcher import Searcher\",\n    \"from tensorflow_examples.lite.model_maker.core.task.searcher import Tree\"\n  ],\n  \"text_classifier\": [\n    \"from tensorflow_examples.lite.model_maker.core.data_util.text_dataloader import TextClassifierDataLoader as DataLoader\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.text_spec import AverageWordVecModelSpec as AverageWordVecSpec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.text_spec import BertClassifierModelSpec as BertClassifierSpec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.model_spec.text_spec import mobilebert_classifier_spec as MobileBertClassifierSpec\",\n    \"from tensorflow_examples.lite.model_maker.core.task.text_classifier import create\",\n    \"from tensorflow_examples.lite.model_maker.core.task.text_classifier import TextClassifier\"\n  ]\n}\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/api/golden_api_doc.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Golden APIs doc.\"\"\"\n\nDOCS = {}\n\n# pylint: disable=line-too-long\nDOCS[''] = \"\"\"\nPublic APIs for TFLite Model Maker, a transfer learning library to train custom TFLite models.\n\nYou can install the package with\n\n```bash\npip install tflite-model-maker\n```\n\nTypical usage of Model Maker is to create a model in a few lines of code, e.g.:\n\n```python\n# Load input data specific to an on-device ML app.\ndata = DataLoader.from_folder('flower_photos/')\ntrain_data, test_data = data.split(0.9)\n\n# Customize the TensorFlow model.\nmodel = image_classifier.create(train_data)\n\n# Evaluate the model.\naccuracy = model.evaluate(test_data)\n\n# Export to Tensorflow Lite model and label file in `export_dir`.\nmodel.export(export_dir='/tmp/')\n```\n\nFor more details, please refer to our guide:\nhttps://www.tensorflow.org/lite/guide/model_maker.\n\"\"\".lstrip()\n\nDOCS['audio_classifier'] = \"\"\"APIs to train an audio classification model.\n\nTutorial:\nhttps://colab.research.google.com/github/googlecodelabs/odml-pathways/blob/main/audio_classification/colab/model_maker_audio_colab.ipynb\n\nDemo code:\nhttps://github.com/tensorflow/examples/blob/master/tensorflow_examples/lite/model_maker/demo/audio_classification_demo.py\n\"\"\"\n\nDOCS['config'] = 'APIs for the config of TFLite Model Maker.'\n\nDOCS['image_classifier'] = \"\"\"APIs to train an image classification model.\n\nTask guide:\nhttps://www.tensorflow.org/lite/tutorials/model_maker_image_classification.\n\"\"\"\n\nDOCS['model_spec'] = 'APIs for the model spec of TFLite Model Maker.'\n\nDOCS['object_detector'] = 'APIs to train an object detection model.'\n\nDOCS['question_answer'] = \"\"\"\nAPIs to train a model that can answer questions based on a predefined text.\n\nTask guide:\nhttps://www.tensorflow.org/lite/tutorials/model_maker_question_answer.\n\"\"\".lstrip()\n\nDOCS['recommendation'] = \"\"\"APIs to train an on-device recommendation model.\n\nDemo code:\nhttps://github.com/tensorflow/examples/blob/master/tensorflow_examples/lite/model_maker/demo/recommendation_demo.py\n\"\"\"\n\nDOCS['recommendation.spec'] = \"\"\"\nAPIs for recommendation specifications.\n\nExample:\n```python\ninput_spec = recommendation.spec.InputSpec(\n    activity_feature_groups=[\n        # Group #1: defines how features are grouped in the first Group.\n        dict(\n            features=[\n                # First feature.\n                dict(\n                    feature_name='context_movie_id',  # Feature name\n                    feature_type='INT',  # Feature type\n                    vocab_size=3953,     # ID size (number of IDs)\n                    embedding_dim=8,     # Projected feature embedding dim\n                    feature_length=10,   # History length of 10.\n                ),\n                # Maybe more features...\n            ],\n            encoder_type='CNN',  # CNN encoder (e.g. CNN, LSTM, BOW)\n        ),\n        # Maybe more groups...\n    ],\n    label_feature=dict(\n        feature_name='label_movie_id',  # Label feature name\n        feature_type='INT',  # Label type\n        vocab_size=3953,   # Label size (number of classes)\n        embedding_dim=8,   # label embedding demension\n        feature_length=1,  # Exactly 1 label\n    ),\n)\n\nmodel_hparams = recommendation.spec.ModelHParams(\n    hidden_layer_dims=[32, 32],  # Hidden layers dimension.\n    eval_top_k=[1, 5],           # Eval top 1 and top 5.\n    conv_num_filter_ratios=[2, 4],  # For CNN encoder, conv filter mutipler.\n    conv_kernel_size=16,            # For CNN encoder, base kernel size.\n    lstm_num_units=16,              # For LSTM/RNN, num units.\n    num_predictions=10,          # Number of output predictions. Select top 10.\n)\n\nspec = recommendation.ModelSpec(\n    input_spec=input_spec, model_hparams=model_hparams)\n# Or:\nspec = model_spec.get(\n    'recommendation', input_spec=input_spec, model_hparams=model_hparams)\n```\n\"\"\".lstrip()\n\nDOCS['searcher'] = \"\"\"APIs to create the searcher model.\n\nTask guide:\nhttps://www.tensorflow.org/lite/tutorials/model_maker_text_searcher.\n\"\"\"\n\nDOCS['text_classifier'] = \"\"\"APIs to train a text classification model.\n\nTask guide:\nhttps://www.tensorflow.org/lite/tutorials/model_maker_text_classification.\n\"\"\"\n# pylint: enable=line-too-long\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/api/include.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Packages included in API.\"\"\"\n\n# pylint: disable=unused-import\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core.data_util import audio_dataloader\nfrom tensorflow_examples.lite.model_maker.core.data_util import image_dataloader\nfrom tensorflow_examples.lite.model_maker.core.data_util import object_detector_dataloader\nfrom tensorflow_examples.lite.model_maker.core.data_util import text_dataloader\n\nfrom tensorflow_examples.lite.model_maker.core.data_util.audio_dataloader import DataLoader as AudioDataLoader\nfrom tensorflow_examples.lite.model_maker.core.data_util.image_dataloader import ImageClassifierDataLoader\nfrom tensorflow_examples.lite.model_maker.core.data_util.object_detector_dataloader import DataLoader as ObjectDetectorDataloader\nfrom tensorflow_examples.lite.model_maker.core.data_util.recommendation_dataloader import RecommendationDataLoader\nfrom tensorflow_examples.lite.model_maker.core.data_util.text_dataloader import QuestionAnswerDataLoader\nfrom tensorflow_examples.lite.model_maker.core.data_util.text_dataloader import TextClassifierDataLoader\n\nfrom tensorflow_examples.lite.model_maker.core.export_format import ExportFormat\n\nfrom tensorflow_examples.lite.model_maker.core.task import audio_classifier\nfrom tensorflow_examples.lite.model_maker.core.task import configs\nfrom tensorflow_examples.lite.model_maker.core.task import image_classifier\nfrom tensorflow_examples.lite.model_maker.core.task import model_spec\nfrom tensorflow_examples.lite.model_maker.core.task import object_detector\nfrom tensorflow_examples.lite.model_maker.core.task import question_answer\nfrom tensorflow_examples.lite.model_maker.core.task import recommendation\nfrom tensorflow_examples.lite.model_maker.core.task import text_classifier\n\nfrom tensorflow_examples.lite.model_maker.core.data_util import image_searcher_dataloader\nfrom tensorflow_examples.lite.model_maker.core.data_util import metadata_loader\nfrom tensorflow_examples.lite.model_maker.core.data_util import searcher_dataloader\nfrom tensorflow_examples.lite.model_maker.core.data_util import text_searcher_dataloader\nfrom tensorflow_examples.lite.model_maker.core.task import searcher\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/compat.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Compat modules.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow as tf\n\n_DEFAULT_TF_BEHAVIOR = 2\n\n# Get version of tf behavior in use (valid 1 or 2).\n_tf_behavior_version = _DEFAULT_TF_BEHAVIOR\n\n\ndef setup_tf_behavior(tf_version=_DEFAULT_TF_BEHAVIOR):\n  \"\"\"Setup tf behavior. It must be used before the main().\"\"\"\n  global _tf_behavior_version\n  if tf_version not in [1, 2]:\n    raise ValueError(\n        'tf_version should be in [1, 2], but got {}'.format(tf_version))\n\n  if tf_version == 1:\n    tf.compat.v1.logging.warn(\n        'Using v1 behavior. Please note that it is mainly to run legacy models,'\n        'however v2 is more preferrable if they are supported.')\n    tf.compat.v1.disable_v2_behavior()\n  else:\n    tf.compat.v2.enable_v2_behavior()\n  _tf_behavior_version = tf_version\n\n\ndef get_tf_behavior():\n  \"\"\"Gets version for tf behavior.\n\n  Returns:\n    int, 1 or 2 indicating the behavior version.\n  \"\"\"\n  return _tf_behavior_version\n\n\ndef get_compat_tf_versions(compat_tf_versions=None):\n  \"\"\"Gets compatible tf versions (default: [2]).\n\n  Args:\n    compat_tf_versions: int, int list or None, indicates compatible versions.\n\n  Returns:\n    A list of compatible tf versions.\n  \"\"\"\n  if compat_tf_versions is None:\n    compat_tf_versions = [2]\n  if not isinstance(compat_tf_versions, list):\n    compat_tf_versions = [compat_tf_versions]\n  return compat_tf_versions\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/__init__.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/audio_dataloader.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Audio dataloader.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nimport random\n\nimport pandas as pd\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core.api.api_util import mm_export\nfrom tensorflow_examples.lite.model_maker.core.data_util import dataloader\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import audio_spec\n\nerror_import_librosa = None\ntry:\n  import librosa  # pylint: disable=g-import-not-at-top\n  ENABLE_RESAMPLE = True\nexcept (OSError, ImportError) as _error_import_librosa:  # pylint: disable=invalid-name\n  ENABLE_RESAMPLE = False\n  error_import_librosa = _error_import_librosa\n\n\nclass ExamplesHelper(object):\n  \"\"\"Helper class for matching examples and labels.\"\"\"\n\n  @classmethod\n  def from_examples_folder(cls, path, examples_filter_fn):\n    \"\"\"Helper function for loading examples and parsing labels from example path.\n\n    This path contain a number of folders, each named by the category name. Each\n    folder contain a number of files. This helper class loads and parse the\n    tree structure.\n\n    Example folder:\n      /category1\n        /file1.wav\n        /file2.wav\n      /category2\n        /file2.wav\n        /README\n\n    Usage:\n    >>> helper = ExamplesHelper.from_example_folder(path, is_wav)\n    >>> # helper.shuffle() if shuffle is needed\n    >>> helper.examples_and_labels()\n    ('/category1/file1.wav', '/category1/file2.wav', '/category2/file2.wav'),\n    ('category1', 'category1', 'category2')\n    >>> helper.index_to_label()\n    ('category1', 'category2')\n    >>> helper.examples_and_label_indices()\n    ('/category1/file1.wav', '/category1/file2.wav', '/category2/file2.wav'),\n    (0, 0, 1)\n\n    Args:\n      path: String, relative path to the data folder. This folder should contain\n        a list of sub-folders, named after its categories.\n      examples_filter_fn: A lambda function to filter out unrelated files. It\n        takes in a full path to the example file and returns a boolean,\n        representing if this example file can be preserved.\n\n    Returns:\n      An instance of ExamplesHelper.\n    \"\"\"\n\n    def _list_files(path):\n      return tf.io.gfile.glob(os.path.join(path, '*', '*'))\n\n    def _get_label(example):\n      \"\"\"Parses the example path and return the label string.\"\"\"\n      return example.rsplit(os.path.sep, 2)[1]\n\n    examples = list(filter(examples_filter_fn, _list_files(path)))\n    labels = list(map(_get_label, examples))\n    return cls(examples, labels)\n\n  def __init__(self, examples_in_absolute_path, labels):\n    self.index_to_label = self._get_index_to_label(labels)  # [label]\n    self.label_to_index = self._get_label_to_index(self.index_to_label)\n    # [(example, label)] in stable order\n    self._data = sorted(list(zip(examples_in_absolute_path, labels)))\n\n  def _get_index_to_label(self, used_labels):\n    return sorted(list(set(used_labels)))\n\n  def _get_label_to_index(self, index_to_label):\n    return {label: i for i, label in enumerate(index_to_label)}\n\n  def shuffle(self):\n    random.shuffle(self._data)\n\n  def filter(self, labels):\n    self._data = filter(lambda v: v[1] in labels, self._data)\n    examples, labels = zip(*self._data)\n    return ExamplesHelper(examples, labels)\n\n  def examples_and_label_indices(self):\n    \"\"\"Returns a tuple of example and a tuple of their corresponding label idx.\"\"\"\n    examples, labels = self.examples_and_labels()\n    label_indicies = tuple(map(self.label_to_index.get, labels))\n    return examples, label_indicies\n\n  def examples_and_labels(self):\n    \"\"\"Returns a tuple of example and a tuple of their corresponding labels.\"\"\"\n    if not self._data:\n      return (), ()\n    examples, labels = zip(*self._data)\n    return examples, labels\n\n  def examples_and_label_indices_ds(self):\n    examples, labels = self.examples_and_label_indices()\n    wav_ds = tf.data.Dataset.from_tensor_slices(list(examples))\n    label_ds = tf.data.Dataset.from_tensor_slices(list(labels))\n    ds = tf.data.Dataset.zip((wav_ds, label_ds))\n    return ds\n\n\n@mm_export('audio_classifier.DataLoader')\nclass DataLoader(dataloader.ClassificationDataLoader):\n  \"\"\"DataLoader for audio tasks.\"\"\"\n\n  def __init__(self, dataset, size, index_to_label, spec, cache=False):\n    super(DataLoader, self).__init__(dataset, size, index_to_label)\n    self._spec = spec\n    self._cache = cache\n\n  def __len__(self):\n    \"\"\"Returns the number of audio files in the DataLoader.\n\n    Note that one audio file could be framed (mostly via a sliding window of\n    fixed size) into None or multiple audio clips during training and\n    evaluation.\n    \"\"\"\n    return self._size\n\n  @classmethod\n  def from_folder(cls,\n                  spec,\n                  data_path,\n                  categories=None,\n                  shuffle=True,\n                  cache=False):\n    \"\"\"Load audio files from a data_path.\n\n    - The root `data_path` folder contains a number of folders. The name for\n    each folder is the name of the audio class.\n\n    - Within each folder, there are a number of .wav files. Each .wav file\n    corresponds to an example. Each .wav file is mono (single-channel) and has\n    the typical 16 bit pulse-code modulation (PCM) encoding.\n\n    - .wav files will be resampled to `spec.target_sample_rate` then fed into\n    `spec.preprocess_ds` for split and other operations. Normally long wav files\n    will be framed into multiple clips. And wav files shorter than a certain\n    threshold will be ignored.\n\n    Args:\n      spec: instance of `audio_spec.BaseSpec`.\n      data_path: string, location to the audio files.\n      categories: A string list of selected categories. If empty, all categories\n        will be selected.\n      shuffle: boolean, if True, random shuffle data.\n      cache: str or boolean. When set to True, intermediate results will be\n        cached in ram. When set to a file path in string, intermediate results\n        will be cached in this file. Please note that, once file based cache is\n        created, changes to the input data will have no effects until the cache\n        file is removed or the filename is changed. More details can be found at\n        https://www.tensorflow.org/api_docs/python/tf/data/Dataset#cache\n\n    Returns:\n      `AudioDataLoader` containing audio spectrogram (or any data type generated\n      by `spec.preprocess_ds`) and labels.\n    \"\"\"\n    assert isinstance(spec, audio_spec.BaseSpec)\n    root_dir = os.path.abspath(data_path)\n    helper = ExamplesHelper.from_examples_folder(root_dir,\n                                                 lambda s: s.endswith('.wav'))\n\n    if categories:\n      helper = helper.filter(categories)\n\n    if shuffle:\n      helper.shuffle()\n\n    ds = helper.examples_and_label_indices_ds()\n    if len(ds) == 0:  # pylint: disable=g-explicit-length-test\n      raise ValueError('No audio files found.')\n\n    return DataLoader(ds, len(ds), helper.index_to_label, spec, cache)\n\n  @classmethod\n  def from_esc50(cls,\n                 spec,\n                 data_path,\n                 folds=None,\n                 categories=None,\n                 shuffle=True,\n                 cache=False):\n    \"\"\"Load ESC50 style audio samples.\n\n    ESC50 file structure is expalined in https://github.com/karolpiczak/ESC-50\n    Audio files should be put in `${data_path}/audio`\n    Metadata file should be put in `${data_path}/meta/esc50.csv`\n\n    Note that instead of relying on the `target` field in the CSV, a new\n    `index_to_label` mapping is created based on the alphabet order of the\n    available categories.\n\n    Args:\n      spec: An instance of audio_spec.YAMNet\n      data_path: A string, location of the ESC50 dataset. It should contain at\n      folds: A integer list of selected folds. If empty, all folds will be\n        selected.\n      categories: A string list of selected categories. If empty, all categories\n        will be selected.\n      shuffle: boolean, if True, random shuffle data.\n      cache: str or boolean. When set to True, intermediate results will be\n        cached in ram. When set to a file path in string, intermediate results\n        will be cached in this file. Please note that, once file based cache is\n        created, changes to the input data will have no effects until the cache\n        file is removed or the filename is changed. More details can be found at\n        https://www.tensorflow.org/api_docs/python/tf/data/Dataset#cache\n\n    Returns:\n      An instance of AudioDataLoader containing audio samples and labels.\n    \"\"\"\n\n    def _fullpath(filename):\n      return os.path.join(data_path, 'audio', filename)\n\n    csv_path = os.path.join(data_path, 'meta/esc50.csv')\n    pd_data = pd.read_csv(csv_path)\n    if categories:\n      pd_data = pd_data[pd_data.category.isin(categories)]\n    if folds:\n      pd_data = pd_data[pd_data.fold.isin(folds)]\n\n    helper = ExamplesHelper(map(_fullpath, pd_data.filename), pd_data.category)\n    if shuffle:\n      helper.shuffle()\n    ds = helper.examples_and_label_indices_ds()\n\n    if len(ds) == 0:  # pylint: disable=g-explicit-length-test\n      raise ValueError('No audio files found.')\n\n    return DataLoader(ds, len(ds), helper.index_to_label, spec, cache)\n\n  def split(self, fraction):\n    return self._split(fraction, self.index_to_label, self._spec, self._cache)\n\n  def gen_dataset(self,\n                  batch_size=1,\n                  is_training=False,\n                  shuffle=False,\n                  input_pipeline_context=None,\n                  preprocess=None,\n                  drop_remainder=False):\n    \"\"\"Generate a shared and batched tf.data.Dataset for training/evaluation.\n\n    Args:\n      batch_size: A integer, the returned dataset will be batched by this size.\n      is_training: A boolean, when True, the returned dataset will be optionally\n        shuffled. Data augmentation, if exists, will also be applied to the\n        returned dataset.\n      shuffle: A boolean, when True, the returned dataset will be shuffled to\n        create randomness during model training. Only applies when `is_training`\n        is set to True.\n      input_pipeline_context: A InputContext instance, used to shared dataset\n        among multiple workers when distribution strategy is used.\n      preprocess: Not in use.\n      drop_remainder: boolean, whether the finaly batch drops remainder.\n\n    Returns:\n      A TF dataset ready to be consumed by Keras model.\n    \"\"\"\n    # This argument is only used for image dataset for now. Audio preprocessing\n    # is defined in the spec.\n    del preprocess\n    ds = self._dataset\n    spec = self._spec\n    autotune = tf.data.AUTOTUNE\n\n    if is_training and shuffle:\n      options = tf.data.Options()\n      options.experimental_deterministic = False\n      ds = ds.with_options(options)\n\n    ds = dataloader.shard(ds, input_pipeline_context)\n\n    @tf.function\n    def _load_wav(filepath, label):\n      file_contents = tf.io.read_file(filepath)\n      # shape: (audio_samples, 1), dtype: float32\n      wav, sample_rate = tf.audio.decode_wav(file_contents, desired_channels=1)\n      # shape: (audio_samples,)\n      wav = tf.squeeze(wav, axis=-1)\n      return wav, sample_rate, label\n\n    # This is a eager mode numpy_function. It can be converted to a tf.function\n    # using https://www.tensorflow.org/io/api_docs/python/tfio/audio/resample\n    def _resample_numpy(waveform, sample_rate, label):\n      if ENABLE_RESAMPLE:\n        waveform = librosa.resample(\n            waveform, orig_sr=sample_rate, target_sr=spec.target_sample_rate)\n      else:\n        error_message = (\n            'Failed to import librosa. You might be missing sndfile, which '\n            'can be installed via `sudo apt-get install libsndfile1` on '\n            'Ubuntu/Debian.')\n        raise RuntimeError(error_message) from error_import_librosa\n      return waveform, label\n\n    @tf.function\n    def _resample(waveform, sample_rate, label):\n      # Short circuit resampling if possible.\n      if sample_rate == spec.target_sample_rate:\n        return [waveform, label]\n      return tf.numpy_function(\n          _resample_numpy,\n          inp=(waveform, sample_rate, label),\n          Tout=[tf.float32, tf.int32])\n\n    @tf.function\n    def _elements_finite(preprocess_data, unused_label):\n      # Make sure that the data sent to the model does not contain nan or inf\n      # values. This should be the last filter applied to the dataset.\n      # Arguably we could possibly apply this filter to all tasks.\n      return tf.size(preprocess_data) > 0 and tf.math.reduce_all(\n          tf.math.is_finite(preprocess_data))\n\n    ds = ds.map(_load_wav, num_parallel_calls=autotune)\n    ds = ds.map(_resample, num_parallel_calls=autotune)\n\n    def _cache_fn(dataset):\n      if self._cache:\n        if isinstance(self._cache, str):\n          # Cache to a file\n          dataset = dataset.cache(self._cache)\n        else:\n          # In ram cache.\n          dataset = dataset.cache()\n      return dataset\n\n    # `preprocess_ds` contains data augmentation, so it knows when it's the best\n    # time to do caching.\n    ds = spec.preprocess_ds(ds, is_training=is_training, cache_fn=_cache_fn)\n    ds = ds.filter(_elements_finite)\n\n    # Apply one-hot encoding after caching to reduce the cache size.\n    @tf.function\n    def _one_hot_encoding_label(wav, label):\n      return wav, tf.one_hot(label, len(self.index_to_label))\n\n    ds = ds.map(_one_hot_encoding_label, num_parallel_calls=autotune)\n\n    # Shuffle needs to be done after caching to create randomness across epochs.\n    if is_training:\n      if shuffle:\n        # Shuffle size should be bigger than the batch_size. Otherwise it's only\n        # shuffling within the batch, which equals to not having shuffle.\n        buffer_size = 3 * batch_size\n        # But since we are doing shuffle before repeat, it doesn't make sense to\n        # shuffle more than total available entries.\n        # TODO(wangtz): Do we want to do shuffle before / after repeat?\n        # Shuffle after repeat will give a more randomized dataset and mix the\n        # epoch boundary: https://www.tensorflow.org/guide/data\n        ds = ds.shuffle(buffer_size=min(self._size, buffer_size))\n\n    ds = ds.batch(batch_size, drop_remainder=drop_remainder)\n    ds = ds.prefetch(tf.data.experimental.AUTOTUNE)\n    # TODO(b/171449557): Consider converting ds to distributed ds here.\n    return ds\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/audio_dataloader_test.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport csv\nimport os\nimport shutil\nimport unittest\n\nimport numpy as np\nfrom packaging import version\nfrom scipy.io import wavfile\nimport tensorflow.compat.v2 as tf\nfrom tensorflow_examples.lite.model_maker.core.data_util import audio_dataloader\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import audio_spec\n\n\ndef write_file(root, filepath):\n  full_path = os.path.join(root, filepath)\n  os.makedirs(os.path.dirname(full_path), exist_ok=True)\n  with open(full_path, 'w') as f:\n    f.write('<content>')\n\n\ndef write_sample(root,\n                 category,\n                 file_name,\n                 sample_rate,\n                 duration_sec,\n                 value,\n                 dtype=np.int16):\n  os.makedirs(os.path.join(root, category), exist_ok=True)\n  xs = value * np.ones(shape=(int(sample_rate * duration_sec),), dtype=dtype)\n  wavfile.write(os.path.join(root, category, file_name), sample_rate, xs)\n\n\ndef write_csv(root, folder, filename, headers, rows):\n  os.makedirs(os.path.join(root, folder), exist_ok=True)\n\n  with open(os.path.join(root, folder, filename), 'w') as f:\n    writer = csv.DictWriter(f, fieldnames=headers)\n    writer.writeheader()\n    for row in rows:\n      writer.writerow(dict(zip(headers, row)))\n\n\nclass MockSpec(audio_spec.BaseSpec):\n\n  def __init__(self, *args, **kwargs):\n    super(MockSpec, self).__init__(*args, **kwargs)\n    self.expected_waveform_len = 44100\n\n  def create_model(self):\n    return None\n\n  def run_classifier(self, *args, **kwargs):\n    return None\n\n  @property\n  def target_sample_rate(self):\n    return 44100\n\n  def preprocess_ds(self, ds, is_training=False, cache_fn=None):\n    _ = is_training\n\n    @tf.function\n    def _ensure_length(wav, unused_label):\n      return len(wav) >= self.expected_waveform_len\n\n    @tf.function\n    def _split(wav, label):\n      # wav shape: (audio_samples, )\n      chunks = tf.math.floordiv(len(wav), self.expected_waveform_len)\n      unused = tf.math.floormod(len(wav), self.expected_waveform_len)\n      # Drop unused data\n      wav = wav[:len(wav) - unused]\n      # Split the audio sample into multiple chunks\n      wav = tf.reshape(wav, (chunks, 1, self.expected_waveform_len))\n\n      return wav, tf.repeat(tf.expand_dims(label, 0), len(wav))\n\n    autotune = tf.data.AUTOTUNE\n    ds = ds.filter(_ensure_length)\n    ds = ds.map(_split, num_parallel_calls=autotune).unbatch()\n\n    if cache_fn:\n      ds = cache_fn(ds)\n    return ds\n\n\nclass Base(tf.test.TestCase):\n\n  def _get_folder_path(self, sub_folder_name):\n    folder_path = os.path.join(self.get_temp_dir(), sub_folder_name)\n    if os.path.exists(folder_path):\n      shutil.rmtree(folder_path)\n    tf.compat.v1.logging.info('Test path: %s', folder_path)\n    os.mkdir(folder_path)\n    return folder_path\n\n\n@unittest.skipIf(\n    version.parse(tf.__version__) < version.parse('2.5'),\n    'Audio Classification requires TF 2.5 or later')\nclass LoadFromESC50Test(Base):\n\n  def test_from_esc50(self):\n    folder_path = self._get_folder_path('test_from_esc50')\n\n    headers = [\n        'filename', 'fold', 'target', 'category', 'esc10', 'src_file', 'take'\n    ]\n    rows = []\n    rows.append(['1-100032-A-0.wav', '1', '0', 'dog', 'True', '100032', 'A'])\n    rows.append([\n        '1-100210-B-36.wav', '2', '36', 'vacuum_cleaner', 'False', '100210', 'B'\n    ])\n    rows.append([\n        '1-100210-A-36.wav', '1', '36', 'vacuum_cleaner', 'False', '100210', 'A'\n    ])\n    write_csv(folder_path, 'meta', 'esc50.csv', headers, rows)\n\n    spec = audio_spec.YAMNetSpec()\n    loader = audio_dataloader.DataLoader.from_esc50(spec, folder_path)\n\n    self.assertEqual(len(loader), 3)\n    self.assertEqual(loader.index_to_label, ['dog', 'vacuum_cleaner'])\n\n    expected_results = {\n        '1-100032-A-0.wav': 0,\n        '1-100210-B-36.wav': 1,\n        '1-100210-A-36.wav': 1,\n    }\n    for full_path, label in loader._dataset:\n      filename = full_path.numpy().decode('utf-8').split('/')[-1]\n      self.assertEqual(expected_results[filename], label)\n\n    # fitlered dataset\n    with self.assertRaisesRegex(ValueError, 'No audio files found'):\n      loader = audio_dataloader.DataLoader.from_esc50(\n          spec, folder_path, folds=[\n              3,\n          ])\n\n    with self.assertRaisesRegex(ValueError, 'No audio files found'):\n      loader = audio_dataloader.DataLoader.from_esc50(\n          spec, folder_path, categories=['unknown'])\n\n    loader = audio_dataloader.DataLoader.from_esc50(\n        spec, folder_path, folds=[\n            1,\n        ])\n    self.assertEqual(len(loader), 2)\n\n    loader = audio_dataloader.DataLoader.from_esc50(\n        spec, folder_path, categories=['vacuum_cleaner'])\n    self.assertEqual(len(loader), 2)\n\n    loader = audio_dataloader.DataLoader.from_esc50(\n        spec, folder_path, folds=[\n            1,\n        ], categories=['vacuum_cleaner'])\n    self.assertEqual(len(loader), 1)\n\n    loader = audio_dataloader.DataLoader.from_esc50(\n        spec, folder_path, folds=[1, 2], categories=['vacuum_cleaner'])\n    self.assertEqual(len(loader), 2)\n\n\nclass ExamplesHelperTest(Base):\n\n  def test_examples_helper(self):\n    root = self._get_folder_path('test_examples_helper')\n    write_file(root, 'a/1.wav')\n    write_file(root, 'a/2.wav')\n    write_file(root, 'b/1.wav')\n    write_file(root, 'b/README')  # Ignored\n    write_file(root, 'a/b/c/d.wav')  # Ignored\n    write_file(root, 'AUTHORS.md')  # Ignored\n    write_file(root, 'temp.wav')  # Ignored\n\n    def is_wav_files(name):\n      return name.endswith('.wav')\n\n    def fullpath(name):\n      return os.path.join(root, name)\n\n    helper = audio_dataloader.ExamplesHelper.from_examples_folder(\n        root, is_wav_files)\n    self.assertEqual(helper.index_to_label, ['a', 'b'])\n    self.assertEqual(\n        helper.examples_and_labels(),\n        ((fullpath('a/1.wav'), fullpath('a/2.wav'), fullpath('b/1.wav')),\n         ('a', 'a', 'b')))\n    self.assertEqual(\n        helper.examples_and_label_indices(),\n        ((fullpath('a/1.wav'), fullpath('a/2.wav'), fullpath('b/1.wav')),\n         (0, 0, 1)))\n\n\n@unittest.skipIf(\n    version.parse(tf.__version__) < version.parse('2.5'),\n    'Audio Classification requires TF 2.5 or later')\nclass LoadFromFolderTest(Base):\n\n  def test_spec(self):\n    folder_path = self._get_folder_path('test_spec')\n    write_sample(folder_path, 'unknown', '2s.wav', 44100, 2, value=1)\n\n    spec = audio_spec.YAMNetSpec()\n    audio_dataloader.DataLoader.from_folder(spec, folder_path)\n\n    spec = audio_spec.BrowserFFTSpec()\n    audio_dataloader.DataLoader.from_folder(spec, folder_path)\n\n  def test_no_audio_files_found(self):\n    folder_path = self._get_folder_path('test_no_audio_files_found')\n    write_sample(folder_path, 'unknown', '2s.bak', 44100, 2, value=1)\n    with self.assertRaisesRegex(ValueError, 'No audio files found'):\n      spec = MockSpec(model_dir=folder_path)\n      audio_dataloader.DataLoader.from_folder(spec, folder_path)\n\n  def test_failed_librosa_imoprt(self):\n    # Temporarily disable resampling.\n    audio_dataloader.ENABLE_RESAMPLE = False\n\n    # Pretend a real import failure.\n    try:\n      import inexistent_package  # pylint: disable=g-import-not-at-top,unused-import\n    except (OSError, ImportError) as e:\n      audio_dataloader.error_import_librosa = e\n\n    try:\n      folder_path = self._get_folder_path('test_failed_librosa_imoprt')\n\n      # No error occured if resampling is not needed.\n      write_sample(folder_path, 'background', '1s.wav', 44100, 1, value=0)\n      spec = MockSpec(model_dir=folder_path)\n      loader = audio_dataloader.DataLoader.from_folder(spec, folder_path)\n      self.assertEqual(len(loader), 1)\n      self.assertEqual(len(list(loader.gen_dataset())), 1)\n\n      # Error occured when resampling is needed.\n      write_sample(folder_path, 'command0', '1.8s.wav', 4410, 1.8, value=5)\n      spec = MockSpec(model_dir=folder_path)\n      loader = audio_dataloader.DataLoader.from_folder(spec, folder_path)\n      self.assertEqual(len(loader), 2)\n      with self.assertRaisesRegex(\n          tf.errors.UnknownError, 'sudo apt-get install libsndfile1'\n      ):\n        _ = list(loader.gen_dataset())\n\n    finally:\n      # Set it back\n      audio_dataloader.ENABLE_RESAMPLE = True\n      audio_dataloader.error_import_librosa = None\n\n  def test_from_folder(self):\n    folder_path = self._get_folder_path('test_from_folder')\n    write_sample(folder_path, 'background', '2s.wav', 44100, 2, value=0)\n    write_sample(folder_path, 'command1', '1s.wav', 44100, 1, value=1)\n    # Too short, skipped.\n    write_sample(folder_path, 'command1', '0.1s.wav', 44100, .1, value=2)\n    # Not long enough for 2 files, the remaining .5s will be skipped.\n    write_sample(folder_path, 'command2', '1.5s.wav', 44100, 1.5, value=3)\n    # Skipped, too short.\n    write_sample(folder_path, 'command0', '0.1s.wav', 4410, .1, value=4)\n    # Resampled, after resample, the content becomes [4 5 5 ... 4 5 4]\n    write_sample(folder_path, 'command0', '1.8s.wav', 4410, 1.8, value=5)\n    # Ignored due to wrong file extension\n    write_sample(folder_path, 'command0', '1.8s.bak', 4410, 1.8, value=6)\n    # Category not in use.\n    write_sample(folder_path, 'unused_command', '2s.wav', 44410, 1, value=7)\n\n    spec = MockSpec(model_dir=folder_path)\n    loader = audio_dataloader.DataLoader.from_folder(\n        spec,\n        folder_path,\n        categories=['background', 'command0', 'command1', 'command2'])\n\n    # 6 files with .wav extennsion\n    self.assertEqual(len(loader), 6)\n    self.assertEqual(loader.index_to_label,\n                     ['background', 'command0', 'command1', 'command2'])\n\n    # 5 valid audio snippets\n    self.assertEqual(len(list(loader.gen_dataset())), 5)\n\n    spec = MockSpec(model_dir=folder_path)\n    loader = audio_dataloader.DataLoader.from_folder(\n        spec, folder_path, cache=True)\n    self.assertEqual(len(list(loader.gen_dataset())), 6)\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/data_util.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Custom classification model that is already retained by data.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow.compat.v2 as tf\nfrom tensorflow_examples.lite.model_maker.core import compat\n\n\ndef generate_elements(ds):\n  \"\"\"Generates elements from `tf.data.dataset`.\"\"\"\n  if compat.get_tf_behavior() == 2:\n    for element in ds.as_numpy_iterator():\n      yield element\n  else:\n    iterator = tf.compat.v1.data.make_one_shot_iterator(ds)\n    next_element = iterator.get_next()\n    with tf.compat.v1.Session() as sess:\n      try:\n        while True:\n          yield sess.run(next_element)\n      except tf.errors.OutOfRangeError:\n        return\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/dataloader.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Common Dataset used for tasks.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport functools\nfrom typing import Optional\n\nimport tensorflow as tf\n\n\ndef shard(ds, input_pipeline_context):\n  # The dataset is always sharded by number of hosts.\n  # num_input_pipelines is the number of hosts rather than number of cores.\n  if (input_pipeline_context and\n      input_pipeline_context.num_input_pipelines > 1):\n    ds = ds.shard(input_pipeline_context.num_input_pipelines,\n                  input_pipeline_context.input_pipeline_id)\n  return ds\n\n\nclass DataLoader(object):\n  \"\"\"This class provides generic utilities for loading customized domain data that will be used later in model retraining.\n\n  For different ML problems or tasks, such as image classification, text\n  classification etc., a subclass is provided to handle task-specific data\n  loading requirements.\n  \"\"\"\n\n  def __init__(self, dataset, size=None):\n    \"\"\"Init function for class `DataLoader`.\n\n    In most cases, one should use helper functions like `from_folder` to create\n    an instance of this class.\n\n    Args:\n      dataset: A tf.data.Dataset object that contains a potentially large set of\n        elements, where each element is a pair of (input_data, target). The\n        `input_data` means the raw input data, like an image, a text etc., while\n        the `target` means some ground truth of the raw input data, such as the\n        classification label of the image etc.\n      size: The size of the dataset. tf.data.Dataset donesn't support a function\n        to get the length directly since it's lazy-loaded and may be infinite.\n    \"\"\"\n    self._dataset = dataset\n    self._size = size\n\n  @property\n  def size(self) -> Optional[int]:\n    \"\"\"Returns the size of the dataset.\n\n    Note that this function may return None becuase the exact size of the\n    dataset isn't a necessary parameter to create an instance of this class,\n    and tf.data.Dataset donesn't support a function to get the length directly\n    since it's lazy-loaded and may be infinite.\n    In most cases, however, when an instance of this class is created by helper\n    functions like 'from_folder', the size of the dataset will be preprocessed,\n    and this function can return an int representing the size of the dataset.\n    \"\"\"\n    return self._size\n\n  def gen_dataset(self,\n                  batch_size=1,\n                  is_training=False,\n                  shuffle=False,\n                  input_pipeline_context=None,\n                  preprocess=None,\n                  drop_remainder=False):\n    \"\"\"Generate a shared and batched tf.data.Dataset for training/evaluation.\n\n    Args:\n      batch_size: A integer, the returned dataset will be batched by this size.\n      is_training: A boolean, when True, the returned dataset will be optionally\n        shuffled and repeated as an endless dataset.\n      shuffle: A boolean, when True, the returned dataset will be shuffled to\n        create randomness during model training.\n      input_pipeline_context: A InputContext instance, used to shared dataset\n        among multiple workers when distribution strategy is used.\n      preprocess: A function taking three arguments in order, feature, label and\n        boolean is_training.\n      drop_remainder: boolean, whether the finaly batch drops remainder.\n\n    Returns:\n      A TF dataset ready to be consumed by Keras model.\n    \"\"\"\n    ds = self._dataset\n    ds = shard(ds, input_pipeline_context)\n\n    if preprocess:\n      preprocess = functools.partial(preprocess, is_training=is_training)\n      ds = ds.map(preprocess, num_parallel_calls=tf.data.AUTOTUNE)\n\n    if is_training:\n      if shuffle:\n        # Shuffle size should be bigger than the batch_size. Otherwise it's only\n        # shuffling within the batch, which equals to not having shuffle.\n        buffer_size = 3 * batch_size\n        # But since we are doing shuffle before repeat, it doesn't make sense to\n        # shuffle more than total available entries.\n        # TODO(wangtz): Do we want to do shuffle before / after repeat?\n        # Shuffle after repeat will give a more randomized dataset and mix the\n        # epoch boundary: https://www.tensorflow.org/guide/data\n        if self._size:\n          buffer_size = min(self._size, buffer_size)\n        ds = ds.shuffle(buffer_size=buffer_size)\n\n    ds = ds.batch(batch_size, drop_remainder=drop_remainder)\n    ds = ds.prefetch(tf.data.AUTOTUNE)\n    # TODO(b/171449557): Consider converting ds to distributed ds here.\n    return ds\n\n  def __len__(self):\n    if self._size is not None:\n      return self._size\n    else:\n      return len(self._dataset)\n\n  def split(self, fraction):\n    \"\"\"Splits dataset into two sub-datasets with the given fraction.\n\n    Primarily used for splitting the data set into training and testing sets.\n\n    Args:\n      fraction: float, demonstrates the fraction of the first returned\n        subdataset in the original data.\n\n    Returns:\n      The splitted two sub datasets.\n    \"\"\"\n    return self._split(fraction)\n\n  def _split(self, fraction, *args):\n    \"\"\"Actual implementation for `split` method and returns sub-class instances.\n\n    Child DataLoader, if requires additional constructor arguments, should\n      implement their own `split` method by calling `_split` with all arguments\n      to the constructor.\n\n    Args:\n      fraction: float, demonstrates the fraction of the first returned\n        subdataset in the original data.\n      *args: additional arguments passed to the sub-class constructor.\n\n    Returns:\n      The splitted two sub datasets.\n    \"\"\"\n    assert (fraction > 0 and fraction < 1)\n\n    ds = self._dataset\n\n    train_size = int(self._size * fraction)\n    trainset = self.__class__(ds.take(train_size), train_size, *args)\n\n    test_size = self._size - train_size\n    testset = self.__class__(ds.skip(train_size), test_size, *args)\n\n    return trainset, testset\n\n\nclass ClassificationDataLoader(DataLoader):\n  \"\"\"DataLoader for classification models.\"\"\"\n\n  def __init__(self, dataset, size, index_to_label):\n    super(ClassificationDataLoader, self).__init__(dataset, size)\n    self.index_to_label = index_to_label\n\n  @property\n  def num_classes(self):\n    return len(self.index_to_label)\n\n  def split(self, fraction):\n    \"\"\"Splits dataset into two sub-datasets with the given fraction.\n\n    Primarily used for splitting the data set into training and testing sets.\n\n    Args:\n      fraction: float, demonstrates the fraction of the first returned\n        subdataset in the original data.\n\n    Returns:\n      The splitted two sub datasets.\n    \"\"\"\n    return self._split(fraction, self.index_to_label)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/dataloader_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport numpy as np\nimport tensorflow.compat.v2 as tf\nfrom tensorflow_examples.lite.model_maker.core import test_util\nfrom tensorflow_examples.lite.model_maker.core.data_util import dataloader\n\n\nclass DataLoaderTest(tf.test.TestCase):\n\n  def test_split(self):\n    ds = tf.data.Dataset.from_tensor_slices([[0, 1], [1, 1], [0, 0], [1, 0]])\n    data = dataloader.DataLoader(ds, 4)\n    train_data, test_data = data.split(0.5)\n\n    self.assertEqual(len(train_data), 2)\n    self.assertIsInstance(train_data, dataloader.DataLoader)\n    self.assertIsInstance(test_data, dataloader.DataLoader)\n    for i, elem in enumerate(train_data.gen_dataset()):\n      self.assertTrue((elem.numpy() == np.array([i, 1])).all())\n\n    self.assertEqual(len(test_data), 2)\n    for i, elem in enumerate(test_data.gen_dataset()):\n      self.assertTrue((elem.numpy() == np.array([i, 0])).all())\n\n  def test_len(self):\n    size = 4\n    ds = tf.data.Dataset.from_tensor_slices([[0, 1], [1, 1], [0, 0], [1, 0]])\n    data = dataloader.DataLoader(ds, size)\n    self.assertEqual(len(data), size)\n\n  def test_gen_dataset(self):\n    input_dim = 8\n    data = test_util.get_dataloader(\n        data_size=2, input_shape=[input_dim], num_classes=2)\n\n    ds = data.gen_dataset()\n    self.assertEqual(len(ds), 2)\n    for (feature, label) in ds:\n      self.assertTrue((tf.shape(feature).numpy() == np.array([1, 8])).all())\n      self.assertTrue((tf.shape(label).numpy() == np.array([1])).all())\n\n    ds2 = data.gen_dataset(batch_size=2)\n    self.assertEqual(len(ds2), 1)\n    for (feature, label) in ds2:\n      self.assertTrue((tf.shape(feature).numpy() == np.array([2, 8])).all())\n      self.assertTrue((tf.shape(label).numpy() == np.array([2])).all())\n\n    ds3 = data.gen_dataset(batch_size=2, is_training=True, shuffle=True)\n    self.assertEqual(ds3.cardinality(), 1)\n    for (feature, label) in ds3.take(10):\n      self.assertTrue((tf.shape(feature).numpy() == np.array([2, 8])).all())\n      self.assertTrue((tf.shape(label).numpy() == np.array([2])).all())\n\n\nclass ClassificationDataLoaderTest(tf.test.TestCase):\n\n  def test_split(self):\n\n    class MagicClassificationDataLoader(dataloader.ClassificationDataLoader):\n\n      def __init__(self, dataset, size, index_to_label, value):\n        super(MagicClassificationDataLoader,\n              self).__init__(dataset, size, index_to_label)\n        self.value = value\n\n      def split(self, fraction):\n        return self._split(fraction, self.index_to_label, self.value)\n\n    # Some dummy inputs.\n    magic_value = 42\n    num_classes = 2\n    index_to_label = (False, True)\n\n    # Create data loader from sample data.\n    ds = tf.data.Dataset.from_tensor_slices([[0, 1], [1, 1], [0, 0], [1, 0]])\n    data = MagicClassificationDataLoader(ds, len(ds), index_to_label,\n                                         magic_value)\n\n    # Train/Test data split.\n    fraction = .25\n    train_data, test_data = data.split(fraction)\n\n    # `split` should return instances of child DataLoader.\n    self.assertIsInstance(train_data, MagicClassificationDataLoader)\n    self.assertIsInstance(test_data, MagicClassificationDataLoader)\n\n    # Make sure number of entries are right.\n    self.assertEqual(len(train_data.gen_dataset()), len(train_data))\n    self.assertEqual(len(train_data), fraction * len(ds))\n    self.assertEqual(len(test_data), len(ds) - len(train_data))\n\n    # Make sure attributes propagated correctly.\n    self.assertEqual(train_data.num_classes, num_classes)\n    self.assertEqual(test_data.index_to_label, index_to_label)\n    self.assertEqual(train_data.value, magic_value)\n    self.assertEqual(test_data.value, magic_value)\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/image_dataloader.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Image dataloader.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nimport random\n\nimport tensorflow as tf\nimport tensorflow_datasets as tfds\nfrom tensorflow_examples.lite.model_maker.core.api import mm_export\nfrom tensorflow_examples.lite.model_maker.core.data_util import dataloader\n\n\ndef load_image(path):\n  \"\"\"Loads image.\"\"\"\n  image_raw = tf.io.read_file(path)\n  image_tensor = tf.cond(\n      tf.image.is_jpeg(image_raw),\n      lambda: tf.image.decode_jpeg(image_raw, channels=3),\n      lambda: tf.image.decode_png(image_raw, channels=3))\n  return image_tensor\n\n\ndef create_data(name, data, info, label_names):\n  \"\"\"Creates an ImageClassifierDataLoader object from tfds data.\"\"\"\n  if name not in data:\n    return None\n  data = data[name]\n  data = data.map(lambda a: (a['image'], a['label']))\n  size = info.splits[name].num_examples\n  return ImageClassifierDataLoader(data, size, label_names)\n\n\n@mm_export('image_classifier.DataLoader')\nclass ImageClassifierDataLoader(dataloader.ClassificationDataLoader):\n  \"\"\"DataLoader for image classifier.\"\"\"\n\n  @classmethod\n  def from_folder(cls, filename, shuffle=True):\n    \"\"\"Image analysis for image classification load images with labels.\n\n    Assume the image data of the same label are in the same subdirectory.\n\n    Args:\n      filename: Name of the file.\n      shuffle: boolean, if shuffle, random shuffle data.\n\n    Returns:\n      ImageDataset containing images and labels and other related info.\n    \"\"\"\n    data_root = os.path.abspath(filename)\n\n    # Assumes the image data of the same label are in the same subdirectory,\n    # gets image path and label names.\n    all_image_paths = list(tf.io.gfile.glob(data_root + r'/*/*'))\n    all_image_size = len(all_image_paths)\n    if all_image_size == 0:\n      raise ValueError('Image size is zero')\n\n    if shuffle:\n      # Random shuffle data.\n      random.shuffle(all_image_paths)\n\n    label_names = sorted(\n        name for name in os.listdir(data_root)\n        if os.path.isdir(os.path.join(data_root, name)))\n    all_label_size = len(label_names)\n    label_to_index = dict(\n        (name, index) for index, name in enumerate(label_names))\n    all_image_labels = [\n        label_to_index[os.path.basename(os.path.dirname(path))]\n        for path in all_image_paths\n    ]\n\n    path_ds = tf.data.Dataset.from_tensor_slices(all_image_paths)\n\n    autotune = tf.data.AUTOTUNE\n    image_ds = path_ds.map(load_image, num_parallel_calls=autotune)\n\n    # Loads label.\n    label_ds = tf.data.Dataset.from_tensor_slices(\n        tf.cast(all_image_labels, tf.int64))\n\n    # Creates  a dataset if (image, label) pairs.\n    image_label_ds = tf.data.Dataset.zip((image_ds, label_ds))\n\n    tf.compat.v1.logging.info(\n        'Load image with size: %d, num_label: %d, labels: %s.', all_image_size,\n        all_label_size, ', '.join(label_names))\n    return ImageClassifierDataLoader(image_label_ds, all_image_size,\n                                     label_names)\n\n  @classmethod\n  def from_tfds(cls, name):\n    \"\"\"Loads data from tensorflow_datasets.\"\"\"\n    data, info = tfds.load(name, with_info=True)\n    if 'label' not in info.features:\n      raise ValueError('info.features need to contain \\'label\\' key.')\n    label_names = info.features['label'].names\n\n    train_data = create_data('train', data, info, label_names)\n    validation_data = create_data('validation', data, info, label_names)\n    test_data = create_data('test', data, info, label_names)\n    return train_data, validation_data, test_data\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/image_dataloader_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nimport random\nimport numpy as np\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.core.data_util import image_dataloader\n\n\ndef _fill_image(rgb, image_size):\n  r, g, b = rgb\n  return np.broadcast_to(\n      np.array([[[r, g, b]]], dtype=np.uint8),\n      shape=(image_size, image_size, 3))\n\n\ndef _write_filled_jpeg_file(path, rgb, image_size):\n  tf.keras.preprocessing.image.save_img(path, _fill_image(rgb, image_size),\n                                        'channels_last', 'jpeg')\n\n\nclass ImageDataLoaderTest(tf.test.TestCase):\n\n  def setUp(self):\n    super(ImageDataLoaderTest, self).setUp()\n    self.image_path = os.path.join(self.get_temp_dir(), 'random_image_dir')\n    if os.path.exists(self.image_path):\n      return\n    os.mkdir(self.image_path)\n    for class_name in ('daisy', 'tulips'):\n      class_subdir = os.path.join(self.image_path, class_name)\n      os.mkdir(class_subdir)\n      _write_filled_jpeg_file(\n          os.path.join(class_subdir, '0.jpeg'),\n          [random.uniform(0, 255) for _ in range(3)], 224)\n\n  def test_split(self):\n    ds = tf.data.Dataset.from_tensor_slices([[0, 1], [1, 1], [0, 0], [1, 0]])\n    data = image_dataloader.ImageClassifierDataLoader(ds, 4, ['pos', 'neg'])\n    train_data, test_data = data.split(0.5)\n\n    self.assertEqual(len(train_data), 2)\n    for i, elem in enumerate(train_data._dataset):\n      self.assertTrue((elem.numpy() == np.array([i, 1])).all())\n    self.assertEqual(train_data.num_classes, 2)\n    self.assertEqual(train_data.index_to_label, ['pos', 'neg'])\n\n    self.assertEqual(len(test_data), 2)\n    for i, elem in enumerate(test_data._dataset):\n      self.assertTrue((elem.numpy() == np.array([i, 0])).all())\n    self.assertEqual(test_data.num_classes, 2)\n    self.assertEqual(test_data.index_to_label, ['pos', 'neg'])\n\n  def test_from_folder(self):\n    data = image_dataloader.ImageClassifierDataLoader.from_folder(\n        self.image_path)\n\n    self.assertEqual(len(data), 2)\n    self.assertEqual(data.num_classes, 2)\n    self.assertEqual(data.index_to_label, ['daisy', 'tulips'])\n    for image, label in data.gen_dataset():\n      self.assertTrue(label.numpy() == 1 or label.numpy() == 0)\n      if label.numpy() == 0:\n        raw_image_tensor = image_dataloader.load_image(\n            os.path.join(self.image_path, 'daisy', '0.jpeg'))\n      else:\n        raw_image_tensor = image_dataloader.load_image(\n            os.path.join(self.image_path, 'tulips', '0.jpeg'))\n      self.assertTrue((image.numpy() == raw_image_tensor.numpy()).all())\n\n  def test_from_tfds(self):\n    # TODO(b/204836815): Remove this once tfds download error is fixed.\n    self.skipTest('Temporarily skip the unittest due to tfds download error.')\n    train_data, validation_data, test_data = (\n        image_dataloader.ImageClassifierDataLoader.from_tfds('beans'))\n    self.assertIsInstance(train_data.gen_dataset(), tf.data.Dataset)\n    self.assertEqual(len(train_data), 1034)\n    self.assertEqual(train_data.num_classes, 3)\n    self.assertEqual(train_data.index_to_label,\n                     ['angular_leaf_spot', 'bean_rust', 'healthy'])\n\n    self.assertIsInstance(validation_data.gen_dataset(), tf.data.Dataset)\n    self.assertEqual(len(validation_data), 133)\n    self.assertEqual(validation_data.num_classes, 3)\n    self.assertEqual(validation_data.index_to_label,\n                     ['angular_leaf_spot', 'bean_rust', 'healthy'])\n\n    self.assertIsInstance(test_data.gen_dataset(), tf.data.Dataset)\n    self.assertEqual(len(test_data), 128)\n    self.assertEqual(test_data.num_classes, 3)\n    self.assertEqual(test_data.index_to_label,\n                     ['angular_leaf_spot', 'bean_rust', 'healthy'])\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/image_searcher_dataloader.py",
    "content": "# Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Image DataLoader for Searcher task.\"\"\"\n\nimport logging\nimport os\n\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core.api.api_util import mm_export\nfrom tensorflow_examples.lite.model_maker.core.data_util import metadata_loader\nfrom tensorflow_examples.lite.model_maker.core.data_util import searcher_dataloader\nfrom tensorflow_lite_support.python.task.core import base_options as base_options_module\nfrom tensorflow_lite_support.python.task.processor.proto import embedding_options_pb2\nfrom tensorflow_lite_support.python.task.vision import image_embedder\nfrom tensorflow_lite_support.python.task.vision.core import tensor_image\n\n_MetadataType = metadata_loader.MetadataType\n_BaseOptions = base_options_module.BaseOptions\n\n\n@mm_export(\"searcher.ImageDataLoader\")\nclass DataLoader(searcher_dataloader.DataLoader):\n  \"\"\"DataLoader class for Image Searcher Task.\"\"\"\n\n  def __init__(\n      self,\n      embedder: image_embedder.ImageEmbedder,\n      metadata_type: _MetadataType = _MetadataType.FROM_FILE_NAME) -> None:\n    \"\"\"Initializes DataLoader for Image Searcher task.\n\n    Args:\n      embedder: Embedder to generate embedding from raw input image.\n      metadata_type: Type of MetadataLoader to load metadata for each input\n        data. By default, load the file name as metadata for each input data.\n    \"\"\"\n    self._embedder = embedder\n    super().__init__(embedder_path=embedder.options.base_options.file_name)\n\n    # Creates the metadata loader.\n    self.metadata_type = metadata_type\n    if metadata_type is _MetadataType.FROM_FILE_NAME:\n      self._metadata_loader = metadata_loader.MetadataLoader.from_file_name()\n    elif metadata_type is _MetadataType.FROM_DAT_FILE:\n      self._metadata_loader = metadata_loader.MetadataLoader.from_dat_file()\n    else:\n      raise ValueError(\"Unsuported metadata_type.\")\n\n  @classmethod\n  def create(cls,\n             image_embedder_path: str,\n             metadata_type: _MetadataType = _MetadataType.FROM_FILE_NAME,\n             l2_normalize: bool = False) -> \"DataLoader\":\n    \"\"\"Creates DataLoader for the Image Searcher task.\n\n    Args:\n      image_embedder_path: Path to the \".tflite\" image embedder model.\n      metadata_type: Type of MetadataLoader to load metadata for each input\n        image based on image path. By default, load the file name as metadata\n        for each input image.\n      l2_normalize: Whether to normalize the returned feature vector with L2\n        norm. Use this option only if the model does not already contain a\n        native L2_NORMALIZATION TF Lite Op. In most cases, this is already the\n        case and L2 norm is thus achieved through TF Lite inference.\n\n    Returns:\n      DataLoader object created for the Image Searcher task.\n    \"\"\"\n    # Creates ImageEmbedder.\n    image_embedder_path = os.path.abspath(image_embedder_path)\n    with tf.io.gfile.GFile(image_embedder_path, \"rb\") as f:\n      image_embedder_content = f.read()\n    base_options = _BaseOptions(\n        file_content=image_embedder_content, file_name=image_embedder_path)\n    embedding_options = embedding_options_pb2.EmbeddingOptions(\n        l2_normalize=l2_normalize)\n    options = image_embedder.ImageEmbedderOptions(\n        base_options=base_options, embedding_options=embedding_options)\n    embedder = image_embedder.ImageEmbedder.create_from_options(options)\n\n    return cls(embedder, metadata_type)\n\n  def load_from_folder(self, path: str, mode: str = \"r\") -> None:\n    \"\"\"Loads image data from folder.\n\n    Users can load images from different folders one by one. For instance,\n    ```\n    # Creates data_loader instance.\n    data_loader = image_searcher_dataloader.DataLoader.create(tflite_path)\n\n    # Loads images, first from `image_path1` and secondly from `image_path2`.\n    data_loader.load_from_folder(image_path1)\n    data_loader.load_from_folder(image_path2)\n    ```\n\n    Args:\n      path: image directory to be loaded.\n      mode: mode in which the file is opened, Used when metadata_type is\n        FROM_DAT_FILE. Only 'r' and 'rb' are supported. 'r' means opening for\n        reading, 'rb' means opening for reading binary.\n    \"\"\"\n    embedding_list = []\n    metadata_list = []\n\n    i = 0\n    # Gets the image files in the folder and loads images.\n    for root, _, files in tf.io.gfile.walk(path):\n      for name in files:\n        image_path = os.path.join(root, name)\n        if image_path.lower().endswith(\".dat\"):\n          continue\n\n        try:\n          with tf.io.gfile.GFile(image_path, \"rb\") as f:\n            buffer = f.read()\n          image = tensor_image.TensorImage.create_from_buffer(buffer)\n        except RuntimeError as e:\n          logging.warning(\n              \"Can't read image from the image path %s with the error %s\",\n              image_path, e)\n          continue\n\n        try:\n          embedding = self._embedder.embed(\n              image).embeddings[0].feature_vector.value\n        except (RuntimeError, ValueError) as e:\n          logging.warning(\"Can't get the embedding of %s with the error %s\",\n                          image_path, e)\n          continue\n\n        embedding_list.append(embedding)\n        if self.metadata_type == _MetadataType.FROM_DAT_FILE:\n          metadata = self._metadata_loader.load(image_path, mode=mode)\n        else:\n          metadata = self._metadata_loader.load(image_path)\n        metadata_list.append(metadata)\n\n        i += 1\n        if i % 1000 == 0:\n          logging.info(\"Processed %d images.\", i)\n\n    cache_dataset = np.stack(embedding_list)\n    self._cache_dataset_list.append(cache_dataset)\n    self._metadata = self._metadata + metadata_list\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/metadata_loader.py",
    "content": "# Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"MetadataLoader to load metadata for each input data.\"\"\"\n\nimport enum\nimport os\nfrom typing import AnyStr, Callable\n\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core.api.api_util import mm_export\n\n\n@mm_export(\"searcher.MetadataType\")\n@enum.unique\nclass MetadataType(enum.Enum):\n  FROM_FILE_NAME = 1\n  FROM_DAT_FILE = 2\n\n\nclass MetadataLoader(object):\n  \"\"\"MetadataLoader class to load metadata for each input data.\"\"\"\n\n  def __init__(self, func: Callable[..., AnyStr]) -> None:\n    self._func = func\n\n  def load(self, path: str, **kwargs) -> AnyStr:\n    \"\"\"Loads metadata from input data path.\"\"\"\n    return self._func(path, **kwargs)\n\n  @classmethod\n  def from_file_name(cls) -> \"MetadataLoader\":\n    \"\"\"Loads the file name as metadata.\"\"\"\n\n    def _load_metadata(path):\n      return os.path.splitext(os.path.basename(path))[0]\n\n    return cls(_load_metadata)\n\n  @classmethod\n  def from_dat_file(cls) -> \"MetadataLoader\":\n    \"\"\"Loads the metadata from .dat file.\n\n    Reads metadata from a file assumed to be stored next to the (image, audio or\n    text) input file, with the same basename and a different extension \".dat\",\n    e.g: if input file is \"/path/to/foo.jpg\", read metadata from\n    \"/path/to/foo.dat\". When loading metadata, user can pass `mode` parameter\n    which is a string that defines which mode you want to open the file in: \"r\"\n    (text metadata) and \"rb\" (binary metadata) are supported.\n\n    Returns:\n      MetadataLoader object.\n    \"\"\"\n\n    def _load_metadata(path, mode=\"r\"):\n      if mode != \"r\" and mode != \"rb\":\n        raise ValueError(\n            \"`mode` must be \\\"r\\\" or \\\"rb\\\" to read text or binary metadata.\")\n\n      metadata_file_path = os.path.splitext(path)[0] + \".dat\"\n      with tf.io.gfile.GFile(metadata_file_path, mode) as f:\n        return f.read()\n\n    return cls(_load_metadata)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/metadata_loader_test.py",
    "content": "# Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Tests for metadata_loader.\"\"\"\n\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core.data_util import metadata_loader\nfrom tensorflow_examples.lite.model_maker.core import test_util\n\n\nclass MetadataLoaderTest(tf.test.TestCase):\n\n  def test_from_file_name(self):\n    loader = metadata_loader.MetadataLoader.from_file_name()\n    metadata = loader.load(\"/tmp/searcher_dataset/burger.jpg\")\n    self.assertEqual(metadata, \"burger\")\n\n  def test_from_dat_file(self):\n\n    loader = metadata_loader.MetadataLoader.from_dat_file()\n\n    expected_sparrow_metadata = \"{ 'type': 'sparrow', 'class': 'Aves'}\"\n    sparrow_metadata = loader.load(test_util.get_test_data_path(\"sparrow.png\"))\n    self.assertEqual(sparrow_metadata, expected_sparrow_metadata)\n\n    expected_burger_metadata = b\"\\x00\\x11\\x22\"\n    burger_metadata = loader.load(\n        test_util.get_test_data_path(\"cats_and_dogs.jpg\"), mode=\"rb\")\n    self.assertEqual(burger_metadata, expected_burger_metadata)\n\n\nif __name__ == \"__main__\":\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/object_detector_dataloader.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Dataloader for object detection.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport csv\nfrom typing import Collection, Dict, List, Optional, Tuple, TypeVar, Union\n\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core.api.api_util import mm_export\nfrom tensorflow_examples.lite.model_maker.core.data_util import dataloader\nfrom tensorflow_examples.lite.model_maker.core.data_util import object_detector_dataloader_util as util\nimport yaml\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import dataloader as det_dataloader\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import label_util\n\nDetectorDataLoader = TypeVar('DetectorDataLoader', bound='DataLoader')\n# Csv lines with the label map.\nCsvLines = Tuple[List[List[List[str]]], Dict[int, str]]\n\n\ndef _get_label_map(label_map):\n  \"\"\"Gets the label map dict.\"\"\"\n  if isinstance(label_map, list):\n    label_map_dict = {}\n    for i, label in enumerate(label_map):\n      # 0 is resevered for background.\n      label_map_dict[i + 1] = label\n    label_map = label_map_dict\n  label_map = label_util.get_label_map(label_map)\n\n  if 0 in label_map and label_map[0] != 'background':\n    raise ValueError('0 must be resevered for background.')\n  label_map.pop(0, None)\n  name_set = set()\n  for idx, name in label_map.items():\n    if not isinstance(idx, int):\n      raise ValueError('The key (label id) in label_map must be integer.')\n    if not isinstance(name, str):\n      raise ValueError('The value (label name) in label_map must be string.')\n    if name in name_set:\n      raise ValueError('The value: %s (label name) can\\'t be duplicated.' %\n                       name)\n    name_set.add(name)\n  return label_map\n\n\ndef _group_csv_lines(csv_file: str,\n                     set_prefixes: List[str],\n                     delimiter: str = ',',\n                     quotechar: str = '\"') -> CsvLines:\n  \"\"\"Groups csv_lines for different set_names and label_map.\n\n  Args:\n    csv_file: filename of the csv file.\n    set_prefixes: Set prefix names for training, validation and test data. e.g.\n      ['TRAIN', 'VAL', 'TEST'].\n    delimiter: Character used to separate fields.\n    quotechar: Character used to quote fields containing special characters.\n\n  Returns:\n    [training csv lines, validation csv lines, test csv lines], label_map\n  \"\"\"\n  # Dict that maps integer label ids to string label names.\n  label_map = {}\n  with tf.io.gfile.GFile(csv_file, 'r') as f:\n    reader = csv.reader(f, delimiter=delimiter, quotechar=quotechar)\n    # `lines_list` = [training csv lines, validation csv lines, test csv lines]\n    # Each csv line is a list of strings separated by delimiter. e.g.\n    # row 'one,two,three' in the csv file will be ['one', two', 'three'].\n    lines_list = [[], [], []]\n    for line in reader:\n      # Groups lines by the set_name.\n      set_name = line[0].strip()\n      for i, set_prefix in enumerate(set_prefixes):\n        if set_name.startswith(set_prefix):\n          lines_list[i].append(line)\n\n      label = line[2].strip()\n      # Updates label_map if it's a new label.\n      if label not in label_map.values():\n        label_map[len(label_map) + 1] = label\n\n  return lines_list, label_map\n\n\n@mm_export('object_detector.DataLoader')\nclass DataLoader(dataloader.DataLoader):\n  \"\"\"DataLoader for object detector.\"\"\"\n\n  def __init__(self,\n               tfrecord_file_patten,\n               size,\n               label_map,\n               annotations_json_file=None):\n    \"\"\"Initialize DataLoader for object detector.\n\n    Args:\n      tfrecord_file_patten: Glob for tfrecord files. e.g. \"/tmp/coco*.tfrecord\".\n      size: The size of the dataset.\n      label_map: Variable shows mapping label integers ids to string label\n        names. 0 is the reserved key for `background` and doesn't need to be\n        included in label_map. Label names can't be duplicated. Supported\n        formats are:\n        1. Dict, map label integers ids to string label names, such as {1:\n          'person', 2: 'notperson'}. 2. List, a list of label names such as\n            ['person', 'notperson'] which is\n           the same as setting label_map={1: 'person', 2: 'notperson'}.\n        3. String, name for certain dataset. Accepted values are: 'coco', 'voc'\n          and 'waymo'. 4. String, yaml filename that stores label_map.\n      annotations_json_file: JSON with COCO data format containing golden\n        bounding boxes. Used for validation. If None, use the ground truth from\n        the dataloader. Refer to\n        https://towardsdatascience.com/coco-data-format-for-object-detection-a4c5eaf518c5\n          for the description of COCO data format.\n    \"\"\"\n    super(DataLoader, self).__init__(dataset=None, size=size)\n    self.tfrecord_file_patten = tfrecord_file_patten\n    self.label_map = _get_label_map(label_map)\n    self.annotations_json_file = annotations_json_file\n\n  @classmethod\n  def from_pascal_voc(\n      cls,\n      images_dir: str,\n      annotations_dir: str,\n      label_map: Union[List[str], Dict[int, str], str],\n      annotation_filenames: Optional[Collection[str]] = None,\n      ignore_difficult_instances: bool = False,\n      num_shards: int = 100,\n      max_num_images: Optional[int] = None,\n      cache_dir: Optional[str] = None,\n      cache_prefix_filename: Optional[str] = None) -> DetectorDataLoader:\n    \"\"\"Loads from dataset with PASCAL VOC format.\n\n    Refer to\n    https://towardsdatascience.com/coco-data-format-for-object-detection-a4c5eaf518c5\n    for the description of PASCAL VOC data format.\n\n    LabelImg Tool (https://github.com/tzutalin/labelImg) can annotate the image\n    and save annotations as XML files in PASCAL VOC data format.\n\n    Annotations are in the folder: `annotations_dir`.\n    Raw images are in the foloder: `images_dir`.\n\n    Args:\n      images_dir: Path to directory that store raw images.\n      annotations_dir: Path to the annotations directory.\n      label_map: Variable shows mapping label integers ids to string label\n        names. 0 is the reserved key for `background`. Label names can't be\n        duplicated. Supported format: 1. Dict, map label integers ids to string\n          label names, e.g.\n           {1: 'person', 2: 'notperson'}. 2. List, a list of label names. e.g.\n             ['person', 'notperson'] which is\n           the same as setting label_map={1: 'person', 2: 'notperson'}.\n        3. String, name for certain dataset. Accepted values are: 'coco', 'voc'\n          and 'waymo'. 4. String, yaml filename that stores label_map.\n      annotation_filenames: Collection of annotation filenames (strings) to be\n        loaded. For instance, if there're 3 annotation files [0.xml, 1.xml,\n        2.xml] in `annotations_dir`, setting annotation_filenames=['0', '1']\n        makes this method only load [0.xml, 1.xml].\n      ignore_difficult_instances: Whether to ignore difficult instances.\n        `difficult` can be set inside `object` item in the annotation xml file.\n      num_shards: Number of shards for output file.\n      max_num_images: Max number of imags to process.\n      cache_dir: The cache directory to save TFRecord, metadata and json file.\n        When cache_dir is not set, a temporary folder will be created and will\n        not be removed automatically after training which makes it can be used\n        later.\n      cache_prefix_filename: The cache prefix filename. If not set, will\n        automatically generate it based on `image_dir`, `annotations_dir` and\n        `annotation_filenames`.\n\n    Returns:\n      ObjectDetectorDataLoader object.\n    \"\"\"\n    label_map = _get_label_map(label_map)\n\n    # If `cache_prefix_filename` is None, automatically generates a hash value.\n    if cache_prefix_filename is None:\n      cache_prefix_filename = util.get_cache_prefix_filename_from_pascal(\n          images_dir=images_dir,\n          annotations_dir=annotations_dir,\n          annotation_filenames=annotation_filenames,\n          num_shards=num_shards)\n\n    cache_files = util.get_cache_files(\n        cache_dir=cache_dir,\n        cache_prefix_filename=cache_prefix_filename,\n        num_shards=num_shards)\n\n    # If not cached, writes data into tfrecord_file_paths and\n    # annotations_json_file_path.\n    # If `num_shards` differs, it's still not cached.\n    if not util.is_cached(cache_files):\n      cache_writer = util.PascalVocCacheFilesWriter(\n          label_map=label_map,\n          images_dir=images_dir,\n          num_shards=num_shards,\n          max_num_images=max_num_images,\n          ignore_difficult_instances=ignore_difficult_instances)\n      cache_writer.write_files(\n          cache_files=cache_files,\n          annotations_dir=annotations_dir,\n          annotation_filenames=annotation_filenames)\n\n    return cls.from_cache(cache_files.cache_prefix)\n\n  @classmethod\n  def from_csv(\n      cls,\n      filename: str,\n      images_dir: Optional[str] = None,\n      delimiter: str = ',',\n      quotechar: str = '\"',\n      num_shards: int = 10,\n      max_num_images: Optional[int] = None,\n      cache_dir: Optional[str] = None,\n      cache_prefix_filename: Optional[str] = None\n  ) -> List[Optional[DetectorDataLoader]]:\n    \"\"\"Loads the data from the csv file.\n\n    The csv format is shown in\n    https://cloud.google.com/vision/automl/object-detection/docs/csv-format. We\n    supports bounding box with 2 vertices for now. We support the files in the\n    local machine as well.\n\n    Args:\n      filename: Name of the csv file.\n      images_dir: Path to directory that store raw images. If None, the image\n        path in the csv file is the path to Google Cloud Storage or the absolute\n        path in the local machine.\n      delimiter: Character used to separate fields.\n      quotechar: Character used to quote fields containing special characters.\n      num_shards: Number of shards for output file.\n      max_num_images: Max number of imags to process.\n      cache_dir: The cache directory to save TFRecord, metadata and json file.\n        When cache_dir is None, a temporary folder will be created and will not\n        be removed automatically after training which makes it can be used\n        later.\n      cache_prefix_filename: The cache prefix filename. If None, will\n        automatically generate it based on `filename`.\n\n    Returns:\n      train_data, validation_data, test_data which are ObjectDetectorDataLoader\n      objects. Can be None if without such data.\n    \"\"\"\n    # If `cache_prefix_filename` is None, automatically generates a hash value.\n    if cache_prefix_filename is None:\n      cache_prefix_filename = util.get_cache_prefix_filename_from_csv(\n          csv_file=filename, num_shards=num_shards)\n\n    # Gets a list of cache files mapping `set_prefixes`.\n    set_prefixes = ['TRAIN', 'VAL', 'TEST']\n    cache_files_list = util.get_cache_files_sequence(\n        cache_dir=cache_dir,\n        cache_prefix_filename=cache_prefix_filename,\n        set_prefixes=set_prefixes,\n        num_shards=num_shards)\n\n    # If not cached, writes data into tfrecord_file_paths and\n    # annotations_json_file_path.\n    # If `num_shards` differs, it's still not cached.\n    if not util.is_all_cached(cache_files_list):\n      lines_list, label_map = _group_csv_lines(\n          csv_file=filename,\n          set_prefixes=set_prefixes,\n          delimiter=delimiter,\n          quotechar=quotechar)\n      cache_writer = util.CsvCacheFilesWriter(\n          label_map=label_map,\n          images_dir=images_dir,\n          num_shards=num_shards,\n          max_num_images=max_num_images)\n      for cache_files, csv_lines in zip(cache_files_list, lines_list):\n        if csv_lines:\n          cache_writer.write_files(cache_files, csv_lines=csv_lines)\n\n    # Loads training & validation & test data from cache.\n    data = []\n    for cache_files in cache_files_list:\n      cache_prefix = cache_files.cache_prefix\n      try:\n        data.append(cls.from_cache(cache_prefix))\n      except ValueError:\n        # No training / validation / test data in the csv file.\n        # For instance, there're only training and test data in the csv file,\n        # this will make this function return `train_data, None, test_data`\n        data.append(None)\n    return data\n\n  @classmethod\n  def from_cache(cls, cache_prefix):\n    \"\"\"Loads the data from cache.\n\n    Args:\n      cache_prefix: The cache prefix including the cache directory and the cache\n        prefix filename, e.g: '/tmp/cache/train'.\n\n    Returns:\n      ObjectDetectorDataLoader object.\n    \"\"\"\n    # Gets TFRecord files.\n    tfrecord_file_patten = cache_prefix + '*.tfrecord'\n    if not tf.io.gfile.glob(tfrecord_file_patten):\n      raise ValueError('TFRecord files are empty.')\n\n    # Loads meta_data.\n    meta_data_file = cache_prefix + util.META_DATA_FILE_SUFFIX\n    if not tf.io.gfile.exists(meta_data_file):\n      raise ValueError('Metadata file %s doesn\\'t exist.' % meta_data_file)\n    with tf.io.gfile.GFile(meta_data_file, 'r') as f:\n      meta_data = yaml.load(f, Loader=yaml.FullLoader)\n\n    # Gets annotation json file.\n    ann_json_file = cache_prefix + util.ANN_JSON_FILE_SUFFIX\n    if not tf.io.gfile.exists(ann_json_file):\n      ann_json_file = None\n\n    return DataLoader(tfrecord_file_patten, meta_data['size'],\n                      meta_data['label_map'], ann_json_file)\n\n  def gen_dataset(self,\n                  model_spec,\n                  batch_size=None,\n                  is_training=False,\n                  use_fake_data=False):\n    \"\"\"Generate a batched tf.data.Dataset for training/evaluation.\n\n    Args:\n      model_spec: Specification for the model.\n      batch_size: A integer, the returned dataset will be batched by this size.\n      is_training: A boolean, when True, the returned dataset will be optionally\n        shuffled and repeated as an endless dataset.\n      use_fake_data: Use fake input.\n\n    Returns:\n      A TF dataset ready to be consumed by Keras model.\n    \"\"\"\n    reader = det_dataloader.InputReader(\n        self.tfrecord_file_patten,\n        is_training=is_training,\n        use_fake_data=use_fake_data,\n        max_instances_per_image=model_spec.config.max_instances_per_image,\n        debug=model_spec.config.debug)\n    self._dataset = reader(model_spec.config.as_dict(), batch_size=batch_size)\n    return self._dataset\n\n  def split(self, fraction):\n    \"\"\"This function isn't implemented for the object detection task.\"\"\"\n    raise NotImplementedError(\n        'split function is not supported in the object detection task.')\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/object_detector_dataloader_test.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport filecmp\nimport os\n\nimport numpy as np\n\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core import test_util\n\nfrom tensorflow_examples.lite.model_maker.core.data_util import object_detector_dataloader\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import hparams_config\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\n\n\nclass MockDetectorModelSpec(object):\n\n  def __init__(self, model_name):\n    self.model_name = model_name\n    config = hparams_config.get_detection_config(model_name)\n    config.image_size = utils.parse_image_size(config.image_size)\n    config.update({'debug': False})\n    self.config = config\n\n\nclass ObjectDectectorDataLoaderTest(tf.test.TestCase):\n\n  def test_from_pascal_voc(self):\n\n    images_dir, annotations_dir, label_map = test_util.create_pascal_voc(\n        self.get_temp_dir())\n    model_spec = MockDetectorModelSpec('efficientdet-lite0')\n\n    data = object_detector_dataloader.DataLoader.from_pascal_voc(\n        images_dir, annotations_dir, label_map)\n\n    self.assertIsInstance(data, object_detector_dataloader.DataLoader)\n    self.assertLen(data, 1)\n    self.assertEqual(data.label_map, label_map)\n\n    self.assertTrue(os.path.isfile(data.annotations_json_file))\n    self.assertGreater(os.path.getsize(data.annotations_json_file), 0)\n    expected_json_file = test_util.get_test_data_path('annotations.json')\n    self.assertTrue(filecmp.cmp(data.annotations_json_file, expected_json_file))\n\n    ds = data.gen_dataset(model_spec, batch_size=1, is_training=False)\n    for i, (images, labels) in enumerate(ds):\n      self.assertEqual(i, 0)\n      images_shape = tf.shape(images).numpy()\n      expected_shape = np.array([1, *model_spec.config.image_size, 3])\n      self.assertTrue((images_shape == expected_shape).all())\n      self.assertLen(labels, 15)\n\n    ds1 = data.gen_dataset(model_spec, batch_size=1, is_training=True)\n    # Comments out this assert since it fails externally.\n    # self.assertEqual(ds1.cardinality(), tf.data.INFINITE_CARDINALITY)\n    for images, labels in ds1.take(10):\n      images_shape = tf.shape(images).numpy()\n      expected_shape = np.array([1, *model_spec.config.image_size, 3])\n      self.assertTrue((images_shape == expected_shape).all())\n      self.assertLen(labels, 15)\n\n  def test_from_csv(self):\n    model_spec = MockDetectorModelSpec('efficientdet-lite0')\n\n    csv_file = test_util.get_test_data_path('salads_ml_use.csv')\n    image_dir = test_util.get_test_data_path('salad_images')\n    train_data, validation_data, test_data = \\\n        object_detector_dataloader.DataLoader.from_csv(csv_file, image_dir)\n\n    label_map = {1: 'Baked Goods', 2: 'Cheese', 3: 'Salad'}\n    # Checks the training data.\n    self.assertIsInstance(train_data, object_detector_dataloader.DataLoader)\n    self.assertLen(train_data, 1)\n    self.assertEqual(train_data.label_map, label_map)\n\n    self.assertTrue(os.path.isfile(train_data.annotations_json_file))\n    self.assertGreater(os.path.getsize(train_data.annotations_json_file), 0)\n    expected_json_file = test_util.get_test_data_path('train_annotations.json')\n    self.assertTrue(\n        filecmp.cmp(train_data.annotations_json_file, expected_json_file))\n\n    dataset = train_data.gen_dataset(model_spec, batch_size=1, is_training=True)\n    for images, labels in dataset.take(10):\n      images_shape = tf.shape(images).numpy()\n      expected_shape = np.array([1, *model_spec.config.image_size, 3])\n      self.assertTrue((images_shape == expected_shape).all())\n      self.assertLen(labels, 15)\n\n    # Checks the validation data.\n    self.assertIsNone(validation_data)\n\n    # Checks the test data.\n    self.assertIsInstance(test_data, object_detector_dataloader.DataLoader)\n    self.assertLen(test_data, 2)\n    self.assertEqual(test_data.label_map, label_map)\n\n    self.assertTrue(os.path.isfile(test_data.annotations_json_file))\n    self.assertGreater(os.path.getsize(test_data.annotations_json_file), 0)\n    expected_json_file = test_util.get_test_data_path('test_annotations.json')\n    self.assertTrue(\n        filecmp.cmp(test_data.annotations_json_file, expected_json_file))\n\n    dataset = test_data.gen_dataset(model_spec, batch_size=1, is_training=False)\n    for i, (images, labels) in enumerate(dataset):\n      self.assertLess(i, 2)\n      images_shape = tf.shape(images).numpy()\n      expected_shape = np.array([1, *model_spec.config.image_size, 3])\n      self.assertTrue((images_shape == expected_shape).all())\n      self.assertLen(labels, 15)\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/object_detector_dataloader_util.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Utilities for Object Detection Dataloader.\"\"\"\n\nimport abc\nimport hashlib\nimport io\nimport json\nimport os\nimport tempfile\nfrom typing import Any, Collection, Dict, List, Sequence, Optional\n\nimport dataclasses\nfrom lxml import etree\nfrom PIL import JpegImagePlugin\nimport PIL.Image\n\nimport tensorflow as tf\nimport yaml\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.dataset import create_pascal_tfrecord\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.dataset import tfrecord_util\n\n# A workaround to avoid a JPEG image being identified as MPO.\nJpegImagePlugin._getmp = lambda: None  # pylint: disable=protected-access\n\n# Suffix of the annotations json file name and the meta data file name.\nANN_JSON_FILE_SUFFIX = '_annotations.json'\nMETA_DATA_FILE_SUFFIX = '_meta_data.yaml'\n\n\ndef _get_cache_dir_or_create(cache_dir: Optional[str]) -> str:\n  \"\"\"Gets the cache directory or creates it if not exists.\"\"\"\n  # TODO(b/183683348): Unifies with other tasks as well.\n  # If `cache_dir` is None, a temporary folder will be created and will not be\n  # removed automatically after training which makes it can be used later.\n  if cache_dir is None:\n    cache_dir = tempfile.mkdtemp()\n  if not tf.io.gfile.exists(cache_dir):\n    tf.io.gfile.makedirs(cache_dir)\n  return cache_dir\n\n\ndef _get_dir_basename(dirname):\n  \"\"\"Gets the base name of the directory.\"\"\"\n  return os.path.basename(os.path.abspath(dirname))\n\n\ndef get_cache_prefix_filename_from_pascal(images_dir: str,\n                                          annotations_dir: str,\n                                          annotation_filenames: Optional[\n                                              Collection[str]],\n                                          num_shards: int = 10) -> str:\n  \"\"\"Gets the prefix of cached files from PASCAL VOC data.\n\n  Args:\n    images_dir: Path to directory that store raw images.\n    annotations_dir: Path to the annotations directory.\n    annotation_filenames: Collection of annotation filenames (strings) to be\n      loaded. For instance, if there're 3 annotation files [0.xml, 1.xml, 2.xml]\n      in `annotations_dir`, setting annotation_filenames=['0', '1'] makes this\n      method only load [0.xml, 1.xml].\n    num_shards: Number of shards for output file.\n\n  Returns:\n    The prefix of cached files.\n  \"\"\"\n  hasher = hashlib.md5()\n  hasher.update(_get_dir_basename(images_dir).encode('utf-8'))\n  hasher.update(_get_dir_basename(annotations_dir).encode('utf-8'))\n  if annotation_filenames:\n    hasher.update(' '.join(sorted(annotation_filenames)).encode('utf-8'))\n  hasher.update(str(num_shards).encode('utf-8'))\n  return hasher.hexdigest()\n\n\ndef get_cache_prefix_filename_from_csv(csv_file: str, num_shards: int) -> str:\n  \"\"\"Gets the prefix of cached files from the csv file.\n\n  Args:\n    csv_file: Name of the csv file.\n    num_shards: Number of shards for output file.\n\n  Returns:\n    The prefix of cached files.\n  \"\"\"\n  hasher = hashlib.md5()\n  hasher.update(os.path.basename(csv_file).encode('utf-8'))\n  hasher.update(str(num_shards).encode('utf-8'))\n  return hasher.hexdigest()\n\n\n@dataclasses.dataclass(frozen=True)\nclass CacheFiles:\n  \"\"\"Cache files for object detection.\"\"\"\n  cache_prefix: str\n  tfrecord_files: Sequence[str]\n  meta_data_file: str\n  annotations_json_file: Optional[str]\n\n\ndef get_cache_files(cache_dir: Optional[str],\n                    cache_prefix_filename: str,\n                    num_shards: int = 10) -> CacheFiles:\n  \"\"\"Creates an object of CacheFiles class.\n\n  Args:\n    cache_dir: The cache directory to save TFRecord, metadata and json file.\n      When cache_dir is None, a temporary folder will be created and will not be\n      removed automatically after training which makes it can be used later.\n     cache_prefix_filename: The cache prefix filename.\n     num_shards: Number of shards for output file.\n\n  Returns:\n    An object of CacheFiles class.\n  \"\"\"\n  cache_dir = _get_cache_dir_or_create(cache_dir)\n  # The cache prefix including the cache directory and the cache prefix\n  # filename, e.g: '/tmp/cache/train'.\n  cache_prefix = os.path.join(cache_dir, cache_prefix_filename)\n  tf.compat.v1.logging.info(\n      'Cache will be stored in %s with prefix filename %s. Cache_prefix is %s' %\n      (cache_dir, cache_prefix_filename, cache_prefix))\n\n  # Cached files including the TFRecord files, the annotations json file and\n  # the meda data file.\n  tfrecord_files = [\n      cache_prefix + '-%05d-of-%05d.tfrecord' % (i, num_shards)\n      for i in range(num_shards)\n  ]\n  annotations_json_file = cache_prefix + ANN_JSON_FILE_SUFFIX\n  meta_data_file = cache_prefix + META_DATA_FILE_SUFFIX\n  return CacheFiles(\n      cache_prefix=cache_prefix,\n      tfrecord_files=tuple(tfrecord_files),\n      meta_data_file=meta_data_file,\n      annotations_json_file=annotations_json_file)\n\n\ndef is_cached(cache_files: CacheFiles) -> bool:\n  \"\"\"Checks whether cache files are already cached.\"\"\"\n  # annotations_json_file is optional, thus we don't check whether it is cached.\n  all_cached_files = list(\n      cache_files.tfrecord_files) + [cache_files.meta_data_file]\n  return all(tf.io.gfile.exists(path) for path in all_cached_files)\n\n\ndef is_all_cached(cache_files_collection: Collection[CacheFiles]) -> bool:\n  \"\"\"Checks whether a collection of cache files are all already cached.\"\"\"\n  return all(map(is_cached, cache_files_collection))\n\n\ndef get_cache_files_sequence(cache_dir: str, cache_prefix_filename: str,\n                             set_prefixes: Collection[str],\n                             num_shards: int) -> Sequence[CacheFiles]:\n  \"\"\"Gets a sequence of cache files.\n\n  Args:\n    cache_dir: The cache directory to save TFRecord, metadata and json file.\n      When cache_dir is None, a temporary folder will be created and will not be\n      removed automatically after training which makes it can be used later.\n      cache_prefix_filename: The cache prefix filename.\n    set_prefixes: Set prefix names for training, validation and test data. e.g.\n      ['TRAIN', 'VAL', 'TEST'].\n    num_shards: Number of shards for output file.\n\n  Returns:\n    A sequence of CachFiles objects mapping the set_prefixes.\n  \"\"\"\n  cache_files_list = []\n  for set_prefix in set_prefixes:\n    prefix_filename = set_prefix.lower() + '_' + cache_prefix_filename\n    cache_files = get_cache_files(cache_dir, prefix_filename, num_shards)\n    cache_files_list.append(cache_files)\n  return tuple(cache_files_list)\n\n\nclass CacheFilesWriter(abc.ABC):\n  \"\"\"CacheFilesWriter class to write the cached files.\"\"\"\n\n  def __init__(self,\n               label_map: Dict[int, str],\n               images_dir: Optional[str],\n               num_shards: int = 10,\n               max_num_images: Optional[int] = None,\n               ignore_difficult_instances: bool = False) -> None:\n    \"\"\"Initializes CacheFilesWriter for object detector.\n\n    Args:\n      label_map: Dict, map label integer ids to string label names such as {1:\n        'person', 2: 'notperson'}. 0 is the reserved key for `background` and\n          doesn't need to be included in `label_map`. Label names can't be\n          duplicated.\n      images_dir: Path to directory that store raw images. If None, the image\n        path is the path to Google Cloud Storage or the absolute path in the\n        local machine.\n      num_shards: Number of shards for the output file.\n      max_num_images: Max number of images to process. If None, process all the\n        images.\n      ignore_difficult_instances: Whether to ignore difficult instances.\n        `difficult` can be set inside `object` item in the annotation xml file.\n    \"\"\"\n    self.label_map = label_map\n    self.images_dir = images_dir if images_dir else ''\n    self.num_shards = num_shards\n    self.max_num_images = max_num_images\n    self.ignore_difficult_instances = ignore_difficult_instances\n    self.unique_id = create_pascal_tfrecord.UniqueId()\n\n    self.label_name2id_dict = {'background': 0}\n    for idx, name in self.label_map.items():\n      self.label_name2id_dict[name] = idx\n\n  def write_files(self, cache_files: CacheFiles, *args, **kwargs) -> None:\n    \"\"\"Writes TFRecord, meta_data and annotations json files.\n\n    Args:\n      cache_files: CacheFiles object including a list of TFRecord files, the\n        annotations json file with COCO data format containing golden bounding\n        boxes and the meda data yaml file to save the meta_data including data\n        size and label_map.\n      *args: Non-keyword of parameters used in the `_get_xml_dict` method.\n      **kwargs: Keyword parameters used in the `_get_xml_dict` method.\n    \"\"\"\n    writers = [\n        tf.io.TFRecordWriter(path) for path in cache_files.tfrecord_files\n    ]\n\n    ann_json_dict = {'images': [], 'annotations': [], 'categories': []}\n    for class_id, class_name in self.label_map.items():\n      c = {'supercategory': 'none', 'id': class_id, 'name': class_name}\n      ann_json_dict['categories'].append(c)\n\n    # Writes tf.Example into TFRecord files.\n    size = 0\n    for idx, xml_dict in enumerate(self._get_xml_dict(*args, **kwargs)):\n      if self.max_num_images and idx >= self.max_num_images:\n        break\n      if idx % 100 == 0:\n        tf.compat.v1.logging.info('On image %d' % idx)\n      tf_example = create_pascal_tfrecord.dict_to_tf_example(\n          xml_dict,\n          self.images_dir,\n          self.label_name2id_dict,\n          self.unique_id,\n          ignore_difficult_instances=self.ignore_difficult_instances,\n          ann_json_dict=ann_json_dict)\n      writers[idx % self.num_shards].write(tf_example.SerializeToString())\n      size = idx + 1\n\n    for writer in writers:\n      writer.close()\n\n    # Writes meta_data into meta_data_file.\n    meta_data = {'size': size, 'label_map': self.label_map}\n    with tf.io.gfile.GFile(cache_files.meta_data_file, 'w') as f:\n      yaml.dump(meta_data, f)\n\n    # Writes ann_json_dict into annotations_json_file.\n    with tf.io.gfile.GFile(cache_files.annotations_json_file, 'w') as f:\n      json.dump(ann_json_dict, f, indent=2)\n\n  @abc.abstractmethod\n  def _get_xml_dict(self, *args, **kwargs) -> tf.train.Example:\n    \"\"\"Gets the dict holding PASCAL XML fields one by one.\"\"\"\n    raise NotImplementedError\n\n\nclass PascalVocCacheFilesWriter(CacheFilesWriter):\n  \"\"\"CacheFilesWriter class to write the cached files for Pascal Voc data.\"\"\"\n\n  def _get_xml_dict(\n      self,\n      annotations_dir: str,\n      annotation_filenames: Optional[List[str]] = None) -> tf.train.Example:\n    \"\"\"Gets the tf example one by one from data with Pascal Voc format.\n\n    Args:\n      annotations_dir: Path to the annotations directory.\n      annotation_filenames: Collection of annotation filenames (strings) to be\n        loaded. For instance, if there're 3 annotation files [0.xml, 1.xml,\n        2.xml] in `annotations_dir`, setting annotation_filenames=['0', '1']\n        makes this method only load [0.xml, 1.xml].\n\n    Yields:\n      tf.train.Example\n    \"\"\"\n    # Gets the paths to annotations.\n    if annotation_filenames:\n      ann_path_list = [\n          os.path.join(annotations_dir, annotation + '.xml')\n          for annotation in annotation_filenames\n      ]\n    else:\n      ann_path_list = list(tf.io.gfile.glob(annotations_dir + r'/*.xml'))\n\n    for ann_path in ann_path_list:\n      with tf.io.gfile.GFile(ann_path, 'r') as fid:\n        xml_str = fid.read()\n      xml = etree.fromstring(xml_str)\n      xml_dict = tfrecord_util.recursive_parse_xml_to_dict(xml)['annotation']\n      yield xml_dict\n\n\ndef _get_xml_dict_from_csv_lines(images_dir: str, image_filename: str,\n                                 lines: List[List[str]]) -> Dict[str, Any]:\n  \"\"\"Gets dict holding PASCAL VOC XML fields from the csv lines for an image.\"\"\"\n  image_path = os.path.join(images_dir, image_filename)\n  with tf.io.gfile.GFile(image_path, 'rb') as fid:\n    encoded_jpg = fid.read()\n  encoded_jpg_io = io.BytesIO(encoded_jpg)\n  image = PIL.Image.open(encoded_jpg_io)\n  width, height = image.size\n\n  xml_dict = {\n      'size': {\n          'width': width,\n          'height': height\n      },\n      'filename': image_filename,\n      'object': [],\n  }\n\n  for line in lines:\n    label = line[2].strip()\n    xmin, ymin = float(line[3]) * width, float(line[4]) * height\n    xmax, ymax = float(line[7]) * width, float(line[8]) * height\n    obj = {\n        'name': label,\n        'bndbox': {\n            'xmin': xmin,\n            'ymin': ymin,\n            'xmax': xmax,\n            'ymax': ymax,\n            'name': label,\n        },\n        'difficult': 0,\n        'truncated': 0,\n        'pose': 'Unspecified',\n    }\n    xml_dict['object'].append(obj)\n\n  return xml_dict\n\n\nclass CsvCacheFilesWriter(CacheFilesWriter):\n  \"\"\"DataLoader utilities to write the cached files for the csv file.\"\"\"\n\n  def _get_xml_dict(self, csv_lines: List[List[str]]) -> tf.train.Example:\n    \"\"\"Gets the tf example one by one from data with Pascal Voc format.\n\n    Args:\n      csv_lines: Lines in the csv files.\n\n    Yields:\n      tf.train.Example\n    \"\"\"\n    image_dict = {}\n    # Groups the line by image_path.\n    for line in csv_lines:\n      image_filename = line[1].strip()\n      if image_filename not in image_dict:\n        image_dict[image_filename] = []\n      image_dict[image_filename].append(line)\n\n    for image_filename, lines in image_dict.items():\n      # Converts csv_lines for a certain image to dict holding PASCAL VOC XML\n      # fields.\n      xml_dict = _get_xml_dict_from_csv_lines(self.images_dir, image_filename,\n                                              lines)\n      yield xml_dict\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/object_detector_dataloader_util_test.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport csv\nimport filecmp\nimport os\n\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core import test_util\nfrom tensorflow_examples.lite.model_maker.core.data_util import object_detector_dataloader_util as dataloader_util\nimport yaml\n\n\nclass CacheFilesTest(tf.test.TestCase):\n\n  def test_get_cache_files(self):\n    cache_files = dataloader_util.get_cache_files(\n        cache_dir='/tmp/', cache_prefix_filename='train', num_shards=1)\n    self.assertEqual(cache_files.cache_prefix, '/tmp/train')\n    self.assertLen(cache_files.tfrecord_files, 1)\n    self.assertEqual(cache_files.tfrecord_files[0],\n                     '/tmp/train-00000-of-00001.tfrecord')\n    self.assertEqual(cache_files.meta_data_file, '/tmp/train_meta_data.yaml')\n    self.assertEqual(cache_files.annotations_json_file,\n                     '/tmp/train_annotations.json')\n\n  def test_filename_from_pascal(self):\n    # Checks the filenames are not equal if any of the parameters is changed.\n    images_dir = '/tmp/images/'\n    annotations_dir = '/tmp/annotations/'\n    annotation_filenames = None\n    num_shards = 1\n    filename = dataloader_util.get_cache_prefix_filename_from_pascal(\n        images_dir=images_dir,\n        annotations_dir=annotations_dir,\n        annotation_filenames=annotation_filenames,\n        num_shards=num_shards)\n\n    images_dir = '/tmp/images_1/'\n    filename1 = dataloader_util.get_cache_prefix_filename_from_pascal(\n        images_dir=images_dir,\n        annotations_dir=annotations_dir,\n        annotation_filenames=annotation_filenames,\n        num_shards=num_shards)\n    self.assertNotEqual(filename, filename1)\n\n    annotations_dir = '/tmp/annotations_2/'\n    filename2 = dataloader_util.get_cache_prefix_filename_from_pascal(\n        images_dir=images_dir,\n        annotations_dir=annotations_dir,\n        annotation_filenames=annotation_filenames,\n        num_shards=num_shards)\n    self.assertNotEqual(filename1, filename2)\n\n    annotation_filenames = ['1', '2']\n    filename3 = dataloader_util.get_cache_prefix_filename_from_pascal(\n        images_dir=images_dir,\n        annotations_dir=annotations_dir,\n        annotation_filenames=annotation_filenames,\n        num_shards=num_shards)\n    self.assertNotEqual(filename2, filename3)\n\n    num_shards = 10\n    filename4 = dataloader_util.get_cache_prefix_filename_from_pascal(\n        images_dir=images_dir,\n        annotations_dir=annotations_dir,\n        annotation_filenames=annotation_filenames,\n        num_shards=num_shards)\n    self.assertNotEqual(filename3, filename4)\n\n  def test_filename_from_csv(self):\n    # Checks the filenames are not equal if any of the parameters is changed.\n    csv_file = '/tmp/1.csv'\n    num_shards = 1\n    filename = dataloader_util.get_cache_prefix_filename_from_csv(\n        csv_file, num_shards)\n\n    csv_file = '/tmp/2.csv'\n    filename1 = dataloader_util.get_cache_prefix_filename_from_csv(\n        csv_file, num_shards)\n    self.assertNotEqual(filename, filename1)\n\n    num_shards = 10\n    filename2 = dataloader_util.get_cache_prefix_filename_from_csv(\n        csv_file, num_shards)\n    self.assertNotEqual(filename1, filename2)\n\n\nclass CacheFilesWriterTest(tf.test.TestCase):\n\n  def test_pascal_voc_cache_writer(self):\n    images_dir, annotations_dir, label_map = test_util.create_pascal_voc(\n        self.get_temp_dir())\n\n    cache_writer = dataloader_util.PascalVocCacheFilesWriter(\n        label_map, images_dir, num_shards=1)\n\n    cache_files = dataloader_util.get_cache_files(\n        cache_dir=self.get_temp_dir(), cache_prefix_filename='pascal')\n    cache_writer.write_files(cache_files, annotations_dir)\n\n    # Checks the TFRecord file.\n    tfrecord_files = cache_files.tfrecord_files\n    self.assertTrue(os.path.isfile(tfrecord_files[0]))\n    self.assertGreater(os.path.getsize(tfrecord_files[0]), 0)\n\n    # Checks the annotation json file.\n    annotations_json_file = cache_files.annotations_json_file\n    self.assertTrue(os.path.isfile(annotations_json_file))\n    self.assertGreater(os.path.getsize(annotations_json_file), 0)\n    expected_json_file = test_util.get_test_data_path('annotations.json')\n    self.assertTrue(filecmp.cmp(annotations_json_file, expected_json_file))\n\n    # Checks the meta_data file.\n    meta_data_file = cache_files.meta_data_file\n    self.assertTrue(os.path.isfile(meta_data_file))\n    self.assertGreater(os.path.getsize(meta_data_file), 0)\n    with tf.io.gfile.GFile(meta_data_file, 'r') as f:\n      meta_data_dict = yaml.load(f, Loader=yaml.FullLoader)\n      self.assertEqual(meta_data_dict['size'], 1)\n      self.assertEqual(meta_data_dict['label_map'], label_map)\n\n    # Checks xml_dict from `_get_xml_dict` function.\n    xml_dict = next(cache_writer._get_xml_dict(annotations_dir))\n    expected_xml_dict = {\n        'filename': '2012_12.jpg',\n        'folder': '',\n        'object': [{\n            'difficult': '1',\n            'bndbox': {\n                'xmin': '64',\n                'ymin': '64',\n                'xmax': '192',\n                'ymax': '192',\n            },\n            'name': 'person',\n            'truncated': '0',\n            'pose': '',\n        }],\n        'size': {\n            'width': '256',\n            'height': '256',\n        }\n    }\n    self.assertEqual(xml_dict, expected_xml_dict)\n\n  def test_csv_cache_writer(self):\n    label_map = {1: 'Baked Goods', 2: 'Cheese', 3: 'Salad'}\n    images_dir = test_util.get_test_data_path('salad_images')\n    cache_writer = dataloader_util.CsvCacheFilesWriter(\n        label_map, images_dir, num_shards=1)\n\n    csv_file = test_util.get_test_data_path('salads_ml_use.csv')\n    for set_name, size in [('TRAIN', 1), ('TEST', 2)]:\n      with tf.io.gfile.GFile(csv_file, 'r') as f:\n        lines = [line for line in csv.reader(f) if line[0].startswith(set_name)]\n\n      cache_files = dataloader_util.get_cache_files(\n          cache_dir=self.get_temp_dir(), cache_prefix_filename='csv')\n      cache_writer.write_files(cache_files, lines)\n\n      # Checks the TFRecord file.\n      tfrecord_files = cache_files.tfrecord_files\n      self.assertTrue(os.path.isfile(tfrecord_files[0]))\n      self.assertGreater(os.path.getsize(tfrecord_files[0]), 0)\n\n      # Checks the annotation json file.\n      annotations_json_file = cache_files.annotations_json_file\n      self.assertTrue(os.path.isfile(annotations_json_file))\n      self.assertGreater(os.path.getsize(annotations_json_file), 0)\n      expected_json_file = test_util.get_test_data_path(set_name.lower() +\n                                                        '_annotations.json')\n      self.assertTrue(filecmp.cmp(annotations_json_file, expected_json_file))\n\n      # Checks the meta_data file.\n      meta_data_file = cache_files.meta_data_file\n      self.assertTrue(os.path.isfile(meta_data_file))\n      self.assertGreater(os.path.getsize(meta_data_file), 0)\n      with tf.io.gfile.GFile(meta_data_file, 'r') as f:\n        meta_data_dict = yaml.load(f, Loader=yaml.FullLoader)\n        self.assertEqual(meta_data_dict['size'], size)\n        self.assertEqual(meta_data_dict['label_map'], label_map)\n\n    # Checks xml_dict from `_get_xml_dict` function.\n    xml_dict = next(cache_writer._get_xml_dict(lines))\n    expected_xml_dict = {\n        'filename': '279324025_3e74a32a84_o.jpg',\n        'object': [{\n            'name': 'Baked Goods',\n            'bndbox': {\n                'xmin': 9.1888,\n                'ymin': 101.982,\n                'xmax': 908.0176,\n                'ymax': 882.8832,\n                'name': 'Baked Goods',\n            },\n            'difficult': 0,\n            'truncated': 0,\n            'pose': 'Unspecified',\n        }],\n        'size': {\n            'width': 1600,\n            'height': 1200,\n        }\n    }\n    self.assertEqual(xml_dict, expected_xml_dict)\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/recommendation_config.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Recommendation dataloader class.\"\"\"\n\nfrom tensorflow_examples.lite.model_maker.core.api import mm_export\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.configs import input_config_pb2\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.configs import model_config\n\n# Shortcut for classes.\nModelHParams = model_config.ModelConfig\nmm_export('recommendation.spec.ModelHParams').export_constant(\n    __name__, 'ModelHParams')\n\nInputSpec = input_config_pb2.InputConfig\nFeature = input_config_pb2.Feature\nFeatureGroup = input_config_pb2.FeatureGroup\nFeatureType = input_config_pb2.FeatureType\nEncoderType = input_config_pb2.EncoderType\n\nmm_export('recommendation.spec.InputSpec').export_constant(\n    __name__, 'InputSpec')\nmm_export('recommendation.spec.Feature').export_constant(__name__, 'Feature')\nmm_export('recommendation.spec.FeatureGroup').export_constant(\n    __name__, 'FeatureGroup')\n\nEncoderType.__doc__ = 'EncoderType Enum (valid: BOW, CNN, LSTM).'\nmm_export('recommendation.spec.EncoderType').export_constant(\n    __name__, 'EncoderType')\n\nFeatureType.__doc__ = 'FeatureType Enum (valid: STRING, INT, FLOAT).'\nmm_export('recommendation.spec.FeatureType').export_constant(\n    __name__, 'FeatureType')\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/recommendation_dataloader.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Recommendation dataloader class.\"\"\"\n\nimport collections\nimport functools\nimport json\nimport os\n\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.core import file_util\nfrom tensorflow_examples.lite.model_maker.core.api import mm_export\nfrom tensorflow_examples.lite.model_maker.core.data_util import dataloader\nfrom tensorflow_examples.lite.model_maker.core.data_util import recommendation_config\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.data import example_generation_movielens as _gen\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import input_pipeline\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import utils\n\n\n@mm_export('recommendation.DataLoader')\nclass RecommendationDataLoader(dataloader.DataLoader):\n  \"\"\"Recommendation data loader.\"\"\"\n\n  def __init__(self, dataset, size, vocab):\n    \"\"\"Init data loader.\n\n    Dataset is tf.data.Dataset of examples, containing:\n      for inputs:\n      - 'context': int64[], context ids as the input of variable length.\n      for outputs:\n      - 'label': int64[1], label id to predict.\n    where context is controlled by `max_context_length` in generating examples.\n\n    The vocab should be a dict maps `id` to `item`, where:\n    - id: int\n    - item: a vocab entry. For example, for movielens, the item is a dict:\n        {'id': int, 'title': str, 'genres': list[str], 'count': int}\n\n    Args:\n      dataset: tf.data.Dataset for recommendation.\n      size: int, dataset size.\n      vocab: list of dict, each vocab item is described above.\n    \"\"\"\n    super(RecommendationDataLoader, self).__init__(dataset, size)\n    if not isinstance(vocab, dict):\n      raise ValueError('Expect vocab to be a dict, but got: {}'.format(vocab))\n    self.vocab = vocab\n    self.max_vocab_id = max(self.vocab.keys())  # The max id in the vocab.\n\n  def gen_dataset(self,\n                  batch_size=1,\n                  is_training=False,\n                  shuffle=False,\n                  input_pipeline_context=None,\n                  preprocess=None,\n                  drop_remainder=True,\n                  total_steps=None):\n    \"\"\"Generates dataset, and overwrites default drop_remainder = True.\"\"\"\n    ds = super(RecommendationDataLoader, self).gen_dataset(\n        batch_size=batch_size,\n        is_training=is_training,\n        shuffle=shuffle,\n        input_pipeline_context=input_pipeline_context,\n        preprocess=preprocess,\n        drop_remainder=drop_remainder,\n    )\n    # TODO(tianlin): Consider to move the num_batches below to the super class.\n    # Calculate steps by train data, if it is not set.\n    if total_steps:\n      num_batches = total_steps\n    else:\n      num_batches = self._size // batch_size\n    return ds.take(num_batches)\n\n  def split(self, fraction):\n    return self._split(fraction, self.vocab)\n\n  @classmethod\n  def load_vocab(cls, vocab_file) -> collections.OrderedDict:\n    \"\"\"Loads vocab from file.\n\n    The vocab file should be json format of: a list of list[size=4], where the 4\n    elements are ordered as:\n      [id=int, title=str, genres=str joined with '|', count=int]\n    It is generated when preparing movielens dataset.\n\n    Args:\n      vocab_file: str, path to vocab file.\n\n    Returns:\n      vocab: an OrderedDict maps id to item. Each item represents a movie\n         {\n           'id': int,\n           'title': str,\n           'genres': list[str],\n           'count': int,\n         }\n    \"\"\"\n    with tf.io.gfile.GFile(vocab_file) as f:\n      vocab_json = json.load(f)\n      vocab = collections.OrderedDict()\n      for v in vocab_json:\n        item = {\n            'id': int(v[0]),\n            'title': v[1],\n            'genres': v[2].split('|'),\n            'count': int(v[3]),\n        }\n        vocab[item['id']] = item\n      return vocab\n\n  @classmethod\n  def _read_dataset(cls, data_filepattern: str,\n                    input_spec: recommendation_config.InputSpec,\n                    vocab_file_dir: str) -> tf.data.Dataset:\n    features_and_vocabs_by_name = input_pipeline.get_features_and_vocabs_by_name(\n        input_spec, vocab_file_dir)\n    if not input_spec.HasField('label_feature'):\n      raise ValueError('Field label_feature is required.')\n    input_files = utils.GetShardFilenames(data_filepattern)\n    d = tf.data.TFRecordDataset(input_files)\n    d = d.shuffle(len(input_files))\n    decode_fn = functools.partial(\n        input_pipeline.decode_example,\n        features_and_vocabs_by_name=features_and_vocabs_by_name,\n        label_feature_name=input_spec.label_feature.feature_name)\n    return d.map(decode_fn, num_parallel_calls=tf.data.AUTOTUNE)\n\n  @classmethod\n  def download_and_extract_movielens(cls, download_dir):\n    \"\"\"Downloads and extracts movielens dataset, then returns extracted dir.\"\"\"\n    return _gen.download_and_extract_data(download_dir)\n\n  @classmethod\n  def generate_movielens_dataset(\n      cls,\n      data_dir,\n      generated_examples_dir=None,\n      train_filename='train_movielens_1m.tfrecord',\n      test_filename='test_movielens_1m.tfrecord',\n      vocab_filename='movie_vocab.json',\n      meta_filename='meta.json',\n      min_timeline_length=3,\n      max_context_length=10,\n      max_context_movie_genre_length=10,\n      min_rating=None,\n      train_data_fraction=0.9,\n      build_vocabs=True,\n  ):\n    \"\"\"Generate movielens dataset, and returns a dict contains meta.\n\n    Args:\n      data_dir: str, path to dataset containing (unzipped) text data.\n      generated_examples_dir: str, path to generate preprocessed examples.\n        (default: same as data_dir)\n      train_filename: str, generated file name for training data.\n      test_filename: str, generated file name for test data.\n      vocab_filename: str, generated file name for vocab data.\n      meta_filename: str, generated file name for meta data.\n      min_timeline_length: int, min timeline length to split train/eval set.\n      max_context_length: int, max context length as one input.\n      max_context_movie_genre_length: int, max context length of movie genre as\n        one input.\n      min_rating: int or None, include examples with min rating.\n      train_data_fraction: float, percentage of training data [0.0, 1.0].\n      build_vocabs: boolean, whether to build vocabs.\n\n    Returns:\n      Dict, metadata for the movielens dataset. Containing keys:\n        `train_file`, `train_size`, `test_file`, `test_size`, vocab_file`,\n        `vocab_size`, etc.\n    \"\"\"\n    if not generated_examples_dir:\n      # By default, set generated examples dir to data_dir\n      generated_examples_dir = data_dir\n    train_file = os.path.join(generated_examples_dir, train_filename)\n    test_file = os.path.join(generated_examples_dir, test_filename)\n    meta_file = os.path.join(generated_examples_dir, meta_filename)\n    # Create dataset and meta, only if they are not existed.\n    if not all([os.path.exists(f) for f in (train_file, test_file, meta_file)]):\n      stats = _gen.generate_datasets(\n          data_dir,\n          output_dir=generated_examples_dir,\n          min_timeline_length=min_timeline_length,\n          max_context_length=max_context_length,\n          max_context_movie_genre_length=max_context_movie_genre_length,\n          min_rating=min_rating,\n          build_vocabs=build_vocabs,\n          train_data_fraction=train_data_fraction,\n          train_filename=train_filename,\n          test_filename=test_filename,\n          vocab_filename=vocab_filename,\n      )\n      file_util.write_json_file(meta_file, stats)\n    meta = file_util.load_json_file(meta_file)\n    return meta\n\n  @classmethod\n  def get_num_classes(cls, meta) -> int:\n    \"\"\"Gets number of classes.\n\n    0 is reserved. Number of classes is Max Id + 1, e.g., if Max Id = 100,\n    then classes are [0, 100], that is 101 classes in total.\n\n    Args:\n      meta: dict, containing meta['vocab_max_id'].\n\n    Returns:\n      Number of classes.\n    \"\"\"\n    return meta['vocab_max_id'] + 1\n\n  @classmethod\n  def from_movielens(cls,\n                     data_dir,\n                     data_tag,\n                     input_spec: recommendation_config.InputSpec,\n                     generated_examples_dir=None,\n                     min_timeline_length=3,\n                     max_context_length=10,\n                     max_context_movie_genre_length=10,\n                     min_rating=None,\n                     train_data_fraction=0.9,\n                     build_vocabs=True,\n                     train_filename='train_movielens_1m.tfrecord',\n                     test_filename='test_movielens_1m.tfrecord',\n                     vocab_filename='movie_vocab.json',\n                     meta_filename='meta.json'):\n    \"\"\"Generates data loader from movielens dataset.\n\n    The method downloads and prepares dataset, then generates for train/eval.\n\n    For `movielens` data format, see:\n    - function `_generate_fake_data` in `recommendation_testutil.py`\n    - Or, zip file: http://files.grouplens.org/datasets/movielens/ml-1m.zip\n\n    Args:\n      data_dir: str, path to dataset containing (unzipped) text data.\n      data_tag: str, specify dataset in {'train', 'test'}.\n      input_spec: InputSpec, specify data format for input and embedding.\n      generated_examples_dir: str, path to generate preprocessed examples.\n        (default: same as data_dir)\n      min_timeline_length: int, min timeline length to split train/eval set.\n      max_context_length: int, max context length as one input.\n      max_context_movie_genre_length: int, max context length of movie genre as\n        one input.\n      min_rating: int or None, include examples with min rating.\n      train_data_fraction: float, percentage of training data [0.0, 1.0].\n      build_vocabs: boolean, whether to build vocabs.\n      train_filename: str, generated file name for training data.\n      test_filename: str, generated file name for test data.\n      vocab_filename: str, generated file name for vocab data.\n      meta_filename: str, generated file name for meta data.\n\n    Returns:\n      Data Loader.\n    \"\"\"\n    if data_tag not in ('train', 'test'):\n      raise ValueError(\n          'Expected data_tag is train or test, but got {}'.format(data_tag))\n    if not generated_examples_dir:\n      # By default, set generated examples dir to data_dir\n      generated_examples_dir = data_dir\n    meta = cls.generate_movielens_dataset(\n        data_dir,\n        generated_examples_dir,\n        train_filename=train_filename,\n        test_filename=test_filename,\n        vocab_filename=vocab_filename,\n        meta_filename=meta_filename,\n        min_timeline_length=min_timeline_length,\n        max_context_length=max_context_length,\n        max_context_movie_genre_length=max_context_movie_genre_length,\n        min_rating=min_rating,\n        train_data_fraction=train_data_fraction,\n        build_vocabs=build_vocabs,\n    )\n    vocab = cls.load_vocab(meta['vocab_file'])\n    if data_tag == 'train':\n      ds = cls._read_dataset(meta['train_file'], input_spec,\n                             generated_examples_dir)\n      return cls(ds, meta['train_size'], vocab)\n    elif data_tag == 'test':\n      ds = cls._read_dataset(meta['test_file'], input_spec,\n                             generated_examples_dir)\n      return cls(ds, meta['test_size'], vocab)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/recommendation_dataloader_test.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Tests for Recommendation dataloader.\"\"\"\nimport collections\nimport os\n\nimport tensorflow.compat.v2 as tf\nfrom tensorflow_examples.lite.model_maker.core.data_util import recommendation_dataloader as _dl\nfrom tensorflow_examples.lite.model_maker.core.data_util import recommendation_testutil as _testutil\n\n\nclass RecommendationDataLoaderTest(tf.test.TestCase):\n\n  def setUp(self):\n    super().setUp()\n    _testutil.setup_fake_testdata(self)\n    self.input_spec = _testutil.get_input_spec()\n\n  def test_download_and_extract_data(self):\n    with _testutil.patch_download_and_extract_data(self.dataset_dir) as fn:\n      out_dir = _dl.RecommendationDataLoader.download_and_extract_movielens(\n          self.download_dir)\n      fn.assert_called_once_with(self.download_dir)\n      self.assertEqual(out_dir, self.dataset_dir)\n\n  def test_generate_movielens_dataset(self):\n    loader = _dl.RecommendationDataLoader\n    gen_dir = os.path.join(self.dataset_dir, 'generated_examples')\n    stats = loader.generate_movielens_dataset(self.dataset_dir, gen_dir,\n                                              'train.tfrecord', 'test.tfrecord',\n                                              'movie_vocab.json', 'meta.json')\n    expected = {\n        'train_file': os.path.join(gen_dir, 'train.tfrecord'),\n        'test_file': os.path.join(gen_dir, 'test.tfrecord'),\n        'vocab_file': os.path.join(gen_dir, 'movie_vocab.json'),\n        'train_size': _testutil.TRAIN_SIZE,\n        'test_size': _testutil.TEST_SIZE,\n        'vocab_size': _testutil.VOCAB_SIZE,\n        'vocab_max_id': _testutil.MAX_ITEM_ID,\n    }\n    self.assertEqual(stats, {**stats, **expected})\n    self.assertTrue(os.path.exists(gen_dir))\n    self.assertGreater(len(os.listdir(gen_dir)), 0)\n\n    meta_file = os.path.join(gen_dir, 'meta.json')\n    self.assertTrue(os.path.exists(meta_file))\n\n    self.assertEqual(loader.get_num_classes(stats), _testutil.MAX_ITEM_ID + 1)\n\n  def test_from_movielens(self):\n    train_loader = _dl.RecommendationDataLoader.from_movielens(\n        self.dataset_dir, 'train', self.input_spec)\n    test_loader = _dl.RecommendationDataLoader.from_movielens(\n        self.dataset_dir, 'test', self.input_spec)\n\n    self.assertEqual(len(train_loader), _testutil.TRAIN_SIZE)\n    self.assertIsNotNone(train_loader._dataset)\n    self.assertIsInstance(train_loader.vocab, collections.OrderedDict)\n    self.assertEqual(len(train_loader.vocab), _testutil.VOCAB_SIZE)\n    self.assertEqual(train_loader.max_vocab_id, _testutil.MAX_ITEM_ID)\n\n    self.assertEqual(len(test_loader), _testutil.TEST_SIZE)\n    self.assertIsNotNone(test_loader._dataset)\n    self.assertEqual(len(test_loader.vocab), _testutil.VOCAB_SIZE)\n    self.assertIsInstance(test_loader.vocab, collections.OrderedDict)\n    self.assertEqual(test_loader.max_vocab_id, _testutil.MAX_ITEM_ID)\n\n  def test_split(self):\n    test_loader = _dl.RecommendationDataLoader.from_movielens(\n        self.dataset_dir, 'test', self.input_spec)\n    test0, test1 = test_loader.split(0.1)\n    expected_size0 = int(0.1 * _testutil.TEST_SIZE)\n    expected_size1 = _testutil.TEST_SIZE - expected_size0\n    self.assertEqual(len(test0), expected_size0)\n    self.assertIsNotNone(test0._dataset)\n\n    self.assertEqual(len(test1), expected_size1)\n    self.assertIsNotNone(test1._dataset)\n\n  def test_gen_dataset(self):\n    test_loader = _dl.RecommendationDataLoader.from_movielens(\n        self.dataset_dir, 'test', self.input_spec)\n    batch_size = 5\n    ds = test_loader.gen_dataset(batch_size, is_training=False)\n    self.assertIsInstance(ds, tf.data.Dataset)\n\n    example = iter(ds).next()\n    tf.compat.v1.logging.info('example: %s', example)\n    features, label = example\n    expected_keys_shapes = [\n        ('context_movie_id', [batch_size, 10], tf.int32),\n        ('label_movie_id', [batch_size, 1], tf.int32),\n    ]\n    for key, shape, dtype in expected_keys_shapes:\n      self.assertIn(key, features,\n                    'Expect key: {} in features {}'.format(key, features))\n      v = features[key]\n      self.assertListEqual(v.shape.as_list(), shape)\n      self.assertEqual(v.dtype, dtype)\n    self.assertListEqual(label.shape.as_list(), [batch_size, 1])\n    self.assertEqual(label.dtype, tf.int32)\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/recommendation_testutil.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Tests for Recommendation dataloader.\"\"\"\n\nimport os\nimport tempfile\nfrom unittest import mock\n\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.core.data_util import recommendation_config\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.data import example_generation_movielens as gen\n\nMOVIE_SIZE = 101\nRATING_SIZE = 5000\nUSER_SIZE = 50\n\nTRAIN_SIZE = 4455\nTEST_SIZE = 495\nVOCAB_SIZE = 101\nMAX_ITEM_ID = 999\n\n\ndef _generate_fake_data(data_dir):\n  \"\"\"Generates fake data to files.\n\n  It generates 3 files.\n  - movies.dat: movies data, with format per line:\n               MovieID::Title::Genres.\n  - users.dat: users data, with format per line:\n               UserID::Gender::Age::Occupation::Zip-code.\n  - ratings.dat: movie ratings by users, with format per line:\n               UserID::MovieID::Rating::Timestamp\n  It aligns with movielens dataset. IDs start from 1, and 0 is reserved for OOV.\n\n  Args:\n    data_dir: str, dir name to generate dataset.\n  \"\"\"\n  if not tf.io.gfile.exists(data_dir):\n    tf.io.gfile.makedirs(data_dir)\n\n  # Movies:\n  # MovieID::Title::Genres\n  movies = ['{i}::title{i}::genre1|genre2'.format(i=i) for i in range(1, 101)]\n  movies.append('999::title999::genere10')  # Add a movie with a larger id.\n  movie_file = os.path.join(data_dir, 'movies.dat')\n  _write_file_by_lines(movies, movie_file)\n\n  # Users:\n  # UserID::Gender::Age::Occupation::Zip-code\n  users = ['{user}::F::0::0::00000'.format(user=user) for user in range(1, 51)]\n  user_file = os.path.join(data_dir, 'users.dat')\n  _write_file_by_lines(users, user_file)\n\n  # Ratings:\n  # UserID::MovieID::Rating::Timestamp\n  ratings = []\n  for user in range(1, 51):\n    ratings += [\n        '{user}::{movie}::5::{timestamp}'.format(\n            user=user, movie=movie, timestamp=978000000 + 1)\n        for movie in range(1, 101)\n    ]\n  rating_file = os.path.join(data_dir, 'ratings.dat')\n  _write_file_by_lines(ratings, rating_file)\n\n\ndef _write_file_by_lines(data, filename):\n  \"\"\"Writes data to file line per line.\"\"\"\n  with tf.io.gfile.GFile(filename, 'w') as f:\n    for line in data:\n      f.write(str(line))\n      f.write('\\n')\n\n\ndef setup_fake_testdata(obj):\n  \"\"\"Setup fake testdata folder.\n\n  This function creates new attrs:\n  - test_tempdir: temporary dir (optional), if not exists.\n  - download_dir: datasets dir for downloaded zip.\n  - dataset_dir: extracted dir for movielens data.\n\n  Args:\n    obj: object, usually TestCase instance's self or cls.\n  \"\"\"\n  if not hasattr(obj, 'test_tempdir'):\n    obj.test_tempdir = tempfile.mkdtemp()\n  obj.download_dir = os.path.join(obj.test_tempdir, 'download')\n  obj.dataset_dir = os.path.join(obj.download_dir, 'fake_movielens')\n  _generate_fake_data(obj.dataset_dir)\n\n\ndef patch_download_and_extract_data(data_dir):\n  \"\"\"Patch download and extract data for testing.\n\n  The common usage is to generate data loader:\n\n  with patch_download_and_extract_data(movielens_dir):\n    train_loader = RecommendationDataLoader.from_movielens(\n        generated_dir, 'train', test_tempdir)\n\n  Args:\n    data_dir: str, path to data dir.\n\n  Returns:\n    mocked context.\n  \"\"\"\n\n  def side_effect(*args, **kwargs):\n    del args, kwargs\n    tf.compat.v1.logging.info('Use patched dataset dir: %s', data_dir)\n    return data_dir\n\n  return mock.patch.object(\n      gen, 'download_and_extract_data', side_effect=side_effect)\n\n\ndef get_input_spec(encoder_type='cnn') -> recommendation_config.InputSpec:\n  \"\"\"Gets input spec (for test).\n\n  Input spec defines how the input features are extracted.\n\n  Args:\n    encoder_type: str. Case-insensitive {'CNN', 'LSTM', 'BOW'}.\n\n  Returns:\n    InputSpec.\n  \"\"\"\n  etype = encoder_type.upper()\n  if etype not in {'CNN', 'LSTM', 'BOW'}:\n    raise ValueError('Not support encoder_type: {}'.format(etype))\n\n  return recommendation_config.InputSpec(\n      activity_feature_groups=[\n          # Group #1: defines how features are grouped in the first Group.\n          dict(\n              features=[\n                  # First feature.\n                  dict(\n                      feature_name='context_movie_id',  # Feature name\n                      feature_type='INT',  # Feature type\n                      vocab_size=3953,  # ID size (number of IDs)\n                      embedding_dim=8,  # Projected feature embedding dim\n                      feature_length=10,  # History length of 10.\n                  ),\n                  # Maybe more features...\n              ],\n              encoder_type='CNN',  # CNN encoder (e.g. CNN, LSTM, BOW)\n          ),\n          # Maybe more groups...\n      ],\n      label_feature=dict(\n          feature_name='label_movie_id',  # Label feature name\n          feature_type='INT',  # Label type\n          vocab_size=3953,  # Label size (number of classes)\n          embedding_dim=8,  # Label embedding demension\n          feature_length=1,  # Exactly 1 label\n      ),\n  )\n\n\ndef get_model_hparams() -> recommendation_config.ModelHParams:\n  \"\"\"Gets model hparams (for test).\n\n  ModelHParams defines the model architecture.\n\n  Returns:\n    ModelHParams.\n  \"\"\"\n  return recommendation_config.ModelHParams(\n      hidden_layer_dims=[32, 32],  # Hidden layers dimension.\n      eval_top_k=[1, 5],  # Eval top 1 and top 5.\n      conv_num_filter_ratios=[2, 4],  # For CNN encoder, conv filter mutipler.\n      conv_kernel_size=16,  # For CNN encoder, base kernel size.\n      lstm_num_units=16,  # For LSTM/RNN, num units.\n      num_predictions=10,  # Number of output predictions. Select top 10.\n  )\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/searcher_dataloader.py",
    "content": "# Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"DataLoader for Searcher task.\"\"\"\n\nfrom typing import AnyStr, List, Optional\n\nimport numpy as np\nfrom tensorflow_examples.lite.model_maker.core.api.api_util import mm_export\n\n\n@mm_export(\"searcher.DataLoader\")\nclass DataLoader(object):\n  \"\"\"Base DataLoader class for Searcher task.\"\"\"\n\n  def __init__(\n      self,\n      embedder_path: Optional[str] = None,\n      dataset: Optional[np.ndarray] = None,\n      metadata: Optional[List[AnyStr]] = None,\n  ) -> None:\n    \"\"\"Initializes DataLoader for Searcher task.\n\n    Args:\n      embedder_path: Path to the TFLite Embedder model file.\n      dataset: Embedding dataset used to build on-device ScaNN index file. The\n        dataset shape should be (dataset_size, embedding_dim). If None,\n        `dataset` will be generated from raw input data later.\n      metadata:  The metadata for each data in the dataset. The length of\n        `metadata` should be same as `dataset` and passed in the same order as\n        `dataset`. If `dataset` is set, `metadata` should be set as well.\n    \"\"\"\n    self._embedder_path = embedder_path\n\n    # Cache dataset list which can be concatenated to the single dataset. This\n    # is used since users may load the data several times, if each time the data\n    # are directly concatenated together, memory copy will be costed each time.\n    self._cache_dataset_list = []\n\n    if dataset is None:\n      # Sets dataset and metadata as empty. Will load them from raw input data\n      # later.\n      self._dataset = np.array([])\n      self._metadata = []\n    else:\n      # Directly sets dataset and metadata.\n      self._dataset = dataset\n      self._metadata = metadata\n\n  def __len__(self):\n    return len(self.dataset)\n\n  @property\n  def dataset(self) -> np.ndarray:\n    \"\"\"Gets the dataset.\n\n    Due to performance consideration, we don't return a copy, but the returned\n    `self._dataset` should never be changed.\n    \"\"\"\n    if self._cache_dataset_list:\n      # Concatenates the `self._dataset` and the datasets in\n      # `self._cache_dataset_list`.\n      if self._dataset.size > 0:\n        dataset_list = [self._dataset] + self._cache_dataset_list\n      else:\n        dataset_list = self._cache_dataset_list\n\n      self._dataset = np.vstack(dataset_list)\n      self._cache_dataset_list = []\n    return self._dataset\n\n  @property\n  def metadata(self) -> List[AnyStr]:\n    \"\"\"Gets the metadata.\"\"\"\n    return self._metadata\n\n  @property\n  def embedder_path(self) -> Optional[str]:\n    \"\"\"Gets the path to the TFLite Embedder model file.\"\"\"\n    return self._embedder_path\n\n  def append(self, data_loader: \"DataLoader\") -> None:\n    \"\"\"Appends the dataset.\n\n    Don't check if embedders from the two data loader are the same in this\n    function. Users are responsible to keep the embedder identical.\n\n    Args:\n      data_loader: The data loader in which the data will be appended.\n    \"\"\"\n    if self._dataset.shape[1] != data_loader.dataset.shape[1]:\n      raise ValueError(\"The embedding dimension must be the same.\")\n\n    # Appends the array.\n    self._cache_dataset_list.append(data_loader.dataset)\n    self._metadata = self._metadata + data_loader.metadata\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/searcher_dataloader_test.py",
    "content": "# Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Tests for searcher_dataloader.\"\"\"\n\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core.data_util import searcher_dataloader\nfrom tensorflow_lite_support.python.task.core.proto import base_options_pb2\nfrom tensorflow_lite_support.python.task.vision import image_embedder\nfrom tensorflow_examples.lite.model_maker.core import test_util\n\n_BaseOptions = base_options_pb2.BaseOptions\n_ImageEmbedder = image_embedder.ImageEmbedder\n_ImageEmbedderOptions = image_embedder.ImageEmbedderOptions\n_SearcherDataLoader = searcher_dataloader.DataLoader\n\n\nclass SearcherDataloaderTest(tf.test.TestCase):\n\n  def setUp(self):\n    super().setUp()\n    self.tflite_path = test_util.get_test_data_path(\n        \"mobilenet_v2_035_96_embedder_with_metadata.tflite\")\n\n  def test_concat_dataset(self):\n    dataset1 = np.random.rand(2, 1280)\n    dataset2 = np.random.rand(2, 1280)\n    dataset3 = np.random.rand(1, 1280)\n    metadata = [\"0\", \"1\", b\"\\x11\\x22\", b\"\\x33\\x44\", \"4\"]\n    data = _SearcherDataLoader(embedder_path=self.tflite_path)\n    data._dataset = dataset1\n    data._cache_dataset_list = [dataset2, dataset3]\n    data._metadata = metadata\n\n    self.assertTrue((data.dataset == np.vstack([dataset1, dataset2,\n                                                dataset3])).all())\n    self.assertEqual(data.metadata, metadata)\n\n  def test_append(self):\n    dataset1 = np.random.rand(4, 1280)\n    metadata1 = [\"0\", \"1\", b\"\\x11\\x22\", \"3\"]\n    data_loader1 = _SearcherDataLoader(embedder_path=self.tflite_path)\n    data_loader1._dataset = dataset1\n    data_loader1._metadata = metadata1\n\n    dataset2 = np.random.rand(2, 1280)\n    metadata2 = [b\"\\x33\\x44\", \"5\"]\n    data_loader2 = _SearcherDataLoader(embedder_path=self.tflite_path)\n    data_loader2._dataset = dataset2\n    data_loader2._metadata = metadata2\n\n    data_loader1.append(data_loader2)\n    self.assertEqual(data_loader1.dataset.shape, (6, 1280))\n    self.assertEqual(data_loader1.metadata,\n                     [\"0\", \"1\", b\"\\x11\\x22\", \"3\", b\"\\x33\\x44\", \"5\"])\n\n  def test_init(self):\n    # Initializes from embedder first and then generates the embedding dataset\n    # from raw input leverage embedder later.\n    data = _SearcherDataLoader(embedder_path=self.tflite_path)\n    self.assertEqual(data.dataset.shape, (0,))\n    self.assertEqual(data.metadata, [])\n\n    # Initializes directly from the dataset and metadata.\n    dataset = np.random.rand(2, 512)\n    metadata = [\"0\", b\"\\66\"]\n    data = _SearcherDataLoader(dataset=dataset, metadata=metadata)\n    self.assertTrue((data.dataset == dataset).all())\n    self.assertEqual(data.metadata, metadata)\n\n\nif __name__ == \"__main__\":\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/testdata/annotations/2012_12.xml",
    "content": "<annotation>\n  <folder></folder>\n  <filename>2012_12.jpg</filename>\n  <size>\n    <width>256</width>\n    <height>256</height>\n  </size>\n  <object>\n    <difficult>1</difficult>\n    <bndbox>\n      <xmin>64</xmin>\n      <ymin>64</ymin>\n      <xmax>192</xmax>\n      <ymax>192</ymax>\n    </bndbox>\n    <name>person</name>\n    <truncated>0</truncated>\n    <pose></pose>\n  </object>\n</annotation>\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/testdata/annotations.json",
    "content": "{\n  \"images\": [\n    {\n      \"file_name\": \"2012_12.jpg\",\n      \"height\": 256,\n      \"width\": 256,\n      \"id\": 1\n    }\n  ],\n  \"annotations\": [\n    {\n      \"area\": 16384,\n      \"iscrowd\": 0,\n      \"image_id\": 1,\n      \"bbox\": [\n        64,\n        64,\n        128,\n        128\n      ],\n      \"category_id\": 1,\n      \"id\": 1,\n      \"ignore\": 0,\n      \"segmentation\": []\n    }\n  ],\n  \"categories\": [\n    {\n      \"supercategory\": \"none\",\n      \"id\": 1,\n      \"name\": \"person\"\n    },\n    {\n      \"supercategory\": \"none\",\n      \"id\": 2,\n      \"name\": \"notperson\"\n    }\n  ]\n}"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/testdata/movies.csv",
    "content": "text,metadata\nGood fun movie with good action and good dialogue.,good movie\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/testdata/object_detection_csv/json_files/test_annotations.json",
    "content": "{\n  \"images\": [\n    {\n      \"file_name\": \"279324025_3e74a32a84_o.jpg\",\n      \"height\": 1200,\n      \"width\": 1600,\n      \"id\": 2\n    },\n    {\n      \"file_name\": \"3167707458_7b2eebed9e_o.jpg\",\n      \"height\": 2167,\n      \"width\": 2207,\n      \"id\": 3\n    }\n  ],\n  \"annotations\": [\n    {\n      \"area\": 702119,\n      \"iscrowd\": 0,\n      \"image_id\": 2,\n      \"bbox\": [\n        9,\n        101,\n        899,\n        781\n      ],\n      \"category_id\": 1,\n      \"id\": 3,\n      \"ignore\": 0,\n      \"segmentation\": []\n    },\n    {\n      \"area\": 125961,\n      \"iscrowd\": 0,\n      \"image_id\": 3,\n      \"bbox\": [\n        0,\n        0,\n        121,\n        1041\n      ],\n      \"category_id\": 2,\n      \"id\": 4,\n      \"ignore\": 0,\n      \"segmentation\": []\n    }\n  ],\n  \"categories\": [\n    {\n      \"supercategory\": \"none\",\n      \"id\": 1,\n      \"name\": \"Baked Goods\"\n    },\n    {\n      \"supercategory\": \"none\",\n      \"id\": 2,\n      \"name\": \"Cheese\"\n    },\n    {\n      \"supercategory\": \"none\",\n      \"id\": 3,\n      \"name\": \"Salad\"\n    }\n  ]\n}"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/testdata/object_detection_csv/json_files/train_annotations.json",
    "content": "{\n  \"images\": [\n    {\n      \"file_name\": \"8515203476_b575d9b2fc_o.jpg\",\n      \"height\": 612,\n      \"width\": 612,\n      \"id\": 1\n    }\n  ],\n  \"annotations\": [\n    {\n      \"area\": 13120,\n      \"iscrowd\": 0,\n      \"image_id\": 1,\n      \"bbox\": [\n        225,\n        233,\n        82,\n        160\n      ],\n      \"category_id\": 2,\n      \"id\": 1,\n      \"ignore\": 0,\n      \"segmentation\": []\n    },\n    {\n      \"area\": 342108,\n      \"iscrowd\": 0,\n      \"image_id\": 1,\n      \"bbox\": [\n        53,\n        0,\n        559,\n        612\n      ],\n      \"category_id\": 3,\n      \"id\": 2,\n      \"ignore\": 0,\n      \"segmentation\": []\n    }\n  ],\n  \"categories\": [\n    {\n      \"supercategory\": \"none\",\n      \"id\": 1,\n      \"name\": \"Baked Goods\"\n    },\n    {\n      \"supercategory\": \"none\",\n      \"id\": 2,\n      \"name\": \"Cheese\"\n    },\n    {\n      \"supercategory\": \"none\",\n      \"id\": 3,\n      \"name\": \"Salad\"\n    }\n  ]\n}"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/testdata/object_detection_csv/salads_ml_use.csv",
    "content": "TEST,279324025_3e74a32a84_o.jpg,Baked Goods,0.005743,0.084985,,,0.567511,0.735736,,\nTEST,3167707458_7b2eebed9e_o.jpg,Cheese,0.000000,0.000000,,,0.054865,0.480665,,\nTRAIN,8515203476_b575d9b2fc_o.jpg,Cheese,0.367681,0.381481,,,0.503247,0.643684,,\nTRAIN,8515203476_b575d9b2fc_o.jpg,Salad,0.086808,0.000000,,,1.000000,1.000000,,\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/testdata/squad_testdata/dev-v1.1.json",
    "content": "{\n    \"version\":\n        \"toy\",\n    \"data\": [{\n        \"title\":\n            \"test\",\n        \"paragraphs\": [{\n            \"context\":\n                \"This is frog.\",\n            \"qas\": [{\n                \"question\": \"What is this?\",\n                \"id\": \"first\",\n                \"answers\": [{\n                    \"answer_start\": 9,\n                    \"text\": \"rog\"\n                }]\n            }]\n        }]\n    }]\n}\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/testdata/squad_testdata/dev-v2.0.json",
    "content": "{\n  \"version\": \"v2.0\",\n  \"data\": [\n    {\n      \"title\": \"Normans\",\n      \"paragraphs\": [\n        {\n          \"qas\": [\n            {\n              \"question\": \"When were the Normans in Normandy?\",\n              \"id\": \"56ddde6b9a695914005b9629\",\n              \"answers\": [\n                {\n                  \"text\": \"10th and 11th centuries\",\n                  \"answer_start\": 94\n                }\n              ],\n              \"is_impossible\": false\n            },\n            {\n              \"plausible_answers\": [\n                {\n                  \"text\": \"Rollo\",\n                  \"answer_start\": 308\n                }\n              ],\n              \"question\": \"Who did King Charles III swear fealty to?\",\n              \"id\": \"5ad39d53604f3c001a3fe8d3\",\n              \"answers\": [],\n              \"is_impossible\": true\n            }\n          ],\n          \"context\": \"The Normans (Norman: Nourmands; French: Normands; Latin: Normanni) were the people who in the 10th and 11th centuries gave their name to Normandy, a region in France. They were descended from Norse (\\\"Norman\\\" comes from \\\"Norseman\\\") raiders and pirates from Denmark, Iceland and Norway who, under their leader Rollo, agreed to swear fealty to King Charles III of West Francia. Through generations of assimilation and mixing with the native Frankish and Roman-Gaulish populations, their descendants would gradually merge with the Carolingian-based cultures of West Francia. The distinct cultural and ethnic identity of the Normans emerged initially in the first half of the 10th century, and it continued to evolve over the succeeding centuries.\"\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/testdata/squad_testdata/train-v1.1.json",
    "content": "{\n    \"version\":\n        \"toy\",\n    \"data\": [{\n        \"title\":\n            \"test\",\n        \"paragraphs\": [{\n            \"context\":\n                \"This is test.\",\n            \"qas\": [{\n                \"question\": \"What is this?\",\n                \"id\": \"my_id\",\n                \"answers\": [{\n                    \"answer_start\": 8,\n                    \"text\": \"test\"\n                }]\n            }]\n        }]\n    }]\n}\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/testdata/squad_testdata/train-v2.0.json",
    "content": "{\n  \"version\": \"v2.0\",\n  \"data\": [\n    {\n      \"title\": \"Normans\",\n      \"paragraphs\": [\n        {\n          \"qas\": [\n            {\n              \"question\": \"When were the Normans in Normandy?\",\n              \"id\": \"56ddde6b9a695914005b9629\",\n              \"answers\": [\n                {\n                  \"text\": \"10th and 11th centuries\",\n                  \"answer_start\": 94\n                }\n              ],\n              \"is_impossible\": false\n            },\n            {\n              \"plausible_answers\": [\n                {\n                  \"text\": \"Rollo\",\n                  \"answer_start\": 308\n                }\n              ],\n              \"question\": \"Who did King Charles III swear fealty to?\",\n              \"id\": \"5ad39d53604f3c001a3fe8d3\",\n              \"answers\": [],\n              \"is_impossible\": true\n            }\n          ],\n          \"context\": \"The Normans (Norman: Nourmands; French: Normands; Latin: Normanni) were the people who in the 10th and 11th centuries gave their name to Normandy, a region in France. They were descended from Norse (\\\"Norman\\\" comes from \\\"Norseman\\\") raiders and pirates from Denmark, Iceland and Norway who, under their leader Rollo, agreed to swear fealty to King Charles III of West Francia. Through generations of assimilation and mixing with the native Frankish and Roman-Gaulish populations, their descendants would gradually merge with the Carolingian-based cultures of West Francia. The distinct cultural and ethnic identity of the Normans emerged initially in the first half of the 10th century, and it continued to evolve over the succeeding centuries.\"\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/testdata/trips.csv",
    "content": "text,metadata\nit's a charming and often affecting journey,charming trip\nwhat a great and fantastic trip,great trip\n\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/text_dataloader.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Text dataloader.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport csv\nimport hashlib\nimport os\nimport random\nimport tempfile\n\nfrom absl import logging\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core import file_util\nfrom tensorflow_examples.lite.model_maker.core.api import mm_export\nfrom tensorflow_examples.lite.model_maker.core.data_util import dataloader\nfrom tensorflow_examples.lite.model_maker.core.task import model_spec as ms\n\nfrom official.nlp.bert import input_pipeline\nfrom official.nlp.data import classifier_data_lib\nfrom official.nlp.data import squad_lib\n\n\ndef _load(tfrecord_file, meta_data_file, model_spec, is_training=None):\n  \"\"\"Loads data from tfrecord file and metada file.\"\"\"\n\n  if is_training is None:\n    name_to_features = model_spec.get_name_to_features()\n  else:\n    name_to_features = model_spec.get_name_to_features(is_training=is_training)\n\n  dataset = input_pipeline.single_file_dataset(tfrecord_file, name_to_features)\n  dataset = dataset.map(\n      model_spec.select_data_from_record, num_parallel_calls=tf.data.AUTOTUNE)\n\n  meta_data = file_util.load_json_file(meta_data_file)\n\n  logging.info(\n      'Load preprocessed data and metadata from %s and %s '\n      'with size: %d', tfrecord_file, meta_data_file, meta_data['size'])\n  return dataset, meta_data\n\n\ndef _get_cache_filenames(cache_dir, model_spec, data_name, is_training):\n  \"\"\"Gets cache tfrecord filename, metada filename and prefix of filenames.\"\"\"\n  hasher = hashlib.md5()\n  hasher.update(data_name.encode('utf-8'))\n  hasher.update(str(model_spec.get_config()).encode('utf-8'))\n  hasher.update(str(is_training).encode('utf-8'))\n  cache_prefix = os.path.join(cache_dir, hasher.hexdigest())\n  cache_tfrecord_file = cache_prefix + '.tfrecord'\n  cache_meta_data_file = cache_prefix + '_meta_data'\n\n  return cache_tfrecord_file, cache_meta_data_file, cache_prefix\n\n\ndef _get_cache_info(cache_dir, data_name, model_spec, is_training):\n  \"\"\"Gets cache related information: whether is cached, related filenames.\"\"\"\n  if cache_dir is None:\n    cache_dir = tempfile.mkdtemp()\n  tfrecord_file, meta_data_file, file_prefix = _get_cache_filenames(\n      cache_dir, model_spec, data_name, is_training)\n  is_cached = tf.io.gfile.exists(tfrecord_file) and tf.io.gfile.exists(\n      meta_data_file)\n\n  return is_cached, tfrecord_file, meta_data_file, file_prefix\n\n\n@mm_export('text_classifier.DataLoader')\nclass TextClassifierDataLoader(dataloader.ClassificationDataLoader):\n  \"\"\"DataLoader for text classifier.\"\"\"\n\n  @classmethod\n  def from_folder(cls,\n                  filename,\n                  model_spec='average_word_vec',\n                  is_training=True,\n                  class_labels=None,\n                  shuffle=True,\n                  cache_dir=None):\n    \"\"\"Loads text with labels and preproecess text according to `model_spec`.\n\n    Assume the text data of the same label are in the same subdirectory. each\n    file is one text.\n\n    Args:\n      filename: Name of the file.\n      model_spec: Specification for the model.\n      is_training: Whether the loaded data is for training or not.\n      class_labels: Class labels that should be considered. Name of the\n        subdirectory not in `class_labels` will be ignored. If None, all the\n        subdirectories will be considered.\n      shuffle: boolean, if shuffle, random shuffle data.\n      cache_dir: The cache directory to save preprocessed data. If None,\n        generates a temporary directory to cache preprocessed data.\n\n    Returns:\n      TextDataset containing text, labels and other related info.\n    \"\"\"\n    model_spec = ms.get(model_spec)\n    data_root = os.path.abspath(filename)\n    folder_name = os.path.basename(data_root)\n\n    is_cached, tfrecord_file, meta_data_file, vocab_file = cls._get_cache_info(\n        cache_dir, folder_name, model_spec, is_training)\n    # If cached, directly loads data from cache directory.\n    if is_cached:\n      return cls._load_data(tfrecord_file, meta_data_file, model_spec)\n\n    # Gets paths of all text.\n    if class_labels:\n      all_text_paths = []\n      for class_label in class_labels:\n        all_text_paths.extend(\n            list(\n                tf.io.gfile.glob(os.path.join(data_root, class_label) + r'/*')))\n    else:\n      all_text_paths = list(tf.io.gfile.glob(data_root + r'/*/*'))\n\n    all_text_size = len(all_text_paths)\n    if all_text_size == 0:\n      raise ValueError('Text size is zero')\n\n    if shuffle:\n      random.shuffle(all_text_paths)\n\n    # Gets label and its index.\n    if class_labels:\n      label_names = sorted(class_labels)\n    else:\n      label_names = sorted(\n          name for name in os.listdir(data_root)\n          if os.path.isdir(os.path.join(data_root, name)))\n\n    # Generates text examples from folder.\n    examples = []\n    for i, path in enumerate(all_text_paths):\n      with tf.io.gfile.GFile(path, 'r') as f:\n        text = f.read()\n      guid = '%s-%d' % (folder_name, i)\n      label = os.path.basename(os.path.dirname(path))\n      examples.append(classifier_data_lib.InputExample(guid, text, None, label))\n\n    # Saves preprocessed data and other assets into files.\n    cls._save_data(examples, model_spec, label_names, tfrecord_file,\n                   meta_data_file, vocab_file, is_training)\n\n    # Loads data from cache directory.\n    return cls._load_data(tfrecord_file, meta_data_file, model_spec)\n\n  @classmethod\n  def from_csv(cls,\n               filename,\n               text_column,\n               label_column,\n               fieldnames=None,\n               model_spec='average_word_vec',\n               is_training=True,\n               delimiter=',',\n               quotechar='\"',\n               shuffle=False,\n               cache_dir=None):\n    \"\"\"Loads text with labels from the csv file and preproecess text according to `model_spec`.\n\n    Args:\n      filename: Name of the file.\n      text_column: String, Column name for input text.\n      label_column: String, Column name for labels.\n      fieldnames: A sequence, used in csv.DictReader. If fieldnames is omitted,\n        the values in the first row of file f will be used as the fieldnames.\n      model_spec: Specification for the model.\n      is_training: Whether the loaded data is for training or not.\n      delimiter: Character used to separate fields.\n      quotechar: Character used to quote fields containing special characters.\n      shuffle: boolean, if shuffle, random shuffle data.\n      cache_dir: The cache directory to save preprocessed data. If None,\n        generates a temporary directory to cache preprocessed data.\n\n    Returns:\n      TextDataset containing text, labels and other related info.\n    \"\"\"\n    model_spec = ms.get(model_spec)\n    csv_name = os.path.basename(filename)\n\n    is_cached, tfrecord_file, meta_data_file, vocab_file = cls._get_cache_info(\n        cache_dir, csv_name, model_spec, is_training)\n    # If cached, directly loads data from cache directory.\n    if is_cached:\n      return cls._load_data(tfrecord_file, meta_data_file, model_spec)\n\n    lines = cls._read_csv(filename, fieldnames, delimiter, quotechar)\n    if shuffle:\n      random.shuffle(lines)\n\n    # Gets labels: if already loaded labels previously, directly use the\n    # loaded labels. Otherwise, load the labels and sort the labels by name.\n    if model_spec.index_to_label and not is_training:\n      label_names = model_spec.index_to_label\n      label_set = set(label_names)\n    else:\n      label_set = set()\n      for line in lines:\n        label_set.add(line[label_column])\n      label_names = sorted(label_set)\n      model_spec.index_to_label = label_names\n\n    # Generates text examples from csv file.\n    examples = []\n    for i, line in enumerate(lines):\n      text, label = line[text_column], line[label_column]\n      if label not in label_set:\n        logging.warning('Skip line: \"%s\" since label %s is not in label set',\n                        line, label)\n      guid = '%s-%d' % (csv_name, i)\n      examples.append(classifier_data_lib.InputExample(guid, text, None, label))\n\n    # Saves preprocessed data and other assets into files.\n    cls._save_data(examples, model_spec, label_names, tfrecord_file,\n                   meta_data_file, vocab_file, is_training)\n\n    # Loads data from cache directory.\n    return cls._load_data(tfrecord_file, meta_data_file, model_spec)\n\n  @classmethod\n  def _load_data(cls, tfrecord_file, meta_data_file, model_spec):\n    \"\"\"Gets `TextClassifierDataLoader` object from tfrecord file and metadata file.\"\"\"\n\n    dataset, meta_data = _load(tfrecord_file, meta_data_file, model_spec)\n    return TextClassifierDataLoader(dataset, meta_data['size'],\n                                    meta_data['index_to_label'])\n\n  @classmethod\n  def _save_data(cls, examples, model_spec, label_names, tfrecord_file,\n                 meta_data_file, vocab_file, is_training):\n    \"\"\"Saves preprocessed data and other assets into files.\"\"\"\n    # If needed, generates and saves vocabulary in vocab_file=None,\n    if model_spec.need_gen_vocab and is_training:\n      model_spec.gen_vocab(examples)\n      model_spec.save_vocab(vocab_file)\n\n    # Converts examples into preprocessed features and saves in tfrecord_file.\n    model_spec.convert_examples_to_features(examples, tfrecord_file,\n                                            label_names)\n\n    # Generates and saves meta data in meta_data_file.\n    meta_data = {\n        'size': len(examples),\n        'num_classes': len(label_names),\n        'index_to_label': label_names\n    }\n    file_util.write_json_file(meta_data_file, meta_data)\n\n  @classmethod\n  def _get_cache_info(cls, cache_dir, data_name, model_spec, is_training):\n    \"\"\"Gets cache related information for text classifier.\"\"\"\n    is_cached, tfrecord_file, meta_data_file, file_prefix = _get_cache_info(\n        cache_dir, data_name, model_spec, is_training)\n\n    vocab_file = file_prefix + '_vocab'\n    if is_cached:\n      if model_spec.need_gen_vocab and is_training:\n        model_spec.load_vocab(vocab_file)\n      is_cached = True\n    return is_cached, tfrecord_file, meta_data_file, vocab_file\n\n  @classmethod\n  def _read_csv(cls, input_file, fieldnames=None, delimiter=',', quotechar='\"'):\n    \"\"\"Reads a separated value file.\"\"\"\n    with tf.io.gfile.GFile(input_file, 'r') as f:\n      reader = csv.DictReader(\n          f, fieldnames=fieldnames, delimiter=delimiter, quotechar=quotechar)\n      lines = []\n      for line in reader:\n        lines.append(line)\n      return lines\n\n\n@mm_export('question_answer.DataLoader')\nclass QuestionAnswerDataLoader(dataloader.DataLoader):\n  \"\"\"DataLoader for question answering.\"\"\"\n\n  def __init__(self, dataset, size, version_2_with_negative, examples, features,\n               squad_file):\n    super(QuestionAnswerDataLoader, self).__init__(dataset, size)\n    self.version_2_with_negative = version_2_with_negative\n    self.examples = examples\n    self.features = features\n    self.squad_file = squad_file\n\n  @classmethod\n  def from_squad(cls,\n                 filename,\n                 model_spec,\n                 is_training=True,\n                 version_2_with_negative=False,\n                 cache_dir=None):\n    \"\"\"Loads data in SQuAD format and preproecess text according to `model_spec`.\n\n    Args:\n      filename: Name of the file.\n      model_spec: Specification for the model.\n      is_training: Whether the loaded data is for training or not.\n      version_2_with_negative: Whether it's SQuAD 2.0 format.\n      cache_dir: The cache directory to save preprocessed data. If None,\n        generates a temporary directory to cache preprocessed data.\n\n    Returns:\n      QuestionAnswerDataLoader object.\n    \"\"\"\n    model_spec = ms.get(model_spec)\n    file_base_name = os.path.basename(filename)\n    is_cached, tfrecord_file, meta_data_file, _ = _get_cache_info(\n        cache_dir, file_base_name, model_spec, is_training)\n    # If cached, directly loads data from cache directory.\n    if is_cached and is_training:\n      dataset, meta_data = _load(tfrecord_file, meta_data_file, model_spec,\n                                 is_training)\n      return QuestionAnswerDataLoader(\n          dataset=dataset,\n          size=meta_data['size'],\n          version_2_with_negative=meta_data['version_2_with_negative'],\n          examples=[],\n          features=[],\n          squad_file=filename)\n\n    meta_data, examples, features = cls._generate_tf_record_from_squad_file(\n        filename, model_spec, tfrecord_file, is_training,\n        version_2_with_negative)\n\n    file_util.write_json_file(meta_data_file, meta_data)\n\n    dataset, meta_data = _load(tfrecord_file, meta_data_file, model_spec,\n                               is_training)\n    return QuestionAnswerDataLoader(dataset, meta_data['size'],\n                                    meta_data['version_2_with_negative'],\n                                    examples, features, filename)\n\n  @classmethod\n  def _generate_tf_record_from_squad_file(cls,\n                                          input_file_path,\n                                          model_spec,\n                                          output_path,\n                                          is_training,\n                                          version_2_with_negative=False):\n    \"\"\"Generates and saves training/validation data into a tf record file.\"\"\"\n    examples = squad_lib.read_squad_examples(\n        input_file=input_file_path,\n        is_training=is_training,\n        version_2_with_negative=version_2_with_negative)\n    writer = squad_lib.FeatureWriter(\n        filename=output_path, is_training=is_training)\n\n    features = []\n\n    def _append_feature(feature, is_padding):\n      if not is_padding:\n        features.append(feature)\n      writer.process_feature(feature)\n\n    if is_training:\n      batch_size = None\n    else:\n      batch_size = model_spec.predict_batch_size\n\n    number_of_examples = model_spec.convert_examples_to_features(\n        examples=examples,\n        is_training=is_training,\n        output_fn=writer.process_feature if is_training else _append_feature,\n        batch_size=batch_size)\n    writer.close()\n\n    meta_data = {\n        'size': number_of_examples,\n        'version_2_with_negative': version_2_with_negative\n    }\n\n    if is_training:\n      examples = []\n    return meta_data, examples, features\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/text_dataloader_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport collections\nimport csv\nimport json\nimport os\n\nfrom absl.testing import parameterized\nimport numpy as np\nimport tensorflow.compat.v2 as tf\n\nfrom tensorflow_examples.lite.model_maker.core import test_util\nfrom tensorflow_examples.lite.model_maker.core.data_util import text_dataloader\nfrom official.nlp.bert import tokenization\nfrom official.nlp.data import squad_lib\n\n\nclass MockClassifierModelSpec(object):\n  need_gen_vocab = False\n\n  def __init__(self, seq_len=4, index_to_label=None):\n    self.seq_len = seq_len\n    self.index_to_label = index_to_label\n\n  def get_name_to_features(self):\n    \"\"\"Gets the dictionary describing the features.\"\"\"\n    name_to_features = {\n        'input_ids': tf.io.FixedLenFeature([self.seq_len], tf.int64),\n        'label_ids': tf.io.FixedLenFeature([], tf.int64),\n    }\n    return name_to_features\n\n  def select_data_from_record(self, record):\n    \"\"\"Dispatches records to features and labels.\"\"\"\n    x = record['input_ids']\n    y = record['label_ids']\n    return (x, y)\n\n  def convert_examples_to_features(self, examples, tfrecord_file, label_names):\n    \"\"\"Converts examples to features and write them into TFRecord file.\"\"\"\n    writer = tf.io.TFRecordWriter(tfrecord_file)\n\n    label_to_id = dict((name, i) for i, name in enumerate(label_names))\n    for example in examples:\n      features = collections.OrderedDict()\n\n      label_id = label_to_id[example.label]\n      input_ids = [label_id] * self.seq_len\n\n      features['input_ids'] = tf.train.Feature(\n          int64_list=tf.train.Int64List(value=list(input_ids)))\n      features['label_ids'] = tf.train.Feature(\n          int64_list=tf.train.Int64List(value=list([label_id])))\n      tf_example = tf.train.Example(\n          features=tf.train.Features(feature=features))\n      writer.write(tf_example.SerializeToString())\n    writer.close()\n\n  def get_config(self):\n    return {'seq_len': self.seq_len}\n\n\nclass MockQAModelSpec(object):\n\n  def __init__(self, vocab_dir):\n    self.seq_len = 384\n    self.predict_batch_size = 8\n    self.query_len = 64\n    self.doc_stride = 128\n\n    vocab_file = os.path.join(vocab_dir, 'vocab.txt')\n    vocab = ['[PAD]', '[UNK]', '[CLS]', '[SEP]', '[MASK]', 'good', 'bad']\n    with open(vocab_file, 'w') as f:\n      f.write('\\n'.join(vocab))\n    self.tokenizer = tokenization.FullTokenizer(vocab_file, do_lower_case=True)\n\n  def get_name_to_features(self, is_training):\n    \"\"\"Gets the dictionary describing the features.\"\"\"\n    name_to_features = {\n        'input_ids': tf.io.FixedLenFeature([self.seq_len], tf.int64),\n        'input_mask': tf.io.FixedLenFeature([self.seq_len], tf.int64),\n        'segment_ids': tf.io.FixedLenFeature([self.seq_len], tf.int64),\n    }\n\n    if is_training:\n      name_to_features['start_positions'] = tf.io.FixedLenFeature([], tf.int64)\n      name_to_features['end_positions'] = tf.io.FixedLenFeature([], tf.int64)\n    else:\n      name_to_features['unique_ids'] = tf.io.FixedLenFeature([], tf.int64)\n\n    return name_to_features\n\n  def select_data_from_record(self, record):\n    \"\"\"Dispatches records to features and labels.\"\"\"\n    x, y = {}, {}\n    for name, tensor in record.items():\n      if name in ('start_positions', 'end_positions'):\n        y[name] = tensor\n      elif name == 'input_ids':\n        x['input_word_ids'] = tensor\n      elif name == 'segment_ids':\n        x['input_type_ids'] = tensor\n      else:\n        x[name] = tensor\n    return (x, y)\n\n  def get_config(self):\n    \"\"\"Gets the configuration.\"\"\"\n    # Only preprocessing related variables are included.\n    return {\n        'seq_len': self.seq_len,\n        'query_len': self.query_len,\n        'doc_stride': self.doc_stride\n    }\n\n  def convert_examples_to_features(self, examples, is_training, output_fn,\n                                   batch_size):\n    \"\"\"Converts examples to features and write them into TFRecord file.\"\"\"\n    return squad_lib.convert_examples_to_features(\n        examples=examples,\n        tokenizer=self.tokenizer,\n        max_seq_length=self.seq_len,\n        doc_stride=self.doc_stride,\n        max_query_length=self.query_len,\n        is_training=is_training,\n        output_fn=output_fn,\n        batch_size=batch_size)\n\n\nclass LoaderFunctionTest(tf.test.TestCase):\n\n  def setUp(self):\n    super(LoaderFunctionTest, self).setUp()\n    self.model_spec = MockClassifierModelSpec()\n\n  def test_load(self):\n    tfrecord_file = self._get_tfrecord_file()\n    meta_data_file = self._get_meta_data_file()\n    dataset, meta_data = text_dataloader._load(tfrecord_file, meta_data_file,\n                                               self.model_spec)\n    for i, (input_ids, label_ids) in enumerate(dataset):\n      self.assertEqual(i, 0)\n      self.assertTrue((input_ids.numpy() == [0, 1, 2, 3]).all())\n      self.assertTrue((label_ids.numpy() == [0]).all())\n    self.assertEqual(meta_data['size'], 1)\n    self.assertEqual(meta_data['num_classes'], 1)\n    self.assertEqual(meta_data['index_to_label'], ['0'])\n\n  def test_get_cache_filenames(self):\n    tfrecord_file, meta_data_file, prefix = text_dataloader._get_cache_filenames(\n        cache_dir='/tmp',\n        model_spec=self.model_spec,\n        data_name='train',\n        is_training=True)\n    self.assertTrue(tfrecord_file.startswith(prefix))\n    self.assertTrue(meta_data_file.startswith(prefix))\n\n    _, _, new_dir_prefix = text_dataloader._get_cache_filenames(\n        cache_dir='/tmp1',\n        model_spec=self.model_spec,\n        data_name='train',\n        is_training=True)\n    self.assertNotEqual(new_dir_prefix, prefix)\n\n    _, _, new_model_spec_prefix = text_dataloader._get_cache_filenames(\n        cache_dir='/tmp',\n        model_spec=MockClassifierModelSpec(seq_len=8),\n        data_name='train',\n        is_training=True)\n    self.assertNotEqual(new_model_spec_prefix, prefix)\n\n    _, _, new_data_name_prefix = text_dataloader._get_cache_filenames(\n        cache_dir='/tmp',\n        model_spec=self.model_spec,\n        data_name='test',\n        is_training=True)\n    self.assertNotEqual(new_data_name_prefix, prefix)\n\n    _, _, new_is_training_false_prefix = text_dataloader._get_cache_filenames(\n        cache_dir='/tmp',\n        model_spec=self.model_spec,\n        data_name='train',\n        is_training=False)\n    self.assertNotEqual(new_is_training_false_prefix, prefix)\n\n  def _get_tfrecord_file(self):\n    tfrecord_file = os.path.join(self.get_temp_dir(), 'tmp.tfrecord')\n    writer = tf.io.TFRecordWriter(tfrecord_file)\n    input_ids = tf.train.Int64List(value=[0, 1, 2, 3])\n    label_ids = tf.train.Int64List(value=[0])\n    features = collections.OrderedDict()\n    features['input_ids'] = tf.train.Feature(int64_list=input_ids)\n    features['label_ids'] = tf.train.Feature(int64_list=label_ids)\n    tf_example = tf.train.Example(features=tf.train.Features(feature=features))\n    writer.write(tf_example.SerializeToString())\n    writer.close()\n    return tfrecord_file\n\n  def _get_meta_data_file(self):\n    meta_data_file = os.path.join(self.get_temp_dir(), 'tmp_meta_data')\n    meta_data = {'size': 1, 'num_classes': 1, 'index_to_label': ['0']}\n    with tf.io.gfile.GFile(meta_data_file, 'w') as f:\n      json.dump(meta_data, f)\n    return meta_data_file\n\n\nclass TextClassifierDataLoaderTest(tf.test.TestCase):\n  TRAIN_LABELS_AND_TEXT = (('neutral', 'indifferent'),\n                           ('pos', 'extremely great'), ('neg', 'totally awful'))\n  TEST_LABELS_AND_TEXT = (('pos', 'super good'), ('neg', 'really bad'))\n\n  def _get_folder_path(self):\n    folder_path = os.path.join(self.get_temp_dir(), 'random_text_dir')\n    if os.path.exists(folder_path):\n      return\n    os.mkdir(folder_path)\n\n    for label, text in self.TEST_LABELS_AND_TEXT:\n      class_subdir = os.path.join(folder_path, label)\n      os.mkdir(class_subdir)\n      with open(os.path.join(class_subdir, '0.txt'), 'w') as f:\n        f.write(text)\n    return folder_path\n\n  def _get_csv_file(self, is_training):\n    file_name = 'train.csv' if is_training else 'test.csv'\n    csv_file = os.path.join(self.get_temp_dir(), file_name)\n\n    labels_and_text = (\n        self.TRAIN_LABELS_AND_TEXT\n        if is_training else self.TEST_LABELS_AND_TEXT)\n    if os.path.exists(csv_file):\n      return csv_file\n    fieldnames = ['text', 'label']\n    with open(csv_file, 'w') as f:\n      writer = csv.DictWriter(f, fieldnames=fieldnames)\n      writer.writeheader()\n      for label, text in labels_and_text:\n        writer.writerow({'text': text, 'label': label})\n    return csv_file\n\n  def test_split(self):\n    ds = tf.data.Dataset.from_tensor_slices([[0, 1], [1, 1], [0, 0], [1, 0]])\n    data = text_dataloader.TextClassifierDataLoader(ds, 4, ['pos', 'neg'])\n    train_data, test_data = data.split(0.5)\n\n    self.assertLen(train_data, 2)\n    for i, elem in enumerate(train_data.gen_dataset()):\n      self.assertTrue((elem.numpy() == np.array([i, 1])).all())\n    self.assertEqual(train_data.num_classes, 2)\n    self.assertEqual(train_data.index_to_label, ['pos', 'neg'])\n\n    self.assertLen(test_data, 2)\n    for i, elem in enumerate(test_data.gen_dataset()):\n      self.assertTrue((elem.numpy() == np.array([i, 0])).all())\n    self.assertEqual(test_data.num_classes, 2)\n    self.assertEqual(test_data.index_to_label, ['pos', 'neg'])\n\n  def test_from_csv(self):\n    train_csv_file = self._get_csv_file(is_training=True)\n    model_spec = MockClassifierModelSpec()\n    train_data = text_dataloader.TextClassifierDataLoader.from_csv(\n        train_csv_file,\n        text_column='text',\n        label_column='label',\n        model_spec=model_spec,\n        is_training=True)\n    self._test_data(\n        train_data,\n        model_spec,\n        size=3,\n        num_classes=3,\n        index_to_label=['neg', 'neutral', 'pos'])\n\n    test_csv_file = self._get_csv_file(is_training=False)\n    test_data = text_dataloader.TextClassifierDataLoader.from_csv(\n        test_csv_file,\n        text_column='text',\n        label_column='label',\n        model_spec=model_spec,\n        is_training=False)\n\n    # Labels should be the same as the training data.\n    self._test_data(\n        test_data,\n        model_spec,\n        size=2,\n        num_classes=3,\n        index_to_label=['neg', 'neutral', 'pos'])\n\n  def test_from_folder(self):\n    folder_path = self._get_folder_path()\n    model_spec = MockClassifierModelSpec()\n    data = text_dataloader.TextClassifierDataLoader.from_folder(\n        folder_path, model_spec=model_spec)\n    self._test_data(\n        data, model_spec, size=2, num_classes=2, index_to_label=['neg', 'pos'])\n\n  def _test_data(self, data, model_spec, size, num_classes, index_to_label):\n    self.assertLen(data, size)\n    self.assertEqual(data.num_classes, num_classes)\n    self.assertEqual(data.index_to_label, index_to_label)\n    for input_ids, label in data.gen_dataset():\n      self.assertTrue(label.numpy() >= 0 and label.numpy() < num_classes)\n      actual_input_ids = [label.numpy()] * model_spec.seq_len\n      self.assertTrue((input_ids.numpy() == actual_input_ids).all())\n\n\nclass QuestionAnswerDataLoaderTest(tf.test.TestCase, parameterized.TestCase):\n\n  @parameterized.parameters(\n      ('train-v1.1.json', True, False, 1),\n      ('dev-v1.1.json', False, False, 8),\n      ('train-v2.0.json', True, True, 2),\n      ('dev-v2.0.json', False, True, 8),\n  )\n  def test_from_squad(self, test_file, is_training, version_2_with_negative,\n                      size):\n\n    path = test_util.get_test_data_path('squad_testdata')\n    squad_path = os.path.join(path, test_file)\n    model_spec = MockQAModelSpec(self.get_temp_dir())\n    data = text_dataloader.QuestionAnswerDataLoader.from_squad(\n        squad_path,\n        model_spec,\n        is_training=is_training,\n        version_2_with_negative=version_2_with_negative)\n\n    self.assertIsInstance(data, text_dataloader.QuestionAnswerDataLoader)\n    self.assertLen(data, size)\n    self.assertEqual(data.version_2_with_negative, version_2_with_negative)\n    self.assertEqual(data.squad_file, squad_path)\n\n    if is_training:\n      self.assertEmpty(data.features)\n      self.assertEmpty(data.examples)\n    else:\n      self.assertNotEmpty(data.features)\n      self.assertIsInstance(data.features[0], squad_lib.InputFeatures)\n\n      self.assertEqual(len(data.features), len(data.examples))\n      self.assertIsInstance(data.examples[0], squad_lib.SquadExample)\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/text_searcher_dataloader.py",
    "content": "# Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Text DataLoader for Searcher task.\"\"\"\n\nimport csv\nimport logging\nimport os\n\nimport numpy as np\nfrom tensorflow_examples.lite.model_maker.core.api.api_util import mm_export\nfrom tensorflow_examples.lite.model_maker.core.data_util import searcher_dataloader\nfrom tensorflow_lite_support.python.task.core import base_options as base_options_module\nfrom tensorflow_lite_support.python.task.processor.proto import embedding_options_pb2\nfrom tensorflow_lite_support.python.task.text import text_embedder\n\n_BaseOptions = base_options_module.BaseOptions\n\n\n@mm_export(\"searcher.TextDataLoader\")\nclass DataLoader(searcher_dataloader.DataLoader):\n  \"\"\"DataLoader class for Text Searcher.\"\"\"\n\n  def __init__(self, embedder: text_embedder.TextEmbedder) -> None:\n    \"\"\"Initializes DataLoader for Image Searcher task.\n\n    Args:\n      embedder: Embedder to generate embedding from raw input image.\n    \"\"\"\n    self._embedder = embedder\n    super().__init__(embedder_path=embedder.options.base_options.file_name)\n\n  @classmethod\n  def create(cls,\n             text_embedder_path: str,\n             l2_normalize: bool = False) -> \"DataLoader\":\n    \"\"\"Creates DataLoader for the Text Searcher task.\n\n    Args:\n      text_embedder_path: Path to the \".tflite\" text embedder model. case and L2\n        norm is thus achieved through TF Lite inference.\n      l2_normalize: Whether to normalize the returned feature vector with L2\n        norm. Use this option only if the model does not already contain a\n        native L2_NORMALIZATION TF Lite Op. In most cases, this is already the\n        case and L2 norm is thus achieved through TF Lite inference.\n\n    Returns:\n      DataLoader object created for the Text Searcher task.\n    \"\"\"\n    # Creates TextEmbedder.\n    text_embedder_path = os.path.abspath(text_embedder_path)\n    base_options = _BaseOptions(file_name=text_embedder_path)\n    embedding_options = embedding_options_pb2.EmbeddingOptions(\n        l2_normalize=l2_normalize)\n    options = text_embedder.TextEmbedderOptions(\n        base_options=base_options, embedding_options=embedding_options)\n    embedder = text_embedder.TextEmbedder.create_from_options(options)\n\n    return cls(embedder)\n\n  def load_from_csv(self,\n                    path: str,\n                    text_column: str,\n                    metadata_column: str,\n                    delimiter: str = \",\",\n                    quotechar: str = \"\\\"\") -> None:\n    \"\"\"Loads text data from csv file that includes a \"header\" line with titles.\n\n    Users can load text from different csv files one by one. For instance,\n    ```\n    # Creates data_loader instance.\n    data_loader = text_searcher_dataloader.DataLoader.create(tflite_path)\n\n    # Loads text, first from `text_path1` and secondly from `text_path2`.\n    data_loader.load_from_csv(\n        text_path1, text_column='text', metadata_column='metadata')\n    data_loader.load_from_csv(\n        text_path2, text_column='text', metadata_column='metadata')\n    ```\n\n    Args:\n      path: Text csv file path to be loaded.\n      text_column: Column name for input text.\n      metadata_column: Column name for user metadata associated with each input\n        text.\n      delimiter: Character used to separate fields.\n      quotechar: Character used to quote fields containing special characters.\n    \"\"\"\n    embedding_list = []\n    metadata_list = []\n\n    i = 0\n    # Reads the text and metadata one by one from the csv file.\n    with open(path, \"r\") as f:\n      reader = csv.DictReader(f, delimiter=delimiter, quotechar=quotechar)\n      for line in reader:\n        text, metadata = line[text_column], line[metadata_column]\n        try:\n          embedding = self._embedder.embed(\n              text).embeddings[0].feature_vector.value\n        except (ValueError, RuntimeError) as e:\n          logging.warning(\"Can't get the embedding of %s with the error %s\",\n                          text, e)\n          continue\n\n        embedding_list.append(embedding)\n        metadata_list.append(metadata)\n\n        i += 1\n        if i % 1000 == 0:\n          logging.info(\"Processed %d text strings.\", i)\n\n    cache_dataset = np.stack(embedding_list)\n    self._cache_dataset_list.append(cache_dataset)\n    self._metadata = self._metadata + metadata_list\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/data_util/text_searcher_dataloader_test.py",
    "content": "# Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Tests for text_searcher_dataloader.\"\"\"\n\nfrom absl.testing import parameterized\n\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core.data_util import text_searcher_dataloader\nfrom tensorflow_examples.lite.model_maker.core import test_util\n\n\nclass TextSearcherDataloaderTest(parameterized.TestCase, tf.test.TestCase):\n\n  def setUp(self):\n    super().setUp()\n    self.tflite_path = test_util.get_test_data_path(\n        \"regex_one_embedding_with_metadata.tflite\")\n    self.text_csv_file1 = test_util.get_test_data_path(\"movies.csv\")\n    self.text_csv_file2 = test_util.get_test_data_path(\"trips.csv\")\n\n  @parameterized.parameters(\n      (False, 0.0301549),\n      (True, 0.538059),\n  )\n  def test_from_csv(self, l2_normalize, expected_value):\n    data_loader = text_searcher_dataloader.DataLoader.create(\n        self.tflite_path, l2_normalize=l2_normalize)\n    data_loader.load_from_csv(\n        self.text_csv_file1, text_column=\"text\", metadata_column=\"metadata\")\n    data_loader.load_from_csv(\n        self.text_csv_file2, text_column=\"text\", metadata_column=\"metadata\")\n    self.assertLen(data_loader, 3)\n    self.assertEqual(data_loader.dataset.shape, (3, 16))\n    self.assertAlmostEqual(data_loader.dataset[0][0], expected_value, places=6)\n    self.assertEqual(data_loader.metadata,\n                     [\"good movie\", \"charming trip\", \"great trip\"])\n\n\nif __name__ == \"__main__\":\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/export_format.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Export format such as saved_model / tflite.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport enum\nfrom tensorflow_examples.lite.model_maker.core.api import mm_export\n\n\n@mm_export(\"config.ExportFormat\")\n@enum.unique\nclass ExportFormat(enum.Enum):\n  TFLITE = \"TFLITE\"\n  SAVED_MODEL = \"SAVED_MODEL\"\n  LABEL = \"LABEL\"\n  VOCAB = \"VOCAB\"\n  TFJS = \"TFJS\"\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/file_util.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"File related util for model maker.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport json\n\nimport tensorflow as tf\n\n\ndef load_json_file(json_file):\n  \"\"\"Loads json data from file.\"\"\"\n  with tf.io.gfile.GFile(json_file, 'r') as reader:\n    return json.load(reader)\n\n\ndef write_json_file(json_file, data):\n  \"\"\"Writes json data into file.\"\"\"\n  with tf.io.gfile.GFile(json_file, 'w') as f:\n    json.dump(data, f)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/optimization/__init__.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/optimization/warmup.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Functions and classes related to optimization (weight updates).\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow.compat.v2 as tf\n\n\nclass WarmUp(tf.keras.optimizers.schedules.LearningRateSchedule):\n  \"\"\"Applies a warmup schedule on a given learning rate decay schedule.\"\"\"\n\n  def __init__(self,\n               initial_learning_rate,\n               decay_schedule_fn,\n               warmup_steps,\n               name=None):\n    super(WarmUp, self).__init__()\n    self.initial_learning_rate = initial_learning_rate\n    self.warmup_steps = warmup_steps\n    self.decay_schedule_fn = decay_schedule_fn\n    self.name = name\n\n  def __call__(self, step):\n    with tf.name_scope(self.name or 'WarmUp') as name:\n      # Implements linear warmup. i.e., if global_step < warmup_steps, the\n      # learning rate will be `global_step/num_warmup_steps * init_lr`.\n      global_step_float = tf.cast(step, tf.float32)\n      warmup_steps_float = tf.cast(self.warmup_steps, tf.float32)\n      warmup_percent_done = global_step_float / warmup_steps_float\n      warmup_learning_rate = self.initial_learning_rate * warmup_percent_done\n      return tf.cond(\n          global_step_float < warmup_steps_float,\n          lambda: warmup_learning_rate,\n          lambda: self.decay_schedule_fn(step),\n          name=name)\n\n  def get_config(self):\n    return {\n        'initial_learning_rate': self.initial_learning_rate,\n        'decay_schedule_fn': self.decay_schedule_fn,\n        'warmup_steps': self.warmup_steps,\n        'name': self.name\n    }\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/__init__.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/audio_classifier.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"APIs to train an audio classification model.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core.api import mm_export\nfrom tensorflow_examples.lite.model_maker.core.export_format import ExportFormat\nfrom tensorflow_examples.lite.model_maker.core.task import classification_model\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import audio_spec\n\n\n@mm_export('audio_classifier.AudioClassifier')\nclass AudioClassifier(classification_model.ClassificationModel):\n  \"\"\"Audio classifier for training/inference and exporing.\"\"\"\n\n  # TODO(b/171848856): Add TFJS export.\n  DEFAULT_EXPORT_FORMAT = (ExportFormat.TFLITE)\n  ALLOWED_EXPORT_FORMAT = (ExportFormat.LABEL, ExportFormat.TFLITE,\n                           ExportFormat.SAVED_MODEL)\n\n  def train(self, train_data, validation_data, epochs, batch_size):\n    # TODO(b/171449557): Upstream this to the parent class.\n    if len(train_data) < batch_size:\n      raise ValueError('The size of the train_data (%d) couldn\\'t be smaller '\n                       'than batch_size (%d). To solve this problem, set '\n                       'the batch_size smaller or increase the size of the '\n                       'train_data.' % (len(train_data), batch_size))\n\n    with self.model_spec.strategy.scope():\n      train_ds = train_data.gen_dataset(\n          batch_size, is_training=True, shuffle=self.shuffle)\n      validation_ds = validation_data.gen_dataset(\n          batch_size, is_training=False) if validation_data else None\n\n      self.create_model(train_data.num_classes, self.train_whole_model)\n\n      return self.model_spec.run_classifier(\n          self.model,\n          epochs,\n          train_ds,\n          validation_ds,\n          callbacks=self._keras_callbacks(self.model_spec.model_dir))\n\n  def create_model(self, num_classes, train_whole_model):\n    self.model = self.model_spec.create_model(\n        num_classes, train_whole_model=train_whole_model)\n    self.model.summary()\n\n  def _export_tflite(self,\n                     tflite_filepath,\n                     with_metadata=True,\n                     export_metadata_json_file=True,\n                     quantization_config='default'):\n    \"\"\"Converts the retrained model to tflite format and saves it.\n\n    Args:\n      tflite_filepath: File path to save tflite model.\n      with_metadata: Whether the output tflite model contains metadata.\n      export_metadata_json_file: Whether to export metadata in json file. If\n        True, export the metadata in the same directory as tflite model. Used\n        only if `with_metadata` is True.\n      quantization_config: Configuration for post-training quantization.\n    \"\"\"\n    if quantization_config == 'default':\n      quantization_config = self.model_spec.get_default_quantization_config()\n\n    self.model_spec.export_tflite(self.model, tflite_filepath, with_metadata,\n                                  export_metadata_json_file,\n                                  self.index_to_label, quantization_config)\n\n  def confusion_matrix(self, data, batch_size=32):\n    # TODO(b/171449557): Consider moving this to ClassificationModel\n    ds = data.gen_dataset(\n        batch_size, is_training=False, preprocess=self.preprocess)\n    predicated = []\n    truth = []\n    for item, label in ds:\n      if tf.rank(label) == 2:  # One-hot encoded labels (batch, num_classes)\n        truth.extend(tf.math.argmax(label, axis=-1))\n        predicated.extend(tf.math.argmax(self.model.predict(item), axis=-1))\n      else:\n        truth.extend(label)\n        predicated.extend(self.model.predict(item))\n\n    return tf.math.confusion_matrix(\n        labels=truth, predictions=predicated, num_classes=data.num_classes)\n\n  @classmethod\n  def create(cls,\n             train_data,\n             model_spec,\n             validation_data=None,\n             batch_size=32,\n             epochs=5,\n             model_dir=None,\n             do_train=True,\n             train_whole_model=False):\n    \"\"\"Loads data and retrains the model.\n\n    Args:\n      train_data: A instance of audio_dataloader.DataLoader class.\n      model_spec: Specification for the model.\n      validation_data: Validation DataLoader. If None, skips validation process.\n      batch_size: Number of samples per training step. If `use_hub_library` is\n        False, it represents the base learning rate when train batch size is 256\n        and it's linear to the batch size.\n      epochs: Number of epochs for training.\n      model_dir: The location of the model checkpoint files.\n      do_train: Whether to run training.\n      train_whole_model: Boolean. By default, only the classification head is\n        trained. When True, the base model is also trained.\n\n    Returns:\n      An instance based on AudioClassifier.\n    \"\"\"\n    if not isinstance(model_spec, audio_spec.BaseSpec):\n      model_spec = model_spec.get(model_spec, model_dir=model_dir)\n    task = cls(\n        model_spec,\n        train_data.index_to_label,\n        shuffle=True,\n        train_whole_model=train_whole_model)\n    if do_train:\n      task.train(train_data, validation_data, epochs, batch_size)\n    return task\n\n\n# Shortcut function.\ncreate = AudioClassifier.create\nmm_export('audio_classifier.create').export_constant(__name__, 'create')\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/audio_classifier_test.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nimport unittest\n\nimport numpy as np\nfrom packaging import version\nfrom scipy.io import wavfile\nimport tensorflow.compat.v2 as tf\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core.data_util import audio_dataloader\nfrom tensorflow_examples.lite.model_maker.core.export_format import ExportFormat\nfrom tensorflow_examples.lite.model_maker.core.task import audio_classifier\nfrom tensorflow_examples.lite.model_maker.core.task import configs\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import audio_spec\n\n\nclass BrowserFFTWithoutPreprocessing(audio_spec.BrowserFFTSpec):\n\n  def preprocess_ds(self, ds, is_training=False, cache_fn=None):\n    _ = is_training\n\n    @tf.function\n    def _crop(wav, label):\n      wav = wav[:self.EXPECTED_WAVEFORM_LENGTH]\n      return wav, label\n\n    ds = ds.map(_crop)\n\n    if cache_fn:\n      ds = cache_fn(ds)\n    return ds\n\n\nclass YAMNetWithoutPreprcessing(audio_spec.YAMNetSpec):\n\n  def preprocess_ds(self, ds, is_training=False, cache_fn=None):\n\n    @tf.function\n    def _crop(wav, label):\n      wav = wav[:audio_spec.YAMNetSpec.EXPECTED_WAVEFORM_LENGTH]\n      return wav, label\n\n    ds = ds.map(_crop)\n    return ds\n\n\ndef write_sample(root,\n                 category,\n                 file_name,\n                 sample_rate,\n                 duration_sec,\n                 dtype=np.int16):\n  os.makedirs(os.path.join(root, category), exist_ok=True)\n  xs = np.random.rand(int(sample_rate * duration_sec),) * (1 << 15)\n  xs = xs.astype(dtype)\n  full_path = os.path.join(root, category, file_name)\n  wavfile.write(full_path, sample_rate, xs)\n  return full_path\n\n\n@unittest.skipIf(\n    version.parse(tf.__version__) < version.parse('2.5'),\n    'Audio Classification requires TF 2.5 or later')\nclass AudioClassifierTest(tf.test.TestCase):\n\n  def testBrowserFFT(self):\n    self._test_spec(audio_spec.BrowserFFTSpec(),\n                    BrowserFFTWithoutPreprocessing())\n\n  def testYAMNet(self):\n    self._test_spec(audio_spec.YAMNetSpec(), YAMNetWithoutPreprcessing())\n\n  def testConfusionMatrix(self):\n    spec = audio_spec.BrowserFFTSpec()\n\n    temp_folder = self.get_temp_dir()\n    cat1 = write_sample(temp_folder, 'cat', '1.wav', 44100, duration_sec=1)\n    cat2 = write_sample(temp_folder, 'cat', '2.wav', 44100, duration_sec=2)\n    dog1 = write_sample(temp_folder, 'dog', '1.wav', 44100, duration_sec=3)\n    dog2 = write_sample(temp_folder, 'dog', '2.wav', 44100, duration_sec=4)\n    index_to_labels = ['cat', 'dog']\n\n    # Prepare data.\n    ds = tf.data.Dataset.from_tensor_slices(([cat1, cat2, dog1,\n                                              dog2], [0, 0, 1, 1]))\n    data_loader = audio_dataloader.DataLoader(ds, len(ds), index_to_labels,\n                                              spec)\n\n    # Train a floating point model.\n    task = audio_classifier.create(data_loader, spec, batch_size=1, epochs=15)\n\n    confusion_matrx = task.confusion_matrix(data_loader)\n\n    # BrowserFFTSpec generates 1 sample for 1 second audio so there are\n    # 10 samples in total.\n    self.assertEqual(tf.math.reduce_sum(confusion_matrx), 10)\n    # confusion_matrix is of shape (truth, predication)\n    # We have 2 classes, 3 cat samples and 7 dog samples.\n    self.assertEqual(confusion_matrx.shape, (2, 2))\n    self.assertAllEqual(\n        tf.math.reduce_sum(confusion_matrx, axis=-1).numpy(), np.array([3, 7]))\n\n  def _test_spec(self, train_spec, tflite_eval_spec):\n    temp_folder = self.get_temp_dir()\n    cat1 = write_sample(temp_folder, 'cat', '1.wav', 44100, duration_sec=1)\n    cat2 = write_sample(temp_folder, 'cat', '2.wav', 44100, duration_sec=2)\n    dog1 = write_sample(temp_folder, 'dog', '1.wav', 44100, duration_sec=3)\n    dog2 = write_sample(temp_folder, 'dog', '2.wav', 44100, duration_sec=4)\n    index_to_labels = ['cat', 'dog']\n\n    np.random.seed(123)\n    tf.random.set_seed(123)\n\n    # Prepare data.\n    ds = tf.data.Dataset.from_tensor_slices(([cat1, cat2, dog1,\n                                              dog2], [0, 0, 1, 1]))\n    data_loader = audio_dataloader.DataLoader(ds, len(ds), index_to_labels,\n                                              train_spec)\n\n    # Train a floating point model.\n    task = audio_classifier.create(\n        data_loader, train_spec, batch_size=1, epochs=15)\n\n    # Evaluate trained model\n    _, acc = task.evaluate(data_loader)\n    # Better than random guessing.\n    self.assertGreater(acc, .5)\n\n    # Create a new dataset without preprocessing since preprocessing has been\n    # packaged inside TFLite model.\n    tflite_dataloader = audio_dataloader.DataLoader(ds, len(ds),\n                                                    index_to_labels,\n                                                    tflite_eval_spec)\n\n    # Export the floating point model to TFLite.\n    output_path = os.path.join(train_spec.model_dir, 'float.tflite')\n    task.export(\n        train_spec.model_dir,\n        tflite_filename='float.tflite',\n        export_format=ExportFormat.TFLITE,\n        quantization_config=None)\n    self.assertTrue(tf.io.gfile.exists(output_path))\n    self.assertGreater(os.path.getsize(output_path), 0)\n    result = task.evaluate_tflite(\n        output_path,\n        tflite_dataloader,\n        # Skip yamnet output during TFLite evaluation.\n        postprocess_fn=lambda x: x[-1])\n    self.assertGreaterEqual(result['accuracy'], .5)\n\n    # Export the model to TFLite with dynamic range quantization.\n    dynamic_range_output_path = os.path.join(train_spec.model_dir,\n                                             'dynamic_range.tflite')\n    task.export(\n        train_spec.model_dir,\n        tflite_filename='dynamic_range.tflite',\n        export_format=ExportFormat.TFLITE,\n        quantization_config=configs.QuantizationConfig.for_dynamic())\n    self.assertTrue(tf.io.gfile.exists(dynamic_range_output_path))\n    self.assertGreater(os.path.getsize(dynamic_range_output_path), 0)\n    result = task.evaluate_tflite(\n        dynamic_range_output_path,\n        tflite_dataloader,\n        # Skip yamnet output during TFLite evaluation.\n        postprocess_fn=lambda x: x[-1])\n    self.assertGreaterEqual(result['accuracy'], .5)\n    # Float model should be bigger than the dynamic range quantized model by\n    # a margin.\n    self.assertGreater(\n        os.path.getsize(output_path),\n        os.path.getsize(dynamic_range_output_path) + 1 * 1000 * 1000)\n\n    # Test serving model.\n    keras_model = task.create_serving_model()\n    self.assertAllEqual(keras_model.input_shape,\n                        [None, train_spec.EXPECTED_WAVEFORM_LENGTH])\n\n    # Test exporting to the saved model.\n    task.export(train_spec.model_dir, export_format=ExportFormat.SAVED_MODEL)\n    new_model = tf.keras.models.load_model(\n        os.path.join(train_spec.model_dir, 'saved_model'))\n    self.assertAllEqual(new_model.input_shape,\n                        [None, train_spec.EXPECTED_WAVEFORM_LENGTH])\n    output_path = os.path.join(train_spec.model_dir, 'saved_model')\n    self.assertTrue(os.path.isdir(output_path))\n    self.assertNotEqual(len(os.listdir(output_path)), 0)\n\n\nif __name__ == '__main__':\n  # Load compressed models from tensorflow_hub\n  os.environ['TFHUB_MODEL_LOAD_FORMAT'] = 'COMPRESSED'\n  compat.setup_tf_behavior(tf_version=2)\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/classification_model.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Custom classification model that is already retained by data.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\n\nimport numpy as np\nimport tensorflow.compat.v2 as tf\nfrom tensorflow_examples.lite.model_maker.core.data_util import data_util\nfrom tensorflow_examples.lite.model_maker.core.data_util import dataloader\nfrom tensorflow_examples.lite.model_maker.core.export_format import ExportFormat\nfrom tensorflow_examples.lite.model_maker.core.task import custom_model\nfrom tensorflow_examples.lite.model_maker.core.task import model_util\n\n\nclass ClassificationModel(custom_model.CustomModel):\n  \"\"\"\"The abstract base class that represents a Tensorflow classification model.\"\"\"\n\n  DEFAULT_EXPORT_FORMAT = (ExportFormat.TFLITE, ExportFormat.LABEL)\n  ALLOWED_EXPORT_FORMAT = (ExportFormat.TFLITE, ExportFormat.LABEL,\n                           ExportFormat.SAVED_MODEL, ExportFormat.TFJS)\n\n  def __init__(self, model_spec, index_to_label, shuffle, train_whole_model):\n    \"\"\"Initialize a instance with data, deploy mode and other related parameters.\n\n    Args:\n      model_spec: Specification for the model.\n      index_to_label: A list that map from index to label class name.\n      shuffle: Whether the data should be shuffled.\n      train_whole_model: If true, the Hub module is trained together with the\n        classification layer on top. Otherwise, only train the top\n        classification layer.\n    \"\"\"\n    super(ClassificationModel, self).__init__(model_spec, shuffle)\n    self.index_to_label = index_to_label\n    self.num_classes = len(index_to_label)\n    self.train_whole_model = train_whole_model\n\n  def evaluate(self, data, batch_size=32):\n    \"\"\"Evaluates the model.\n\n    Args:\n      data: Data to be evaluated.\n      batch_size: Number of samples per evaluation step.\n\n    Returns:\n      The loss value and accuracy.\n    \"\"\"\n    ds = data.gen_dataset(\n        batch_size, is_training=False, preprocess=self.preprocess)\n    return self.model.evaluate(ds)\n\n  def predict_top_k(self, data, k=1, batch_size=32):\n    \"\"\"Predicts the top-k predictions.\n\n    Args:\n      data: Data to be evaluated. Either an instance of DataLoader or just raw\n        data entries such TF tensor or numpy array.\n      k: Number of top results to be predicted.\n      batch_size: Number of samples per evaluation step.\n\n    Returns:\n      top k results. Each one is (label, probability).\n    \"\"\"\n    if k < 0:\n      raise ValueError('K should be equal or larger than 0.')\n    if isinstance(data, dataloader.DataLoader):\n      ds = data.gen_dataset(\n          batch_size, is_training=False, preprocess=self.preprocess)\n    else:\n      ds = data\n\n    predicted_prob = self.model.predict(ds)\n    topk_prob, topk_id = tf.math.top_k(predicted_prob, k=k)\n    topk_label = np.array(self.index_to_label)[topk_id.numpy()]\n\n    label_prob = []\n    for label, prob in zip(topk_label, topk_prob.numpy()):\n      label_prob.append(list(zip(label, prob)))\n\n    return label_prob\n\n  def _export_labels(self, label_filepath):\n    if label_filepath is None:\n      raise ValueError(\"Label filepath couldn't be None when exporting labels.\")\n\n    tf.compat.v1.logging.info('Saving labels in %s', label_filepath)\n    with tf.io.gfile.GFile(label_filepath, 'w') as f:\n      f.write('\\n'.join(self.index_to_label))\n\n  def evaluate_tflite(self, tflite_filepath, data, postprocess_fn=None):\n    \"\"\"Evaluates the tflite model.\n\n    Args:\n      tflite_filepath: File path to the TFLite model.\n      data: Data to be evaluated.\n      postprocess_fn: Postprocessing function that will be applied to the output\n        of `lite_runner.run` before calculating the probabilities.\n\n    Returns:\n      The evaluation result of TFLite model - accuracy.\n    \"\"\"\n    ds = data.gen_dataset(\n        batch_size=1, is_training=False, preprocess=self.preprocess)\n\n    predictions, labels = [], []\n\n    lite_runner = model_util.get_lite_runner(tflite_filepath, self.model_spec)\n    for i, (feature, label) in enumerate(data_util.generate_elements(ds)):\n      log_steps = 1000\n      tf.compat.v1.logging.log_every_n(tf.compat.v1.logging.DEBUG,\n                                       'Processing example: #%d\\n%s', log_steps,\n                                       i, feature)\n\n      probabilities = lite_runner.run(feature)\n\n      if postprocess_fn:\n        probabilities = postprocess_fn(probabilities)\n      predictions.append(np.argmax(probabilities))\n\n      # Gets the ground-truth labels.\n      label = label[0]\n      if label.size > 1:  # one-hot tensor.\n        label = np.argmax(label)\n      labels.append(label)\n\n    predictions, labels = np.array(predictions), np.array(labels)\n    result = {'accuracy': (predictions == labels).mean()}\n    return result\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/classification_model_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\n\nimport tensorflow.compat.v2 as tf\nfrom tensorflow_examples.lite.model_maker.core import test_util\nfrom tensorflow_examples.lite.model_maker.core.task import classification_model\n\n\nclass MockClassificationModel(classification_model.ClassificationModel):\n\n  def train(self, train_data, validation_data=None, **kwargs):\n    pass\n\n  def export(self, **kwargs):\n    pass\n\n  def evaluate(self, data, **kwargs):\n    pass\n\n\nclass ClassificationModelTest(tf.test.TestCase):\n\n  def setUp(self):\n    super(ClassificationModelTest, self).setUp()\n    self.num_classes = 2\n    self.model = MockClassificationModel(\n        model_spec=None,\n        index_to_label=['pos', 'neg'],\n        train_whole_model=False,\n        shuffle=False)\n\n  def test_predict_top_k(self):\n    input_shape = [24, 24, 3]\n    self.model.model = test_util.build_model(input_shape, self.num_classes)\n    ds = test_util.get_dataloader(2, input_shape, self.num_classes)\n    raw_data = tf.random.uniform(\n        shape=[2] + input_shape, minval=0, maxval=1, dtype=tf.float32)\n\n    for data in [ds, raw_data]:\n      topk_results = self.model.predict_top_k(data, k=2, batch_size=1)\n      for topk_result in topk_results:\n        top1_result, top2_result = topk_result[0], topk_result[1]\n        top1_label, top1_prob = top1_result[0], top1_result[1]\n        top2_label, top2_prob = top2_result[0], top2_result[1]\n\n        self.assertIn(top1_label, self.model.index_to_label)\n        self.assertIn(top2_label, self.model.index_to_label)\n        self.assertNotEqual(top1_label, top2_label)\n\n        self.assertLessEqual(top1_prob, 1)\n        self.assertGreaterEqual(top1_prob, top2_prob)\n        self.assertGreaterEqual(top2_prob, 0)\n\n        self.assertEqual(top1_prob + top2_prob, 1.0)\n\n  def test_export_labels(self):\n    labels_output_file = os.path.join(self.get_temp_dir(), 'label')\n    self.model._export_labels(labels_output_file)\n    with tf.io.gfile.GFile(labels_output_file, 'r') as f:\n      labels = [label.strip() for label in f]\n    self.assertEqual(labels, ['pos', 'neg'])\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/configs.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Configurations.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nfrom typing import Optional, Union\n\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core.api import mm_export\n\nDEFAULT_QUANTIZATION_STEPS = 500\n\n\ndef _get_representative_dataset_gen(dataset, num_steps):\n  \"\"\"Gets the function that generates representative dataset for quantized.\"\"\"\n\n  def representative_dataset_gen():\n    \"\"\"Generates representative dataset for quantized.\"\"\"\n    if compat.get_tf_behavior() == 2:\n      for image, _ in dataset.take(num_steps):\n        yield [image]\n    else:\n      iterator = tf.compat.v1.data.make_initializable_iterator(\n          dataset.take(num_steps))\n      next_element = iterator.get_next()\n      with tf.compat.v1.Session() as sess:\n        sess.run(iterator.initializer)\n        while True:\n          try:\n            image, _ = sess.run(next_element)\n            yield [image]\n          except tf.errors.OutOfRangeError:\n            break\n\n  return representative_dataset_gen\n\n\n@mm_export('config.QuantizationConfig')\nclass QuantizationConfig(object):\n  \"\"\"Configuration for post-training quantization.\n\n  Refer to\n  https://www.tensorflow.org/lite/performance/post_training_quantization\n  for different post-training quantization options.\n  \"\"\"\n\n  def __init__(\n      self,\n      optimizations=None,\n      representative_data=None,\n      quantization_steps=None,\n      inference_input_type=None,\n      inference_output_type=None,\n      supported_ops=None,\n      supported_types=None,\n      experimental_new_quantizer=None,\n  ):\n    \"\"\"Constructs QuantizationConfig.\n\n    Args:\n      optimizations: A list of optimizations to apply when converting the model.\n        If not set, use `[Optimize.DEFAULT]` by default.\n      representative_data: A DataLoader holding representative data for\n        post-training quantization.\n      quantization_steps: Number of post-training quantization calibration steps\n        to run.\n      inference_input_type: Target data type of real-number input arrays. Allows\n        for a different type for input arrays. Defaults to None. If set, must be\n        be `{tf.float32, tf.uint8, tf.int8}`.\n      inference_output_type: Target data type of real-number output arrays.\n        Allows for a different type for output arrays. Defaults to None. If set,\n        must be `{tf.float32, tf.uint8, tf.int8}`.\n      supported_ops: Set of OpsSet options supported by the device. Used to Set\n        converter.target_spec.supported_ops.\n      supported_types: List of types for constant values on the target device.\n        Supported values are types exported by lite.constants. Frequently, an\n        optimization choice is driven by the most compact (i.e. smallest) type\n        in this list (default [constants.FLOAT]).\n      experimental_new_quantizer: Whether to enable experimental new quantizer.\n    \"\"\"\n\n    if optimizations is None:\n      optimizations = [tf.lite.Optimize.DEFAULT]\n    if not isinstance(optimizations, list):\n      optimizations = [optimizations]\n    self.optimizations = optimizations\n\n    self.representative_data = representative_data\n    if self.representative_data is not None and quantization_steps is None:\n      quantization_steps = DEFAULT_QUANTIZATION_STEPS\n    self.quantization_steps = quantization_steps\n\n    self.inference_input_type = inference_input_type\n    self.inference_output_type = inference_output_type\n\n    if supported_ops is not None and not isinstance(supported_ops, list):\n      supported_ops = [supported_ops]\n    self.supported_ops = supported_ops\n\n    if supported_types is not None and not isinstance(supported_types, list):\n      supported_types = [supported_types]\n    self.supported_types = supported_types\n\n    self.experimental_new_quantizer = experimental_new_quantizer\n\n  @classmethod\n  def for_dynamic(cls):\n    \"\"\"Creates configuration for dynamic range quantization.\"\"\"\n    return QuantizationConfig()\n\n  @classmethod\n  def for_int8(cls,\n               representative_data,\n               quantization_steps=DEFAULT_QUANTIZATION_STEPS,\n               inference_input_type=tf.uint8,\n               inference_output_type=tf.uint8,\n               supported_ops=tf.lite.OpsSet.TFLITE_BUILTINS_INT8):\n    \"\"\"Creates configuration for full integer quantization.\n\n    Args:\n      representative_data: Representative data used for post-training\n        quantization.\n      quantization_steps: Number of post-training quantization calibration steps\n        to run.\n      inference_input_type: Target data type of real-number input arrays. Used\n        only when `is_integer_only` is True. Must be in `{tf.uint8, tf.int8}`.\n      inference_output_type: Target data type of real-number output arrays. Used\n        only when `is_integer_only` is True. Must be in `{tf.uint8, tf.int8}`.\n      supported_ops:  Set of `tf.lite.OpsSet` options, where each option\n        represents a set of operators supported by the target device.\n\n    Returns:\n      QuantizationConfig.\n    \"\"\"\n    return QuantizationConfig(\n        representative_data=representative_data,\n        quantization_steps=quantization_steps,\n        inference_input_type=inference_input_type,\n        inference_output_type=inference_output_type,\n        supported_ops=supported_ops)\n\n  @classmethod\n  def for_float16(cls):\n    \"\"\"Creates configuration for float16 quantization.\"\"\"\n    return QuantizationConfig(supported_types=[tf.float16])\n\n  def get_converter_with_quantization(self, converter, **kwargs):\n    \"\"\"Gets TFLite converter with settings for quantization.\"\"\"\n    converter.optimizations = self.optimizations\n\n    if self.representative_data is not None:\n      ds = self.representative_data.gen_dataset(\n          batch_size=1, is_training=False, **kwargs)\n      converter.representative_dataset = tf.lite.RepresentativeDataset(\n          _get_representative_dataset_gen(ds, self.quantization_steps))\n\n    if self.inference_input_type:\n      converter.inference_input_type = self.inference_input_type\n    if self.inference_output_type:\n      converter.inference_output_type = self.inference_output_type\n    if self.supported_ops:\n      converter.target_spec.supported_ops = self.supported_ops\n    if self.supported_types:\n      converter.target_spec.supported_types = self.supported_types\n\n    if self.experimental_new_quantizer is not None:\n      converter.experimental_new_quantizer = self.experimental_new_quantizer\n    return converter\n\n# Type for QuantizationConfig\nQuantizationConfigType = Optional[Union[str, QuantizationConfig]]\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/custom_model.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Base custom model that is already retained by data.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport abc\nimport inspect\nimport os\n\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.core.export_format import ExportFormat\nfrom tensorflow_examples.lite.model_maker.core.task import model_util\n\n\ndef _get_params(f, **kwargs):\n  \"\"\"Gets parameters of the function `f` from `**kwargs`.\"\"\"\n  parameters = inspect.signature(f).parameters\n  f_kwargs = {}  # kwargs for the function `f`\n  for param_name in parameters.keys():\n    if param_name in kwargs:\n      f_kwargs[param_name] = kwargs.pop(param_name)\n  return f_kwargs, kwargs\n\n\nclass CustomModel(abc.ABC):\n  \"\"\"\"The abstract base class that represents a Tensorflow classification model.\"\"\"\n\n  DEFAULT_EXPORT_FORMAT = (ExportFormat.TFLITE)\n  ALLOWED_EXPORT_FORMAT = (ExportFormat.TFLITE, ExportFormat.SAVED_MODEL,\n                           ExportFormat.TFJS)\n\n  def __init__(self, model_spec, shuffle):\n    \"\"\"Initialize a instance with data, deploy mode and other related parameters.\n\n    Args:\n      model_spec: Specification for the model.\n      shuffle: Whether the training data should be shuffled.\n    \"\"\"\n    self.model_spec = model_spec\n    self.shuffle = shuffle\n    self.model = None\n    # TODO(yuqili): remove this method once preprocess for image classifier is\n    # also moved to DataLoader part.\n    self.preprocess = None\n\n  @abc.abstractmethod\n  def train(self, train_data, validation_data=None, **kwargs):\n    return\n\n  def summary(self):\n    self.model.summary()\n\n  @abc.abstractmethod\n  def evaluate(self, data, **kwargs):\n    return\n\n  def _get_default_export_format(self, **kwargs):\n    \"\"\"Gets the default export format.\"\"\"\n    if kwargs.get('with_metadata', True):\n      export_format = (ExportFormat.TFLITE)\n    else:\n      export_format = self.DEFAULT_EXPORT_FORMAT\n    return export_format\n\n  def _get_export_format(self, export_format, **kwargs):\n    \"\"\"Get export format.\"\"\"\n    if export_format is None:\n      export_format = self._get_default_export_format(**kwargs)\n\n    if not isinstance(export_format, (list, tuple)):\n      export_format = (export_format,)\n\n    # Checks whether each export format is allowed.\n    for e_format in export_format:\n      if e_format not in self.ALLOWED_EXPORT_FORMAT:\n        raise ValueError('Export format %s is not allowed.' % e_format)\n\n    return export_format\n\n  def export(self,\n             export_dir,\n             tflite_filename='model.tflite',\n             label_filename='labels.txt',\n             vocab_filename='vocab.txt',\n             saved_model_filename='saved_model',\n             tfjs_folder_name='tfjs',\n             export_format=None,\n             **kwargs):\n    \"\"\"Converts the retrained model based on `export_format`.\n\n    Args:\n      export_dir: The directory to save exported files.\n      tflite_filename: File name to save tflite model. The full export path is\n        {export_dir}/{tflite_filename}.\n      label_filename: File name to save labels. The full export path is\n        {export_dir}/{label_filename}.\n      vocab_filename: File name to save vocabulary. The full export path is\n        {export_dir}/{vocab_filename}.\n      saved_model_filename: Path to SavedModel or H5 file to save the model. The\n        full export path is\n        {export_dir}/{saved_model_filename}/{saved_model.pb|assets|variables}.\n      tfjs_folder_name: Folder name to save tfjs model. The full export path is\n        {export_dir}/{tfjs_folder_name}.\n      export_format: List of export format that could be saved_model, tflite,\n        label, vocab.\n      **kwargs: Other parameters like `quantized_config` for TFLITE model.\n    \"\"\"\n    export_format = self._get_export_format(export_format, **kwargs)\n\n    if not tf.io.gfile.exists(export_dir):\n      tf.io.gfile.makedirs(export_dir)\n\n    if ExportFormat.TFLITE in export_format:\n      with_metadata = kwargs.get('with_metadata', True)\n      tflite_filepath = os.path.join(export_dir, tflite_filename)\n      export_tflite_kwargs, kwargs = _get_params(self._export_tflite, **kwargs)\n      self._export_tflite(tflite_filepath, **export_tflite_kwargs)\n      tf.compat.v1.logging.info(\n          'TensorFlow Lite model exported successfully: %s' % tflite_filepath)\n    else:\n      tflite_filepath = None\n      with_metadata = False\n\n    if ExportFormat.SAVED_MODEL in export_format:\n      saved_model_filepath = os.path.join(export_dir, saved_model_filename)\n      export_saved_model_kwargs, kwargs = _get_params(self._export_saved_model,\n                                                      **kwargs)\n      self._export_saved_model(saved_model_filepath,\n                               **export_saved_model_kwargs)\n\n    if ExportFormat.TFJS in export_format:\n      tfjs_output_path = os.path.join(export_dir, tfjs_folder_name)\n      self._export_tfjs(tfjs_output_path, tflite_filepath=tflite_filepath)\n\n    if ExportFormat.VOCAB in export_format:\n      if with_metadata:\n        tf.compat.v1.logging.warn('Export a separated vocab file even though '\n                                  'vocab file is already inside the TFLite '\n                                  'model with metadata.')\n      vocab_filepath = os.path.join(export_dir, vocab_filename)\n      self.model_spec.save_vocab(vocab_filepath)\n\n    if ExportFormat.LABEL in export_format:\n      if with_metadata:\n        tf.compat.v1.logging.warn('Export a separated label file even though '\n                                  'label file is already inside the TFLite '\n                                  'model with metadata.')\n      label_filepath = os.path.join(export_dir, label_filename)\n      self._export_labels(label_filepath)\n\n    if kwargs:\n      tf.compat.v1.logging.warn('Encountered unknown parameters: ' +\n                                str(kwargs))\n\n  def create_serving_model(self):\n    \"\"\"Returns the underlining Keras model for serving.\"\"\"\n    if hasattr(self.model_spec, 'create_serving_model'):\n      model = self.model_spec.create_serving_model(self.model)\n    else:\n      model = self.model\n    return model\n\n  def _export_saved_model(self,\n                          filepath,\n                          overwrite=True,\n                          include_optimizer=True,\n                          save_format=None,\n                          signatures=None,\n                          options=None):\n    \"\"\"Saves the model to Tensorflow SavedModel or a single HDF5 file.\n\n    Args:\n      filepath: String, path to SavedModel or H5 file to save the model.\n      overwrite: Whether to silently overwrite any existing file at the target\n        location, or provide the user with a manual prompt.\n      include_optimizer: If True, save optimizer's state together.\n      save_format: Either 'tf' or 'h5', indicating whether to save the model to\n        Tensorflow SavedModel or HDF5. Defaults to 'tf' in TF 2.X, and 'h5' in\n        TF 1.X.\n      signatures: Signatures to save with the SavedModel. Applicable to the 'tf'\n        format only. Please see the `signatures` argument in\n        `tf.saved_model.save` for details.\n      options: Optional `tf.saved_model.SaveOptions` object that specifies\n        options for saving to SavedModel.\n    \"\"\"\n    model = self.create_serving_model()\n    model_util.export_saved_model(model, filepath, overwrite, include_optimizer,\n                                  save_format, signatures, options)\n\n  def _export_tflite(self, tflite_filepath, quantization_config=None):\n    \"\"\"Converts the retrained model to tflite format and saves it.\n\n    Args:\n      tflite_filepath: File path to save tflite model.\n      quantization_config: Configuration for post-training quantization.\n    \"\"\"\n    model_util.export_tflite(self.model, tflite_filepath, quantization_config)\n\n  def _export_tfjs(self, tfjs_filepath, tflite_filepath=None, **kwargs):\n    \"\"\"Converts the retrained model to tflite format.\n\n    Args:\n      tfjs_filepath: File path to save tflite model.\n      tflite_filepath: File path to existing tflite model.\n      **kwargs: Additional kwargs.\n    \"\"\"\n    model_util.export_tfjs(\n        self.model, tfjs_filepath, tflite_filepath=tflite_filepath, **kwargs)\n\n  def _keras_callbacks(self, model_dir):\n    \"\"\"Returns a list of default keras callbacks for `model.fit`.\"\"\"\n    summary_dir = os.path.join(model_dir, 'summaries')\n    summary_callback = tf.keras.callbacks.TensorBoard(summary_dir)\n    checkpoint_path = os.path.join(model_dir, 'checkpoint')\n    checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(\n        checkpoint_path, save_weights_only=True)\n    return [summary_callback, checkpoint_callback]\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/custom_model_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport inspect\nimport os\n\nimport tensorflow.compat.v2 as tf\nfrom tensorflow_examples.lite.model_maker.core import test_util\nfrom tensorflow_examples.lite.model_maker.core.export_format import ExportFormat\nfrom tensorflow_examples.lite.model_maker.core.task import custom_model\n\n\nclass MockCustomModel(custom_model.CustomModel):\n\n  DEFAULT_EXPORT_FORMAT = (ExportFormat.TFLITE, ExportFormat.LABEL)\n  ALLOWED_EXPORT_FORMAT = (ExportFormat.TFLITE, ExportFormat.LABEL,\n                           ExportFormat.SAVED_MODEL, ExportFormat.TFJS)\n\n  def _export_labels(self, label_filepath):\n    with open(label_filepath, 'w') as f:\n      f.write('0\\n')\n\n  def train(self, train_data, validation_data=None, **kwargs):\n    pass\n\n  def evaluate(self, data, **kwargs):\n    pass\n\n\nclass CustomModelTest(tf.test.TestCase):\n\n  def setUp(self):\n    super(CustomModelTest, self).setUp()\n    self.model = MockCustomModel(\n        model_spec=None,\n        shuffle=False)\n    self.model.model = test_util.build_model(input_shape=[4], num_classes=2)\n\n  def _check_nonempty_dir(self, dirpath):\n    self.assertTrue(os.path.isdir(dirpath))\n    self.assertNotEmpty(os.listdir(dirpath))\n\n  def _check_nonempty_file(self, filepath):\n    self.assertTrue(os.path.isfile(filepath))\n    self.assertGreater(os.path.getsize(filepath), 0)\n\n  def test_export_saved_model(self):\n    saved_model_filepath = os.path.join(self.get_temp_dir(), 'saved_model/')\n    self.model._export_saved_model(saved_model_filepath)\n    self._check_nonempty_dir(saved_model_filepath)\n\n  def test_export(self):\n    # Test whether there's naming conflict in different export functions.\n    params1 = inspect.signature(\n        self.model._export_saved_model).parameters.keys()\n    params2 = inspect.signature(self.model._export_tflite).parameters.keys()\n    self.assertTrue(params1.isdisjoint(params2))\n\n    export_path = os.path.join(self.get_temp_dir(), 'export0/')\n    self.model.export(export_path)\n    self._check_nonempty_file(os.path.join(export_path, 'model.tflite'))\n    self.assertFalse(os.path.exists(os.path.join(export_path, 'labels.txt')))\n\n    export_path = os.path.join(self.get_temp_dir(), 'export1/')\n    self.model.export(export_path, with_metadata=False)\n    self._check_nonempty_file(os.path.join(export_path, 'model.tflite'))\n    self._check_nonempty_file(os.path.join(export_path, 'labels.txt'))\n\n    export_path = os.path.join(self.get_temp_dir(), 'export2/')\n    self.model.export(\n        export_path,\n        export_format=[\n            ExportFormat.TFLITE, ExportFormat.LABEL, ExportFormat.SAVED_MODEL\n        ],\n        with_metadata=True)\n    self._check_nonempty_file(os.path.join(export_path, 'model.tflite'))\n    self._check_nonempty_file(os.path.join(export_path, 'labels.txt'))\n    self._check_nonempty_dir(os.path.join(export_path, 'saved_model'))\n\n    export_path = os.path.join(self.get_temp_dir(), 'export3/')\n    self.model.export(\n        export_path,\n        export_format=[ExportFormat.TFLITE, ExportFormat.SAVED_MODEL],\n        with_metadata=True,\n        include_optimizer=False)\n    self._check_nonempty_file(os.path.join(export_path, 'model.tflite'))\n    self._check_nonempty_dir(os.path.join(export_path, 'saved_model'))\n\n    export_path = os.path.join(self.get_temp_dir(), 'export4/')\n    self.model.export(export_path, export_format=[ExportFormat.TFJS])\n    expected_file = os.path.join(export_path, 'tfjs', 'model.json')\n    self._check_nonempty_file(expected_file)\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/hub_loader.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Handles both V1 and V2 modules.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow_hub as hub\n\n\nclass HubKerasLayerV1V2(hub.KerasLayer):\n  \"\"\"Class to loads TF v1 and TF v1 hub modules that could be fine-tuned.\n\n  Since TF v1 modules couldn't be retrained in hub.KerasLayer. This class\n  provides a workaround for retraining the whole tf1 model in tf2. In\n  particular, it extract self._func._self_unconditional_checkpoint_dependencies\n  into trainable variable in tf1.\n\n  Doesn't update moving-mean/moving-variance for BatchNormalization during\n  fine-tuning.\n  \"\"\"\n\n  def _setup_layer(self, trainable=False, **kwargs):\n    if self._is_hub_module_v1:\n      self._setup_layer_v1(trainable, **kwargs)\n    else:\n      # call _setup_layer from the base class for v2.\n      super(HubKerasLayerV1V2, self)._setup_layer(trainable, **kwargs)\n\n  def _check_trainability(self):\n    if self._is_hub_module_v1:\n      self._check_trainability_v1()\n    else:\n      # call _check_trainability from the base class for v2.\n      super(HubKerasLayerV1V2, self)._check_trainability()\n\n  def _setup_layer_v1(self, trainable=False, **kwargs):\n    \"\"\"Constructs keras layer with relevant weights and losses.\"\"\"\n    # Initialize an empty layer, then add_weight() etc. as needed.\n    super(hub.KerasLayer, self).__init__(trainable=trainable, **kwargs)\n\n    if not self._is_hub_module_v1:\n      raise ValueError(\n          'Only supports to set up v1 hub module in this function.')\n\n    # v2 trainable_variable:\n    if hasattr(self._func, 'trainable_variables'):\n      for v in self._func.trainable_variables:\n        self._add_existing_weight(v, trainable=True)\n      trainable_variables = {id(v) for v in self._func.trainable_variables}\n    else:\n      trainable_variables = set()\n\n    if not hasattr(self._func, '_self_unconditional_checkpoint_dependencies'):\n      raise ValueError('_func doesn\\'t contains attribute '\n                       '_self_unconditional_checkpoint_dependencies.')\n    dependencies = self._func._self_unconditional_checkpoint_dependencies  # pylint: disable=protected-access\n\n    # Adds trainable variables.\n    for dep in dependencies:\n      if dep.name == 'variables':\n        for v in dep.ref:\n          if id(v) not in trainable_variables:\n            self._add_existing_weight(v, trainable=True)\n            trainable_variables.add(id(v))\n\n    # Adds non-trainable variables.\n    if hasattr(self._func, 'variables'):\n      for v in self._func.variables:\n        if id(v) not in trainable_variables:\n          self._add_existing_weight(v, trainable=False)\n\n    # Forward the callable's regularization losses (if any).\n    if hasattr(self._func, 'regularization_losses'):\n      for l in self._func.regularization_losses:\n        if not callable(l):\n          raise ValueError(\n              'hub.KerasLayer(obj) expects obj.regularization_losses to be an '\n              'iterable of callables, each returning a scalar loss term.')\n        self.add_loss(self._call_loss_if_trainable(l))  # Supports callables.\n\n  def _check_trainability_v1(self):\n    \"\"\"\"Ignores trainability checks for V1.\"\"\"\n    if self._is_hub_module_v1:\n      return  # Nothing to do.\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/hub_loader_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nfrom absl.testing import parameterized\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.core import test_util\nfrom tensorflow_examples.lite.model_maker.core.task import hub_loader\n\n\nclass HubKerasLayerV1V2Test(tf.test.TestCase, parameterized.TestCase):\n\n  @parameterized.parameters(\n      (\"hub_module_v1_mini\", True),\n      (\"saved_model_v2_mini\", True),\n      (\"hub_module_v1_mini\", False),\n      (\"saved_model_v2_mini\", False),\n  )\n  def test_load_with_defaults(self, module_name, trainable):\n    inputs, expected_outputs = 10., 11.  # Test modules perform increment op.\n    path = test_util.get_test_data_path(module_name)\n    layer = hub_loader.HubKerasLayerV1V2(path, trainable=trainable)\n    output = layer(inputs)\n    self.assertEqual(output, expected_outputs)\n\n  def test_trainable_varaible(self):\n    path = test_util.get_test_data_path(\"hub_module_v1_mini_train\")\n    layer = hub_loader.HubKerasLayerV1V2(path, trainable=True)\n    # Checks trainable variables.\n    self.assertLen(layer.trainable_variables, 2)\n    self.assertEqual(layer.trainable_variables[0].name, \"a:0\")\n    self.assertEqual(layer.trainable_variables[1].name, \"b:0\")\n    self.assertEqual(layer.variables, layer.trainable_variables)\n    # Checks non-trainable variables.\n    self.assertEmpty(layer.non_trainable_variables)\n\n    layer = hub_loader.HubKerasLayerV1V2(path, trainable=False)\n    # Checks trainable variables.\n    self.assertEmpty(layer.trainable_variables)\n    # Checks non-trainable variables.\n    self.assertLen(layer.non_trainable_variables, 2)\n    self.assertEqual(layer.non_trainable_variables[0].name, \"a:0\")\n    self.assertEqual(layer.non_trainable_variables[1].name, \"b:0\")\n    self.assertEqual(layer.variables, layer.non_trainable_variables)\n\n\nif __name__ == \"__main__\":\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/image_classifier.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"APIs to train an image classification model.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nimport tempfile\n\nimport tensorflow.compat.v2 as tf\nfrom tensorflow_examples.lite.model_maker.core.task import metadata_writer_for_image_classifier as metadata_writer\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core.api import mm_export\nfrom tensorflow_examples.lite.model_maker.core.task import classification_model\nfrom tensorflow_examples.lite.model_maker.core.task import hub_loader\nfrom tensorflow_examples.lite.model_maker.core.task import image_preprocessing\nfrom tensorflow_examples.lite.model_maker.core.task import make_image_classifier\nfrom tensorflow_examples.lite.model_maker.core.task import model_spec as ms\nfrom tensorflow_examples.lite.model_maker.core.task import model_util\nfrom tensorflow_examples.lite.model_maker.core.task import train_image_classifier_lib\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import image_spec\n\n\ndef get_hub_lib_hparams(**kwargs):\n  \"\"\"Gets the hyperparameters for the tensorflow hub's library.\"\"\"\n  hparams = make_image_classifier.get_default_hparams()\n  return train_image_classifier_lib.add_params(hparams, **kwargs)\n\n\ndef _get_model_info(model_spec,\n                    num_classes,\n                    quantization_config=None,\n                    version='v1'):\n  \"\"\"Gets the specific info for the image model.\"\"\"\n\n  if not isinstance(model_spec, image_spec.ImageModelSpec):\n    raise ValueError('Currently only support models for image classification.')\n\n  image_min = 0\n  image_max = 1\n\n  name = model_spec.name\n  if quantization_config:\n    name += '_quantized'\n    if quantization_config.inference_input_type == tf.uint8:\n      image_min = 0\n      image_max = 255\n    elif quantization_config.inference_input_type == tf.int8:\n      image_min = -128\n      image_max = 127\n\n  return metadata_writer.ModelSpecificInfo(\n      model_spec.name,\n      version,\n      image_width=model_spec.input_image_shape[1],\n      image_height=model_spec.input_image_shape[0],\n      mean=model_spec.mean_rgb,\n      std=model_spec.stddev_rgb,\n      image_min=image_min,\n      image_max=image_max,\n      num_classes=num_classes,\n      author='TensorFlow Lite Model Maker')\n\n\n@mm_export('image_classifier.ImageClassifier')\nclass ImageClassifier(classification_model.ClassificationModel):\n  \"\"\"ImageClassifier class for inference and exporting to tflite.\"\"\"\n\n  def __init__(\n      self,\n      model_spec,\n      index_to_label,\n      shuffle=True,\n      hparams=make_image_classifier.get_default_hparams(),\n      use_augmentation=False,\n      representative_data=None,\n  ):\n    \"\"\"Init function for ImageClassifier class.\n\n    Args:\n      model_spec: Specification for the model.\n      index_to_label: A list that map from index to label class name.\n      shuffle: Whether the data should be shuffled.\n      hparams: A namedtuple of hyperparameters. This function expects\n        .dropout_rate: The fraction of the input units to drop, used in dropout\n          layer.\n        .do_fine_tuning: If true, the Hub module is trained together with the\n          classification layer on top.\n      use_augmentation: Use data augmentation for preprocessing.\n      representative_data:  Representative dataset for full integer\n        quantization. Used when converting the keras model to the TFLite model\n        with full integer quantization.\n    \"\"\"\n    super(ImageClassifier, self).__init__(model_spec, index_to_label, shuffle,\n                                          hparams.do_fine_tuning)\n    num_classes = len(index_to_label)\n    self._hparams = hparams\n    self.preprocess = image_preprocessing.Preprocessor(\n        self.model_spec.input_image_shape,\n        num_classes,\n        self.model_spec.mean_rgb,\n        self.model_spec.stddev_rgb,\n        use_augmentation=use_augmentation)\n    self.history = None  # Training history that returns from `keras_model.fit`.\n    self.representative_data = representative_data\n\n  def _get_tflite_input_tensors(self, input_tensors):\n    \"\"\"Gets the input tensors for the TFLite model.\"\"\"\n    return input_tensors\n\n  def create_model(self, hparams=None, with_loss_and_metrics=False):\n    \"\"\"Creates the classifier model for retraining.\"\"\"\n    hparams = self._get_hparams_or_default(hparams)\n\n    module_layer = hub_loader.HubKerasLayerV1V2(\n        self.model_spec.uri, trainable=hparams.do_fine_tuning)\n    self.model = make_image_classifier.build_model(\n        module_layer,\n        hparams,\n        self.model_spec.input_image_shape,\n        self.num_classes,\n    )\n    if with_loss_and_metrics:\n      # Adds loss and metrics in the keras model.\n      self.model.compile(\n          loss=tf.keras.losses.CategoricalCrossentropy(label_smoothing=0.1),\n          metrics=['accuracy'])\n\n  def train(self,\n            train_data,\n            validation_data=None,\n            hparams=None,\n            steps_per_epoch=None):\n    \"\"\"Feeds the training data for training.\n\n    Args:\n      train_data: Training data.\n      validation_data: Validation data. If None, skips validation process.\n      hparams: An instance of make_image_classifier.HParams or\n        train_image_classifier_lib.HParams. Anamedtuple of hyperparameters.\n      steps_per_epoch: Integer or None. Total number of steps (batches of\n        samples) before declaring one epoch finished and starting the next\n        epoch. If 'steps_per_epoch' is None, the epoch will run until the input\n        dataset is exhausted.\n\n    Returns:\n      The tf.keras.callbacks.History object returned by tf.keras.Model.fit*().\n    \"\"\"\n    self.create_model()\n    hparams = self._get_hparams_or_default(hparams)\n\n    if len(train_data) < hparams.batch_size:\n      raise ValueError('The size of the train_data (%d) couldn\\'t be smaller '\n                       'than batch_size (%d). To solve this problem, set '\n                       'the batch_size smaller or increase the size of the '\n                       'train_data.' % (len(train_data), hparams.batch_size))\n\n    train_ds = train_data.gen_dataset(\n        hparams.batch_size,\n        is_training=True,\n        shuffle=self.shuffle,\n        preprocess=self.preprocess)\n    steps_per_epoch = model_util.get_steps_per_epoch(steps_per_epoch,\n                                                     hparams.batch_size,\n                                                     train_data)\n    if steps_per_epoch is not None:\n      train_ds = train_ds.take(steps_per_epoch)\n\n    validation_ds = None\n    if validation_data is not None:\n      validation_ds = validation_data.gen_dataset(\n          hparams.batch_size, is_training=False, preprocess=self.preprocess)\n\n    # Trains the models.\n    if isinstance(hparams, train_image_classifier_lib.HParams):\n      train_model = train_image_classifier_lib.train_model\n    else:\n      train_model = train_image_classifier_lib.hub_train_model\n\n    self.history = train_model(\n        model=self.model,\n        hparams=hparams,\n        train_ds=train_ds,\n        validation_ds=validation_ds,\n        steps_per_epoch=steps_per_epoch)\n\n  def _export_tflite(self,\n                     tflite_filepath,\n                     quantization_config='default',\n                     with_metadata=True,\n                     export_metadata_json_file=False):\n    \"\"\"Converts the retrained model to tflite format and saves it.\n\n    Args:\n      tflite_filepath: File path to save tflite model.\n      quantization_config: Configuration for post-training quantization. If\n        'default', sets the `quantization_config` by default according to\n        `self.model_spec`. If None, exports the float tflite model without\n        quantization.\n      with_metadata: Whether the output tflite model contains metadata.\n      export_metadata_json_file: Whether to export metadata in json file. If\n        True, export the metadata in the same directory as tflite model.Used\n        only if `with_metadata` is True.\n    \"\"\"\n    if quantization_config == 'default':\n      quantization_config = self.model_spec.get_default_quantization_config(\n          self.representative_data)\n    model_util.export_tflite(\n        self.model,\n        tflite_filepath,\n        quantization_config,\n        preprocess=self.preprocess)\n    if with_metadata:\n      with tempfile.TemporaryDirectory() as temp_dir:\n        tf.compat.v1.logging.info(\n            'Label file is inside the TFLite model with metadata.')\n        label_filepath = os.path.join(temp_dir, 'labels.txt')\n        self._export_labels(label_filepath)\n\n        model_info = _get_model_info(\n            self.model_spec,\n            self.num_classes,\n            quantization_config=quantization_config)\n        # Generate the metadata objects and put them in the model file\n        populator = metadata_writer.MetadataPopulatorForImageClassifier(\n            tflite_filepath, model_info, label_filepath)\n        populator.populate()\n\n      # Validate the output model file by reading the metadata and produce\n      # a json file with the metadata under the export path\n      if export_metadata_json_file:\n        metadata_json = model_util.extract_tflite_metadata_json(tflite_filepath)\n        export_json_file = os.path.splitext(tflite_filepath)[0] + '.json'\n        with open(export_json_file, 'w') as f:\n          f.write(metadata_json)\n\n  def _get_hparams_or_default(self, hparams):\n    \"\"\"Returns hparams if not none, otherwise uses default one.\"\"\"\n    return hparams if hparams else self._hparams\n\n  @classmethod\n  def create(cls,\n             train_data,\n             model_spec='efficientnet_lite0',\n             validation_data=None,\n             batch_size=None,\n             epochs=None,\n             steps_per_epoch=None,\n             train_whole_model=None,\n             dropout_rate=None,\n             learning_rate=None,\n             momentum=None,\n             shuffle=False,\n             use_augmentation=False,\n             use_hub_library=True,\n             warmup_steps=None,\n             model_dir=None,\n             do_train=True):\n    \"\"\"Loads data and retrains the model based on data for image classification.\n\n    Args:\n      train_data: Training data.\n      model_spec: Specification for the model.\n      validation_data: Validation data. If None, skips validation process.\n      batch_size: Number of samples per training step. If `use_hub_library` is\n        False, it represents the base learning rate when train batch size is 256\n        and it's linear to the batch size.\n      epochs: Number of epochs for training.\n      steps_per_epoch: Integer or None. Total number of steps (batches of\n        samples) before declaring one epoch finished and starting the next\n        epoch. If `steps_per_epoch` is None, the epoch will run until the input\n        dataset is exhausted.\n      train_whole_model: If true, the Hub module is trained together with the\n        classification layer on top. Otherwise, only train the top\n        classification layer.\n      dropout_rate: The rate for dropout.\n      learning_rate: Base learning rate when train batch size is 256. Linear to\n        the batch size.\n      momentum: a Python float forwarded to the optimizer. Only used when\n        `use_hub_library` is True.\n      shuffle: Whether the data should be shuffled.\n      use_augmentation: Use data augmentation for preprocessing.\n      use_hub_library: Use `make_image_classifier_lib` from tensorflow hub to\n        retrain the model.\n      warmup_steps: Number of warmup steps for warmup schedule on learning rate.\n        If None, the default warmup_steps is used which is the total training\n        steps in two epochs. Only used when `use_hub_library` is False.\n      model_dir: The location of the model checkpoint files. Only used when\n        `use_hub_library` is False.\n      do_train: Whether to run training.\n\n    Returns:\n      An instance based on ImageClassifier.\n    \"\"\"\n    model_spec = ms.get(model_spec)\n    if compat.get_tf_behavior() not in model_spec.compat_tf_versions:\n      raise ValueError('Incompatible versions. Expect {}, but got {}.'.format(\n          model_spec.compat_tf_versions, compat.get_tf_behavior()))\n\n    if use_hub_library:\n      hparams = get_hub_lib_hparams(\n          batch_size=batch_size,\n          train_epochs=epochs,\n          do_fine_tuning=train_whole_model,\n          dropout_rate=dropout_rate,\n          learning_rate=learning_rate,\n          momentum=momentum)\n    else:\n      hparams = train_image_classifier_lib.HParams.get_hparams(\n          batch_size=batch_size,\n          train_epochs=epochs,\n          do_fine_tuning=train_whole_model,\n          dropout_rate=dropout_rate,\n          learning_rate=learning_rate,\n          warmup_steps=warmup_steps,\n          model_dir=model_dir)\n\n    image_classifier = cls(\n        model_spec,\n        train_data.index_to_label,\n        shuffle=shuffle,\n        hparams=hparams,\n        use_augmentation=use_augmentation,\n        representative_data=train_data)\n\n    if do_train:\n      tf.compat.v1.logging.info('Retraining the models...')\n      image_classifier.train(train_data, validation_data, steps_per_epoch)\n    else:\n      # Used in evaluation.\n      image_classifier.create_model(with_loss_and_metrics=True)\n\n    return image_classifier\n\n\n# Shortcut function.\ncreate = ImageClassifier.create\nmm_export('image_classifier.create').export_constant(__name__, 'create')\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/image_classifier_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport filecmp\nimport os\n\nimport numpy as np\nimport tensorflow.compat.v2 as tf\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core import test_util\nfrom tensorflow_examples.lite.model_maker.core.data_util import image_dataloader\nfrom tensorflow_examples.lite.model_maker.core.export_format import ExportFormat\nfrom tensorflow_examples.lite.model_maker.core.task import image_classifier\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import image_spec\n\n\ndef _fill_image(rgb, image_size):\n  r, g, b = rgb\n  return np.broadcast_to(\n      np.array([[[r, g, b]]], dtype=np.uint8),\n      shape=(image_size, image_size, 3))\n\n\nclass ImageClassifierTest(tf.test.TestCase):\n  IMAGE_SIZE = 24\n  IMAGES_PER_CLASS = 2\n  CMY_NAMES_AND_RGB_VALUES = (('cyan', (0, 255, 255)),\n                              ('magenta', (255, 0, 255)), ('yellow', (255, 255,\n                                                                      0)))\n\n  def _gen(self):\n    for i, (_, rgb) in enumerate(self.CMY_NAMES_AND_RGB_VALUES):\n      for _ in range(self.IMAGES_PER_CLASS):\n        yield (_fill_image(rgb, self.IMAGE_SIZE), i)\n\n  def _gen_cmy_data(self):\n    ds = tf.data.Dataset.from_generator(\n        self._gen, (tf.uint8, tf.int64), (tf.TensorShape(\n            [self.IMAGE_SIZE, self.IMAGE_SIZE, 3]), tf.TensorShape([])))\n    data = image_dataloader.ImageClassifierDataLoader(\n        ds, self.IMAGES_PER_CLASS * 3, ['cyan', 'magenta', 'yellow'])\n    return data\n\n  def setUp(self):\n    super(ImageClassifierTest, self).setUp()\n    all_data = self._gen_cmy_data()\n    # Splits data, 90% data for training, 10% for testing\n    self.train_data, self.test_data = all_data.split(0.9)\n\n  @test_util.test_in_tf_2\n  def test_mobilenetv2_model(self):\n    model = image_classifier.create(\n        self.train_data,\n        image_spec.mobilenet_v2_spec(),\n        epochs=1,\n        batch_size=1,\n        shuffle=True)\n    self._test_accuracy(model)\n    self._test_predict_top_k(model)\n    self._test_export_to_tflite(model)\n    self._test_export_to_tflite_quantized(model, model_size=2779577)\n    self._test_export_to_tflite_with_metadata(model)\n    self._test_export_to_saved_model(model)\n    self._test_export_labels(model)\n    self._test_export_to_tfjs(model)\n\n  @test_util.test_in_tf_1\n  def test_mobilenetv2_model_create_v1_incompatible(self):\n    with self.assertRaisesRegex(ValueError, 'Incompatible versions'):\n      _ = image_classifier.create(self.train_data,\n                                  image_spec.mobilenet_v2_spec())\n\n  @test_util.test_in_tf_1and2\n  def test_efficientnetlite0_model_with_model_maker_retraining_lib(self):\n    model = image_classifier.create(\n        self.train_data,\n        image_spec.efficientnet_lite0_spec(),\n        epochs=1,\n        batch_size=1,\n        shuffle=True,\n        use_hub_library=False)\n    self._test_accuracy(model)\n    self._test_export_to_tflite(model)\n\n  @test_util.test_in_tf_1and2\n  def test_efficientnetlite0_model(self):\n    model = image_classifier.create(\n        self.train_data,\n        image_spec.efficientnet_lite0_spec(),\n        epochs=1,\n        batch_size=1,\n        shuffle=True)\n    self._test_accuracy(model)\n    self._test_export_to_tflite(model)\n    self._test_export_to_tflite_quantized(model, model_size=4007249)\n    self._test_export_to_tflite_with_metadata(\n        model, expected_json_file='efficientnet_lite0_metadata.json')\n    self._test_export_to_tfjs(model)\n\n  @test_util.test_in_tf_1and2\n  def test_efficientnetlite0_model_without_training(self):\n    model = image_classifier.create(\n        self.train_data, image_spec.efficientnet_lite0_spec(), do_train=False)\n    self._test_accuracy(model, threshold=0.0)\n    self._test_export_to_tflite(model, threshold=0.0)\n\n  @test_util.test_in_tf_2\n  def test_resnet_50_model(self):\n    model = image_classifier.create(\n        self.train_data,\n        image_spec.resnet_50_spec(),\n        epochs=1,\n        batch_size=1,\n        shuffle=True)\n    self._test_accuracy(model)\n    self._test_export_to_tflite(model)\n    self._test_export_to_tflite_quantized(model, model_size=24377953)\n    self._test_export_to_tflite_with_metadata(model)\n    self._test_export_to_tfjs(model)\n\n  def _test_predict_top_k(self, model, threshold=0.0):\n    topk = model.predict_top_k(self.test_data, batch_size=4)\n    for i, (_, label) in enumerate(self.test_data.gen_dataset().unbatch()):\n      predict_label, predict_prob = topk[i][0][0], topk[i][0][1]\n      self.assertEqual(model.index_to_label[label], predict_label)\n      self.assertGreaterEqual(predict_prob, threshold)\n\n  def _test_accuracy(self, model, threshold=0.0):\n    _, accuracy = model.evaluate(self.test_data)\n    self.assertGreaterEqual(accuracy, threshold)\n\n  def _load_labels(self, filename):\n    with tf.io.gfile.GFile(filename, 'r') as f:\n      return [label.strip() for label in f]\n\n  def _test_export_labels(self, model):\n    labels_output_file = os.path.join(self.get_temp_dir(), 'labels.txt')\n    model.export(self.get_temp_dir(), export_format=ExportFormat.LABEL)\n    self._check_label_file(labels_output_file)\n\n  def _test_export_to_tflite(self, model, threshold=0.0):\n    tflite_output_file = os.path.join(self.get_temp_dir(), 'model.tflite')\n\n    model.export(\n        self.get_temp_dir(),\n        export_format=ExportFormat.TFLITE,\n        quantization_config=None)\n\n    result = model.evaluate_tflite(tflite_output_file, self.test_data)\n    self.assertGreaterEqual(result['accuracy'], threshold)\n\n    random_input = np.random.uniform(\n        size=[1] + model.model_spec.input_image_shape + [3]).astype(np.float32)\n    self.assertTrue(\n        test_util.is_same_output(tflite_output_file, model.model, random_input,\n                                 model.model_spec))\n\n  def _test_export_to_tflite_quantized(self, model, model_size, err_ratio=0.08):\n    # Just test whether quantization will crash, can't guarantee the result.\n    tflile_filename = 'model_quantized.tflite'\n    tflite_output_file = os.path.join(self.get_temp_dir(), tflile_filename)\n    model.export(\n        self.get_temp_dir(),\n        tflile_filename,\n        export_format=ExportFormat.TFLITE)\n    self.assertTrue(os.path.isfile(tflite_output_file))\n    err = model_size * err_ratio\n    self.assertNear(os.path.getsize(tflite_output_file), model_size, err)\n\n  def _check_label_file(self, labels_output_file):\n    labels = self._load_labels(labels_output_file)\n    self.assertEqual(labels, ['cyan', 'magenta', 'yellow'])\n\n  def _test_export_to_tflite_with_metadata(self,\n                                           model,\n                                           expected_json_file=None):\n    model_name = 'model_with_metadata'\n    tflite_output_file = os.path.join(self.get_temp_dir(),\n                                      '%s.tflite' % model_name)\n    json_output_file = os.path.join(self.get_temp_dir(), '%s.json' % model_name)\n    labels_output_file = os.path.join(self.get_temp_dir(), 'labels.txt')\n\n    model.export(\n        self.get_temp_dir(),\n        '%s.tflite' % model_name,\n        quantization_config=None,\n        with_metadata=True,\n        export_metadata_json_file=True)\n\n    self.assertTrue(os.path.isfile(tflite_output_file))\n    self.assertGreater(os.path.getsize(tflite_output_file), 0)\n\n    self.assertFalse(os.path.isfile(labels_output_file))\n\n    self.assertTrue(os.path.isfile(json_output_file))\n    self.assertGreater(os.path.getsize(json_output_file), 0)\n\n    if expected_json_file is not None:\n      expected_json_file = test_util.get_test_data_path(expected_json_file)\n      self.assertTrue(filecmp.cmp(json_output_file, expected_json_file))\n\n  def _test_export_to_saved_model(self, model):\n    save_model_output_path = os.path.join(self.get_temp_dir(), 'saved_model')\n    model.export(self.get_temp_dir(), export_format=ExportFormat.SAVED_MODEL)\n\n    self.assertTrue(os.path.isdir(save_model_output_path))\n    self.assertNotEqual(len(os.listdir(save_model_output_path)), 0)\n\n  def _test_export_to_tfjs(self, model):\n    output_path = os.path.join(self.get_temp_dir(), 'tfjs')\n    model.export(\n        self.get_temp_dir(),\n        export_format=[ExportFormat.TFLITE, ExportFormat.TFJS])\n\n    self.assertTrue(os.path.isdir(output_path))\n    self.assertNotEqual(len(os.listdir(output_path)), 0)\n\n\nif __name__ == '__main__':\n  # Load compressed models from tensorflow_hub\n  os.environ['TFHUB_MODEL_LOAD_FORMAT'] = 'COMPRESSED'\n  compat.setup_tf_behavior(tf_version=2)\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/image_classifier_v1_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core.task import image_classifier_test\n\n\nclass ImageClassifierV1Test(image_classifier_test.ImageClassifierTest):\n  \"\"\"Share image tests of the base class, but in tf v1 behavior.\"\"\"\n\n\nif __name__ == '__main__':\n  compat.setup_tf_behavior(tf_version=1)\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/image_preprocessing.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"ImageNet preprocessing.\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow.compat.v2 as tf\n\n\nIMAGE_SIZE = 224\nCROP_PADDING = 32\n\n\nclass Preprocessor(object):\n  \"\"\"Preprocessing for image classification.\"\"\"\n\n  def __init__(self,\n               input_shape,\n               num_classes,\n               mean_rgb,\n               stddev_rgb,\n               use_augmentation=False):\n    self.input_shape = input_shape\n    self.num_classes = num_classes\n    self.mean_rgb = mean_rgb\n    self.stddev_rgb = stddev_rgb\n    self.use_augmentation = use_augmentation\n\n  def __call__(self, image, label, is_training=True):\n    if self.use_augmentation:\n      return self._preprocess_with_augmentation(image, label, is_training)\n    return self._preprocess_without_augmentation(image, label)\n\n  def _preprocess_with_augmentation(self, image, label, is_training):\n    \"\"\"Image preprocessing method with data augmentation.\"\"\"\n    image_size = self.input_shape[0]\n    if is_training:\n      image = preprocess_for_train(image, image_size)\n    else:\n      image = preprocess_for_eval(image, image_size)\n\n    image -= tf.constant(self.mean_rgb, shape=[1, 1, 3], dtype=image.dtype)\n    image /= tf.constant(self.stddev_rgb, shape=[1, 1, 3], dtype=image.dtype)\n\n    label = tf.one_hot(label, depth=self.num_classes)\n    return image, label\n\n  # TODO(yuqili): Changes to preprocess to support batch input.\n  def _preprocess_without_augmentation(self, image, label):\n    \"\"\"Image preprocessing method without data augmentation.\"\"\"\n    image = tf.cast(image, tf.float32)\n\n    image -= tf.constant(self.mean_rgb, shape=[1, 1, 3], dtype=image.dtype)\n    image /= tf.constant(self.stddev_rgb, shape=[1, 1, 3], dtype=image.dtype)\n\n    image = tf.compat.v1.image.resize(image, self.input_shape)\n    label = tf.one_hot(label, depth=self.num_classes)\n    return image, label\n\n\ndef distorted_bounding_box_crop(image,\n                                bbox,\n                                min_object_covered=0.1,\n                                aspect_ratio_range=(0.75, 1.33),\n                                area_range=(0.05, 1.0),\n                                max_attempts=100):\n  \"\"\"Generates cropped_image using one of the bboxes randomly distorted.\n\n  See `tf.image.sample_distorted_bounding_box` for more documentation.\n\n  Args:\n    image: 4-D Tensor of shape [batch, height, width, channels] or 3-D Tensor\n      of shape [height, width, channels].\n    bbox: `Tensor` of bounding boxes arranged `[1, num_boxes, coords]` where\n      each coordinate is [0, 1) and the coordinates are arranged as `[ymin,\n      xmin, ymax, xmax]`. If num_boxes is 0 then use the whole image.\n    min_object_covered: An optional `float`. Defaults to `0.1`. The cropped area\n      of the image must contain at least this fraction of any bounding box\n      supplied.\n    aspect_ratio_range: An optional list of `float`s. The cropped area of the\n      image must have an aspect ratio = width / height within this range.\n    area_range: An optional list of `float`s. The cropped area of the image must\n      contain a fraction of the supplied image within in this range.\n    max_attempts: An optional `int`. Number of attempts at generating a cropped\n      region of the image of the specified constraints. After `max_attempts`\n      failures, return the entire image.\n\n  Returns:\n    cropped image `Tensor`\n  \"\"\"\n  with tf.name_scope('distorted_bounding_box_crop'):\n    shape = tf.shape(image)\n    sample_distorted_bounding_box = tf.image.sample_distorted_bounding_box(\n        shape,\n        bounding_boxes=bbox,\n        min_object_covered=min_object_covered,\n        aspect_ratio_range=aspect_ratio_range,\n        area_range=area_range,\n        max_attempts=max_attempts,\n        use_image_if_no_bounding_boxes=True)\n    bbox_begin, bbox_size, _ = sample_distorted_bounding_box\n\n    # Crop the image to the specified bounding box.\n    offset_y, offset_x, _ = tf.unstack(bbox_begin)\n    target_height, target_width, _ = tf.unstack(bbox_size)\n    image = tf.image.crop_to_bounding_box(image, offset_y, offset_x,\n                                          target_height, target_width)\n\n    return image\n\n\ndef _at_least_x_are_equal(a, b, x):\n  \"\"\"At least `x` of `a` and `b` `Tensors` are equal.\"\"\"\n  match = tf.equal(a, b)\n  match = tf.cast(match, tf.int32)\n  return tf.greater_equal(tf.reduce_sum(match), x)\n\n\ndef _resize_image(image, image_size, method=None):\n  if method is not None:\n    tf.compat.v1.logging.info('Use customized resize method {}'.format(method))\n    return tf.compat.v1.image.resize([image], [image_size, image_size],\n                                     method)[0]\n  tf.compat.v1.logging.info('Use default resize_bicubic.')\n  return tf.compat.v1.image.resize_bicubic([image], [image_size, image_size])[0]\n\n\ndef _decode_and_random_crop(original_image, image_size, resize_method=None):\n  \"\"\"Make a random crop of image_size.\"\"\"\n  bbox = tf.constant([0.0, 0.0, 1.0, 1.0], dtype=tf.float32, shape=[1, 1, 4])\n  image = distorted_bounding_box_crop(\n      original_image,\n      bbox,\n      min_object_covered=0.1,\n      aspect_ratio_range=(3. / 4, 4. / 3.),\n      area_range=(0.08, 1.0),\n      max_attempts=10)\n  original_shape = tf.shape(original_image)\n  bad = _at_least_x_are_equal(original_shape, tf.shape(image), 3)\n\n  image = tf.cond(bad,\n                  lambda: _decode_and_center_crop(original_image, image_size),\n                  lambda: _resize_image(image, image_size, resize_method))\n\n  return image\n\n\ndef _decode_and_center_crop(image, image_size, resize_method=None):\n  \"\"\"Crops to center of image with padding then scales image_size.\"\"\"\n  shape = tf.shape(image)\n  image_height = shape[0]\n  image_width = shape[1]\n\n  padded_center_crop_size = tf.cast(\n      ((image_size / (image_size + CROP_PADDING)) *\n       tf.cast(tf.minimum(image_height, image_width), tf.float32)), tf.int32)\n\n  offset_height = ((image_height - padded_center_crop_size) + 1) // 2\n  offset_width = ((image_width - padded_center_crop_size) + 1) // 2\n  image = tf.image.crop_to_bounding_box(image, offset_height, offset_width,\n                                        padded_center_crop_size,\n                                        padded_center_crop_size)\n  image = _resize_image(image, image_size, resize_method)\n  return image\n\n\ndef _flip(image):\n  \"\"\"Random horizontal image flip.\"\"\"\n  image = tf.image.random_flip_left_right(image)\n  return image\n\n\ndef preprocess_for_train(image,\n                         image_size=IMAGE_SIZE,\n                         resize_method=tf.image.ResizeMethod.BILINEAR):\n  \"\"\"Preprocesses the given image for evaluation.\n\n  Args:\n    image: 4-D Tensor of shape [batch, height, width, channels] or 3-D Tensor\n      of shape [height, width, channels].\n    image_size: image size.\n    resize_method: resize method. If none, use bicubic.\n\n  Returns:\n    A preprocessed image `Tensor`.\n  \"\"\"\n  image = _decode_and_random_crop(image, image_size, resize_method)\n  image = _flip(image)\n  image = tf.reshape(image, [image_size, image_size, 3])\n\n  image = tf.image.convert_image_dtype(image, dtype=tf.float32)\n\n  return image\n\n\ndef preprocess_for_eval(image,\n                        image_size=IMAGE_SIZE,\n                        resize_method=tf.image.ResizeMethod.BILINEAR):\n  \"\"\"Preprocesses the given image for evaluation.\n\n  Args:\n    image: 4-D Tensor of shape [batch, height, width, channels] or 3-D Tensor\n      of shape [height, width, channels].\n    image_size: image size.\n    resize_method: if None, use bicubic.\n\n  Returns:\n    A preprocessed image `Tensor`.\n  \"\"\"\n  image = _decode_and_center_crop(image, image_size, resize_method)\n  image = tf.reshape(image, [image_size, image_size, 3])\n  image = tf.image.convert_image_dtype(image, dtype=tf.float32)\n  return image\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/image_preprocessing_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport numpy as np\nimport tensorflow.compat.v2 as tf\n\nfrom tensorflow_examples.lite.model_maker.core.task import image_preprocessing\n\n\ndef _get_preprocessed_image(preprocessor, is_training=False):\n  image_placeholder = tf.compat.v1.placeholder(tf.uint8, [24, 24, 3])\n  label_placeholder = tf.compat.v1.placeholder(tf.int32, [1])\n  image_tensor, _ = preprocessor(image_placeholder, label_placeholder,\n                                 is_training)\n\n  with tf.compat.v1.Session() as sess:\n    input_image = np.arange(24 * 24 * 3, dtype=np.uint8).reshape([24, 24, 3])\n    image = sess.run(\n        image_tensor,\n        feed_dict={\n            image_placeholder: input_image,\n            label_placeholder: [0]\n        })\n    return image\n\n\nclass PreprocessorTest(tf.test.TestCase):\n\n  def test_without_augmentation(self):\n    preprocessor = image_preprocessing.Preprocessor([2, 2],\n                                                    2,\n                                                    mean_rgb=[0.0],\n                                                    stddev_rgb=[255.0],\n                                                    use_augmentation=False)\n    actual_image = np.array([[[0., 0.00392157, 0.00784314],\n                              [0.14117648, 0.14509805, 0.14901961]],\n                             [[0.37647063, 0.3803922, 0.38431376],\n                              [0.5176471, 0.52156866, 0.5254902]]])\n\n    image = _get_preprocessed_image(preprocessor)\n    self.assertTrue(np.allclose(image, actual_image, atol=1e-05))\n\n  def test_with_augmentation(self):\n    image_preprocessing.CROP_PADDING = 1\n    preprocessor = image_preprocessing.Preprocessor([2, 2],\n                                                    2,\n                                                    mean_rgb=[0.0],\n                                                    stddev_rgb=[255.0],\n                                                    use_augmentation=True)\n    # Tests validation image.\n    actual_eval_image = np.array([[[0.17254902, 0.1764706, 0.18039216],\n                                   [0.26666668, 0.27058825, 0.27450982]],\n                                  [[0.42352945, 0.427451, 0.43137258],\n                                   [0.5176471, 0.52156866, 0.5254902]]])\n\n    image = _get_preprocessed_image(preprocessor, is_training=False)\n    self.assertTrue(np.allclose(image, actual_eval_image, atol=1e-05))\n\n    # Tests training image.\n    image1 = _get_preprocessed_image(preprocessor, is_training=True)\n    image2 = _get_preprocessed_image(preprocessor, is_training=True)\n    self.assertFalse(np.allclose(image1, image2, atol=1e-05))\n    self.assertEqual(image1.shape, (2, 2, 3))\n    self.assertEqual(image2.shape, (2, 2, 3))\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/make_image_classifier.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Utils for building image classifiers.\"\"\"\n\nimport collections\nimport tensorflow as tf\n\n\nclass HParams(\n    collections.namedtuple(\n        'HParams',\n        [\n            'train_epochs',\n            'do_fine_tuning',\n            'batch_size',\n            'learning_rate',\n            'momentum',\n            'dropout_rate',\n            'l1_regularizer',\n            'l2_regularizer',\n            'label_smoothing',\n            'validation_split',\n            'do_data_augmentation',\n            'rotation_range',\n            'horizontal_flip',\n            'width_shift_range',\n            'height_shift_range',\n            'shear_range',\n            'zoom_range',\n        ],\n    )\n):\n  \"\"\"The hyperparameters for make_image_classifier.\n\n  train_epochs: Training will do this many iterations over the dataset.\n  do_fine_tuning: If true, the Hub module is trained together with the\n    classification layer on top.\n  batch_size: Each training step samples a batch of this many images.\n  learning_rate: The learning rate to use for gradient descent training.\n  momentum: The momentum parameter to use for gradient descent training.\n  dropout_rate: The fraction of the input units to drop, used in dropout layer.\n  \"\"\"\n\n\ndef get_default_hparams():\n  return HParams(\n      train_epochs=5,\n      do_fine_tuning=False,\n      batch_size=32,\n      learning_rate=0.005,\n      momentum=0.9,\n      dropout_rate=0.2,\n      l1_regularizer=0.0,\n      l2_regularizer=0.0001,\n      label_smoothing=0.1,\n      validation_split=0.2,\n      do_data_augmentation=False,\n      rotation_range=40,\n      horizontal_flip=True,\n      width_shift_range=0.2,\n      height_shift_range=0.2,\n      shear_range=0,\n      zoom_range=0.2,\n  )\n\n\ndef build_model(module_layer, hparams, image_size, num_classes):\n  \"\"\"Builds the full classifier model from the given module_layer.\n\n  If using a DistributionStrategy, call this under its `.scope()`.\n  Args:\n    module_layer: Pre-trained tfhub model layer.\n    hparams: A namedtuple of hyperparameters. This function expects\n      .dropout_rate: The fraction of the input units to drop, used in dropout\n      layer.\n    image_size: The input image size to use with the given module layer.\n    num_classes: Number of the classes to be predicted.\n\n  Returns:\n    The full classifier model.\n  \"\"\"\n  model = tf.keras.Sequential([\n      tf.keras.Input(shape=(image_size[0], image_size[1], 3)),\n      module_layer,\n      tf.keras.layers.Dropout(rate=hparams.dropout_rate),\n      tf.keras.layers.Dense(\n          num_classes,\n          activation='softmax',\n          kernel_regularizer=tf.keras.regularizers.l1_l2(\n              l1=hparams.l1_regularizer, l2=hparams.l2_regularizer\n          ),\n      ),\n  ])\n  print(model.summary())\n  return model\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/metadata_writer_for_image_classifier.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Writes metadata and label file to the image classifier models.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\n\nfrom absl import app\nfrom absl import flags\nimport flatbuffers\nimport tensorflow as tf\n\n# pylint: disable=g-direct-tensorflow-import\nfrom tflite_support import metadata_schema_py_generated as _metadata_fb\nfrom tflite_support import metadata as _metadata\n# pylint: enable=g-direct-tensorflow-import\n\nFLAGS = flags.FLAGS\n\n\ndef define_flags():\n  flags.DEFINE_string(\"model_file\", None,\n                      \"Path and file name to the TFLite model file.\")\n  flags.DEFINE_string(\"label_file\", None, \"Path to the label file.\")\n  flags.DEFINE_string(\"export_directory\", None,\n                      \"Path to save the TFLite model files with metadata.\")\n  flags.mark_flag_as_required(\"model_file\")\n  flags.mark_flag_as_required(\"label_file\")\n  flags.mark_flag_as_required(\"export_directory\")\n\n\nclass ModelSpecificInfo(object):\n  \"\"\"Holds information that is specificly tied to an image classifier.\"\"\"\n\n  def __init__(self, name, version, image_width, image_height, image_min,\n               image_max, mean, std, num_classes, author):\n    self.name = name\n    self.version = version\n    self.image_width = image_width\n    self.image_height = image_height\n    self.image_min = image_min\n    self.image_max = image_max\n    self.mean = mean\n    self.std = std\n    self.num_classes = num_classes\n    self.author = author\n\n\n_MODEL_INFO = {\n    \"mobilenet_v1_0.75_160_quantized.tflite\":\n        ModelSpecificInfo(\n            name=\"MobileNetV1 image classifier\",\n            version=\"v1\",\n            image_width=160,\n            image_height=160,\n            image_min=0,\n            image_max=255,\n            mean=[127.5],\n            std=[127.5],\n            num_classes=1001,\n            author=\"TensorFlow\")\n}\n\n\nclass MetadataPopulatorForImageClassifier(object):\n  \"\"\"Populates the metadata for an image classifier.\"\"\"\n\n  def __init__(self, model_file, model_info, label_file_path):\n    self.model_file = model_file\n    self.model_info = model_info\n    self.label_file_path = label_file_path\n    self.metadata_buf = None\n\n  def populate(self):\n    \"\"\"Creates metadata and then populates it for an image classifier.\"\"\"\n    self._create_metadata()\n    self._populate_metadata()\n\n  def _create_metadata(self):\n    \"\"\"Creates the metadata for an image classifier.\"\"\"\n\n    # Creates model info.\n    model_meta = _metadata_fb.ModelMetadataT()\n    model_meta.name = self.model_info.name\n    model_meta.description = (\"Identify the most prominent object in the \"\n                              \"image from a set of %d categories.\" %\n                              self.model_info.num_classes)\n    model_meta.version = self.model_info.version\n    model_meta.author = self.model_info.author\n    model_meta.license = (\"Apache License. Version 2.0 \"\n                          \"http://www.apache.org/licenses/LICENSE-2.0.\")\n\n    # Creates input info.\n    input_meta = _metadata_fb.TensorMetadataT()\n    input_meta.name = \"image\"\n    input_meta.description = (\n        \"Input image to be classified. The expected image is {0} x {1}, with \"\n        \"three channels (red, blue, and green) per pixel. Each value in the \"\n        \"tensor is a single byte between {2} and {3}.\".format(\n            self.model_info.image_width, self.model_info.image_height,\n            self.model_info.image_min, self.model_info.image_max))\n    input_meta.content = _metadata_fb.ContentT()\n    input_meta.content.contentProperties = _metadata_fb.ImagePropertiesT()\n    input_meta.content.contentProperties.colorSpace = (\n        _metadata_fb.ColorSpaceType.RGB)\n    input_meta.content.contentPropertiesType = (\n        _metadata_fb.ContentProperties.ImageProperties)\n    input_normalization = _metadata_fb.ProcessUnitT()\n    input_normalization.optionsType = (\n        _metadata_fb.ProcessUnitOptions.NormalizationOptions)\n    input_normalization.options = _metadata_fb.NormalizationOptionsT()\n    input_normalization.options.mean = self.model_info.mean\n    input_normalization.options.std = self.model_info.std\n    input_meta.processUnits = [input_normalization]\n    input_stats = _metadata_fb.StatsT()\n    input_stats.max = [self.model_info.image_max]\n    input_stats.min = [self.model_info.image_min]\n    input_meta.stats = input_stats\n\n    # Creates output info.\n    output_meta = _metadata_fb.TensorMetadataT()\n    output_meta.name = \"probability\"\n    output_meta.description = \"Probabilities of the %d labels respectively.\" % self.model_info.num_classes\n    output_meta.content = _metadata_fb.ContentT()\n    output_meta.content.content_properties = _metadata_fb.FeaturePropertiesT()\n    output_meta.content.contentPropertiesType = (\n        _metadata_fb.ContentProperties.FeatureProperties)\n    output_stats = _metadata_fb.StatsT()\n    output_stats.max = [1.0]\n    output_stats.min = [0.0]\n    output_meta.stats = output_stats\n    label_file = _metadata_fb.AssociatedFileT()\n    label_file.name = os.path.basename(self.label_file_path)\n    label_file.description = \"Labels for objects that the model can recognize.\"\n    label_file.type = _metadata_fb.AssociatedFileType.TENSOR_AXIS_LABELS\n    output_meta.associatedFiles = [label_file]\n\n    # Creates subgraph info.\n    subgraph = _metadata_fb.SubGraphMetadataT()\n    subgraph.inputTensorMetadata = [input_meta]\n    subgraph.outputTensorMetadata = [output_meta]\n    model_meta.subgraphMetadata = [subgraph]\n\n    b = flatbuffers.Builder(0)\n    b.Finish(\n        model_meta.Pack(b),\n        _metadata.MetadataPopulator.METADATA_FILE_IDENTIFIER)\n    self.metadata_buf = b.Output()\n\n  def _populate_metadata(self):\n    \"\"\"Populates metadata and label file to the model file.\"\"\"\n    populator = _metadata.MetadataPopulator.with_model_file(self.model_file)\n    populator.load_metadata_buffer(self.metadata_buf)\n    populator.load_associated_files([self.label_file_path])\n    populator.populate()\n\n\ndef main(_):\n  model_file = FLAGS.model_file\n  model_basename = os.path.basename(model_file)\n  if model_basename not in _MODEL_INFO:\n    raise ValueError(\n        \"The model info for, {0}, is not defined yet.\".format(model_basename))\n\n  export_model_path = os.path.join(FLAGS.export_directory, model_basename)\n\n  # Copies model_file to export_path.\n  tf.io.gfile.copy(model_file, export_model_path, overwrite=False)\n\n  # Generate the metadata objects and put them in the model file\n  populator = MetadataPopulatorForImageClassifier(\n      export_model_path, _MODEL_INFO.get(model_basename), FLAGS.label_file)\n  populator.populate()\n\n  # Validate the output model file by reading the metadata and produce\n  # a json file with the metadata under the export path\n  displayer = _metadata.MetadataDisplayer.with_model_file(export_model_path)\n  export_json_file = os.path.join(FLAGS.export_directory,\n                                  os.path.splitext(model_basename)[0] + \".json\")\n  json_file = displayer.get_metadata_json()\n  with open(export_json_file, \"w\") as f:\n    f.write(json_file)\n\n  print(\"Finished populating metadata and associated file to the model:\")\n  print(model_file)\n  print(\"The metadata json file has been saved to:\")\n  print(export_json_file)\n  print(\"The associated file that has been been packed to the model is:\")\n  print(displayer.get_packed_associated_file_list())\n\n\nif __name__ == \"__main__\":\n  define_flags()\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/metadata_writers/__init__.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/metadata_writers/bert/__init__.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/metadata_writers/bert/metadata_writer_for_bert.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Helper methods to writes metadata into Bert models.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport abc\nimport enum\nimport os\n\nimport flatbuffers\nfrom tensorflow_examples.lite.model_maker.core.task.metadata_writers import metadata_writer\n\nfrom tflite_support import metadata_schema_py_generated as _metadata_fb\nfrom tflite_support import metadata as _metadata\n\n\nclass Tokenizer(enum.Enum):\n  BERT_TOKENIZER = \"BERT_TOKENIZER\"\n  SENTENCE_PIECE = \"SENTENCE_PIECE\"\n\n\ndef bert_qa_inputs(ids_name, mask_name, segment_ids_name):\n  \"\"\"Creates the input tensor names of a Bert model in order.\n\n  The names correspond to `Tensor.name` in the TFLite schema. It helps to\n  determine the tensor order when populating the metadata.\n\n  Args:\n    ids_name: name of the ids tensor, which represents the tokenized ids of\n      input text as concatenated query and passage.\n    mask_name: name of the mask tensor, which represents the mask with 1 for\n      real tokens and 0 for padding tokens.\n    segment_ids_name: name of the segment ids tensor, where 0 is for query and 1\n      is for passage tokens.\n\n  Returns:\n    The input name list.\n  \"\"\"\n  return [ids_name, mask_name, segment_ids_name]\n\n\nclass ModelSpecificInfo(object):\n  \"\"\"Holds information that is specificly tied to a Bert model.\"\"\"\n\n  def __init__(self,\n               name,\n               version,\n               description,\n               input_names,\n               tokenizer_type,\n               vocab_file=None,\n               sp_model=None):\n    \"\"\"Constructor for ModelSpecificInfo.\n\n    Args:\n      name: name of the model in string.\n      version: version of the model in string.\n      description: description of the model.\n      input_names: the name list returned by bert_qa_inputs.\n      tokenizer_type: one of the tokenizer types in Tokenizer.\n      vocab_file: the vocab file name to be packed into the model. If the\n        tokenizer is BERT_TOKENIZER, the vocab file is required; if the\n        tokenizer is SENTENCE_PIECE, the vocab file is optional.\n      sp_model: the SentencePiece model file, only valid for the SENTENCE_PIECE\n        tokenizer.\n    \"\"\"\n    if tokenizer_type is Tokenizer.BERT_TOKENIZER:\n      if vocab_file is None:\n        raise ValueError(\n            \"The vocab file cannot be None for the BERT_TOKENIZER.\")\n    elif tokenizer_type is Tokenizer.SENTENCE_PIECE:\n      if sp_model is None:\n        raise ValueError(\n            \"The sentence piece model file cannot be None for the \"\n            \"SENTENCE_PIECE tokenizer. The vocab file is optional though.\")\n    else:\n      raise ValueError(\n          \"The tokenizer type, {0}, is unsupported.\".format(tokenizer_type))\n\n    self.name = name\n    self.version = version\n    self.description = description\n    self.input_names = input_names\n    self.tokenizer_type = tokenizer_type\n    self.vocab_file = vocab_file\n    self.sp_model = sp_model\n\n\nclass MetadataPopulatorForBert(metadata_writer.MetadataWriter):\n  \"\"\"Populates the metadata for a Bert model.\"\"\"\n\n  def __init__(self, model_file, export_directory, model_info):\n    self.model_info = model_info\n    model_dir_name = os.path.dirname(model_file)\n    file_paths = []\n    if model_info.vocab_file is not None:\n      file_paths.append(os.path.join(model_dir_name, model_info.vocab_file))\n    if model_info.sp_model is not None:\n      file_paths.append(os.path.join(model_dir_name, model_info.sp_model))\n    super(MetadataPopulatorForBert, self).__init__(model_file, export_directory,\n                                                   file_paths)\n\n  def _create_metadata(self):\n    \"\"\"Creates model metadata for bert models.\"\"\"\n\n    model_meta = _metadata_fb.ModelMetadataT()\n    model_meta.name = self.model_info.name\n    model_meta.description = self.model_info.description\n    model_meta.version = self.model_info.version\n    model_meta.author = \"TensorFlow Lite Model Maker\"\n    model_meta.license = (\"Apache License. Version 2.0 \"\n                          \"http://www.apache.org/licenses/LICENSE-2.0.\")\n\n    # Creates the tokenizer info.\n    if self.model_info.tokenizer_type is Tokenizer.BERT_TOKENIZER:\n      tokenizer = self._create_bert_tokenizer()\n    elif self.model_info.tokenizer_type is Tokenizer.SENTENCE_PIECE:\n      tokenizer = self._create_sentence_piece_tokenizer()\n    else:\n      raise ValueError(\"The tokenizer type, {0}, is unsupported.\".format(\n          self.model_info.tokenizer_type))\n\n    # Creates subgraph info.\n    subgraph = _metadata_fb.SubGraphMetadataT()\n    subgraph.inputTensorMetadata = self._create_input_metadata()\n    subgraph.outputTensorMetadata = self._create_output_metadata()\n    subgraph.inputProcessUnits = [tokenizer]\n    model_meta.subgraphMetadata = [subgraph]\n\n    b = flatbuffers.Builder(0)\n    b.Finish(\n        model_meta.Pack(b),\n        _metadata.MetadataPopulator.METADATA_FILE_IDENTIFIER)\n    self.metadata_buf = b.Output()\n\n  def _create_input_metadata(self):\n    \"\"\"Creates the input metadata for a Bert model.\n\n    Returns:\n      A list of the three input tensor metadata in flatbuffer objects.\n    \"\"\"\n\n    # Creates inputs info.\n    ids_meta = _metadata_fb.TensorMetadataT()\n    ids_meta.name = \"ids\"\n    ids_meta.description = (\n        \"Tokenized ids of input text.\")\n    ids_meta.content = _metadata_fb.ContentT()\n    ids_meta.content.contentPropertiesType = (\n        _metadata_fb.ContentProperties.FeatureProperties)\n    ids_meta.content.contentProperties = _metadata_fb.FeaturePropertiesT()\n\n    mask_meta = _metadata_fb.TensorMetadataT()\n    mask_meta.name = \"mask\"\n    mask_meta.description = (\"Mask with 1 for real tokens and 0 for padding \"\n                             \"tokens.\")\n    mask_meta.content = _metadata_fb.ContentT()\n    mask_meta.content.contentPropertiesType = (\n        _metadata_fb.ContentProperties.FeatureProperties)\n    mask_meta.content.contentProperties = _metadata_fb.FeaturePropertiesT()\n\n    segment_meta = _metadata_fb.TensorMetadataT()\n    segment_meta.name = \"segment_ids\"\n    segment_meta.description = (\n        \"0 for the first sequence, 1 for the second sequence if exists.\")\n    segment_meta.content = _metadata_fb.ContentT()\n    segment_meta.content.contentPropertiesType = (\n        _metadata_fb.ContentProperties.FeatureProperties)\n    segment_meta.content.contentProperties = _metadata_fb.FeaturePropertiesT()\n\n    # The order of input_metadata should match the order of tensor names in\n    # InputTensorNames.\n    input_metadata = [ids_meta, mask_meta, segment_meta]\n    # Order the tensor metadata according to the input tensor order.\n    ordered_input_names = self._get_input_tensor_names()\n    return self._order_tensor_metadata_with_names(input_metadata,\n                                                  self.model_info.input_names,\n                                                  ordered_input_names)\n\n  @abc.abstractmethod\n  def _create_output_metadata(self):\n    \"\"\"Creates the output metadata for a Bert model.\n\n    Returns:\n      A list of the output tensor metadata in flatbuffer objects.\n    \"\"\"\n    pass\n\n  def _create_bert_tokenizer(self):\n    vocab = _metadata_fb.AssociatedFileT()\n    vocab.name = os.path.basename(self.model_info.vocab_file)\n    vocab.description = \"Vocabulary file for the BertTokenizer.\"\n    vocab.type = _metadata_fb.AssociatedFileType.VOCABULARY\n    tokenizer = _metadata_fb.ProcessUnitT()\n    tokenizer.optionsType = _metadata_fb.ProcessUnitOptions.BertTokenizerOptions\n    tokenizer.options = _metadata_fb.BertTokenizerOptionsT()\n    tokenizer.options.vocabFile = [vocab]\n    return tokenizer\n\n  def _create_sentence_piece_tokenizer(self):\n    sp_model = _metadata_fb.AssociatedFileT()\n    sp_model.name = os.path.basename(self.model_info.sp_model)\n    sp_model.description = \"The sentence piece model file.\"\n    if self.model_info.vocab_file is not None:\n      vocab = _metadata_fb.AssociatedFileT()\n      vocab.name = os.path.basename(self.model_info.vocab_file)\n      vocab.description = (\n          \"Vocabulary file for the SentencePiece tokenizer. This file is \"\n          \"optional during tokenization, while the sentence piece model is \"\n          \"mandatory.\")\n      vocab.type = _metadata_fb.AssociatedFileType.VOCABULARY\n    tokenizer = _metadata_fb.ProcessUnitT()\n    tokenizer.optionsType = (\n        _metadata_fb.ProcessUnitOptions.SentencePieceTokenizerOptions)\n    tokenizer.options = _metadata_fb.SentencePieceTokenizerOptionsT()\n    tokenizer.options.sentencePieceModel = [sp_model]\n    if vocab:\n      tokenizer.options.vocabFile = [vocab]\n    return tokenizer\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/metadata_writers/bert/question_answerer/__init__.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/metadata_writers/bert/question_answerer/metadata_writer_for_bert_question_answerer.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Writes metadata and tokenize file to the Bert QA models.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nfrom tensorflow_examples.lite.model_maker.core.task.metadata_writers.bert.metadata_writer_for_bert import bert_qa_inputs\nfrom tensorflow_examples.lite.model_maker.core.task.metadata_writers.bert.metadata_writer_for_bert import MetadataPopulatorForBert\nfrom tensorflow_examples.lite.model_maker.core.task.metadata_writers.bert.metadata_writer_for_bert import ModelSpecificInfo\nfrom tensorflow_examples.lite.model_maker.core.task.metadata_writers.bert.metadata_writer_for_bert import Tokenizer\nfrom tflite_support import metadata_schema_py_generated as _metadata_fb\n\n\ndef bert_qa_outputs(start_logits_name, end_logits_name):\n  \"\"\"Creates the output tensor names of a Bert question answerer model in order.\n\n  The names correspond to `Tensor.name` in the TFLite schema. It helps to\n  determine the tensor order when populating the metadata.\n\n  Args:\n    start_logits_name: name of the start logits tensor, which represents the\n      start position of the answer span.\n    end_logits_name: name of the end logits tensor, which represents the end\n      position of the answer span.\n\n  Returns:\n    The output name list.\n  \"\"\"\n  return [start_logits_name, end_logits_name]\n\n\nclass QuestionAnswererInfo(ModelSpecificInfo):\n  \"\"\"Holds information specificly tied to a Bert question answerer model.\"\"\"\n\n  def __init__(self,\n               name,\n               version,\n               description,\n               input_names,\n               output_names,\n               tokenizer_type,\n               vocab_file=None,\n               sp_model=None):\n    \"\"\"Constructor for ModelSpecificInfo.\n\n    Args:\n      name: name of the model in string.\n      version: version of the model in string.\n      description: description of the model.\n      input_names: the name list returned by bert_qa_inputs.\n      output_names: the name list returned by bert_qa_outputs.\n      tokenizer_type: one of the tokenizer types in Tokenizer.\n      vocab_file: the vocab file name to be packed into the model. If the\n        tokenizer is BERT_TOKENIZER, the vocab file is required; if the\n        tokenizer is SENTENCE_PIECE, the vocab file is optional.\n      sp_model: the SentencePiece model file, only valid for the SENTENCE_PIECE\n        tokenizer.\n    \"\"\"\n    self.output_names = output_names\n    super(QuestionAnswererInfo,\n          self).__init__(name, version, description, input_names,\n                         tokenizer_type, vocab_file, sp_model)\n\n\nDEFAULT_DESCRIPTION = (\n    \"Answers questions based on the content of a given \"\n    \"passage. To integrate the model into your app, try the \"\n    \"`BertQuestionAnswerer` API in the TensorFlow Lite Task \"\n    \"library. `BertQuestionAnswerer` takes a passage string and a \"\n    \"query string, and returns the answer strings. It encapsulates \"\n    \"the processing logic of inputs and outputs and runs the \"\n    \"inference with the best practice.\")\n\nDEFAULT_INPUT_NAMES = bert_qa_inputs(\n    ids_name=\"input_ids\",\n    mask_name=\"input_mask\",\n    segment_ids_name=\"segment_ids\")\n\nDEFAULT_OUTPUT_NAMES = bert_qa_outputs(\n    start_logits_name=\"start_logits\", end_logits_name=\"end_logits\")\n\n_MODEL_INFO = {\n    \"mobilebert_float.tflite\":\n        QuestionAnswererInfo(\n            name=\"MobileBert Question and Answerer\",\n            version=\"v1\",\n            description=DEFAULT_DESCRIPTION,\n            input_names=DEFAULT_INPUT_NAMES,\n            output_names=DEFAULT_OUTPUT_NAMES,\n            tokenizer_type=Tokenizer.BERT_TOKENIZER,\n            vocab_file=\"vocab.txt\"),\n    \"albert_float.tflite\":\n        QuestionAnswererInfo(\n            name=\"Albert Question and Answerer\",\n            version=\"v1\",\n            description=DEFAULT_DESCRIPTION,\n            input_names=DEFAULT_INPUT_NAMES,\n            output_names=DEFAULT_OUTPUT_NAMES,\n            tokenizer_type=Tokenizer.SENTENCE_PIECE,\n            vocab_file=\"30k-clean.vocab\",\n            sp_model=\"30k-clean.model\"),\n}\n\n\nclass MetadataPopulatorForBertQuestionAndAnswer(MetadataPopulatorForBert):\n  \"\"\"Populates the metadata for a Bert QA model.\"\"\"\n\n  def _create_output_metadata(self):\n    \"\"\"Creates the output metadata for a Bert QA model.\"\"\"\n\n    # Creates outputs info.\n    end_meta = _metadata_fb.TensorMetadataT()\n    end_meta.name = \"end_logits\"\n    end_meta.description = (\n        \"logits over the sequence which indicates the\"\n        \" end position of the answer span with closed interval.\")\n    end_meta.content = _metadata_fb.ContentT()\n    end_meta.content.contentPropertiesType = (\n        _metadata_fb.ContentProperties.FeatureProperties)\n    end_meta.content.contentProperties = _metadata_fb.FeaturePropertiesT()\n\n    start_meta = _metadata_fb.TensorMetadataT()\n    start_meta.name = \"start_logits\"\n    start_meta.description = (\n        \"logits over the sequence which indicates \"\n        \"the start position of the answer span with closed interval.\")\n    start_meta.content = _metadata_fb.ContentT()\n    start_meta.content.contentPropertiesType = (\n        _metadata_fb.ContentProperties.FeatureProperties)\n    start_meta.content.contentProperties = _metadata_fb.FeaturePropertiesT()\n\n    # The order of output_metadata should match the order of tensor names in\n    # OutputTensorNames.\n    output_metadata = [start_meta, end_meta]\n    # Order the tensor metadata according to the output tensor order.\n    ordered_output_names = self._get_output_tensor_names()\n    return self._order_tensor_metadata_with_names(output_metadata,\n                                                  self.model_info.output_names,\n                                                  ordered_output_names)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/metadata_writers/bert/text_classifier/__init__.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/metadata_writers/bert/text_classifier/metadata_writer_for_bert_text_classifier.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Writes metadata and tokenize file to the Bert text classifier models.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\n\nfrom tensorflow_examples.lite.model_maker.core.task.metadata_writers.bert.metadata_writer_for_bert import bert_qa_inputs\nfrom tensorflow_examples.lite.model_maker.core.task.metadata_writers.bert.metadata_writer_for_bert import MetadataPopulatorForBert\nfrom tensorflow_examples.lite.model_maker.core.task.metadata_writers.bert.metadata_writer_for_bert import ModelSpecificInfo\nfrom tensorflow_examples.lite.model_maker.core.task.metadata_writers.bert.metadata_writer_for_bert import Tokenizer\nfrom tflite_support import metadata_schema_py_generated as _metadata_fb\n\n\nclass ClassifierSpecificInfo(ModelSpecificInfo):\n  \"\"\"Holds information specificly tied to a Bert text classifier model.\"\"\"\n\n  def __init__(self,\n               name,\n               version,\n               description,\n               input_names,\n               tokenizer_type,\n               label_file,\n               vocab_file=None,\n               sp_model=None):\n    \"\"\"Constructor for ModelSpecificInfo.\n\n    Args:\n      name: name of the model in string.\n      version: version of the model in string.\n      description: description of the model.\n      input_names: an InputTensorNames object.\n      tokenizer_type: one of the tokenizer types in Tokenizer.\n      label_file: the label file of classification catergories.\n      vocab_file: the vocab file name to be packed into the model. If the\n        tokenizer is BERT_TOKENIZER, the vocab file is required; if the\n        tokenizer is SENTENCE_PIECE, the vocab file is optional.\n      sp_model: the SentencePiece model file, only valid for the SENTENCE_PIECE\n        tokenizer.\n    \"\"\"\n    self.label_file = label_file\n    super(ClassifierSpecificInfo,\n          self).__init__(name, version, description, input_names,\n                         tokenizer_type, vocab_file, sp_model)\n\n\nDEFAULT_DESCRIPTION = (\n    \"Classifies the input string based on the known catergories. To integrate \"\n    \"the model into your app, try the `BertNLClassifier` API in the TensorFlow \"\n    \"Lite Task library. `BertNLClassifier` takes an input string, and returns \"\n    \"the classified label with probability. It encapsulates the processing \"\n    \"logic of inputs and outputs and runs the inference with the best \"\n    \"practice.\")\n\n_MODEL_INFO = {\n    \"sst2_mobilebert_quant.tflite\":\n        ClassifierSpecificInfo(\n            name=\"MobileBert text classifier\",\n            version=\"v1\",\n            description=DEFAULT_DESCRIPTION,\n            input_names=bert_qa_inputs(\n                ids_name=\"serving_default_input_word_ids:0\",\n                mask_name=\"serving_default_input_mask:0\",\n                segment_ids_name=\"serving_default_input_type_ids:0\"),\n            tokenizer_type=Tokenizer.BERT_TOKENIZER,\n            vocab_file=\"vocab.txt\",\n            label_file=\"labels.txt\")\n}\n\n\nclass MetadataPopulatorForBertTextClassifier(MetadataPopulatorForBert):\n  \"\"\"Populates the metadata for a Bert text classifier model.\"\"\"\n\n  def __init__(self, model_file, export_directory, model_info):\n    self.model_info = model_info\n    model_dir_name = os.path.dirname(model_file)\n    file_paths = []\n    if model_info.vocab_file is not None:\n      file_paths.append(os.path.join(model_dir_name, model_info.vocab_file))\n    if model_info.sp_model is not None:\n      file_paths.append(os.path.join(model_dir_name, model_info.sp_model))\n    file_paths.append(os.path.join(model_dir_name, model_info.label_file))\n    self.model_file = model_file\n    self.export_directory = export_directory\n    self.associated_files = file_paths\n    self.metadata_buf = None\n\n  def _create_output_metadata(self):\n    \"\"\"Creates the output metadata for a Bert text classifier model.\"\"\"\n\n    # Create output info.\n    output_meta = _metadata_fb.TensorMetadataT()\n    output_meta.name = \"probability\"\n    output_meta.description = \"Probabilities of labels respectively.\"\n    output_meta.content = _metadata_fb.ContentT()\n    output_meta.content.contentProperties = _metadata_fb.FeaturePropertiesT()\n    output_meta.content.contentPropertiesType = (\n        _metadata_fb.ContentProperties.FeatureProperties)\n    label_file = _metadata_fb.AssociatedFileT()\n    label_file.name = os.path.basename(self.model_info.label_file)\n    label_file.description = (\"Labels for classification categories.\")\n    label_file.type = _metadata_fb.AssociatedFileType.TENSOR_AXIS_LABELS\n    output_meta.associatedFiles = [label_file]\n    return [output_meta]\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/metadata_writers/metadata_writer.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Helper class to write metadata into TFLite models.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport abc\nimport os\n\nimport tensorflow as tf\n# pylint: disable=g-direct-tensorflow-import\nfrom tflite_support import schema_py_generated as _schema_fb\nfrom tflite_support import metadata as _metadata\n# pylint: enable=g-direct-tensorflow-import\n\n\nclass MetadataWriter(abc.ABC):\n  \"\"\"Writes the metadata and associated file into a TFLite model.\"\"\"\n\n  def __init__(self, model_file, export_directory, associated_files):\n    \"\"\"Constructs the MetadataWriter.\n\n    Args:\n      model_file: the path to the model to be populated.\n      export_directory: path to the directory where the model and json file will\n        be exported to.\n      associated_files: path to the associated files to be populated.\n    \"\"\"\n    self.model_file = model_file\n    self.export_directory = export_directory\n    self.associated_files = associated_files\n    self.metadata_buf = None\n\n  def populate(self, export_metadata_json_file=False):\n    \"\"\"Creates metadata and then populates it into a TFLite model.\"\"\"\n    self._create_metadata()\n    self._populate_metadata(export_metadata_json_file)\n\n  @abc.abstractmethod\n  def _create_metadata(self):\n    pass\n\n  def _get_subgraph(self):\n    \"\"\"Gets the input / output tensor names into lists.\"\"\"\n\n    with tf.io.gfile.GFile(self.model_file, \"rb\") as f:\n      model_buf = f.read()\n\n    model = _schema_fb.Model.GetRootAsModel(model_buf, 0)\n\n    # Use the first subgraph as default. TFLite Interpreter doesn't support\n    # multiple subgraphs yet, but models with mini-benchmark may have multiple\n    # subgraphs for acceleration evaluation purpose.\n    return model.Subgraphs(0)\n\n  def _get_input_tensor_names(self):\n    \"\"\"Gets the input tensor names into a list.\"\"\"\n    input_names = []\n    subgraph = self._get_subgraph()\n    for i in range(subgraph.InputsLength()):\n      index = subgraph.Inputs(i)\n      input_names.append(subgraph.Tensors(index).Name())\n\n    return input_names\n\n  def _get_output_tensor_names(self):\n    \"\"\"Gets the output tensor names into a list.\"\"\"\n    output_names = []\n    subgraph = self._get_subgraph()\n    for i in range(subgraph.OutputsLength()):\n      index = subgraph.Outputs(i)\n      output_names.append(subgraph.Tensors(index).Name())\n\n    return output_names\n\n  def _order_tensor_metadata_with_names(self, tensor_metadata, tensor_names,\n                                        ordered_tensor_names):\n    \"\"\"Orders the tensor metadata array according to the ordered tensor names.\n\n    Args:\n      tensor_metadata: the tensor_metadata array in list.\n      tensor_names: name list of the tensors corresponding to tensor_metadata.\n      ordered_tensor_names: name list of the tensors in the expected order, such\n        as in the same order as saved in the model.\n\n    Returns:\n      The ordered tensor metadata list.\n    \"\"\"\n    if len(tensor_names) != len(tensor_metadata):\n      raise ValueError((\n          \"Number of the tensor names ({0}) does not match the number of tensor\"\n          \" metadata ({1}).\".format(len(tensor_names), len(tensor_metadata))))\n    if len(ordered_tensor_names) != len(tensor_names):\n      raise ValueError(\n          (\"Number of the ordered tensor names ({0}) does not match the number \"\n           \"of tensor names ({1}).\".format(\n               len(ordered_tensor_names), len(tensor_names))))\n\n    ordered_metadata = []\n    name_meta_dict = dict(zip(tensor_names, tensor_metadata))\n    for name in ordered_tensor_names:\n      ordered_metadata.append(name_meta_dict[name.decode()])\n    return ordered_metadata\n\n  def _populate_metadata(self, export_metadata_json_file=False):\n    \"\"\"Populates the metadata and label file to the model file.\"\"\"\n    # Copies model_file to export_path.\n    model_basename = os.path.basename(self.model_file)\n    export_model_path = os.path.join(self.export_directory, model_basename)\n    if os.path.abspath(self.model_file) != os.path.abspath(export_model_path):\n      tf.io.gfile.copy(self.model_file, export_model_path, overwrite=True)\n\n    populator = _metadata.MetadataPopulator.with_model_file(export_model_path)\n    populator.load_metadata_buffer(self.metadata_buf)\n    if self.associated_files:\n      populator.load_associated_files(self.associated_files)\n    populator.populate()\n\n    # Displays the model metadata.\n    displayer = _metadata.MetadataDisplayer.with_model_file(export_model_path)\n    export_json_path = os.path.join(\n        self.export_directory,\n        os.path.splitext(model_basename)[0] + \".json\")\n    if export_metadata_json_file:\n      with tf.io.gfile.GFile(export_json_path, \"w\") as f:\n        f.write(displayer.get_metadata_json())\n\n    logging = tf.compat.v1.logging\n    logging.info(\n        \"Finished populating metadata and associated file to the model:\")\n    logging.info(export_model_path)\n    if export_metadata_json_file:\n      logging.info(\"The metadata json file has been saved to:\")\n      logging.info(export_json_path)\n    if self.associated_files:\n      logging.info(\n          \"The associated file that has been been packed to the model is:\")\n      logging.info(displayer.get_packed_associated_file_list())\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/metadata_writers/text_classifier/__init__.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/metadata_writers/text_classifier/metadata_writer_for_text_classifier.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Writes metadata and label file to the text classifier models.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\n\nimport flatbuffers\nfrom tensorflow_examples.lite.model_maker.core.task.metadata_writers import metadata_writer\n# pylint: disable=g-direct-tensorflow-import\nfrom tflite_support import metadata_schema_py_generated as _metadata_fb\nfrom tflite_support import metadata as _metadata\n# pylint: enable=g-direct-tensorflow-import\n\n\n# Default settings that must align with Model Maker.\n# https://www.tensorflow.org/lite/tutorials/model_maker_text_classification\n# Name for associated files.\nDEFAULT_LABEL_FILE = \"labels.txt\"\nDEFAULT_VOCAB_FILE = \"vocab.txt\"\n# Split pattern used in the tokenizer.\nDEFAULT_DELIM_REGEX_PATTERN = r\"[^\\w\\']+\"\n\n\nclass ModelSpecificInfo(object):\n  \"\"\"Holds information that is specificly tied to a text classifier.\"\"\"\n\n  def __init__(self,\n               name,\n               version,\n               description,\n               delim_regex_pattern=DEFAULT_DELIM_REGEX_PATTERN,\n               label_file=DEFAULT_LABEL_FILE,\n               vocab_file=DEFAULT_VOCAB_FILE):\n    self.name = name\n    self.version = version\n    self.description = description\n    self.delim_regex_pattern = delim_regex_pattern\n    self.label_file = label_file\n    self.vocab_file = vocab_file\n\n\n_MODEL_INFO = {\n    # AverageWordVector model trained on IMDB movie reviews dataset.\n    \"text_classification.tflite\":\n        ModelSpecificInfo(\n            name=\"Sentiment Analyzer (AverageWordVecModelSpec)\",\n            description=\"Detect if the input text's sentiment is positive or \"\n            \"negative. The model was trained on the IMDB Movie Reviews dataset \"\n            \"so it is more accurate when input text is a movie review.\",\n            version=\"v1\"),\n}\n\n\nclass MetadataPopulatorForTextClassifier(metadata_writer.MetadataWriter):\n  \"\"\"Populates the metadata for a text classifier.\"\"\"\n\n  def __init__(self, model_file, export_directory, model_info, label_file_path,\n               vocab_file_path):\n    self.model_info = model_info\n    super(MetadataPopulatorForTextClassifier,\n          self).__init__(model_file, export_directory,\n                         [label_file_path, vocab_file_path])\n\n  def _create_metadata(self):\n    \"\"\"Creates the metadata for a text classifier.\"\"\"\n\n    # Creates model info.\n    model_meta = _metadata_fb.ModelMetadataT()\n    model_meta.name = self.model_info.name\n    model_meta.description = self.model_info.description\n    model_meta.version = self.model_info.version\n    model_meta.author = \"TensorFlow Lite Model Maker\"\n    model_meta.license = (\"Apache License. Version 2.0 \"\n                          \"http://www.apache.org/licenses/LICENSE-2.0.\")\n\n    # Creates input info.\n    input_meta = _metadata_fb.TensorMetadataT()\n    input_meta.name = \"input_text\"\n    input_meta.description = (\n        \"Embedding vectors representing the input text to be classified. The \"\n        \"input need to be converted from raw text to embedding vectors using \"\n        \"the attached dictionary file.\")\n    # Create the vocab file.\n    vocab_file = _metadata_fb.AssociatedFileT()\n    vocab_file.name = os.path.basename(self.associated_files[1])\n    vocab_file.description = (\"Vocabulary file to convert natural language \"\n                              \"words to embedding vectors.\")\n    vocab_file.type = _metadata_fb.AssociatedFileType.VOCABULARY\n\n    # Create the RegexTokenizer.\n    tokenizer = _metadata_fb.ProcessUnitT()\n    tokenizer.optionsType = (\n        _metadata_fb.ProcessUnitOptions.RegexTokenizerOptions)\n    tokenizer.options = _metadata_fb.RegexTokenizerOptionsT()\n    tokenizer.options.delimRegexPattern = self.model_info.delim_regex_pattern\n    tokenizer.options.vocabFile = [vocab_file]\n\n    input_meta.content = _metadata_fb.ContentT()\n    input_meta.content.contentPropertiesType = (\n        _metadata_fb.ContentProperties.FeatureProperties)\n    input_meta.content.contentProperties = _metadata_fb.FeaturePropertiesT()\n    input_meta.processUnits = [tokenizer]\n\n    # Creates output info.\n    output_meta = _metadata_fb.TensorMetadataT()\n    output_meta.name = \"probability\"\n    output_meta.description = \"Probabilities of the labels respectively.\"\n    output_meta.content = _metadata_fb.ContentT()\n    output_meta.content.contentProperties = _metadata_fb.FeaturePropertiesT()\n    output_meta.content.contentPropertiesType = (\n        _metadata_fb.ContentProperties.FeatureProperties)\n    output_stats = _metadata_fb.StatsT()\n    output_stats.max = [1.0]\n    output_stats.min = [0.0]\n    output_meta.stats = output_stats\n    label_file = _metadata_fb.AssociatedFileT()\n    label_file.name = os.path.basename(self.associated_files[0])\n    label_file.description = (\"Labels for the categories that the model can \"\n                              \"classify.\")\n    label_file.type = _metadata_fb.AssociatedFileType.TENSOR_AXIS_LABELS\n    output_meta.associatedFiles = [label_file]\n\n    # Creates subgraph info.\n    subgraph = _metadata_fb.SubGraphMetadataT()\n    subgraph.inputTensorMetadata = [input_meta]\n    subgraph.outputTensorMetadata = [output_meta]\n    model_meta.subgraphMetadata = [subgraph]\n\n    b = flatbuffers.Builder(0)\n    b.Finish(\n        model_meta.Pack(b),\n        _metadata.MetadataPopulator.METADATA_FILE_IDENTIFIER)\n    self.metadata_buf = b.Output()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/model_spec/__init__.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Model specification.\"\"\"\n\nimport functools\nimport inspect\n\nfrom tensorflow_examples.lite.model_maker.core.api import mm_export\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import audio_spec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import image_spec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import object_detector_spec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import recommendation_spec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import text_spec\n\n\n# A dict for model specs to make it accessible by string key.\nMODEL_SPECS = {\n    # Image classification\n    'efficientnet_lite0': image_spec.efficientnet_lite0_spec,\n    'efficientnet_lite1': image_spec.efficientnet_lite1_spec,\n    'efficientnet_lite2': image_spec.efficientnet_lite2_spec,\n    'efficientnet_lite3': image_spec.efficientnet_lite3_spec,\n    'efficientnet_lite4': image_spec.efficientnet_lite4_spec,\n    'mobilenet_v2': image_spec.mobilenet_v2_spec,\n    'resnet_50': image_spec.resnet_50_spec,\n\n    # Text classification\n    'average_word_vec': text_spec.AverageWordVecModelSpec,\n    'bert': text_spec.BertModelSpec,\n    'bert_classifier': text_spec.BertClassifierModelSpec,\n    'mobilebert_classifier': text_spec.mobilebert_classifier_spec,\n\n    # Question answering\n    'bert_qa': text_spec.BertQAModelSpec,\n    'mobilebert_qa': text_spec.mobilebert_qa_spec,\n    'mobilebert_qa_squad': text_spec.mobilebert_qa_squad_spec,\n\n    # Audio classification\n    'audio_browser_fft': audio_spec.BrowserFFTSpec,\n    'audio_teachable_machine': audio_spec.BrowserFFTSpec,\n    'audio_yamnet': audio_spec.YAMNetSpec,\n\n    # Recommendation\n    'recommendation': recommendation_spec.RecommendationSpec,\n\n    # Object detection\n    'efficientdet_lite0': object_detector_spec.efficientdet_lite0_spec,\n    'efficientdet_lite1': object_detector_spec.efficientdet_lite1_spec,\n    'efficientdet_lite2': object_detector_spec.efficientdet_lite2_spec,\n    'efficientdet_lite3': object_detector_spec.efficientdet_lite3_spec,\n    'efficientdet_lite4': object_detector_spec.efficientdet_lite4_spec,\n}\n\n# List constants for supported models.\nIMAGE_CLASSIFICATION_MODELS = [\n    'efficientnet_lite0', 'efficientnet_lite1', 'efficientnet_lite2',\n    'efficientnet_lite3', 'efficientnet_lite4', 'mobilenet_v2', 'resnet_50'\n]\nTEXT_CLASSIFICATION_MODELS = [\n    'bert_classifier', 'average_word_vec', 'mobilebert_classifier'\n]\nQUESTION_ANSWER_MODELS = ['bert_qa', 'mobilebert_qa', 'mobilebert_qa_squad']\nAUDIO_CLASSIFICATION_MODELS = [\n    'audio_browser_fft', 'audio_teachable_machine', 'audio_yamnet'\n]\nRECOMMENDATION_MODELS = [\n    'recommendation',\n]\nOBJECT_DETECTION_MODELS = [\n    'efficientdet_lite0',\n    'efficientdet_lite1',\n    'efficientdet_lite2',\n    'efficientdet_lite3',\n    'efficientdet_lite4',\n]\nmm_export('model_spec.IMAGE_CLASSIFICATION_MODELS').export_constant(\n    __name__, 'IMAGE_CLASSIFICATION_MODELS')\nmm_export('model_spec.TEXT_CLASSIFICATION_MODELS').export_constant(\n    __name__, 'TEXT_CLASSIFICATION_MODELS')\nmm_export('model_spec.QUESTION_ANSWER_MODELS').export_constant(\n    __name__, 'QUESTION_ANSWER_MODELS')\nmm_export('model_spec.AUDIO_CLASSIFICATION_MODELS').export_constant(\n    __name__, 'AUDIO_CLASSIFICATION_MODELS')\nmm_export('model_spec.RECOMMENDATION_MODELS').export_constant(\n    __name__, 'RECOMMENDATION_MODELS')\nmm_export('model_spec.OBJECT_DETECTION_MODELS').export_constant(\n    __name__, 'OBJECT_DETECTION_MODELS')\n\n\n@mm_export('model_spec.get')\ndef get(spec_or_str, *args, **kwargs):\n  \"\"\"Gets model spec by name or instance, and init with args and kwarges.\"\"\"\n  if isinstance(spec_or_str, str):\n    model_spec = MODEL_SPECS[spec_or_str]\n  else:\n    model_spec = spec_or_str\n\n  if inspect.isclass(model_spec) or inspect.isfunction(\n      model_spec) or isinstance(model_spec, functools.partial):\n    return model_spec(*args, **kwargs)\n  else:\n    return model_spec\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/model_spec/audio_spec.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Audio model specification.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport abc\nimport collections\nimport csv\nimport io\nimport os\nimport tempfile\n\nfrom packaging import version\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core.api.api_util import mm_export\nfrom tensorflow_examples.lite.model_maker.core.task import model_util\nimport tensorflow_hub as hub\n\ntry:\n  from tflite_support.metadata_writers import audio_classifier as md_writer  # pylint: disable=g-import-not-at-top\n  from tflite_support.metadata_writers import metadata_info as md_info  # pylint: disable=g-import-not-at-top\n  from tflite_support.metadata_writers import writer_utils  # pylint: disable=g-import-not-at-top\n  ENABLE_METADATA = True\nexcept ImportError:\n  ENABLE_METADATA = False\n\n\nclass MetadataWriter:\n  \"\"\"Helper class to populate Audio Metadata, to be used in `with` statement.\n\n  Simple usage for model with two classification heads.\n\n  with MetadataWriter(tflite_path) as writer:\n    writer.add_input(sample_rate=16000, channels=1)\n    writer.add_output(name='animal_sound', labels=['dog', 'cat'])\n    writer.add_output(name='speech_command', labels=['yes', 'no'])\n    writer.save(tflite_path, json_filepath)\n\n  `add_output` can also take an ordered dict for multiple locales, example:\n\n  writer.add_output(name='animal_sound', labels=collections.OrderedDict([\n    ('en', ['bird', 'cat']),\n    ('fr', ['oiseau', 'chat'])\n  ]))\n  \"\"\"\n\n  def __init__(self, tflite_filepath, **kwargs):\n    self._model = writer_utils.load_file(tflite_filepath)\n    self._general_md = md_info.GeneralMd(**kwargs)\n    self._inputs = []\n    self._outputs = []\n\n  def __enter__(self):\n    self._temp_folder = tempfile.TemporaryDirectory()\n    return self\n\n  def __exit__(self, exc_type, exc_val, exc_tb):\n    self._temp_folder.cleanup()\n    # Delete the attribute so that it errors out if not in `with` statement.\n    delattr(self, '_temp_folder')\n\n  def add_input(self, **kwargs):\n    \"\"\"Add metadta for the input tensor.\"\"\"\n    self._inputs.append(md_info.InputAudioTensorMd(**kwargs))\n\n  def add_output(self, name, labels, **kwargs):\n    \"\"\"Add metadata for output tensor in order.\"\"\"\n    if isinstance(labels, list):\n      default_locale = None\n      labels = collections.OrderedDict([(default_locale, labels)])\n      return self.add_output(name, labels, **kwargs)\n\n    label_files = []\n    if isinstance(labels, collections.OrderedDict):\n      for locale, label_list in labels.items():\n        full_path = os.path.join(\n            self._temp_folder.name,\n            '{}_labels{}.txt'.format(name, '_' + locale if locale else ''))\n        model_util.export_labels(full_path, label_list)\n        label_files.append(\n            md_info.LabelFileMd(file_path=full_path, locale=locale))\n    else:\n      raise ValueError(\n          '`labels` should be either a list of labels or an ordered dict mapping `locale` -> list of labels. got: {}'\n          .format(labels))\n\n    idx = len(self._outputs)\n    self._outputs.append(\n        md_info.ClassificationTensorMd(\n            name=name,\n            label_files=label_files,\n            tensor_type=writer_utils.get_output_tensor_types(self._model)[idx],\n            **kwargs))\n\n  def save(self, tflite_filepath=None, json_filepath=None):\n    \"\"\"Persist model with metadata.\"\"\"\n    if len(self._inputs) > 1:\n      raise ValueError('Only supports single input, got {}'.format(\n          len(self._inputs)))\n    input_md = self._inputs[0]\n\n    writer = md_writer.MetadataWriter.create_from_metadata_info_for_multihead(\n        model_buffer=self._model,\n        general_md=self._general_md,\n        input_md=input_md,\n        output_md_list=self._outputs)\n    if tflite_filepath:\n      writer_utils.save_file(writer.populate(), tflite_filepath, mode='wb')\n    if json_filepath:\n      writer_utils.save_file(\n          writer.get_metadata_json(), json_filepath, mode='wt')\n\n\ndef _ensure_tf25(tf_version):\n  if version.parse(tf_version) < version.parse('2.5.0rc0'):\n    raise RuntimeError(\n        'Audio Tasks requires TF2.5 or later. For example, you can run the '\n        'following command to install TF2.5.0rc2:\\n\\n'\n        'pip3 install tensorflow==2.5.0rc2\\n\\n')\n\n\ndef _get_tf_version():\n  return tf.__version__\n\n\nclass BaseSpec(abc.ABC):\n  \"\"\"Base model spec for audio classification.\"\"\"\n\n  def __init__(self, model_dir=None, strategy=None):\n    _ensure_tf25(_get_tf_version())\n    self.model_dir = model_dir\n    if not model_dir:\n      self.model_dir = tempfile.mkdtemp()\n    tf.compat.v1.logging.info('Checkpoints are stored in %s', self.model_dir)\n    self.strategy = strategy or tf.distribute.get_strategy()\n\n  @abc.abstractproperty\n  def target_sample_rate(self):\n    pass\n\n  @abc.abstractmethod\n  def create_model(self, num_classes, train_whole_model=False):\n    pass\n\n  @abc.abstractmethod\n  def run_classifier(self, model, epochs, train_ds, validation_ds, **kwargs):\n    pass\n\n  def preprocess_ds(self, ds, is_training=False, cache_fn=None):\n    \"\"\"Returns a preprocessed dataset.\"\"\"\n    _ = is_training\n    _ = cache_fn\n    return ds\n\n  def get_default_quantization_config(self):\n    \"\"\"Gets the default quantization configuration.\"\"\"\n    return None\n\n\ndef _remove_suffix_if_possible(text, suffix):\n  return text.rsplit(suffix, 1)[0]\n\n\nTFJS_MODEL_ROOT = 'https://storage.googleapis.com/tfjs-models/tfjs'\n\n\ndef _load_browser_fft_preprocess_model():\n  \"\"\"Load a model replicating WebAudio's AnalyzerNode.getFloatFrequencyData.\"\"\"\n  model_name = 'sc_preproc_model'\n  file_extension = '.tar.gz'\n  filename = model_name + file_extension\n  # Load the preprocessing model, which transforms audio waveform into\n  # spectrograms (2D image-like representation of sound).\n  # This model replicates WebAudio's AnalyzerNode.getFloatFrequencyData\n  # (https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/getFloatFrequencyData).\n  # It performs short-time Fourier transform (STFT) using a length-2048 Blackman\n  # window. It opeartes on mono audio at the 44100-Hz sample rate.\n  filepath = tf.keras.utils.get_file(\n      filename,\n      f'{TFJS_MODEL_ROOT}/speech-commands/conversion/{filename}',\n      cache_subdir='model_maker',\n      extract=True)\n  model_path = _remove_suffix_if_possible(filepath, file_extension)\n  return tf.keras.models.load_model(model_path)\n\n\ndef _load_tfjs_speech_command_model():\n  \"\"\"Download TFJS speech command model for fine-tune.\"\"\"\n  origin_root = f'{TFJS_MODEL_ROOT}/speech-commands/v0.3/browser_fft/18w'\n  files_to_download = [\n      'metadata.json', 'model.json', 'group1-shard1of2', 'group1-shard2of2'\n  ]\n  for filename in files_to_download:\n    filepath = tf.keras.utils.get_file(\n        filename,\n        f'{origin_root}/{filename}',\n        cache_subdir='model_maker/tfjs-sc-model')\n  model_path = os.path.join(os.path.dirname(filepath), 'model.json')\n  return model_util.load_tfjs_keras_model(model_path)\n\n\n@mm_export('audio_classifier.BrowserFftSpec')\nclass BrowserFFTSpec(BaseSpec):\n  \"\"\"Model good at detecting speech commands, using Browser FFT spectrum.\"\"\"\n\n  EXPECTED_WAVEFORM_LENGTH = 44032\n\n  # Information used to populate TFLite metadata.\n  _MODEL_NAME = 'AudioClassifier'\n  _MODEL_DESCRIPTION = ('Identify the most prominent type in the audio clip '\n                        'from a known set of categories.')\n\n  _MODEL_VERSION = 'v1'\n  _MODEL_AUTHOR = 'TensorFlow Lite Model Maker'\n  _MODEL_LICENSES = ('Apache License. Version 2.0 '\n                     'http://www.apache.org/licenses/LICENSE-2.0.')\n\n  _SAMPLE_RATE = 44100\n  _CHANNELS = 1\n\n  _INPUT_NAME = 'audio_clip'\n  _INPUT_DESCRIPTION = 'Input audio clip to be classified.'\n\n  _OUTPUT_NAME = 'probability'\n  _OUTPUT_DESCRIPTION = 'Scores of the labels respectively.'\n\n  def __init__(self, model_dir=None, strategy=None):\n    \"\"\"Initialize a new instance for BrowserFFT spec.\n\n    Args:\n      model_dir: The location to save the model checkpoint files.\n      strategy: An instance of TF distribute strategy. If none, it will use the\n        default strategy (either SingleDeviceStrategy or the current scoped\n        strategy.\n    \"\"\"\n    super(BrowserFFTSpec, self).__init__(model_dir, strategy)\n    self._preprocess_model = _load_browser_fft_preprocess_model()\n    self._tfjs_sc_model = _load_tfjs_speech_command_model()\n\n  @property\n  def target_sample_rate(self):\n    return 44100\n\n  @tf.function(input_signature=[\n      tf.TensorSpec(shape=[None], dtype=tf.float32),\n      tf.TensorSpec([], dtype=tf.int32)\n  ])\n  def _ensure_length(self, wav, unused_label):\n    return len(wav) >= self.EXPECTED_WAVEFORM_LENGTH\n\n  @tf.function(input_signature=[\n      tf.TensorSpec(shape=[None], dtype=tf.float32),\n      tf.TensorSpec([], dtype=tf.int32)\n  ])\n  def _split(self, wav, label):\n    \"\"\"Split the long audio samples into multiple trunks.\"\"\"\n    # wav shape: (audio_samples, )\n    chunks = tf.math.floordiv(len(wav), self.EXPECTED_WAVEFORM_LENGTH)\n    unused = tf.math.floormod(len(wav), self.EXPECTED_WAVEFORM_LENGTH)\n    # Drop unused data\n    wav = wav[:len(wav) - unused]\n    # Split the audio sample into multiple chunks\n    wav = tf.reshape(wav, (chunks, 1, self.EXPECTED_WAVEFORM_LENGTH))\n\n    return wav, tf.repeat(tf.expand_dims(label, 0), len(wav))\n\n  @tf.function(input_signature=[\n      tf.TensorSpec(shape=[1, EXPECTED_WAVEFORM_LENGTH], dtype=tf.float32),\n      tf.TensorSpec([], dtype=tf.int32)\n  ])\n  def _preprocess(self, x, label):\n    \"\"\"Preprocess the dataset to extract the spectrum.\"\"\"\n    # Add small Gaussian noise to the input x\n    # to solve the potential \"nan\" problem of the preprocess_model.\n    x = x + 1e-05 * tf.random.normal(x.shape)\n    # x has shape (1, EXPECTED_WAVEFORM_LENGTH)\n    spectrum = self._preprocess_model(x)\n    # y has shape (1, embedding_len)\n    spectrum = tf.squeeze(spectrum, axis=0)\n    # y has shape (embedding_len,)\n    return spectrum, label\n\n  def preprocess_ds(self, ds, is_training=False, cache_fn=None):\n    del is_training\n\n    autotune = tf.data.AUTOTUNE\n    ds = ds.filter(self._ensure_length)\n    ds = ds.map(self._split, num_parallel_calls=autotune).unbatch()\n    ds = ds.map(self._preprocess, num_parallel_calls=autotune)\n    if cache_fn:\n      ds = cache_fn(ds)\n    return ds\n\n  def create_model(self, num_classes, train_whole_model=False):\n    if num_classes <= 1:\n      raise ValueError(\n          'AudioClassifier expects `num_classes` to be greater than 1')\n    model = tf.keras.Sequential()\n    for layer in self._tfjs_sc_model.layers[:-1]:\n      model.add(layer)\n    model.add(\n        tf.keras.layers.Dense(\n            name='classification_head', units=num_classes,\n            activation='softmax'))\n    if not train_whole_model:\n      # Freeze all but the last layer of the model. The last layer will be\n      # fine-tuned during transfer learning.\n      for layer in model.layers[:-1]:\n        layer.trainable = False\n    return model\n\n  def run_classifier(self, model, epochs, train_ds, validation_ds, **kwargs):\n    model.compile(\n        optimizer='adam', loss='categorical_crossentropy', metrics=['acc'])\n\n    hist = model.fit(\n        train_ds, validation_data=validation_ds, epochs=epochs, **kwargs)\n    return hist\n\n  def create_serving_model(self, training_model):\n    \"\"\"Create a model for serving.\"\"\"\n    combined = tf.keras.Sequential()\n    combined.add(self._preprocess_model)\n    combined.add(training_model)\n    # Build the model.\n    combined.build([None, self.EXPECTED_WAVEFORM_LENGTH])\n    return combined\n\n  def _export_metadata(self, tflite_filepath, index_to_label,\n                       export_metadata_json_file):\n    \"\"\"Export TFLite metadata.\"\"\"\n    with MetadataWriter(\n        tflite_filepath,\n        name=self._MODEL_NAME,\n        description=self._MODEL_DESCRIPTION,\n        version=self._MODEL_VERSION,\n        author=self._MODEL_AUTHOR,\n        licenses=self._MODEL_LICENSES) as writer:\n      writer.add_input(\n          name=self._INPUT_NAME,\n          description=self._INPUT_DESCRIPTION,\n          sample_rate=self._SAMPLE_RATE,\n          channels=self._CHANNELS)\n\n      writer.add_output(\n          labels=index_to_label,\n          name=self._OUTPUT_NAME,\n          description=self._OUTPUT_DESCRIPTION)\n\n      json_filepath = (os.path.splitext(tflite_filepath)[0] +\n                       '.json') if export_metadata_json_file else None\n      writer.save(tflite_filepath, json_filepath)\n\n  def export_tflite(self,\n                    model,\n                    tflite_filepath,\n                    with_metadata=True,\n                    export_metadata_json_file=True,\n                    index_to_label=None,\n                    quantization_config=None):\n    \"\"\"Converts the retrained model to tflite format and saves it.\n\n    This method overrides the default `CustomModel._export_tflite` method, and\n    include the pre-processing in the exported TFLite library since support\n    library can't handle audio tasks yet.\n\n    Args:\n      model: An instance of the keras classification model to be exported.\n      tflite_filepath: File path to save tflite model.\n      with_metadata: Whether the output tflite model contains metadata.\n      export_metadata_json_file: Whether to export metadata in json file. If\n        True, export the metadata in the same directory as tflite model.Used\n        only if `with_metadata` is True.\n      index_to_label: A list that map from index to label class name.\n      quantization_config: Configuration for post-training quantization.\n    \"\"\"\n    combined = self.create_serving_model(model)\n\n    # Sets batch size from None to 1 when converting to tflite.\n    model_util.set_batch_size(model, batch_size=1)\n\n    model_util.export_tflite(\n        combined, tflite_filepath, quantization_config=quantization_config)\n\n    # Sets batch size back to None to support retraining later.\n    model_util.set_batch_size(model, batch_size=None)\n\n    if with_metadata:\n      if not ENABLE_METADATA:\n        print('Writing Metadata is not support in the installed tflite-support '\n              'version. Please use tflite-support >= 0.2.*')\n      else:\n        self._export_metadata(tflite_filepath, index_to_label,\n                              export_metadata_json_file)\n\n\n@mm_export('audio_classifier.YamNetSpec')\nclass YAMNetSpec(BaseSpec):\n  \"\"\"Model good at detecting environmental sounds, using YAMNet embedding.\"\"\"\n\n  EXPECTED_WAVEFORM_LENGTH = 15600  # effectively 0.975s\n  EMBEDDING_SIZE = 1024\n\n  # Information used to populate TFLite metadata.\n  _MODEL_NAME = 'yamnet/classification'\n  _MODEL_DESCRIPTION = 'Recognizes sound events'\n  _MODEL_VERSION = 'v1'\n  _MODEL_AUTHOR = 'TensorFlow Lite Model Maker'\n  _MODEL_LICENSES = ('Apache License. Version 2.0 '\n                     'http://www.apache.org/licenses/LICENSE-2.0.')\n\n  _SAMPLE_RATE = 16000\n  _CHANNELS = 1\n\n  _INPUT_NAME = 'audio_clip'\n  _INPUT_DESCRIPTION = 'Input audio clip to be classified.'\n\n  _YAMNET_OUTPUT_NAME = 'yamnet'\n  _YAMNET_OUTPUT_DESCRIPTION = ('Scores in range 0..1.0 for each of the 521 '\n                                'output classes.')\n\n  _CUSTOM_OUTPUT_NAME = 'custom'\n  _CUSTOM_OUTPUT_DESCRIPTION = (\n      'Scores in range 0..1.0 for each output classes.')\n\n  def __init__(\n      self,\n      model_dir: None = None,\n      strategy: None = None,\n      yamnet_model_handle='https://tfhub.dev/google/yamnet/1',\n      frame_length=EXPECTED_WAVEFORM_LENGTH,  # Window size 0.975 s\n      frame_step=EXPECTED_WAVEFORM_LENGTH // 2,  # Hop of 0.975 /2 s\n      keep_yamnet_and_custom_heads=True):\n    \"\"\"Initialize a new instance for YAMNet spec.\n\n    Args:\n      model_dir: The location to save the model checkpoint files.\n      strategy: An instance of TF distribute strategy. If none, it will use the\n        default strategy (either SingleDeviceStrategy or the current scoped\n        strategy.\n      yamnet_model_handle: Path of the TFHub model for retrining.\n      frame_length: The number of samples in each audio frame. If the audio file\n        is shorter than `frame_length`, then the audio file will be ignored.\n      frame_step: The number of samples between two audio frames. This value\n        should be smaller than `frame_length`, otherwise some samples will be\n        ignored.\n      keep_yamnet_and_custom_heads: Boolean, decides if the final TFLite model\n        contains both YAMNet and custom trained classification heads. When set\n        to False, only the trained custom head will be preserved.\n    \"\"\"\n    super(YAMNetSpec, self).__init__(model_dir, strategy)\n    self._yamnet_model_handle = yamnet_model_handle\n    self._yamnet_model = hub.load(yamnet_model_handle)\n    self._frame_length = frame_length\n    self._frame_step = frame_step\n    self._keep_yamnet_and_custom_heads = keep_yamnet_and_custom_heads\n\n  @property\n  def target_sample_rate(self):\n    return self._SAMPLE_RATE\n\n  def create_model(self, num_classes, train_whole_model=False):\n    model = tf.keras.Sequential([\n        tf.keras.layers.InputLayer(\n            input_shape=(YAMNetSpec.EMBEDDING_SIZE),\n            dtype=tf.float32,\n            name='embedding'),\n        tf.keras.layers.Dense(\n            num_classes, name='classification_head', activation='softmax')\n    ])\n    return model\n\n  def run_classifier(self, model, epochs, train_ds, validation_ds, **kwargs):\n    model.compile(\n        optimizer='adam', loss='categorical_crossentropy', metrics=['acc'])\n\n    hist = model.fit(\n        train_ds, validation_data=validation_ds, epochs=epochs, **kwargs)\n    return hist\n\n  # Annotate the TF function with input_signature to avoid re-tracing. Otherwise\n  # the TF function gets retraced everytime the input shape is changed.\n  # Check https://www.tensorflow.org/api_docs/python/tf/function#args_1 for more\n  # information.\n  @tf.function(input_signature=[\n      tf.TensorSpec(shape=[None], dtype=tf.float32),\n      tf.TensorSpec([], dtype=tf.int32)\n  ])\n  def _frame(self, wav, label):\n    clips = tf.signal.frame(\n        wav, frame_length=self._frame_length, frame_step=self._frame_step)\n    batch_labels = tf.repeat(tf.expand_dims(label, 0), len(clips))\n\n    return clips, batch_labels\n\n  @tf.function(input_signature=[\n      tf.TensorSpec(shape=[None], dtype=tf.float32),\n      tf.TensorSpec([], dtype=tf.int32)\n  ])\n  def _extract_embedding(self, wav, label):\n    _, embeddings, _ = self._yamnet_model(wav)  # (chunks, EMBEDDING_SIZE)\n    embedding = tf.reduce_mean(embeddings, axis=0)\n    return embedding, label\n\n  @tf.function(input_signature=[\n      tf.TensorSpec(shape=[EMBEDDING_SIZE], dtype=tf.float32),\n      tf.TensorSpec([], dtype=tf.int32)\n  ])\n  def _add_noise(self, embedding, label):\n    noise = tf.random.normal(\n        embedding.shape, mean=0.0, stddev=.2, dtype=tf.dtypes.float32)\n    return noise + embedding, label\n\n  def preprocess_ds(self, ds, is_training=False, cache_fn=None):\n    autotune = tf.data.AUTOTUNE\n    ds = ds.map(self._frame, num_parallel_calls=autotune).unbatch()\n    ds = ds.map(self._extract_embedding, num_parallel_calls=autotune)\n\n    # Cache intermediate results right before data augmentation.\n    if cache_fn:\n      ds = cache_fn(ds)\n\n    if is_training:\n      ds = ds.map(self._add_noise, num_parallel_calls=autotune)\n    return ds\n\n  def _yamnet_labels(self):\n    class_map_path = self._yamnet_model.class_map_path().numpy()\n    class_map_csv_text = tf.io.read_file(class_map_path).numpy().decode('utf-8')\n    class_map_csv = io.StringIO(class_map_csv_text)\n    class_names = [\n        display_name for (class_index, mid,\n                          display_name) in csv.reader(class_map_csv)\n    ]\n    class_names = class_names[1:]  # Skip CSV header\n    return class_names\n\n  def _export_metadata(self, tflite_filepath, index_to_label,\n                       export_metadata_json_file):\n    \"\"\"Export TFLite metadata.\"\"\"\n    with MetadataWriter(\n        tflite_filepath,\n        name=self._MODEL_NAME,\n        description=self._MODEL_DESCRIPTION,\n        version=self._MODEL_VERSION,\n        author=self._MODEL_AUTHOR,\n        licenses=self._MODEL_LICENSES) as writer:\n      writer.add_input(\n          name=self._INPUT_NAME,\n          description=self._INPUT_DESCRIPTION,\n          sample_rate=self._SAMPLE_RATE,\n          channels=self._CHANNELS)\n\n      if self._keep_yamnet_and_custom_heads:\n        writer.add_output(\n            labels=self._yamnet_labels(),\n            name=self._YAMNET_OUTPUT_NAME,\n            description=self._YAMNET_OUTPUT_DESCRIPTION)\n\n      writer.add_output(\n          labels=index_to_label,\n          name=self._CUSTOM_OUTPUT_NAME,\n          description=self._CUSTOM_OUTPUT_DESCRIPTION)\n\n      json_filepath = (os.path.splitext(tflite_filepath)[0] +\n                       '.json') if export_metadata_json_file else None\n      writer.save(tflite_filepath, json_filepath)\n\n  def create_serving_model(self, training_model):\n    \"\"\"Create a model for serving.\"\"\"\n    embedding_extraction_layer = hub.KerasLayer(\n        self._yamnet_model_handle, trainable=False)\n    keras_input = tf.keras.Input(\n        shape=(YAMNetSpec.EXPECTED_WAVEFORM_LENGTH,),\n        dtype=tf.float32,\n        name='audio')  # (1, wav)\n    reshaped_input = tf.reshape(keras_input,\n                                (YAMNetSpec.EXPECTED_WAVEFORM_LENGTH,))  # (wav)\n\n    scores, embeddings, _ = embedding_extraction_layer(reshaped_input)\n    serving_outputs = training_model(embeddings)\n\n    if self._keep_yamnet_and_custom_heads:\n      serving_model = tf.keras.Model(keras_input, [scores, serving_outputs])\n    else:\n      serving_model = tf.keras.Model(keras_input, serving_outputs)\n\n    return serving_model\n\n  def export_tflite(self,\n                    model,\n                    tflite_filepath,\n                    with_metadata=True,\n                    export_metadata_json_file=True,\n                    index_to_label=None,\n                    quantization_config=None):\n    \"\"\"Converts the retrained model to tflite format and saves it.\n\n    This method overrides the default `CustomModel._export_tflite` method, and\n    include the spectrom extraction in the model.\n\n    The exported model has input shape (1, number of wav samples)\n\n    Args:\n      model: An instance of the keras classification model to be exported.\n      tflite_filepath: File path to save tflite model.\n      with_metadata: Whether the output tflite model contains metadata.\n      export_metadata_json_file: Whether to export metadata in json file. If\n        True, export the metadata in the same directory as tflite model. Used\n        only if `with_metadata` is True.\n      index_to_label: A list that map from index to label class name.\n      quantization_config: Configuration for post-training quantization.\n    \"\"\"\n    serving_model = self.create_serving_model(model)\n\n    # TODO(b/164229433): Remove SELECT_TF_OPS once changes in the bug are\n    # released.\n    model_util.export_tflite(\n        serving_model, tflite_filepath, quantization_config=quantization_config)\n\n    if with_metadata:\n      if not ENABLE_METADATA:\n        print('Writing Metadata is not support in the current tflite-support '\n              'version. Please use tflite-support >= 0.2.*')\n      else:\n        self._export_metadata(tflite_filepath, index_to_label,\n                              export_metadata_json_file)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/model_spec/audio_spec_test.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Tests for audio specs.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nimport unittest\n\nimport numpy as np\nfrom packaging import version\nimport tensorflow.compat.v2 as tf\nfrom tensorflow_examples.lite.model_maker.core.task import configs\nfrom tensorflow_examples.lite.model_maker.core.task import model_util\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import audio_spec\n\n\ndef _gen_dataset(spec, total_samples, num_classes, batch_size, seed):\n\n  def fill_shape(new_shape):\n\n    @tf.function\n    def fn(value):\n      return tf.cast(tf.fill(dims=new_shape, value=value), tf.float32)\n\n    return fn\n\n  wav_ds = tf.data.Dataset.random(seed=seed).take(total_samples)\n  wav_ds = wav_ds.map(fill_shape([\n      spec.target_sample_rate,\n  ]))\n\n  labels = tf.data.Dataset.from_tensor_slices(\n      np.random.randint(low=0, high=num_classes,\n                        size=total_samples).astype('int32'))\n  dataset = tf.data.Dataset.zip((wav_ds, labels))\n  dataset = spec.preprocess_ds(dataset)\n\n  @tf.function\n  def _one_hot_encoding_label(wav, label):\n    return wav, tf.one_hot(label, num_classes)\n\n  dataset = dataset.map(_one_hot_encoding_label)\n\n  dataset = dataset.batch(batch_size)\n\n  return dataset\n\n\nclass BaseSpecTest(tf.test.TestCase):\n\n  def testEnsureVersion(self):\n    valid_versions = ['2.5.0', '2.5.0rc1', '2.6']\n    invalid_versions = [\n        '2.4.1',\n    ]\n    specs = [audio_spec.YAMNetSpec, audio_spec.BrowserFFTSpec]\n\n    tmp_version_fn = audio_spec._get_tf_version\n    for spec in specs:\n      for valid_version in valid_versions:\n        audio_spec._get_tf_version = lambda: valid_version  # pylint: disable=cell-var-from-loop\n        spec()\n\n      for valid_version in invalid_versions:\n        audio_spec._get_tf_version = lambda: valid_version  # pylint: disable=cell-var-from-loop\n        with self.assertRaisesRegex(RuntimeError, '2.5.0'):\n          spec()\n\n    audio_spec._get_tf_version = tmp_version_fn\n\n\nclass BaseTest(tf.test.TestCase):\n\n  def _train_and_export(self,\n                        spec,\n                        num_classes,\n                        filename,\n                        expected_model_size,\n                        quantization_config=None,\n                        training=True):\n    dataset = _gen_dataset(\n        spec, total_samples=10, num_classes=num_classes, batch_size=2, seed=100)\n    model = spec.create_model(num_classes)\n    if training:\n      spec.run_classifier(model, epochs=1, train_ds=dataset, validation_ds=None)\n\n    tflite_filepath = os.path.join(self.get_temp_dir(), filename)\n    spec.export_tflite(\n        model,\n        tflite_filepath,\n        index_to_label=['label_{}'.format(i) for i in range(num_classes)],\n        quantization_config=quantization_config)\n\n    self.assertNear(\n        os.path.getsize(tflite_filepath), expected_model_size, 1000 * 1000)\n\n    return tflite_filepath\n\n\n@unittest.skipIf(\n    version.parse(tf.__version__) < version.parse('2.5'),\n    'Audio Classification requires TF 2.5 or later')\nclass YAMNetSpecTest(BaseTest):\n\n  def _test_preprocess(self, input_shape, input_count, output_shape,\n                       output_count):\n    spec = audio_spec.YAMNetSpec()\n    wav_ds = tf.data.Dataset.from_tensor_slices([tf.ones(input_shape)] *\n                                                input_count)\n    label_ds = tf.data.Dataset.range(input_count).map(\n        lambda x: tf.cast(x, tf.int32))\n\n    ds = tf.data.Dataset.zip((wav_ds, label_ds))\n    ds = spec.preprocess_ds(ds)\n\n    chunks = output_count // input_count\n\n    cnt = 0\n    for item, label in ds:\n      cnt += 1\n    self.assertEqual(cnt, output_count)\n\n    # More thorough checks.\n    cnt = 0\n    for item, label in ds:\n      self.assertEqual(output_shape, item.shape)\n      self.assertEqual(label, cnt // chunks)\n      cnt += 1\n\n  def test_preprocess(self):\n    # No padding on the input.\n    self._test_preprocess(\n        input_shape=(10,), input_count=2, output_shape=(1024,), output_count=0)\n    # Split the input data into trunks\n    self._test_preprocess(\n        input_shape=(16000 * 2,),\n        input_count=2,\n        output_shape=(1024,),\n        output_count=6)\n    self._test_preprocess(\n        input_shape=(15600,),\n        input_count=1,\n        output_shape=(1024,),\n        output_count=1)\n\n  def test_create_model(self):\n    # Make sure that there is no naming conflicts in the graph.\n    spec = audio_spec.YAMNetSpec()\n    model = spec.create_model(10)\n    model = spec.create_model(10)\n    model = spec.create_model(10)\n    self.assertEqual(model.input_shape, (None, 1024))\n    self.assertEqual(model.output_shape, (None, 10))\n\n  def test_yamnet_two_heads(self):\n    tflite_path = self._train_and_export(\n        audio_spec.YAMNetSpec(keep_yamnet_and_custom_heads=True),\n        num_classes=2,\n        filename='two_heads.tflite',\n        expected_model_size=15 * 1000 * 1000)\n    self.assertEqual(\n        2, len(model_util.get_lite_runner(tflite_path).output_details))\n    self.assertAllEqual(\n        [1, 521],\n        model_util.get_lite_runner(tflite_path).output_details[0]['shape'])\n    self.assertAllEqual(\n        [1, 2],\n        model_util.get_lite_runner(tflite_path).output_details[1]['shape'])\n    self.assertEqual(\n        model_util.extract_tflite_metadata_json(tflite_path), \"\"\"{\n  \"name\": \"yamnet/classification\",\n  \"description\": \"Recognizes sound events\",\n  \"version\": \"v1\",\n  \"subgraph_metadata\": [\n    {\n      \"input_tensor_metadata\": [\n        {\n          \"name\": \"audio_clip\",\n          \"description\": \"Input audio clip to be classified.\",\n          \"content\": {\n            \"content_properties_type\": \"AudioProperties\",\n            \"content_properties\": {\n              \"sample_rate\": 16000,\n              \"channels\": 1\n            }\n          },\n          \"stats\": {\n          }\n        }\n      ],\n      \"output_tensor_metadata\": [\n        {\n          \"name\": \"yamnet\",\n          \"description\": \"Scores in range 0..1.0 for each of the 521 output classes.\",\n          \"content\": {\n            \"content_properties_type\": \"FeatureProperties\",\n            \"content_properties\": {\n            }\n          },\n          \"stats\": {\n            \"max\": [\n              1.0\n            ],\n            \"min\": [\n              0.0\n            ]\n          },\n          \"associated_files\": [\n            {\n              \"name\": \"yamnet_labels.txt\",\n              \"description\": \"Labels for categories that the model can recognize.\",\n              \"type\": \"TENSOR_AXIS_LABELS\"\n            }\n          ]\n        },\n        {\n          \"name\": \"custom\",\n          \"description\": \"Scores in range 0..1.0 for each output classes.\",\n          \"content\": {\n            \"content_properties_type\": \"FeatureProperties\",\n            \"content_properties\": {\n            }\n          },\n          \"stats\": {\n            \"max\": [\n              1.0\n            ],\n            \"min\": [\n              0.0\n            ]\n          },\n          \"associated_files\": [\n            {\n              \"name\": \"custom_labels.txt\",\n              \"description\": \"Labels for categories that the model can recognize.\",\n              \"type\": \"TENSOR_AXIS_LABELS\"\n            }\n          ]\n        }\n      ]\n    }\n  ],\n  \"author\": \"TensorFlow Lite Model Maker\",\n  \"license\": \"Apache License. Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.\",\n  \"min_parser_version\": \"1.3.0\"\n}\n\"\"\")\n\n  def test_yamnet_single_head(self):\n    tflite_path = self._train_and_export(\n        audio_spec.YAMNetSpec(keep_yamnet_and_custom_heads=False),\n        num_classes=2,\n        filename='single_head.tflite',\n        expected_model_size=13 * 1000 * 1000)\n    self.assertEqual(\n        1, len(model_util.get_lite_runner(tflite_path).output_details))\n    self.assertAllEqual(\n        [1, 2],\n        model_util.get_lite_runner(tflite_path).output_details[0]['shape'])\n    self.assertEqual(\n        model_util.extract_tflite_metadata_json(tflite_path), \"\"\"{\n  \"name\": \"yamnet/classification\",\n  \"description\": \"Recognizes sound events\",\n  \"version\": \"v1\",\n  \"subgraph_metadata\": [\n    {\n      \"input_tensor_metadata\": [\n        {\n          \"name\": \"audio_clip\",\n          \"description\": \"Input audio clip to be classified.\",\n          \"content\": {\n            \"content_properties_type\": \"AudioProperties\",\n            \"content_properties\": {\n              \"sample_rate\": 16000,\n              \"channels\": 1\n            }\n          },\n          \"stats\": {\n          }\n        }\n      ],\n      \"output_tensor_metadata\": [\n        {\n          \"name\": \"custom\",\n          \"description\": \"Scores in range 0..1.0 for each output classes.\",\n          \"content\": {\n            \"content_properties_type\": \"FeatureProperties\",\n            \"content_properties\": {\n            }\n          },\n          \"stats\": {\n            \"max\": [\n              1.0\n            ],\n            \"min\": [\n              0.0\n            ]\n          },\n          \"associated_files\": [\n            {\n              \"name\": \"custom_labels.txt\",\n              \"description\": \"Labels for categories that the model can recognize.\",\n              \"type\": \"TENSOR_AXIS_LABELS\"\n            }\n          ]\n        }\n      ]\n    }\n  ],\n  \"author\": \"TensorFlow Lite Model Maker\",\n  \"license\": \"Apache License. Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.\",\n  \"min_parser_version\": \"1.3.0\"\n}\n\"\"\")\n\n  def test_no_metadata(self):\n    audio_spec.ENABLE_METADATA = False\n    tflite_path = self._train_and_export(\n        audio_spec.YAMNetSpec(keep_yamnet_and_custom_heads=True),\n        num_classes=2,\n        filename='two_heads.tflite',\n        expected_model_size=15 * 1000 * 1000)\n    self.assertEqual(\n        2, len(model_util.get_lite_runner(tflite_path).output_details))\n    with self.assertRaisesRegex(ValueError, 'The model does not have metadata'):\n      model_util.extract_tflite_metadata_json(tflite_path)\n    audio_spec.ENABLE_METADATA = True\n\n  def test_binary_classification(self):\n    self._train_and_export(\n        audio_spec.YAMNetSpec(keep_yamnet_and_custom_heads=True),\n        num_classes=2,\n        filename='binary_classification.tflite',\n        expected_model_size=15 * 1000 * 1000)\n\n  def test_dynamic_range_quantization(self):\n    self._train_and_export(\n        audio_spec.YAMNetSpec(keep_yamnet_and_custom_heads=True),\n        num_classes=5,\n        filename='basic_5_classes_training.tflite',\n        expected_model_size=4 * 1000 * 1000,\n        quantization_config=configs.QuantizationConfig.for_dynamic())\n\n\n@unittest.skipIf(\n    version.parse(tf.__version__) < version.parse('2.5'),\n    'Audio Classification requires TF 2.5 or later')\nclass BrowserFFTSpecTest(BaseTest):\n\n  @classmethod\n  def setUpClass(cls):\n    super(BrowserFFTSpecTest, cls).setUpClass()\n    cls._spec = audio_spec.BrowserFFTSpec()\n\n  def test_model_initialization(self):\n    model = self._spec.create_model(10)\n\n    self.assertEqual(self._spec._preprocess_model.input_shape,\n                     (None, self._spec.EXPECTED_WAVEFORM_LENGTH))\n    self.assertEqual(self._spec._preprocess_model.output_shape,\n                     (None, None, 232, 1))\n    self.assertEqual(self._spec._tfjs_sc_model.input_shape, (None, 43, 232, 1))\n    self.assertEqual(self._spec._tfjs_sc_model.output_shape, (None, 20))\n    self.assertEqual(model.input_shape, (None, 43, 232, 1))\n    self.assertEqual(model.output_shape, (None, 10))\n\n  def test_create_model(self):\n    # Make sure that there is no naming conflicts.\n    self._spec.create_model(100)\n    self._spec.create_model(100)\n    self._spec.create_model(100)\n\n    tf.keras.backend.clear_session()\n    # Binary classification is not supported yet.\n    with self.assertRaises(ValueError):\n      self._spec.create_model(0)\n    tf.keras.backend.clear_session()\n    with self.assertRaises(ValueError):\n      self._spec.create_model(1)\n    tf.keras.backend.clear_session()\n    # It's more efficient to use BinaryClassification when num_classes=2, but\n    # this is still supported (slightly less efficient).\n    self._spec.create_model(20)\n    tf.keras.backend.clear_session()\n\n  def test_dynamic_range_quantization(self):\n    self._train_and_export(\n        audio_spec.BrowserFFTSpec(),\n        num_classes=2,\n        filename='binary_classification.tflite',\n        expected_model_size=1 * 1000 * 1000,\n        quantization_config=configs.QuantizationConfig.for_dynamic(),\n        training=False)  # Training results Nan values with the current scheme.\n\n  def test_binary_classification(self):\n    self._train_and_export(\n        audio_spec.BrowserFFTSpec(),\n        num_classes=2,\n        filename='binary_classification.tflite',\n        expected_model_size=6 * 1000 * 1000)\n\n  def test_basic_training(self):\n    tflite_path = self._train_and_export(\n        audio_spec.BrowserFFTSpec(),\n        num_classes=5,\n        filename='basic_5_classes_training.tflite',\n        expected_model_size=6 * 1000 * 1000)\n    self.assertEqual(\n        model_util.extract_tflite_metadata_json(tflite_path), \"\"\"{\n  \"name\": \"AudioClassifier\",\n  \"description\": \"Identify the most prominent type in the audio clip from a known set of categories.\",\n  \"version\": \"v1\",\n  \"subgraph_metadata\": [\n    {\n      \"input_tensor_metadata\": [\n        {\n          \"name\": \"audio_clip\",\n          \"description\": \"Input audio clip to be classified.\",\n          \"content\": {\n            \"content_properties_type\": \"AudioProperties\",\n            \"content_properties\": {\n              \"sample_rate\": 44100,\n              \"channels\": 1\n            }\n          },\n          \"stats\": {\n          }\n        }\n      ],\n      \"output_tensor_metadata\": [\n        {\n          \"name\": \"probability\",\n          \"description\": \"Scores of the labels respectively.\",\n          \"content\": {\n            \"content_properties_type\": \"FeatureProperties\",\n            \"content_properties\": {\n            }\n          },\n          \"stats\": {\n            \"max\": [\n              1.0\n            ],\n            \"min\": [\n              0.0\n            ]\n          },\n          \"associated_files\": [\n            {\n              \"name\": \"probability_labels.txt\",\n              \"description\": \"Labels for categories that the model can recognize.\",\n              \"type\": \"TENSOR_AXIS_LABELS\"\n            }\n          ]\n        }\n      ]\n    }\n  ],\n  \"author\": \"TensorFlow Lite Model Maker\",\n  \"license\": \"Apache License. Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.\",\n  \"min_parser_version\": \"1.3.0\"\n}\n\"\"\")\n\n\nif __name__ == '__main__':\n  # Load compressed models from tensorflow_hub\n  os.environ['TFHUB_MODEL_LOAD_FORMAT'] = 'COMPRESSED'\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/model_spec/image_spec.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Image model specification.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport functools\n\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core.api import mm_export\nfrom tensorflow_examples.lite.model_maker.core.task import configs\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import util\n\n\n@mm_export('image_classifier.ModelSpec')\nclass ImageModelSpec(object):\n  \"\"\"A specification of image model.\"\"\"\n\n  mean_rgb = [0.0]\n  stddev_rgb = [255.0]\n\n  def __init__(self,\n               uri,\n               compat_tf_versions=None,\n               input_image_shape=None,\n               name=''):\n    \"\"\"Initializes a new instance of the `ImageModelSpec` class.\n\n    Args:\n      uri: str, URI to the pretrained model.\n      compat_tf_versions: list of int, compatible TF versions.\n      input_image_shape: list of int, input image shape. Default: [224, 224].\n      name: str, model spec name.\n    \"\"\"\n    self.uri = uri\n    self.compat_tf_versions = compat.get_compat_tf_versions(compat_tf_versions)\n    self.name = name\n\n    if input_image_shape is None:\n      input_image_shape = [224, 224]\n    self.input_image_shape = input_image_shape\n\n  def get_default_quantization_config(self, representative_data):\n    \"\"\"Gets the default quantization configuration.\"\"\"\n    config = configs.QuantizationConfig.for_int8(representative_data)\n    return config\n\n\nmobilenet_v2_spec = functools.partial(\n    ImageModelSpec,\n    uri='https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/4',\n    compat_tf_versions=2,\n    name='mobilenet_v2')\nmobilenet_v2_spec.__doc__ = util.wrap_doc(\n    ImageModelSpec,\n    'Creates MobileNet v2 model spec. See also: `tflite_model_maker.image_classifier.ModelSpec`.'\n)\nmm_export('image_classifier.MobileNetV2Spec').export_constant(\n    __name__, 'mobilenet_v2_spec')\n\nresnet_50_spec = functools.partial(\n    ImageModelSpec,\n    uri='https://tfhub.dev/google/imagenet/resnet_v2_50/feature_vector/4',\n    compat_tf_versions=2,\n    name='resnet_50')\nresnet_50_spec.__doc__ = util.wrap_doc(\n    ImageModelSpec,\n    'Creates ResNet 50 model spec. See also: `tflite_model_maker.image_classifier.ModelSpec`.'\n)\nmm_export('image_classifier.Resnet50Spec').export_constant(\n    __name__, 'resnet_50_spec')\n\nefficientnet_lite0_spec = functools.partial(\n    ImageModelSpec,\n    uri='https://tfhub.dev/tensorflow/efficientnet/lite0/feature-vector/2',\n    compat_tf_versions=[1, 2],\n    name='efficientnet_lite0')\nefficientnet_lite0_spec.__doc__ = util.wrap_doc(\n    ImageModelSpec,\n    'Creates EfficientNet-Lite0 model spec. See also: `tflite_model_maker.image_classifier.ModelSpec`.'\n)\nmm_export('image_classifier.EfficientNetLite0Spec').export_constant(\n    __name__, 'efficientnet_lite0_spec')\n\nefficientnet_lite1_spec = functools.partial(\n    ImageModelSpec,\n    uri='https://tfhub.dev/tensorflow/efficientnet/lite1/feature-vector/2',\n    compat_tf_versions=[1, 2],\n    input_image_shape=[240, 240],\n    name='efficientnet_lite1')\nefficientnet_lite1_spec.__doc__ = util.wrap_doc(\n    ImageModelSpec,\n    'Creates EfficientNet-Lite1 model spec. See also: `tflite_model_maker.image_classifier.ModelSpec`.'\n)\nmm_export('image_classifier.EfficientNetLite1Spec').export_constant(\n    __name__, 'efficientnet_lite1_spec')\n\nefficientnet_lite2_spec = functools.partial(\n    ImageModelSpec,\n    uri='https://tfhub.dev/tensorflow/efficientnet/lite2/feature-vector/2',\n    compat_tf_versions=[1, 2],\n    input_image_shape=[260, 260],\n    name='efficientnet_lite2')\nefficientnet_lite2_spec.__doc__ = util.wrap_doc(\n    ImageModelSpec,\n    'Creates EfficientNet-Lite2 model spec. See also: `tflite_model_maker.image_classifier.ModelSpec`.'\n)\nmm_export('image_classifier.EfficientNetLite2Spec').export_constant(\n    __name__, 'efficientnet_lite2_spec')\n\nefficientnet_lite3_spec = functools.partial(\n    ImageModelSpec,\n    uri='https://tfhub.dev/tensorflow/efficientnet/lite3/feature-vector/2',\n    compat_tf_versions=[1, 2],\n    input_image_shape=[280, 280],\n    name='efficientnet_lite3')\nefficientnet_lite3_spec.__doc__ = util.wrap_doc(\n    ImageModelSpec,\n    'Creates EfficientNet-Lite3 model spec. See also: `tflite_model_maker.image_classifier.ModelSpec`.'\n)\nmm_export('image_classifier.EfficientNetLite3Spec').export_constant(\n    __name__, 'efficientnet_lite3_spec')\n\nefficientnet_lite4_spec = functools.partial(\n    ImageModelSpec,\n    uri='https://tfhub.dev/tensorflow/efficientnet/lite4/feature-vector/2',\n    compat_tf_versions=[1, 2],\n    input_image_shape=[300, 300],\n    name='efficientnet_lite4')\nefficientnet_lite4_spec.__doc__ = util.wrap_doc(\n    ImageModelSpec,\n    'Creates EfficientNet-Lite4 model spec. See also: `tflite_model_maker.image_classifier.ModelSpec`.'\n)\nmm_export('image_classifier.EfficientNetLite4Spec').export_constant(\n    __name__, 'efficientnet_lite4_spec')\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/model_spec/model_spec_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nimport unittest\n\nfrom absl.testing import parameterized\nfrom packaging import version\nimport tensorflow.compat.v2 as tf\n\nfrom tensorflow_examples.lite.model_maker.core.data_util import recommendation_testutil\nfrom tensorflow_examples.lite.model_maker.core.task import model_spec as ms\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import image_spec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import text_spec\n\nMODELS = (\n    ms.IMAGE_CLASSIFICATION_MODELS + ms.TEXT_CLASSIFICATION_MODELS +\n    ms.QUESTION_ANSWER_MODELS)\n\n\nclass ModelSpecTest(tf.test.TestCase, parameterized.TestCase):\n\n  def test_get(self):\n    spec = ms.get('mobilenet_v2')\n    self.assertIsInstance(spec, image_spec.ImageModelSpec)\n\n    spec = ms.get('average_word_vec')\n    self.assertIsInstance(spec, text_spec.AverageWordVecModelSpec)\n\n    spec = ms.get(image_spec.mobilenet_v2_spec)\n    self.assertIsInstance(spec, image_spec.ImageModelSpec)\n\n  @parameterized.parameters(MODELS)\n  def test_get_not_none(self, model):\n    spec = ms.get(model)\n    self.assertIsNotNone(spec)\n\n  @unittest.skipIf(\n      version.parse(tf.__version__) < version.parse('2.5'),\n      'Audio Classification requires TF 2.5 or later')\n  @parameterized.parameters(ms.AUDIO_CLASSIFICATION_MODELS)\n  def test_get_not_none_audio_models(self, model):\n    spec = ms.get(model)\n    self.assertIsNotNone(spec)\n\n  @parameterized.parameters(ms.RECOMMENDATION_MODELS)\n  def test_get_not_none_recommendation_models(self, model):\n    spec = ms.get(\n        model,\n        input_spec=recommendation_testutil.get_input_spec(),\n        model_hparams=recommendation_testutil.get_model_hparams())\n    self.assertIsNotNone(spec)\n\n  def test_get_raises(self):\n    with self.assertRaises(KeyError):\n      ms.get('not_exist_model_spec')\n\n\nif __name__ == '__main__':\n  # Load compressed models from tensorflow_hub\n  os.environ['TFHUB_MODEL_LOAD_FORMAT'] = 'COMPRESSED'\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/model_spec/object_detector_spec.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Model specification for object detection.\"\"\"\n\nimport collections\nimport functools\nimport os\nimport tempfile\nfrom typing import Optional, Tuple, Dict\n\nfrom absl import logging\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core.api.api_util import mm_export\nfrom tensorflow_examples.lite.model_maker.core.data_util import object_detector_dataloader\nfrom tensorflow_examples.lite.model_maker.core.task import configs\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import util\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import coco_metric\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import hparams_config\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import efficientdet_keras\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import eval_tflite\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import label_util\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import postprocess\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import train\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import train_lib\n\n# Number of calibration steps for full integer quantization. 500 steps are\n# enough to get a reasonable post-quantization result.\n_NUM_CALIBRATION_STEPS = 500\n\n\ndef _get_ordered_label_map(\n    label_map: Optional[Dict[int, str]]) -> Optional[Dict[int, str]]:\n  \"\"\"Gets label_map as an OrderedDict instance with ids sorted.\"\"\"\n  if not label_map:\n    return label_map\n  ordered_label_map = collections.OrderedDict()\n  for idx in sorted(label_map.keys()):\n    ordered_label_map[idx] = label_map[idx]\n  return ordered_label_map\n\n\nclass ExportModel(efficientdet_keras.EfficientDetModel):\n  \"\"\"Model to be exported as SavedModel/TFLite format.\"\"\"\n\n  def __init__(self,\n               model: tf.keras.Model,\n               config: hparams_config.Config,\n               pre_mode: Optional[str] = 'infer',\n               post_mode: Optional[str] = 'global',\n               name: Optional[str] = ''):\n    \"\"\"Initilizes an instance with the keras model and pre/post_mode paramaters.\n\n    Args:\n      model: The EfficientDetNet model used for training which doesn't have pre\n        and post processing.\n      config: Model configuration.\n      pre_mode: Pre-processing Mode, must be {None, 'infer'}.\n      post_mode: Post-processing Mode, must be {None, 'global', 'per_class',\n        'tflite'}.\n      name: Model name.\n    \"\"\"\n    super(efficientdet_keras.EfficientDetNet, self).__init__(name=name)\n    self.model = model\n    self.config = config\n    self.pre_mode = pre_mode\n    self.post_mode = post_mode\n\n  @tf.function\n  def __call__(self, inputs: tf.Tensor):\n    \"\"\"Calls this model.\n\n    Args:\n      inputs: a tensor with common shape [batch, height, width, channels].\n\n    Returns:\n      the output tensor list.\n    \"\"\"\n    config = self.config\n\n    # Preprocess.\n    inputs, scales = self._preprocessing(inputs, config.image_size,\n                                         config.mean_rgb, config.stddev_rgb,\n                                         self.pre_mode)\n    # Network.\n    outputs = self.model(inputs, training=False)\n\n    # Postprocess for detection.\n    det_outputs = self._postprocess(outputs[0], outputs[1], scales,\n                                    self.post_mode)\n    outputs = det_outputs + outputs[2:]\n\n    return outputs\n\n\n@mm_export('object_detector.EfficientDetSpec')\nclass EfficientDetModelSpec(object):\n  \"\"\"A specification of the EfficientDet model.\"\"\"\n\n  compat_tf_versions = compat.get_compat_tf_versions(2)\n\n  def __init__(self,\n               model_name: str,\n               uri: str,\n               hparams: str = '',\n               model_dir: Optional[str] = None,\n               epochs: int = 50,\n               batch_size: int = 64,\n               steps_per_execution: int = 1,\n               moving_average_decay: int = 0,\n               var_freeze_expr: str = '(efficientnet|fpn_cells|resample_p6)',\n               tflite_max_detections: int = 25,\n               strategy: Optional[str] = None,\n               tpu: Optional[str] = None,\n               gcp_project: Optional[str] = None,\n               tpu_zone: Optional[str] = None,\n               use_xla: bool = False,\n               profile: bool = False,\n               debug: bool = False,\n               tf_random_seed: int = 111111,\n               verbose: int = 0) -> None:\n    \"\"\"Initialze an instance with model paramaters.\n\n    Args:\n      model_name: Model name.\n      uri: TF-Hub path/url to EfficientDet module.\n      hparams: Hyperparameters used to overwrite default configuration. Can be\n        1) Dict, contains parameter names and values; 2) String, Comma separated\n        k=v pairs of hyperparameters; 3) String, yaml filename which's a module\n        containing attributes to use as hyperparameters.\n      model_dir: The location to save the model checkpoint files.\n      epochs: Default training epochs.\n      batch_size: Training & Evaluation batch size.\n      steps_per_execution: Number of steps per training execution.\n      moving_average_decay: Float. The decay to use for maintaining moving\n        averages of the trained parameters.\n      var_freeze_expr: Expression to freeze variables.\n      tflite_max_detections: The max number of output detections in the TFLite\n        model.\n      strategy:  A string specifying which distribution strategy to use.\n        Accepted values are 'tpu', 'gpus', None. tpu' means to use TPUStrategy.\n        'gpus' mean to use MirroredStrategy for multi-gpus. If None, use TF\n        default with OneDeviceStrategy.\n      tpu: The Cloud TPU to use for training. This should be either the name\n        used when creating the Cloud TPU, or a grpc://ip.address.of.tpu:8470\n          url.\n      gcp_project: Project name for the Cloud TPU-enabled project. If not\n        specified, we will attempt to automatically detect the GCE project from\n        metadata.\n      tpu_zone: GCE zone where the Cloud TPU is located in. If not specified, we\n        will attempt to automatically detect the GCE project from metadata.\n      use_xla: Use XLA even if strategy is not tpu. If strategy is tpu, always\n        use XLA, and this flag has no effect.\n      profile: Enable profile mode.\n      debug: Enable debug mode.\n      tf_random_seed: Fixed random seed for deterministic execution across runs\n        for debugging.\n      verbose: verbosity mode for `tf.keras.callbacks.ModelCheckpoint`, 0 or 1.\n    \"\"\"\n    self.model_name = model_name\n    self.uri = uri\n    self.batch_size = batch_size\n    config = hparams_config.get_efficientdet_config(model_name)\n    config.override(hparams)\n    config.image_size = utils.parse_image_size(config.image_size)\n    config.var_freeze_expr = var_freeze_expr\n    config.moving_average_decay = moving_average_decay\n    config.tflite_max_detections = tflite_max_detections\n    if epochs:\n      config.num_epochs = epochs\n\n    if use_xla and strategy != 'tpu':\n      tf.config.optimizer.set_jit(True)\n      for gpu in tf.config.list_physical_devices('GPU'):\n        tf.config.experimental.set_memory_growth(gpu, True)\n\n    if debug:\n      tf.config.experimental_run_functions_eagerly(True)\n      tf.debugging.set_log_device_placement(True)\n      os.environ['TF_DETERMINISTIC_OPS'] = '1'\n      tf.random.set_seed(tf_random_seed)\n      logging.set_verbosity(logging.DEBUG)\n\n    if strategy == 'tpu':\n      tpu_cluster_resolver = tf.distribute.cluster_resolver.TPUClusterResolver(\n          tpu, zone=tpu_zone, project=gcp_project)\n      tf.config.experimental_connect_to_cluster(tpu_cluster_resolver)\n      tf.tpu.experimental.initialize_tpu_system(tpu_cluster_resolver)\n      ds_strategy = tf.distribute.TPUStrategy(tpu_cluster_resolver)\n      logging.info('All devices: %s', tf.config.list_logical_devices('TPU'))\n      tf.config.set_soft_device_placement(True)\n    elif strategy == 'gpus':\n      ds_strategy = tf.distribute.MirroredStrategy()\n      logging.info('All devices: %s', tf.config.list_physical_devices('GPU'))\n    else:\n      if tf.config.list_physical_devices('GPU'):\n        ds_strategy = tf.distribute.OneDeviceStrategy('device:GPU:0')\n      else:\n        ds_strategy = tf.distribute.OneDeviceStrategy('device:CPU:0')\n\n    self.ds_strategy = ds_strategy\n\n    if model_dir is None:\n      model_dir = tempfile.mkdtemp()\n    params = dict(\n        profile=profile,\n        model_name=model_name,\n        steps_per_execution=steps_per_execution,\n        model_dir=model_dir,\n        strategy=strategy,\n        batch_size=batch_size,\n        tf_random_seed=tf_random_seed,\n        debug=debug,\n        verbose=verbose)\n    config.override(params, True)\n    self.config = config\n\n    # set mixed precision policy by keras api.\n    precision = utils.get_precision(config.strategy, config.mixed_precision)\n    tf.keras.mixed_precision.set_global_policy(precision)\n\n  def create_model(self) -> tf.keras.Model:\n    \"\"\"Creates the EfficientDet model.\"\"\"\n    return train_lib.EfficientDetNetTrainHub(\n        config=self.config, hub_module_url=self.uri)\n\n  def train(self,\n            model: tf.keras.Model,\n            train_dataset: tf.data.Dataset,\n            steps_per_epoch: int,\n            val_dataset: Optional[tf.data.Dataset],\n            validation_steps: int,\n            epochs: Optional[int] = None,\n            batch_size: Optional[int] = None,\n            val_json_file: Optional[str] = None) -> tf.keras.Model:\n    \"\"\"Run EfficientDet training.\"\"\"\n    config = self.config\n    if not epochs:\n      epochs = config.num_epochs\n\n    if not batch_size:\n      batch_size = config.batch_size\n\n    config.update(\n        dict(\n            steps_per_epoch=steps_per_epoch,\n            eval_samples=batch_size * validation_steps,\n            val_json_file=val_json_file,\n            batch_size=batch_size))\n    train.setup_model(model, config)\n    train.init_experimental(config)\n    model.fit(\n        train_dataset,\n        epochs=epochs,\n        steps_per_epoch=steps_per_epoch,\n        callbacks=train_lib.get_callbacks(config.as_dict(), val_dataset),\n        validation_data=val_dataset,\n        validation_steps=validation_steps)\n    return model\n\n  def _get_evaluator_and_label_map(\n      self, json_file: str\n  ) -> Tuple[coco_metric.EvaluationMetric, Optional[Dict[int, str]]]:\n    \"\"\"Gets evaluator and label_map for evaluation.\"\"\"\n    label_map = label_util.get_label_map(self.config.label_map)\n    # Sorts label_map.keys since pycocotools.cocoeval uses sorted catIds\n    # (category ids) in COCOeval class.\n    label_map = _get_ordered_label_map(label_map)\n\n    evaluator = coco_metric.EvaluationMetric(\n        filename=json_file, label_map=label_map)\n\n    evaluator.reset_states()\n    return evaluator, label_map\n\n  def _get_metric_dict(self, evaluator: coco_metric.EvaluationMetric,\n                       label_map: collections.OrderedDict) -> Dict[str, float]:\n    \"\"\"Gets the metric dict for evaluation.\"\"\"\n    metrics = evaluator.result(log_level=tf.compat.v1.logging.INFO)\n    metric_dict = {}\n    for i, name in enumerate(evaluator.metric_names):\n      metric_dict[name] = metrics[i]\n\n    if label_map:\n      for i, cid in enumerate(label_map.keys()):\n        name = 'AP_/%s' % label_map[cid]\n        metric_dict[name] = metrics[i + len(evaluator.metric_names)]\n    return metric_dict\n\n  def evaluate(self,\n               model: tf.keras.Model,\n               dataset: tf.data.Dataset,\n               steps: int,\n               json_file: Optional[str] = None) -> Dict[str, float]:\n    \"\"\"Evaluate the EfficientDet keras model.\n\n    Args:\n      model: The keras model to be evaluated.\n      dataset: tf.data.Dataset used for evaluation.\n      steps: Number of steps to evaluate the model.\n      json_file: JSON with COCO data format containing golden bounding boxes.\n        Used for validation. If None, use the ground truth from the dataloader.\n        Refer to\n        https://towardsdatascience.com/coco-data-format-for-object-detection-a4c5eaf518c5\n          for the description of COCO data format.\n\n    Returns:\n      A dict contains AP metrics.\n    \"\"\"\n    evaluator, label_map = self._get_evaluator_and_label_map(json_file)\n    dataset = dataset.take(steps)\n\n    @tf.function\n    def _get_detections(images, labels):\n      cls_outputs, box_outputs = model(images, training=False)\n      detections = postprocess.generate_detections(self.config, cls_outputs,\n                                                   box_outputs,\n                                                   labels['image_scales'],\n                                                   labels['source_ids'])\n      tf.numpy_function(evaluator.update_state, [\n          labels['groundtruth_data'],\n          postprocess.transform_detections(detections)\n      ], [])\n\n    dataset = self.ds_strategy.experimental_distribute_dataset(dataset)\n    progbar = tf.keras.utils.Progbar(steps)\n    for i, (images, labels) in enumerate(dataset):\n      self.ds_strategy.run(_get_detections, (images, labels))\n      progbar.update(i + 1)\n    print()\n\n    metric_dict = self._get_metric_dict(evaluator, label_map)\n    return metric_dict\n\n  def evaluate_tflite(self,\n                      tflite_filepath: str,\n                      dataset: tf.data.Dataset,\n                      steps: int,\n                      json_file: Optional[str] = None) -> Dict[str, float]:\n    \"\"\"Evaluate the EfficientDet TFLite model.\n\n    Args:\n      tflite_filepath: File path to the TFLite model.\n      dataset: tf.data.Dataset used for evaluation.\n      steps: Number of steps to evaluate the model.\n      json_file: JSON with COCO data format containing golden bounding boxes.\n        Used for validation. If None, use the ground truth from the dataloader.\n        Refer to\n        https://towardsdatascience.com/coco-data-format-for-object-detection-a4c5eaf518c5\n          for the description of COCO data format.\n\n    Returns:\n      A dict contains AP metrics.\n    \"\"\"\n    # TODO(b/182441458): Use the task library for evaluation instead once it\n    # supports python interface.\n    evaluator, label_map = self._get_evaluator_and_label_map(json_file)\n    dataset = dataset.take(steps)\n\n    lite_runner = eval_tflite.LiteRunner(tflite_filepath, only_network=False)\n    progbar = tf.keras.utils.Progbar(steps)\n    for i, (images, labels) in enumerate(dataset):\n      # Get the output result after post-processing NMS op.\n      _, nms_scores, nms_classes, nms_boxes = lite_runner.run(images)  # pytype: disable=bad-unpacking\n\n      # CLASS_OFFSET is used since label_id for `background` is 0 in label_map\n      # while it's not actually included the model. We don't need to add the\n      # offset in the Android application.\n      nms_classes += postprocess.CLASS_OFFSET\n\n      height, width = utils.parse_image_size(self.config.image_size)\n      normalize_factor = tf.constant([height, width, height, width],\n                                     dtype=tf.float32)\n      nms_boxes *= normalize_factor\n      if labels['image_scales'] is not None:\n        scales = tf.expand_dims(tf.expand_dims(labels['image_scales'], -1), -1)\n        nms_boxes = nms_boxes * tf.cast(scales, nms_boxes.dtype)\n      detections = postprocess.generate_detections_from_nms_output(\n          nms_boxes, nms_classes, nms_scores, labels['source_ids'])\n\n      detections = postprocess.transform_detections(detections)\n      evaluator.update_state(labels['groundtruth_data'].numpy(),\n                             detections.numpy())\n      progbar.update(i + 1)\n    print()\n\n    metric_dict = self._get_metric_dict(evaluator, label_map)\n    return metric_dict\n\n  def export_saved_model(self,\n                         model: tf.keras.Model,\n                         saved_model_dir: str,\n                         batch_size: Optional[int] = None,\n                         pre_mode: Optional[str] = 'infer',\n                         post_mode: Optional[str] = 'global') -> None:\n    \"\"\"Saves the model to Tensorflow SavedModel.\n\n    Args:\n      model: The EfficientDetNet model used for training which doesn't have pre\n        and post processing.\n      saved_model_dir: Folder path for saved model.\n      batch_size: Batch size to be saved in saved_model.\n      pre_mode: Pre-processing Mode in ExportModel, must be {None, 'infer'}.\n      post_mode: Post-processing Mode in ExportModel, must be {None, 'global',\n        'per_class', 'tflite'}.\n    \"\"\"\n    config = self.config\n    # Sets the keras model optimizer to None when exporting to saved model.\n    # Otherwise, it fails with `NotImplementedError`: \"Learning rate schedule\n    # must override get_config\".\n    original_optimizer = model.optimizer\n    model.optimizer = None\n    # Creates ExportModel which has pre and post processing.\n    export_model = ExportModel(model, config, pre_mode, post_mode)\n\n    # Gets tf.TensorSpec.\n    if pre_mode is None:\n      # Input is the preprocessed image that's already resized to a certain\n      # input shape.\n      input_spec = tf.TensorSpec(\n          shape=[batch_size, *config.image_size, 3],\n          dtype=tf.float32,\n          name='images')\n    else:\n      # Input is that raw image that can be in any input shape,\n      input_spec = tf.TensorSpec(\n          shape=[batch_size, None, None, 3], dtype=tf.uint8, name='images')\n\n    tf.saved_model.save(\n        export_model,\n        saved_model_dir,\n        signatures=export_model.__call__.get_concrete_function(input_spec))\n    model.optimizer = original_optimizer\n\n  def get_default_quantization_config(\n      self, representative_data: object_detector_dataloader.DataLoader\n  ) -> configs.QuantizationConfig:\n    \"\"\"Gets the default quantization configuration.\"\"\"\n    # Sets `inference_output_type=None` since the output op is the custom NMS op\n    # which can't be quantized so that the tflite model output should be float.\n    # `supported_ops` contains both `TFLITE_BUILTINS_INT8` and `TFLITE_BUILTINS`\n    # due to the same reason: custom NMS op should be included as the\n    # `TFLITE_BUILTINS` op.\n    config = configs.QuantizationConfig.for_int8(\n        representative_data,\n        inference_output_type=None,\n        supported_ops=[\n            tf.lite.OpsSet.TFLITE_BUILTINS_INT8, tf.lite.OpsSet.TFLITE_BUILTINS\n        ])\n    config.experimental_new_quantizer = True\n    return config\n\n  def export_tflite(\n      self,\n      model: tf.keras.Model,\n      tflite_filepath: str,\n      quantization_config: Optional[configs.QuantizationConfig] = None) -> None:\n    \"\"\"Converts the retrained model to tflite format and saves it.\n\n    The exported TFLite model has the following inputs & outputs:\n    One input:\n      image: a float32 tensor of shape[1, height, width, 3] containing the\n        normalized input image. `self.config.image_size` is [height, width].\n\n    Four Outputs:\n      detection_boxes: a float32 tensor of shape [1, num_boxes, 4] with box\n        locations.\n      detection_classes: a float32 tensor of shape [1, num_boxes] with class\n        indices.\n      detection_scores: a float32 tensor of shape [1, num_boxes] with class\n        scores.\n      num_boxes: a float32 tensor of size 1 containing the number of detected\n        boxes.\n\n    Args:\n      model: The EfficientDetNet model used for training which doesn't have pre\n        and post processing.\n      tflite_filepath: File path to save tflite model.\n      quantization_config: Configuration for post-training quantization.\n    \"\"\"\n    with tempfile.TemporaryDirectory() as temp_dir:\n      self.export_saved_model(\n          model, temp_dir, batch_size=1, pre_mode=None, post_mode='tflite')\n      converter = tf.lite.TFLiteConverter.from_saved_model(temp_dir)\n\n      if quantization_config:\n        converter = quantization_config.get_converter_with_quantization(\n            converter, model_spec=self)\n\n        # TFLITE_BUILTINS is needed for TFLite's custom NMS op for integer only\n        # quantization.\n        if tf.lite.OpsSet.TFLITE_BUILTINS not in converter.target_spec.supported_ops:\n          converter.target_spec.supported_ops += [\n              tf.lite.OpsSet.TFLITE_BUILTINS\n          ]\n\n      tflite_model = converter.convert()\n\n      with tf.io.gfile.GFile(tflite_filepath, 'wb') as f:\n        f.write(tflite_model)\n\n\nefficientdet_lite0_spec = functools.partial(\n    EfficientDetModelSpec,\n    model_name='efficientdet-lite0',\n    uri='https://tfhub.dev/tensorflow/efficientdet/lite0/feature-vector/1',\n)\nefficientdet_lite0_spec.__doc__ = util.wrap_doc(\n    EfficientDetModelSpec,\n    'Creates EfficientDet-Lite0 model spec. See also: `tflite_model_maker.object_detector.EfficientDetSpec`.'\n)\nmm_export('object_detector.EfficientDetLite0Spec').export_constant(\n    __name__, 'efficientdet_lite0_spec')\n\nefficientdet_lite1_spec = functools.partial(\n    EfficientDetModelSpec,\n    model_name='efficientdet-lite1',\n    uri='https://tfhub.dev/tensorflow/efficientdet/lite1/feature-vector/1',\n)\nefficientdet_lite1_spec.__doc__ = util.wrap_doc(\n    EfficientDetModelSpec,\n    'Creates EfficientDet-Lite1 model spec. See also: `tflite_model_maker.object_detector.EfficientDetSpec`.'\n)\nmm_export('object_detector.EfficientDetLite1Spec').export_constant(\n    __name__, 'efficientdet_lite1_spec')\n\nefficientdet_lite2_spec = functools.partial(\n    EfficientDetModelSpec,\n    model_name='efficientdet-lite2',\n    uri='https://tfhub.dev/tensorflow/efficientdet/lite2/feature-vector/1',\n)\nefficientdet_lite2_spec.__doc__ = util.wrap_doc(\n    EfficientDetModelSpec,\n    'Creates EfficientDet-Lite2 model spec. See also: `tflite_model_maker.object_detector.EfficientDetSpec`.'\n)\nmm_export('object_detector.EfficientDetLite2Spec').export_constant(\n    __name__, 'efficientdet_lite2_spec')\n\nefficientdet_lite3_spec = functools.partial(\n    EfficientDetModelSpec,\n    model_name='efficientdet-lite3',\n    uri='https://tfhub.dev/tensorflow/efficientdet/lite3/feature-vector/1',\n)\nefficientdet_lite3_spec.__doc__ = util.wrap_doc(\n    EfficientDetModelSpec,\n    'Creates EfficientDet-Lite3 model spec. See also: `tflite_model_maker.object_detector.EfficientDetSpec`.'\n)\nmm_export('object_detector.EfficientDetLite3Spec').export_constant(\n    __name__, 'efficientdet_lite3_spec')\n\nefficientdet_lite4_spec = functools.partial(\n    EfficientDetModelSpec,\n    model_name='efficientdet-lite4',\n    uri='https://tfhub.dev/tensorflow/efficientdet/lite4/feature-vector/2',\n)\nefficientdet_lite4_spec.__doc__ = util.wrap_doc(\n    EfficientDetModelSpec,\n    'Creates EfficientDet-Lite4 model spec. See also: `tflite_model_maker.object_detector.EfficientDetSpec`.'\n)\nmm_export('object_detector.EfficientDetLite4Spec').export_constant(\n    __name__, 'efficientdet_lite4_spec')\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/model_spec/object_detector_spec_test.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Tests for object detector specs.\"\"\"\n\nimport math\nimport os\n\nimport tensorflow.compat.v2 as tf\n\nfrom tensorflow_examples.lite.model_maker.core import test_util\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import object_detector_spec\n\n\nclass EfficientDetModelSpecTest(tf.test.TestCase):\n\n  @classmethod\n  def setUpClass(cls):\n    super(EfficientDetModelSpecTest, cls).setUpClass()\n    hub_path = test_util.get_test_data_path('fake_effdet_lite0_hub')\n    cls._spec = object_detector_spec.EfficientDetModelSpec(\n        model_name='efficientdet-lite0', uri=hub_path, hparams=dict(map_freq=1))\n    with cls._spec.ds_strategy.scope():\n      cls.model = cls._spec.create_model()\n\n  def test_export_saved_model(self):\n    saved_model_dir = os.path.join(self.get_temp_dir(), 'saved_model')\n    self._spec.export_saved_model(self.model, saved_model_dir)\n    self.assertTrue(os.path.isdir(saved_model_dir))\n    self.assertNotEqual(len(os.listdir(saved_model_dir)), 0)\n\n  def test_export_tflite(self):\n    tflite_filepath = os.path.join('/tmp/model.tflite')\n    self._spec.export_tflite(self.model, tflite_filepath)\n    self.assertTrue(os.path.isfile(tflite_filepath))\n    self.assertGreater(os.path.getsize(tflite_filepath), 0)\n\n  def test_create_model(self):\n    self.assertIsInstance(self.model, tf.keras.Model)\n    x = tf.ones((1, *self._spec.config.image_size, 3))\n    cls_outputs, box_outputs = self.model(x)\n    self.assertLen(cls_outputs, 5)\n    self.assertLen(box_outputs, 5)\n\n  def test_train(self):\n    with self._spec.ds_strategy.scope():\n      model = self._spec.create_model()\n      model = self._spec.train(\n          model,\n          train_dataset=self._gen_input(),\n          steps_per_epoch=1,\n          val_dataset=self._gen_input(),\n          validation_steps=1,\n          epochs=1,\n          batch_size=1)\n      self.assertIsInstance(model, tf.keras.Model)\n\n  def test_evaluate(self):\n    metrics = self._spec.evaluate(\n        self.model, dataset=self._gen_input(), steps=1)\n    self.assertIsInstance(metrics, dict)\n    self.assertGreaterEqual(metrics['AP'], 0)\n\n  def _gen_input(self):\n    # Image tensors that are preprocessed to have normalized value and fixed\n    # dimension [1, image_height, image_width, 3]\n    images = tf.random.uniform((1, 320, 320, 3), maxval=256)\n\n    # labels contains:\n    # box_targets_dict: ordered dictionary with keys\n    #     [min_level, min_level+1, ..., max_level]. The values are tensor with\n    #     shape [height_l, width_l, num_anchors * 4]. The height_l and\n    #     width_l represent the dimension of bounding box regression output at\n    #     l-th level.\n    # cls_targets_dict: ordered dictionary with keys\n    #     [min_level, min_level+1, ..., max_level]. The values are tensor with\n    #     shape [height_l, width_l, num_anchors]. The height_l and width_l\n    #     represent the dimension of class logits at l-th level.\n    # groundtruth_data: Groundtruth Annotations data.\n    # image_scale: Scale of the processed image to the original image.\n    # source_id: Source image id. Default value -1 if the source id is empty\n    #     in the groundtruth annotation.\n    # mean_num_positives:  Mean number of positive anchors in the batch images.\n    sizes = [(level, math.ceil(320 / 2**level)) for level in range(3, 8)]\n\n    labels = {\n        'box_targets_%d' % level: tf.ones((1, size, size, 36))\n        for level, size in sizes\n    }\n    labels.update({\n        'cls_targets_%d' % level: tf.ones((1, size, size, 9), dtype=tf.int32)\n        for level, size in sizes\n    })\n    labels.update({'groundtruth_data': tf.zeros([1, 100, 7])})\n    labels.update({'image_scales': tf.constant([0.8])})\n    labels.update({'source_ids': tf.constant([1.0])})\n    labels.update({'mean_num_positives': tf.constant([10.0])})\n    ds = tf.data.Dataset.from_tensors((images, labels))\n    return ds\n\n\nif __name__ == '__main__':\n  # Load compressed models from tensorflow_hub\n  os.environ['TFHUB_MODEL_LOAD_FORMAT'] = 'COMPRESSED'\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/model_spec/recommendation_spec.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Recommendation model specification.\"\"\"\n\nimport tensorflow as tf  # pylint: disable=unused-import\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core.api import mm_export\nfrom tensorflow_examples.lite.model_maker.core.data_util import recommendation_config\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import recommendation_model as _model\n\n\n@mm_export('recommendation.ModelSpec')\nclass RecommendationSpec(object):\n  \"\"\"Recommendation model spec.\"\"\"\n\n  compat_tf_versions = compat.get_compat_tf_versions(2)\n\n  def __init__(self, input_spec: recommendation_config.InputSpec,\n               model_hparams: recommendation_config.ModelHParams):\n    \"\"\"Initialize spec.\n\n    Args:\n      input_spec: InputSpec, specify data format for input and embedding.\n      model_hparams: ModelHParams, specify hparams for model achitecture.\n    \"\"\"\n    self.input_spec = input_spec\n    self.model_hparams = model_hparams\n\n  def create_model(self):\n    \"\"\"Creates recommendation model based on params.\n\n    Returns:\n      Keras model.\n    \"\"\"\n    return _model.RecommendationModel(self.input_spec, self.model_hparams)\n\n  def get_default_quantization_config(self):\n    \"\"\"Gets the default quantization configuration.\"\"\"\n    return None\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/model_spec/recommendation_spec_test.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Tests for recommendation spec.\"\"\"\n\nfrom absl.testing import parameterized\nimport tensorflow.compat.v2 as tf\n\nfrom tensorflow_examples.lite.model_maker.core.data_util import recommendation_testutil as _testutil\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import recommendation_spec\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import recommendation_model as _model\n\n\nclass RecommendationSpecTest(tf.test.TestCase, parameterized.TestCase):\n\n  @parameterized.parameters(\n      ('bow'),\n      ('cnn'),\n      ('lstm'),\n  )\n  def test_create_recommendation_model(self, encoder_type):\n    input_spec = _testutil.get_input_spec(encoder_type)\n    model_hparams = _testutil.get_model_hparams()\n    spec = recommendation_spec.RecommendationSpec(input_spec, model_hparams)\n    model = spec.create_model()\n    self.assertIsInstance(model, _model.RecommendationModel)\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/model_spec/testdata/fake_effdet_lite0_hub/keras_metadata.pb",
    "content": "\n\u0001\n\u0004\b\u0001\u0010\u0001\u001a\u0004root\"\u0011_tf_keras_network*\u0001{\"class_name\": \"Functional\", \"name\": \"model\", \"trainable\": true, \"expects_training_arg\": true, \"dtype\": \"float32\", \"batch_input_shape\": null, \"must_restore_from_config\": false, \"config\": {\"name\": \"model\", \"layers\": [{\"class_name\": \"InputLayer\", \"config\": {\"batch_input_shape\": {\"class_name\": \"__tuple__\", \"items\": [null, 320, 320, 3]}, \"dtype\": \"float32\", \"sparse\": false, \"ragged\": false, \"name\": \"input_1\"}, \"name\": \"input_1\", \"inbound_nodes\": []}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [8, 8]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"name\": \"conv2d\", \"inbound_nodes\": [[[\"input_1\", 0, 0, {}]]]}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d_1\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [16, 16]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"name\": \"conv2d_1\", \"inbound_nodes\": [[[\"input_1\", 0, 0, {}]]]}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d_2\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [32, 32]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"name\": \"conv2d_2\", \"inbound_nodes\": [[[\"input_1\", 0, 0, {}]]]}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d_3\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [64, 64]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"name\": \"conv2d_3\", \"inbound_nodes\": [[[\"input_1\", 0, 0, {}]]]}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d_4\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [128, 128]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"name\": \"conv2d_4\", \"inbound_nodes\": [[[\"input_1\", 0, 0, {}]]]}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d_5\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [8, 8]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"name\": \"conv2d_5\", \"inbound_nodes\": [[[\"input_1\", 0, 0, {}]]]}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d_6\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [16, 16]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"name\": \"conv2d_6\", \"inbound_nodes\": [[[\"input_1\", 0, 0, {}]]]}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d_7\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [32, 32]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"name\": \"conv2d_7\", \"inbound_nodes\": [[[\"input_1\", 0, 0, {}]]]}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d_8\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [64, 64]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"name\": \"conv2d_8\", \"inbound_nodes\": [[[\"input_1\", 0, 0, {}]]]}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d_9\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [128, 128]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"name\": \"conv2d_9\", \"inbound_nodes\": [[[\"input_1\", 0, 0, {}]]]}], \"input_layers\": [[\"input_1\", 0, 0]], \"output_layers\": [[[\"conv2d\", 0, 0], [\"conv2d_1\", 0, 0], [\"conv2d_2\", 0, 0], [\"conv2d_3\", 0, 0], [\"conv2d_4\", 0, 0]], [[\"conv2d_5\", 0, 0], [\"conv2d_6\", 0, 0], [\"conv2d_7\", 0, 0], [\"conv2d_8\", 0, 0], [\"conv2d_9\", 0, 0]]]}, \"input_spec\": [{\"class_name\": \"InputSpec\", \"config\": {\"dtype\": null, \"shape\": {\"class_name\": \"__tuple__\", \"items\": [null, 320, 320, 3]}, \"ndim\": 4, \"max_ndim\": null, \"min_ndim\": null, \"axes\": {}}}], \"build_input_shape\": {\"class_name\": \"TensorShape\", \"items\": [null, 320, 320, 3]}, \"is_graph_network\": true, \"save_spec\": {\"class_name\": \"TypeSpec\", \"type_spec\": \"tf.TensorSpec\", \"serialized\": [{\"class_name\": \"TensorShape\", \"items\": [null, 320, 320, 3]}, \"float32\", \"input_1\"]}, \"keras_version\": \"2.5.0\", \"backend\": \"tensorflow\", \"model_config\": {\"class_name\": \"Functional\", \"config\": {\"name\": \"model\", \"layers\": [{\"class_name\": \"InputLayer\", \"config\": {\"batch_input_shape\": {\"class_name\": \"__tuple__\", \"items\": [null, 320, 320, 3]}, \"dtype\": \"float32\", \"sparse\": false, \"ragged\": false, \"name\": \"input_1\"}, \"name\": \"input_1\", \"inbound_nodes\": []}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [8, 8]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"name\": \"conv2d\", \"inbound_nodes\": [[[\"input_1\", 0, 0, {}]]]}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d_1\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [16, 16]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"name\": \"conv2d_1\", \"inbound_nodes\": [[[\"input_1\", 0, 0, {}]]]}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d_2\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [32, 32]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"name\": \"conv2d_2\", \"inbound_nodes\": [[[\"input_1\", 0, 0, {}]]]}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d_3\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [64, 64]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"name\": \"conv2d_3\", \"inbound_nodes\": [[[\"input_1\", 0, 0, {}]]]}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d_4\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [128, 128]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"name\": \"conv2d_4\", \"inbound_nodes\": [[[\"input_1\", 0, 0, {}]]]}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d_5\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [8, 8]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"name\": \"conv2d_5\", \"inbound_nodes\": [[[\"input_1\", 0, 0, {}]]]}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d_6\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [16, 16]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"name\": \"conv2d_6\", \"inbound_nodes\": [[[\"input_1\", 0, 0, {}]]]}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d_7\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [32, 32]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"name\": \"conv2d_7\", \"inbound_nodes\": [[[\"input_1\", 0, 0, {}]]]}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d_8\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [64, 64]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"name\": \"conv2d_8\", \"inbound_nodes\": [[[\"input_1\", 0, 0, {}]]]}, {\"class_name\": \"Conv2D\", \"config\": {\"name\": \"conv2d_9\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [128, 128]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"name\": \"conv2d_9\", \"inbound_nodes\": [[[\"input_1\", 0, 0, {}]]]}], \"input_layers\": [[\"input_1\", 0, 0]], \"output_layers\": [[[\"conv2d\", 0, 0], [\"conv2d_1\", 0, 0], [\"conv2d_2\", 0, 0], [\"conv2d_3\", 0, 0], [\"conv2d_4\", 0, 0]], [[\"conv2d_5\", 0, 0], [\"conv2d_6\", 0, 0], [\"conv2d_7\", 0, 0], [\"conv2d_8\", 0, 0], [\"conv2d_9\", 0, 0]]]}}}\n\u0003\n\u0004\b\u0001\u0010\u0001\u0010\u0001\u001a\froot.layer-0\"\u0015_tf_keras_input_layer*\u0002{\"class_name\": \"InputLayer\", \"name\": \"input_1\", \"dtype\": \"float32\", \"sparse\": false, \"ragged\": false, \"batch_input_shape\": {\"class_name\": \"__tuple__\", \"items\": [null, 320, 320, 3]}, \"config\": {\"batch_input_shape\": {\"class_name\": \"__tuple__\", \"items\": [null, 320, 320, 3]}, \"dtype\": \"float32\", \"sparse\": false, \"ragged\": false, \"name\": \"input_1\"}}\n\b\n\u0004\b\u0001\u0010\u0001\u0010\u0002\u001a\u0019root.layer_with_weights-0\"\u000f_tf_keras_layer*\b{\"class_name\": \"Conv2D\", \"name\": \"conv2d\", \"trainable\": true, \"expects_training_arg\": false, \"dtype\": \"float32\", \"batch_input_shape\": null, \"stateful\": false, \"must_restore_from_config\": false, \"config\": {\"name\": \"conv2d\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [8, 8]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"input_spec\": {\"class_name\": \"InputSpec\", \"config\": {\"dtype\": null, \"shape\": null, \"ndim\": null, \"max_ndim\": null, \"min_ndim\": 4, \"axes\": {\"-1\": 3}}}, \"build_input_shape\": {\"class_name\": \"TensorShape\", \"items\": [null, 320, 320, 3]}}\n\b\n\u0004\b\u0001\u0010\u0001\u0010\u0003\u001a\u0019root.layer_with_weights-1\"\u000f_tf_keras_layer*\b{\"class_name\": \"Conv2D\", \"name\": \"conv2d_1\", \"trainable\": true, \"expects_training_arg\": false, \"dtype\": \"float32\", \"batch_input_shape\": null, \"stateful\": false, \"must_restore_from_config\": false, \"config\": {\"name\": \"conv2d_1\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [16, 16]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"input_spec\": {\"class_name\": \"InputSpec\", \"config\": {\"dtype\": null, \"shape\": null, \"ndim\": null, \"max_ndim\": null, \"min_ndim\": 4, \"axes\": {\"-1\": 3}}}, \"build_input_shape\": {\"class_name\": \"TensorShape\", \"items\": [null, 320, 320, 3]}}\n\b\n\u0004\b\u0001\u0010\u0001\u0010\u0004\u001a\u0019root.layer_with_weights-2\"\u000f_tf_keras_layer*\b{\"class_name\": \"Conv2D\", \"name\": \"conv2d_2\", \"trainable\": true, \"expects_training_arg\": false, \"dtype\": \"float32\", \"batch_input_shape\": null, \"stateful\": false, \"must_restore_from_config\": false, \"config\": {\"name\": \"conv2d_2\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [32, 32]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"input_spec\": {\"class_name\": \"InputSpec\", \"config\": {\"dtype\": null, \"shape\": null, \"ndim\": null, \"max_ndim\": null, \"min_ndim\": 4, \"axes\": {\"-1\": 3}}}, \"build_input_shape\": {\"class_name\": \"TensorShape\", \"items\": [null, 320, 320, 3]}}\n\b\n\u0004\b\u0001\u0010\u0001\u0010\u0005\u001a\u0019root.layer_with_weights-3\"\u000f_tf_keras_layer*\b{\"class_name\": \"Conv2D\", \"name\": \"conv2d_3\", \"trainable\": true, \"expects_training_arg\": false, \"dtype\": \"float32\", \"batch_input_shape\": null, \"stateful\": false, \"must_restore_from_config\": false, \"config\": {\"name\": \"conv2d_3\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [64, 64]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"input_spec\": {\"class_name\": \"InputSpec\", \"config\": {\"dtype\": null, \"shape\": null, \"ndim\": null, \"max_ndim\": null, \"min_ndim\": 4, \"axes\": {\"-1\": 3}}}, \"build_input_shape\": {\"class_name\": \"TensorShape\", \"items\": [null, 320, 320, 3]}}\n\b\n\u0004\b\u0001\u0010\u0001\u0010\u0006\u001a\u0019root.layer_with_weights-4\"\u000f_tf_keras_layer*\b{\"class_name\": \"Conv2D\", \"name\": \"conv2d_4\", \"trainable\": true, \"expects_training_arg\": false, \"dtype\": \"float32\", \"batch_input_shape\": null, \"stateful\": false, \"must_restore_from_config\": false, \"config\": {\"name\": \"conv2d_4\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [128, 128]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"input_spec\": {\"class_name\": \"InputSpec\", \"config\": {\"dtype\": null, \"shape\": null, \"ndim\": null, \"max_ndim\": null, \"min_ndim\": 4, \"axes\": {\"-1\": 3}}}, \"build_input_shape\": {\"class_name\": \"TensorShape\", \"items\": [null, 320, 320, 3]}}\n\b\n\u0004\b\u0001\u0010\u0001\u0010\u0007\u001a\u0019root.layer_with_weights-5\"\u000f_tf_keras_layer*\b{\"class_name\": \"Conv2D\", \"name\": \"conv2d_5\", \"trainable\": true, \"expects_training_arg\": false, \"dtype\": \"float32\", \"batch_input_shape\": null, \"stateful\": false, \"must_restore_from_config\": false, \"config\": {\"name\": \"conv2d_5\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [8, 8]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"input_spec\": {\"class_name\": \"InputSpec\", \"config\": {\"dtype\": null, \"shape\": null, \"ndim\": null, \"max_ndim\": null, \"min_ndim\": 4, \"axes\": {\"-1\": 3}}}, \"build_input_shape\": {\"class_name\": \"TensorShape\", \"items\": [null, 320, 320, 3]}}\n\b\n\u0004\b\u0001\u0010\u0001\u0010\b\u001a\u0019root.layer_with_weights-6\"\u000f_tf_keras_layer*\b{\"class_name\": \"Conv2D\", \"name\": \"conv2d_6\", \"trainable\": true, \"expects_training_arg\": false, \"dtype\": \"float32\", \"batch_input_shape\": null, \"stateful\": false, \"must_restore_from_config\": false, \"config\": {\"name\": \"conv2d_6\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [16, 16]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"input_spec\": {\"class_name\": \"InputSpec\", \"config\": {\"dtype\": null, \"shape\": null, \"ndim\": null, \"max_ndim\": null, \"min_ndim\": 4, \"axes\": {\"-1\": 3}}}, \"build_input_shape\": {\"class_name\": \"TensorShape\", \"items\": [null, 320, 320, 3]}}\n\b\n\u0004\b\u0001\u0010\u0001\u0010\t\u001a\u0019root.layer_with_weights-7\"\u000f_tf_keras_layer*\b{\"class_name\": \"Conv2D\", \"name\": \"conv2d_7\", \"trainable\": true, \"expects_training_arg\": false, \"dtype\": \"float32\", \"batch_input_shape\": null, \"stateful\": false, \"must_restore_from_config\": false, \"config\": {\"name\": \"conv2d_7\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [32, 32]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"input_spec\": {\"class_name\": \"InputSpec\", \"config\": {\"dtype\": null, \"shape\": null, \"ndim\": null, \"max_ndim\": null, \"min_ndim\": 4, \"axes\": {\"-1\": 3}}}, \"build_input_shape\": {\"class_name\": \"TensorShape\", \"items\": [null, 320, 320, 3]}}\n\b\n\u0004\b\u0001\u0010\u0001\u0010\n\u001a\u0019root.layer_with_weights-8\"\u000f_tf_keras_layer*\b{\"class_name\": \"Conv2D\", \"name\": \"conv2d_8\", \"trainable\": true, \"expects_training_arg\": false, \"dtype\": \"float32\", \"batch_input_shape\": null, \"stateful\": false, \"must_restore_from_config\": false, \"config\": {\"name\": \"conv2d_8\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [64, 64]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"input_spec\": {\"class_name\": \"InputSpec\", \"config\": {\"dtype\": null, \"shape\": null, \"ndim\": null, \"max_ndim\": null, \"min_ndim\": 4, \"axes\": {\"-1\": 3}}}, \"build_input_shape\": {\"class_name\": \"TensorShape\", \"items\": [null, 320, 320, 3]}}\n\b\n\u0004\b\u0001\u0010\u0001\u0010\u000b\u001a\u0019root.layer_with_weights-9\"\u000f_tf_keras_layer*\b{\"class_name\": \"Conv2D\", \"name\": \"conv2d_9\", \"trainable\": true, \"expects_training_arg\": false, \"dtype\": \"float32\", \"batch_input_shape\": null, \"stateful\": false, \"must_restore_from_config\": false, \"config\": {\"name\": \"conv2d_9\", \"trainable\": true, \"dtype\": \"float32\", \"filters\": 64, \"kernel_size\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"strides\": {\"class_name\": \"__tuple__\", \"items\": [128, 128]}, \"padding\": \"valid\", \"data_format\": \"channels_last\", \"dilation_rate\": {\"class_name\": \"__tuple__\", \"items\": [1, 1]}, \"groups\": 1, \"activation\": \"linear\", \"use_bias\": true, \"kernel_initializer\": {\"class_name\": \"GlorotUniform\", \"config\": {\"seed\": null}}, \"bias_initializer\": {\"class_name\": \"Zeros\", \"config\": {}}, \"kernel_regularizer\": null, \"bias_regularizer\": null, \"activity_regularizer\": null, \"kernel_constraint\": null, \"bias_constraint\": null}, \"input_spec\": {\"class_name\": \"InputSpec\", \"config\": {\"dtype\": null, \"shape\": null, \"ndim\": null, \"max_ndim\": null, \"min_ndim\": 4, \"axes\": {\"-1\": 3}}}, \"build_input_shape\": {\"class_name\": \"TensorShape\", \"items\": [null, 320, 320, 3]}}"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/model_spec/text_spec.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Text Model specification.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport collections\nimport functools\nimport logging\nimport os\nimport re\nimport tempfile\n\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core import file_util\nfrom tensorflow_examples.lite.model_maker.core.api import mm_export\nfrom tensorflow_examples.lite.model_maker.core.task import configs\nfrom tensorflow_examples.lite.model_maker.core.task import hub_loader\nfrom tensorflow_examples.lite.model_maker.core.task import model_util\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import util\n\nimport tensorflow_hub as hub\nfrom tensorflow_hub import registry\nfrom official.nlp import optimization\nfrom official.nlp.bert import configs as bert_configs\nfrom official.nlp.bert import run_squad_helper\nfrom official.nlp.bert import squad_evaluate_v1_1\nfrom official.nlp.bert import squad_evaluate_v2_0\nfrom official.nlp.bert import tokenization\nfrom official.nlp.data import classifier_data_lib\nfrom official.nlp.data import squad_lib\nfrom official.nlp.modeling import models\n# pylint: disable=g-import-not-at-top,bare-except\ntry:\n  from official.common import distribute_utils\nexcept:\n  from official.utils.misc import distribution_utils as distribute_utils\n# pylint: enable=g-import-not-at-top,bare-except\n\n\n@mm_export('text_classifier.AverageWordVecSpec')\nclass AverageWordVecModelSpec(object):\n  \"\"\"A specification of averaging word vector model.\"\"\"\n  PAD = '<PAD>'  # Index: 0\n  START = '<START>'  # Index: 1\n  UNKNOWN = '<UNKNOWN>'  # Index: 2\n\n  compat_tf_versions = compat.get_compat_tf_versions(2)\n  need_gen_vocab = True\n  convert_from_saved_model_tf2 = False\n\n  def __init__(self,\n               num_words=10000,\n               seq_len=256,\n               wordvec_dim=16,\n               lowercase=True,\n               dropout_rate=0.2,\n               name='AverageWordVec',\n               default_training_epochs=2,\n               default_batch_size=32,\n               model_dir=None,\n               index_to_label=None):\n    \"\"\"Initialze a instance with preprocessing and model paramaters.\n\n    Args:\n      num_words: Number of words to generate the vocabulary from data.\n      seq_len: Length of the sequence to feed into the model.\n      wordvec_dim: Dimension of the word embedding.\n      lowercase: Whether to convert all uppercase character to lowercase during\n        preprocessing.\n      dropout_rate: The rate for dropout.\n      name: Name of the object.\n      default_training_epochs: Default training epochs for training.\n      default_batch_size: Default batch size for training.\n      model_dir: The location of the model checkpoint files.\n      index_to_label: List of labels in the training data. e.g. ['neg', 'pos'].\n    \"\"\"\n    self.num_words = num_words\n    self.seq_len = seq_len\n    self.wordvec_dim = wordvec_dim\n    self.lowercase = lowercase\n    self.dropout_rate = dropout_rate\n    self.name = name\n    self.default_training_epochs = default_training_epochs\n    self.default_batch_size = default_batch_size\n    self.index_to_label = index_to_label\n\n    self.model_dir = model_dir\n    if self.model_dir is None:\n      self.model_dir = tempfile.mkdtemp()\n\n  def get_name_to_features(self):\n    \"\"\"Gets the dictionary describing the features.\"\"\"\n    name_to_features = {\n        'input_ids': tf.io.FixedLenFeature([self.seq_len], tf.int64),\n        'label_ids': tf.io.FixedLenFeature([], tf.int64),\n    }\n    return name_to_features\n\n  def select_data_from_record(self, record):\n    \"\"\"Dispatches records to features and labels.\"\"\"\n    x = record['input_ids']\n    y = record['label_ids']\n    return (x, y)\n\n  def convert_examples_to_features(self, examples, tfrecord_file, label_names):\n    \"\"\"Converts examples to features and write them into TFRecord file.\"\"\"\n    writer = tf.io.TFRecordWriter(tfrecord_file)\n\n    label_to_id = dict((name, i) for i, name in enumerate(label_names))\n    for example in examples:\n      features = collections.OrderedDict()\n\n      input_ids = self.preprocess(example.text_a)\n      label_id = label_to_id[example.label]\n      features['input_ids'] = util.create_int_feature(input_ids)\n      features['label_ids'] = util.create_int_feature([label_id])\n      tf_example = tf.train.Example(\n          features=tf.train.Features(feature=features))\n      writer.write(tf_example.SerializeToString())\n    writer.close()\n\n  def create_model(self,\n                   num_classes,\n                   optimizer='rmsprop',\n                   with_loss_and_metrics=True):\n    \"\"\"Creates the keras model.\"\"\"\n    # Gets a classifier model.\n    model = tf.keras.Sequential([\n        tf.keras.layers.InputLayer(input_shape=[self.seq_len], dtype=tf.int32),\n        tf.keras.layers.Embedding(\n            len(self.vocab), self.wordvec_dim, input_length=self.seq_len),\n        tf.keras.layers.GlobalAveragePooling1D(),\n        tf.keras.layers.Dense(self.wordvec_dim, activation=tf.nn.relu),\n        tf.keras.layers.Dropout(self.dropout_rate),\n        tf.keras.layers.Dense(num_classes, activation='softmax')\n    ])\n    if with_loss_and_metrics:\n      # Add loss and metrics in the keras model.\n      model.compile(\n          optimizer=optimizer,\n          loss='sparse_categorical_crossentropy',\n          metrics=['accuracy'])\n\n    return model\n\n  def run_classifier(self, train_ds, validation_ds, epochs, steps_per_epoch,\n                     num_classes, **kwargs):\n    \"\"\"Creates classifier and runs the classifier training.\"\"\"\n    if epochs is None:\n      epochs = self.default_training_epochs\n\n    model = self.create_model(num_classes)\n\n    # Trains the models.\n    for i in range(epochs):\n      model.fit(\n          train_ds,\n          initial_epoch=i,\n          epochs=i + 1,\n          validation_data=validation_ds,\n          steps_per_epoch=steps_per_epoch,\n          **kwargs)\n\n    return model\n\n  def gen_vocab(self, examples):\n    \"\"\"Generates vocabulary list in `examples` with maximum `num_words` words.\"\"\"\n    vocab_counter = collections.Counter()\n\n    for example in examples:\n      tokens = self._tokenize(example.text_a)\n      for token in tokens:\n        vocab_counter[token] += 1\n\n    self.vocab_freq = vocab_counter.most_common(self.num_words)\n    vocab_list = [self.PAD, self.START, self.UNKNOWN\n                 ] + [word for word, _ in self.vocab_freq]\n    self.vocab = collections.OrderedDict(\n        ((v, i) for i, v in enumerate(vocab_list)))\n    return self.vocab\n\n  def preprocess(self, raw_text):\n    \"\"\"Preprocess the text for text classification.\"\"\"\n    tokens = self._tokenize(raw_text)\n\n    # Gets ids for START, PAD and UNKNOWN tokens.\n    start_id = self.vocab[self.START]\n    pad_id = self.vocab[self.PAD]\n    unknown_id = self.vocab[self.UNKNOWN]\n\n    token_ids = [self.vocab.get(token, unknown_id) for token in tokens]\n    token_ids = [start_id] + token_ids\n\n    if len(token_ids) < self.seq_len:\n      # Padding.\n      pad_length = self.seq_len - len(token_ids)\n      token_ids = token_ids + pad_length * [pad_id]\n    else:\n      token_ids = token_ids[:self.seq_len]\n\n    return token_ids\n\n  def _tokenize(self, text):\n    r\"\"\"Splits by '\\W' except '\\''.\"\"\"\n    text = tf.compat.as_text(text)\n    if self.lowercase:\n      text = text.lower()\n    tokens = re.compile(r'[^\\w\\']+').split(text.strip())\n    return list(filter(None, tokens))\n\n  def save_vocab(self, vocab_filename):\n    \"\"\"Saves the vocabulary in `vocab_filename`.\"\"\"\n    with tf.io.gfile.GFile(vocab_filename, 'w') as f:\n      for token, index in self.vocab.items():\n        f.write('%s %d\\n' % (token, index))\n\n    tf.compat.v1.logging.info('Saved vocabulary in %s.', vocab_filename)\n\n  def load_vocab(self, vocab_filename):\n    \"\"\"Loads vocabulary from `vocab_filename`.\"\"\"\n    with tf.io.gfile.GFile(vocab_filename, 'r') as f:\n      vocab_list = []\n      for line in f:\n        word, index = line.strip().split()\n        vocab_list.append((word, int(index)))\n    self.vocab = collections.OrderedDict(vocab_list)\n    return self.vocab\n\n  def get_config(self):\n    \"\"\"Gets the configuration.\"\"\"\n    return {\n        'num_words': self.num_words,\n        'seq_len': self.seq_len,\n        'wordvec_dim': self.wordvec_dim,\n        'lowercase': self.lowercase\n    }\n\n  def get_default_quantization_config(self):\n    \"\"\"Gets the default quantization configuration.\"\"\"\n    return None\n\n\ndef create_classifier_model(bert_config,\n                            num_labels,\n                            max_seq_length,\n                            initializer=None,\n                            hub_module_url=None,\n                            hub_module_trainable=True,\n                            is_tf2=True):\n  \"\"\"BERT classifier model in functional API style.\n\n  Construct a Keras model for predicting `num_labels` outputs from an input with\n  maximum sequence length `max_seq_length`.\n\n  Args:\n    bert_config: BertConfig, the config defines the core Bert model.\n    num_labels: integer, the number of classes.\n    max_seq_length: integer, the maximum input sequence length.\n    initializer: Initializer for the final dense layer in the span labeler.\n      Defaulted to TruncatedNormal initializer.\n    hub_module_url: TF-Hub path/url to Bert module.\n    hub_module_trainable: True to finetune layers in the hub module.\n    is_tf2: boolean, whether the hub module is in TensorFlow 2.x format.\n\n  Returns:\n    Combined prediction model (words, mask, type) -> (one-hot labels)\n    BERT sub-model (words, mask, type) -> (bert_outputs)\n  \"\"\"\n  if initializer is None:\n    initializer = tf.keras.initializers.TruncatedNormal(\n        stddev=bert_config.initializer_range)\n\n  input_word_ids = tf.keras.layers.Input(\n      shape=(max_seq_length,), dtype=tf.int32, name='input_word_ids')\n  input_mask = tf.keras.layers.Input(\n      shape=(max_seq_length,), dtype=tf.int32, name='input_mask')\n  input_type_ids = tf.keras.layers.Input(\n      shape=(max_seq_length,), dtype=tf.int32, name='input_type_ids')\n\n  if is_tf2:\n    bert_model = hub.KerasLayer(hub_module_url, trainable=hub_module_trainable)\n    pooled_output, _ = bert_model([input_word_ids, input_mask, input_type_ids])\n  else:\n    bert_model = hub_loader.HubKerasLayerV1V2(\n        hub_module_url,\n        signature='tokens',\n        output_key='pooled_output',\n        trainable=hub_module_trainable)\n\n    pooled_output = bert_model({\n        'input_ids': input_word_ids,\n        'input_mask': input_mask,\n        'segment_ids': input_type_ids\n    })\n\n  output = tf.keras.layers.Dropout(rate=bert_config.hidden_dropout_prob)(\n      pooled_output)\n  output = tf.keras.layers.Dense(\n      num_labels,\n      kernel_initializer=initializer,\n      name='output',\n      activation='softmax',\n      dtype=tf.float32)(\n          output)\n\n  return tf.keras.Model(\n      inputs=[input_word_ids, input_mask, input_type_ids],\n      outputs=output), bert_model\n\n\nclass BertModelSpec(object):\n  \"\"\"A specification of BERT model.\"\"\"\n\n  compat_tf_versions = compat.get_compat_tf_versions(2)\n  need_gen_vocab = False\n  convert_from_saved_model_tf2 = True  # Convert to TFLite from saved_model.\n\n  def __init__(\n      self,\n      uri='https://tfhub.dev/tensorflow/bert_en_uncased_L-12_H-768_A-12/1',\n      model_dir=None,\n      seq_len=128,\n      dropout_rate=0.1,\n      initializer_range=0.02,\n      learning_rate=3e-5,\n      distribution_strategy='mirrored',\n      num_gpus=-1,\n      tpu='',\n      trainable=True,\n      do_lower_case=True,\n      is_tf2=True,\n      name='Bert',\n      tflite_input_name=None,\n      default_batch_size=32):\n    \"\"\"Initialze an instance with model parameters.\n\n    Args:\n      uri: TF-Hub path/url to Bert module.\n      model_dir: The location of the model checkpoint files.\n      seq_len: Length of the sequence to feed into the model.\n      dropout_rate: The rate for dropout.\n      initializer_range: The stdev of the truncated_normal_initializer for\n        initializing all weight matrices.\n      learning_rate: The initial learning rate for Adam.\n      distribution_strategy:  A string specifying which distribution strategy to\n        use. Accepted values are 'off', 'one_device', 'mirrored',\n        'parameter_server', 'multi_worker_mirrored', and 'tpu' -- case\n        insensitive. 'off' means not to use Distribution Strategy; 'tpu' means\n        to use TPUStrategy using `tpu_address`.\n      num_gpus: How many GPUs to use at each worker with the\n        DistributionStrategies API. The default is -1, which means utilize all\n        available GPUs.\n      tpu: TPU address to connect to.\n      trainable: boolean, whether pretrain layer is trainable.\n      do_lower_case: boolean, whether to lower case the input text. Should be\n        True for uncased models and False for cased models.\n      is_tf2: boolean, whether the hub module is in TensorFlow 2.x format.\n      name: The name of the object.\n      tflite_input_name: Dict, input names for the TFLite model.\n      default_batch_size: Default batch size for training.\n    \"\"\"\n    if compat.get_tf_behavior() not in self.compat_tf_versions:\n      raise ValueError('Incompatible versions. Expect {}, but got {}.'.format(\n          self.compat_tf_versions, compat.get_tf_behavior()))\n    self.seq_len = seq_len\n    self.dropout_rate = dropout_rate\n    self.initializer_range = initializer_range\n    self.learning_rate = learning_rate\n    self.trainable = trainable\n\n    self.model_dir = model_dir\n    if self.model_dir is None:\n      self.model_dir = tempfile.mkdtemp()\n\n    num_gpus = util.get_num_gpus(num_gpus)\n    # Always set the number of gpus to 0 if distribution strategy is off.\n    if distribution_strategy == 'off':\n      num_gpus = 0\n    self.strategy = distribute_utils.get_distribution_strategy(\n        distribution_strategy=distribution_strategy,\n        num_gpus=num_gpus,\n        tpu_address=tpu)\n    self.tpu = tpu\n    self.uri = uri\n    self.do_lower_case = do_lower_case\n    self.is_tf2 = is_tf2\n\n    self.bert_config = bert_configs.BertConfig(\n        0,\n        initializer_range=self.initializer_range,\n        hidden_dropout_prob=self.dropout_rate)\n\n    self.is_built = False\n    self.name = name\n\n    if tflite_input_name is None:\n      tflite_input_name = {\n          'ids': 'serving_default_input_word_ids:0',\n          'mask': 'serving_default_input_mask:0',\n          'segment_ids': 'serving_default_input_type_ids:0'\n      }\n    self.tflite_input_name = tflite_input_name\n    self.default_batch_size = default_batch_size\n\n  def get_default_quantization_config(self):\n    \"\"\"Gets the default quantization configuration.\"\"\"\n    config = configs.QuantizationConfig.for_dynamic()\n    config.experimental_new_quantizer = True\n    return config\n\n  def reorder_input_details(self, tflite_input_details):\n    \"\"\"Reorders the tflite input details to map the order of keras model.\"\"\"\n    for detail in tflite_input_details:\n      name = detail['name']\n      if 'input_word_ids' in name:\n        input_word_ids_detail = detail\n      elif 'input_mask' in name:\n        input_mask_detail = detail\n      elif 'input_type_ids' in name:\n        input_type_ids_detail = detail\n    return [input_word_ids_detail, input_mask_detail, input_type_ids_detail]\n\n  def build(self):\n    \"\"\"Builds the class. Used for lazy initialization.\"\"\"\n    if self.is_built:\n      return\n    self.vocab_file = os.path.join(\n        registry.resolver(self.uri), 'assets', 'vocab.txt')\n    self.tokenizer = tokenization.FullTokenizer(self.vocab_file,\n                                                self.do_lower_case)\n\n  def save_vocab(self, vocab_filename):\n    \"\"\"Prints the file path to the vocabulary.\"\"\"\n    if not self.is_built:\n      self.build()\n    tf.io.gfile.copy(self.vocab_file, vocab_filename, overwrite=True)\n    tf.compat.v1.logging.info('Saved vocabulary in %s.', vocab_filename)\n\n\n@mm_export('text_classifier.BertClassifierSpec')\nclass BertClassifierModelSpec(BertModelSpec):\n  \"\"\"A specification of BERT model for text classification.\"\"\"\n\n  def __init__(\n      self,\n      uri='https://tfhub.dev/tensorflow/bert_en_uncased_L-12_H-768_A-12/1',\n      model_dir=None,\n      seq_len=128,\n      dropout_rate=0.1,\n      initializer_range=0.02,\n      learning_rate=3e-5,\n      distribution_strategy='mirrored',\n      num_gpus=-1,\n      tpu='',\n      trainable=True,\n      do_lower_case=True,\n      is_tf2=True,\n      name='Bert',\n      tflite_input_name=None,\n      default_batch_size=32,\n      index_to_label=None):\n    \"\"\"Initialze an instance with model parameters.\n\n    Args:\n      uri: TF-Hub path/url to Bert module.\n      model_dir: The location of the model checkpoint files.\n      seq_len: Length of the sequence to feed into the model.\n      dropout_rate: The rate for dropout.\n      initializer_range: The stdev of the truncated_normal_initializer for\n        initializing all weight matrices.\n      learning_rate: The initial learning rate for Adam.\n      distribution_strategy:  A string specifying which distribution strategy to\n        use. Accepted values are 'off', 'one_device', 'mirrored',\n        'parameter_server', 'multi_worker_mirrored', and 'tpu' -- case\n        insensitive. 'off' means not to use Distribution Strategy; 'tpu' means\n        to use TPUStrategy using `tpu_address`.\n      num_gpus: How many GPUs to use at each worker with the\n        DistributionStrategies API. The default is -1, which means utilize all\n        available GPUs.\n      tpu: TPU address to connect to.\n      trainable: boolean, whether pretrain layer is trainable.\n      do_lower_case: boolean, whether to lower case the input text. Should be\n        True for uncased models and False for cased models.\n      is_tf2: boolean, whether the hub module is in TensorFlow 2.x format.\n      name: The name of the object.\n      tflite_input_name: Dict, input names for the TFLite model.\n      default_batch_size: Default batch size for training.\n      index_to_label: List of labels in the training data. e.g. ['neg', 'pos'].\n    \"\"\"\n    super().__init__(\n        uri=uri,\n        model_dir=model_dir,\n        seq_len=seq_len,\n        dropout_rate=dropout_rate,\n        initializer_range=initializer_range,\n        learning_rate=learning_rate,\n        distribution_strategy=distribution_strategy,\n        num_gpus=num_gpus,\n        tpu=tpu,\n        trainable=trainable,\n        do_lower_case=do_lower_case,\n        is_tf2=is_tf2,\n        name=name,\n        tflite_input_name=tflite_input_name,\n        default_batch_size=default_batch_size)\n    self.index_to_label = index_to_label\n\n  def get_name_to_features(self):\n    \"\"\"Gets the dictionary describing the features.\"\"\"\n    name_to_features = {\n        'input_ids': tf.io.FixedLenFeature([self.seq_len], tf.int64),\n        'input_mask': tf.io.FixedLenFeature([self.seq_len], tf.int64),\n        'segment_ids': tf.io.FixedLenFeature([self.seq_len], tf.int64),\n        'label_ids': tf.io.FixedLenFeature([], tf.int64),\n        'is_real_example': tf.io.FixedLenFeature([], tf.int64),\n    }\n    return name_to_features\n\n  def select_data_from_record(self, record):\n    \"\"\"Dispatches records to features and labels.\"\"\"\n    x = {\n        'input_word_ids': record['input_ids'],\n        'input_mask': record['input_mask'],\n        'input_type_ids': record['segment_ids']\n    }\n    y = record['label_ids']\n    return (x, y)\n\n  def convert_examples_to_features(self, examples, tfrecord_file, label_names):\n    \"\"\"Converts examples to features and write them into TFRecord file.\"\"\"\n    if not self.is_built:\n      self.build()\n    classifier_data_lib.file_based_convert_examples_to_features(\n        examples, label_names, self.seq_len, self.tokenizer, tfrecord_file)\n\n  def create_model(self,\n                   num_classes,\n                   optimizer='adam',\n                   with_loss_and_metrics=True):\n    \"\"\"Creates the keras model.\"\"\"\n    bert_model, _ = create_classifier_model(\n        self.bert_config,\n        num_classes,\n        self.seq_len,\n        hub_module_url=self.uri,\n        hub_module_trainable=self.trainable,\n        is_tf2=self.is_tf2)\n\n    # Defines evaluation metrics function, which will create metrics in the\n    # correct device and strategy scope.\n    def metric_fn():\n      return tf.keras.metrics.SparseCategoricalAccuracy(\n          'test_accuracy', dtype=tf.float32)\n\n    if with_loss_and_metrics:\n      # Add loss and metrics in the keras model.\n      bert_model.compile(\n          optimizer=optimizer,\n          loss=tf.keras.losses.SparseCategoricalCrossentropy(),\n          metrics=[metric_fn()])\n\n    return bert_model\n\n  def run_classifier(self, train_ds, validation_ds, epochs, steps_per_epoch,\n                     num_classes, **kwargs):\n    \"\"\"Creates classifier and runs the classifier training.\n\n    Args:\n      train_ds: tf.data.Dataset, training data to be fed in\n        tf.keras.Model.fit().\n      validation_ds: tf.data.Dataset, validation data to be fed in\n        tf.keras.Model.fit().\n      epochs: Integer, training epochs.\n      steps_per_epoch: Integer or None. Total number of steps (batches of\n        samples) before declaring one epoch finished and starting the next\n        epoch. If `steps_per_epoch` is None, the epoch will run until the input\n        dataset is exhausted.\n      num_classes: Interger, number of classes.\n      **kwargs: Other parameters used in the tf.keras.Model.fit().\n\n    Returns:\n      tf.keras.Model, the keras model that's already trained.\n    \"\"\"\n    if steps_per_epoch is None:\n      logging.info(\n          'steps_per_epoch is None, use %d as the estimated steps_per_epoch',\n          model_util.ESTIMITED_STEPS_PER_EPOCH)\n      steps_per_epoch = model_util.ESTIMITED_STEPS_PER_EPOCH\n    total_steps = steps_per_epoch * epochs\n    warmup_steps = int(total_steps * 0.1)\n    initial_lr = self.learning_rate\n\n    with distribute_utils.get_strategy_scope(self.strategy):\n      optimizer = optimization.create_optimizer(initial_lr, total_steps,\n                                                warmup_steps)\n      bert_model = self.create_model(num_classes, optimizer)\n\n    for i in range(epochs):\n      bert_model.fit(\n          x=train_ds,\n          initial_epoch=i,\n          epochs=i + 1,\n          validation_data=validation_ds,\n          **kwargs)\n\n    return bert_model\n\n  def get_config(self):\n    \"\"\"Gets the configuration.\"\"\"\n    # Only preprocessing related variables are included.\n    return {'uri': self.uri, 'seq_len': self.seq_len}\n\n\ndef dump_to_files(all_predictions, all_nbest_json, scores_diff_json,\n                  version_2_with_negative, output_dir):\n  \"\"\"Save output to json files for question answering.\"\"\"\n  output_prediction_file = os.path.join(output_dir, 'predictions.json')\n  output_nbest_file = os.path.join(output_dir, 'nbest_predictions.json')\n  output_null_log_odds_file = os.path.join(output_dir, 'null_odds.json')\n  tf.compat.v1.logging.info('Writing predictions to: %s',\n                            (output_prediction_file))\n  tf.compat.v1.logging.info('Writing nbest to: %s', (output_nbest_file))\n\n  squad_lib.write_to_json_files(all_predictions, output_prediction_file)\n  squad_lib.write_to_json_files(all_nbest_json, output_nbest_file)\n  if version_2_with_negative:\n    squad_lib.write_to_json_files(scores_diff_json, output_null_log_odds_file)\n\n\ndef create_qa_model(bert_config,\n                    max_seq_length,\n                    initializer=None,\n                    hub_module_url=None,\n                    hub_module_trainable=True,\n                    is_tf2=True):\n  \"\"\"Returns BERT qa model along with core BERT model to import weights.\n\n  Args:\n    bert_config: BertConfig, the config defines the core Bert model.\n    max_seq_length: integer, the maximum input sequence length.\n    initializer: Initializer for the final dense layer in the span labeler.\n      Defaulted to TruncatedNormal initializer.\n    hub_module_url: TF-Hub path/url to Bert module.\n    hub_module_trainable: True to finetune layers in the hub module.\n    is_tf2: boolean, whether the hub module is in TensorFlow 2.x format.\n\n  Returns:\n    A tuple of (1) keras model that outputs start logits and end logits and\n    (2) the core BERT transformer encoder.\n  \"\"\"\n\n  if initializer is None:\n    initializer = tf.keras.initializers.TruncatedNormal(\n        stddev=bert_config.initializer_range)\n\n  input_word_ids = tf.keras.layers.Input(\n      shape=(max_seq_length,), dtype=tf.int32, name='input_word_ids')\n  input_mask = tf.keras.layers.Input(\n      shape=(max_seq_length,), dtype=tf.int32, name='input_mask')\n  input_type_ids = tf.keras.layers.Input(\n      shape=(max_seq_length,), dtype=tf.int32, name='input_type_ids')\n\n  if is_tf2:\n    core_model = hub.KerasLayer(hub_module_url, trainable=hub_module_trainable)\n    pooled_output, sequence_output = core_model(\n        [input_word_ids, input_mask, input_type_ids])\n  else:\n    bert_model = hub_loader.HubKerasLayerV1V2(\n        hub_module_url,\n        signature='tokens',\n        signature_outputs_as_dict=True,\n        trainable=hub_module_trainable)\n    outputs = bert_model({\n        'input_ids': input_word_ids,\n        'input_mask': input_mask,\n        'segment_ids': input_type_ids\n    })\n\n    pooled_output = outputs['pooled_output']\n    sequence_output = outputs['sequence_output']\n\n  bert_encoder = tf.keras.Model(\n      inputs=[input_word_ids, input_mask, input_type_ids],\n      outputs=[sequence_output, pooled_output],\n      name='core_model')\n  return models.BertSpanLabeler(\n      network=bert_encoder, initializer=initializer), bert_encoder\n\n\ndef create_qa_model_from_squad(max_seq_length,\n                               hub_module_url,\n                               hub_module_trainable=True,\n                               is_tf2=False):\n  \"\"\"Creates QA model the initialized from the model retrained on Squad dataset.\n\n  Args:\n    max_seq_length: integer, the maximum input sequence length.\n    hub_module_url: TF-Hub path/url to Bert module that's retrained on Squad\n      dataset.\n    hub_module_trainable: True to finetune layers in the hub module.\n    is_tf2: boolean, whether the hub module is in TensorFlow 2.x format.\n\n  Returns:\n    Keras model that outputs start logits and end logits.\n  \"\"\"\n  if is_tf2:\n    raise ValueError('Only supports to load TensorFlow 1.x hub module.')\n\n  input_word_ids = tf.keras.layers.Input(\n      shape=(max_seq_length,), dtype=tf.int32, name='input_word_ids')\n  input_mask = tf.keras.layers.Input(\n      shape=(max_seq_length,), dtype=tf.int32, name='input_mask')\n  input_type_ids = tf.keras.layers.Input(\n      shape=(max_seq_length,), dtype=tf.int32, name='input_type_ids')\n\n  squad_bert = hub_loader.HubKerasLayerV1V2(\n      hub_module_url,\n      signature='squad',\n      signature_outputs_as_dict=True,\n      trainable=hub_module_trainable)\n\n  outputs = squad_bert({\n      'input_ids': input_word_ids,\n      'input_mask': input_mask,\n      'segment_ids': input_type_ids\n  })\n  start_logits = tf.keras.layers.Lambda(\n      tf.identity, name='start_positions')(\n          outputs['start_logits'])\n  end_logits = tf.keras.layers.Lambda(\n      tf.identity, name='end_positions')(\n          outputs['end_logits'])\n\n  return tf.keras.Model(\n      inputs=[input_word_ids, input_mask, input_type_ids],\n      outputs=[start_logits, end_logits])\n\n\n@mm_export('question_answer.BertQaSpec')\nclass BertQAModelSpec(BertModelSpec):\n  \"\"\"A specification of BERT model for question answering.\"\"\"\n\n  def __init__(\n      self,\n      uri='https://tfhub.dev/tensorflow/bert_en_uncased_L-12_H-768_A-12/1',\n      model_dir=None,\n      seq_len=384,\n      query_len=64,\n      doc_stride=128,\n      dropout_rate=0.1,\n      initializer_range=0.02,\n      learning_rate=8e-5,\n      distribution_strategy='mirrored',\n      num_gpus=-1,\n      tpu='',\n      trainable=True,\n      predict_batch_size=8,\n      do_lower_case=True,\n      is_tf2=True,\n      tflite_input_name=None,\n      tflite_output_name=None,\n      init_from_squad_model=False,\n      default_batch_size=16,\n      name='Bert'):\n    \"\"\"Initialze an instance with model paramaters.\n\n    Args:\n      uri: TF-Hub path/url to Bert module.\n      model_dir: The location of the model checkpoint files.\n      seq_len: Length of the sequence to feed into the model.\n      query_len: Length of the query to feed into the model.\n      doc_stride: The stride when we do a sliding window approach to take chunks\n        of the documents.\n      dropout_rate: The rate for dropout.\n      initializer_range: The stdev of the truncated_normal_initializer for\n        initializing all weight matrices.\n      learning_rate: The initial learning rate for Adam.\n      distribution_strategy:  A string specifying which distribution strategy to\n        use. Accepted values are 'off', 'one_device', 'mirrored',\n        'parameter_server', 'multi_worker_mirrored', and 'tpu' -- case\n        insensitive. 'off' means not to use Distribution Strategy; 'tpu' means\n        to use TPUStrategy using `tpu_address`.\n      num_gpus: How many GPUs to use at each worker with the\n        DistributionStrategies API. The default is -1, which means utilize all\n        available GPUs.\n      tpu: TPU address to connect to.\n      trainable: boolean, whether pretrain layer is trainable.\n      predict_batch_size: Batch size for prediction.\n      do_lower_case: boolean, whether to lower case the input text. Should be\n        True for uncased models and False for cased models.\n      is_tf2: boolean, whether the hub module is in TensorFlow 2.x format.\n      tflite_input_name: Dict, input names for the TFLite model.\n      tflite_output_name: Dict, output names for the TFLite model.\n      init_from_squad_model: boolean, whether to initialize from the model that\n        is already retrained on Squad 1.1.\n      default_batch_size: Default batch size for training.\n      name: Name of the object.\n    \"\"\"\n    super(BertQAModelSpec,\n          self).__init__(uri, model_dir, seq_len, dropout_rate,\n                         initializer_range, learning_rate,\n                         distribution_strategy, num_gpus, tpu, trainable,\n                         do_lower_case, is_tf2, name, tflite_input_name,\n                         default_batch_size)\n    self.query_len = query_len\n    self.doc_stride = doc_stride\n    self.predict_batch_size = predict_batch_size\n    if tflite_output_name is None:\n      tflite_output_name = {\n          'start_logits': 'StatefulPartitionedCall:1',\n          'end_logits': 'StatefulPartitionedCall:0'\n      }\n    self.tflite_output_name = tflite_output_name\n    self.init_from_squad_model = init_from_squad_model\n\n  def get_name_to_features(self, is_training):\n    \"\"\"Gets the dictionary describing the features.\"\"\"\n    name_to_features = {\n        'input_ids': tf.io.FixedLenFeature([self.seq_len], tf.int64),\n        'input_mask': tf.io.FixedLenFeature([self.seq_len], tf.int64),\n        'segment_ids': tf.io.FixedLenFeature([self.seq_len], tf.int64),\n    }\n\n    if is_training:\n      name_to_features['start_positions'] = tf.io.FixedLenFeature([], tf.int64)\n      name_to_features['end_positions'] = tf.io.FixedLenFeature([], tf.int64)\n    else:\n      name_to_features['unique_ids'] = tf.io.FixedLenFeature([], tf.int64)\n\n    return name_to_features\n\n  def select_data_from_record(self, record):\n    \"\"\"Dispatches records to features and labels.\"\"\"\n    x, y = {}, {}\n    for name, tensor in record.items():\n      if name in ('start_positions', 'end_positions'):\n        y[name] = tensor\n      elif name == 'input_ids':\n        x['input_word_ids'] = tensor\n      elif name == 'segment_ids':\n        x['input_type_ids'] = tensor\n      else:\n        x[name] = tensor\n    return (x, y)\n\n  def get_config(self):\n    \"\"\"Gets the configuration.\"\"\"\n    # Only preprocessing related variables are included.\n    return {\n        'uri': self.uri,\n        'seq_len': self.seq_len,\n        'query_len': self.query_len,\n        'doc_stride': self.doc_stride\n    }\n\n  def convert_examples_to_features(self, examples, is_training, output_fn,\n                                   batch_size):\n    \"\"\"Converts examples to features and write them into TFRecord file.\"\"\"\n    if not self.is_built:\n      self.build()\n\n    return squad_lib.convert_examples_to_features(\n        examples=examples,\n        tokenizer=self.tokenizer,\n        max_seq_length=self.seq_len,\n        doc_stride=self.doc_stride,\n        max_query_length=self.query_len,\n        is_training=is_training,\n        output_fn=output_fn,\n        batch_size=batch_size)\n\n  def create_model(self):\n    \"\"\"Creates the model for qa task.\"\"\"\n    if self.init_from_squad_model:\n      return create_qa_model_from_squad(self.seq_len, self.uri, self.trainable,\n                                        self.is_tf2)\n    else:\n      qa_model, _ = create_qa_model(\n          self.bert_config,\n          self.seq_len,\n          hub_module_url=self.uri,\n          hub_module_trainable=self.trainable,\n          is_tf2=self.is_tf2)\n      return qa_model\n\n  def train(self, train_ds, epochs, steps_per_epoch, **kwargs):\n    \"\"\"Run bert QA training.\n\n    Args:\n      train_ds: tf.data.Dataset, training data to be fed in\n        tf.keras.Model.fit().\n      epochs: Integer, training epochs.\n      steps_per_epoch: Integer or None. Total number of steps (batches of\n        samples) before declaring one epoch finished and starting the next\n        epoch. If `steps_per_epoch` is None, the epoch will run until the input\n        dataset is exhausted.\n      **kwargs: Other parameters used in the tf.keras.Model.fit().\n\n    Returns:\n      tf.keras.Model, the keras model that's already trained.\n    \"\"\"\n    if steps_per_epoch is None:\n      logging.info(\n          'steps_per_epoch is None, use %d as the estimated steps_per_epoch',\n          model_util.ESTIMITED_STEPS_PER_EPOCH)\n      steps_per_epoch = model_util.ESTIMITED_STEPS_PER_EPOCH\n    total_steps = steps_per_epoch * epochs\n    warmup_steps = int(total_steps * 0.1)\n\n    def _loss_fn(positions, logits):\n      \"\"\"Get losss function for QA model.\"\"\"\n      loss = tf.keras.losses.sparse_categorical_crossentropy(\n          positions, logits, from_logits=True)\n      return tf.reduce_mean(loss)\n\n    with distribute_utils.get_strategy_scope(self.strategy):\n      bert_model = self.create_model()\n      optimizer = optimization.create_optimizer(self.learning_rate, total_steps,\n                                                warmup_steps)\n\n      bert_model.compile(\n          optimizer=optimizer, loss=_loss_fn, loss_weights=[0.5, 0.5])\n\n    if not bert_model.trainable_variables:\n      tf.compat.v1.logging.warning(\n          'Trainable variables in the model are empty.')\n      return bert_model\n\n    bert_model.fit(x=train_ds, epochs=epochs, **kwargs)\n\n    return bert_model\n\n  def _predict(self, model, dataset, num_steps):\n    \"\"\"Predicts the dataset using distribute strategy.\"\"\"\n    # TODO(wangtz): We should probably set default strategy as self.strategy\n    # if not specified.\n    strategy = self.strategy or tf.distribute.get_strategy()\n    predict_iterator = iter(strategy.experimental_distribute_dataset(dataset))\n\n    @tf.function\n    def predict_step(iterator):\n      \"\"\"Predicts on distributed devices.\"\"\"\n\n      def _replicated_step(inputs):\n        \"\"\"Replicated prediction calculation.\"\"\"\n        x, _ = inputs\n        unique_ids = x.pop('unique_ids')\n        start_logits, end_logits = model(x, training=False)\n        return dict(\n            unique_ids=unique_ids,\n            start_logits=start_logits,\n            end_logits=end_logits)\n\n      outputs = strategy.run(_replicated_step, args=(next(iterator),))\n      return tf.nest.map_structure(strategy.experimental_local_results, outputs)\n\n    all_results = []\n    for _ in range(num_steps):\n      predictions = predict_step(predict_iterator)\n      for result in run_squad_helper.get_raw_results(predictions):\n        all_results.append(result)\n      if len(all_results) % 100 == 0:\n        tf.compat.v1.logging.info('Made predictions for %d records.',\n                                  len(all_results))\n    return all_results\n\n  def predict(self, model, dataset, num_steps):\n    \"\"\"Predicts the dataset for `model`.\"\"\"\n    return self._predict(model, dataset, num_steps)\n\n  def reorder_output_details(self, tflite_output_details):\n    \"\"\"Reorders the tflite output details to map the order of keras model.\"\"\"\n    for detail in tflite_output_details:\n      name = detail['name']\n      if self.tflite_output_name['start_logits'] == name:\n        start_logits_detail = detail\n      if self.tflite_output_name['end_logits'] == name:\n        end_logits_detail = detail\n    return (start_logits_detail, end_logits_detail)\n\n  def predict_tflite(self, tflite_filepath, dataset):\n    \"\"\"Predicts the dataset for TFLite model in `tflite_filepath`.\"\"\"\n    all_results = []\n    lite_runner = model_util.LiteRunner(tflite_filepath,\n                                        self.reorder_input_details,\n                                        self.reorder_output_details)\n    for features, _ in dataset:\n      outputs = lite_runner.run(features)\n      for unique_id, start_logits, end_logits in zip(features['unique_ids'],\n                                                     outputs[0], outputs[1]):\n        raw_result = run_squad_helper.RawResult(\n            unique_id=unique_id.numpy(),\n            start_logits=start_logits.tolist(),\n            end_logits=end_logits.tolist())\n        all_results.append(raw_result)\n        if len(all_results) % 100 == 0:\n          tf.compat.v1.logging.info('Made predictions for %d records.',\n                                    len(all_results))\n    return all_results\n\n  def evaluate(self, model, tflite_filepath, dataset, num_steps, eval_examples,\n               eval_features, predict_file, version_2_with_negative,\n               max_answer_length, null_score_diff_threshold, verbose_logging,\n               output_dir):\n    \"\"\"Evaluate QA model.\n\n    Args:\n      model: The keras model to be evaluated.\n      tflite_filepath: File path to the TFLite model.\n      dataset: tf.data.Dataset used for evaluation.\n      num_steps: Number of steps to evaluate the model.\n      eval_examples: List of `squad_lib.SquadExample` for evaluation data.\n      eval_features: List of `squad_lib.InputFeatures` for evaluation data.\n      predict_file: The input predict file.\n      version_2_with_negative: Whether the input predict file is SQuAD 2.0\n        format.\n      max_answer_length: The maximum length of an answer that can be generated.\n        This is needed because the start and end predictions are not conditioned\n        on one another.\n      null_score_diff_threshold: If null_score - best_non_null is greater than\n        the threshold, predict null. This is only used for SQuAD v2.\n      verbose_logging: If true, all of the warnings related to data processing\n        will be printed. A number of warnings are expected for a normal SQuAD\n        evaluation.\n      output_dir: The output directory to save output to json files:\n        predictions.json, nbest_predictions.json, null_odds.json. If None, skip\n        saving to json files.\n\n    Returns:\n      A dict contains two metrics: Exact match rate and F1 score.\n    \"\"\"\n    if model is not None and tflite_filepath is not None:\n      raise ValueError('Exactly one of the paramaters `model` and '\n                       '`tflite_filepath` should be set.')\n    elif model is None and tflite_filepath is None:\n      raise ValueError('At least one of the parameters `model` and '\n                       '`tflite_filepath` are None.')\n\n    if tflite_filepath is not None:\n      all_results = self.predict_tflite(tflite_filepath, dataset)\n    else:\n      all_results = self.predict(model, dataset, num_steps)\n\n    all_predictions, all_nbest_json, scores_diff_json = (\n        squad_lib.postprocess_output(\n            eval_examples,\n            eval_features,\n            all_results,\n            n_best_size=20,\n            max_answer_length=max_answer_length,\n            do_lower_case=self.do_lower_case,\n            version_2_with_negative=version_2_with_negative,\n            null_score_diff_threshold=null_score_diff_threshold,\n            verbose=verbose_logging))\n\n    if output_dir is not None:\n      dump_to_files(all_predictions, all_nbest_json, scores_diff_json,\n                    version_2_with_negative, output_dir)\n\n    dataset_json = file_util.load_json_file(predict_file)\n    pred_dataset = dataset_json['data']\n\n    if version_2_with_negative:\n      eval_metrics = squad_evaluate_v2_0.evaluate(pred_dataset, all_predictions,\n                                                  scores_diff_json)\n    else:\n      eval_metrics = squad_evaluate_v1_1.evaluate(pred_dataset, all_predictions)\n    return eval_metrics\n\n\nmobilebert_classifier_spec = functools.partial(\n    BertClassifierModelSpec,\n    uri='https://tfhub.dev/google/mobilebert/uncased_L-24_H-128_B-512_A-4_F-4_OPT/1',\n    is_tf2=False,\n    distribution_strategy='off',\n    name='MobileBert',\n    default_batch_size=48,\n)\nmobilebert_classifier_spec.__doc__ = util.wrap_doc(\n    BertClassifierModelSpec,\n    'Creates MobileBert model spec for the text classification task. See also: `tflite_model_maker.text_classifier.BertClassifierSpec`.'\n)\nmm_export('text_classifier.MobileBertClassifierSpec').export_constant(\n    __name__, 'mobilebert_classifier_spec')\n\nmobilebert_qa_spec = functools.partial(\n    BertQAModelSpec,\n    uri='https://tfhub.dev/google/mobilebert/uncased_L-24_H-128_B-512_A-4_F-4_OPT/1',\n    is_tf2=False,\n    distribution_strategy='off',\n    learning_rate=4e-05,\n    name='MobileBert',\n    default_batch_size=32,\n)\nmobilebert_qa_spec.__doc__ = util.wrap_doc(\n    BertQAModelSpec,\n    'Creates MobileBert model spec for the question answer task. See also: `tflite_model_maker.question_answer.BertQaSpec`.'\n)\nmm_export('question_answer.MobileBertQaSpec').export_constant(\n    __name__, 'mobilebert_qa_spec')\n\nmobilebert_qa_squad_spec = functools.partial(\n    BertQAModelSpec,\n    uri='https://tfhub.dev/google/mobilebert/uncased_L-24_H-128_B-512_A-4_F-4_OPT/squadv1/1',\n    is_tf2=False,\n    distribution_strategy='off',\n    learning_rate=4e-05,\n    name='MobileBert',\n    init_from_squad_model=True,\n    default_batch_size=32,\n)\nmobilebert_qa_squad_spec.__doc__ = util.wrap_doc(\n    BertQAModelSpec,\n    'Creates MobileBert model spec that\\'s already retrained on SQuAD1.1 for '\n    'the question answer task. See also: `tflite_model_maker.question_answer.BertQaSpec`.'\n)\nmm_export('question_answer.MobileBertQaSquadSpec').export_constant(\n    __name__, 'mobilebert_qa_squad_spec')\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/model_spec/text_spec_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport collections\nimport os\n\nfrom absl.testing import parameterized\nimport tensorflow.compat.v2 as tf\n\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import text_spec\nfrom official.nlp.data import classifier_data_lib\n\n\ndef _gen_examples():\n  examples = []\n  examples.append(\n      classifier_data_lib.InputExample(\n          guid=0, text_a='Really good.', label='pos'))\n  examples.append(\n      classifier_data_lib.InputExample(guid=1, text_a='So bad.', label='neg'))\n  return examples\n\n\ndef _get_dataset_from_tfrecord(tfrecord_file, name_to_features):\n\n  def _parse_function(example_proto):\n    # Parse the input `tf.Example` proto using the dictionary above.\n    return tf.io.parse_single_example(example_proto, name_to_features)\n\n  ds = tf.data.TFRecordDataset(tfrecord_file)\n  ds = ds.map(_parse_function)\n  return ds\n\n\nclass AverageWordVecModelSpecTest(tf.test.TestCase):\n\n  def setUp(self):\n    super(AverageWordVecModelSpecTest, self).setUp()\n    self.model_spec = text_spec.AverageWordVecModelSpec(seq_len=5)\n    self.vocab = collections.OrderedDict(\n        (('<PAD>', 0), ('<START>', 1), ('<UNKNOWN>', 2), ('good', 3), ('bad',\n                                                                       4)))\n    self.model_spec.vocab = self.vocab\n\n  def test_tokenize(self):\n    model_spec = text_spec.AverageWordVecModelSpec()\n    text = model_spec._tokenize('It\\'s really good.')\n    self.assertEqual(text, ['it\\'s', 'really', 'good'])\n\n    model_spec = text_spec.AverageWordVecModelSpec(lowercase=False)\n    text = model_spec._tokenize('That is so cool!!!')\n    self.assertEqual(text, ['That', 'is', 'so', 'cool'])\n\n  def test_convert_examples_to_features(self):\n    examples = _gen_examples()\n    tfrecord_file = os.path.join(self.get_temp_dir(), 'tmp.tfrecord')\n    self.model_spec.convert_examples_to_features(examples, tfrecord_file,\n                                                 ['pos', 'neg'])\n    ds = _get_dataset_from_tfrecord(tfrecord_file,\n                                    self.model_spec.get_name_to_features())\n\n    expected_features = [[[1, 2, 3, 0, 0], 0], [[1, 2, 4, 0, 0], 1]]\n    for i, sample in enumerate(ds):\n      self.assertTrue(\n          (sample['input_ids'].numpy() == expected_features[i][0]).all())\n      self.assertEqual(sample['label_ids'].numpy(), expected_features[i][1])\n\n  def test_preprocess(self):\n    token_ids = self.model_spec.preprocess('It\\'s really good.')\n    expected_token_ids = [1, 2, 2, 3, 0]\n    self.assertEqual(token_ids, expected_token_ids)\n\n  def test_gen_vocab(self):\n    examples = _gen_examples()\n    self.model_spec.gen_vocab(examples)\n    expected_vocab = collections.OrderedDict([('<PAD>', 0), ('<START>', 1),\n                                              ('<UNKNOWN>', 2), ('really', 3),\n                                              ('good', 4), ('so', 5),\n                                              ('bad', 6)])\n    self.assertEqual(self.model_spec.vocab, expected_vocab)\n\n  def test_save_load_vocab(self):\n    vocab_file = os.path.join(self.get_temp_dir(), 'vocab.txt')\n    self.model_spec.save_vocab(vocab_file)\n    vocab = self.model_spec.load_vocab(vocab_file)\n    self.assertEqual(vocab, self.vocab)\n\n  def test_run_classifier(self):\n    num_classes = 2\n    model = self.model_spec.run_classifier(\n        train_ds=self._gen_random_ds(num_classes),\n        validation_ds=self._gen_random_ds(num_classes),\n        epochs=1,\n        steps_per_epoch=1,\n        num_classes=num_classes)\n    self.assertIsInstance(model, tf.keras.Model)\n\n  def test_create_model_without_compilation(self):\n    num_classes = 2\n    model = self.model_spec.create_model(\n        num_classes=num_classes, with_loss_and_metrics=False)\n    with self.assertRaises(RuntimeError):\n      model.fit(\n          self._gen_random_ds(num_classes),\n          validation_data=self._gen_random_ds(num_classes),\n          epochs=1,\n          steps_per_epoch=1)\n\n  def _gen_random_ds(self, num_classes, data_size=1, batch_size=4):\n    batched_features = tf.random.uniform(\n        (data_size, batch_size, self.model_spec.seq_len),\n        minval=0,\n        maxval=len(self.model_spec.vocab),\n        dtype=tf.dtypes.int32)\n\n    batched_labels = tf.random.uniform((data_size, batch_size),\n                                       minval=0,\n                                       maxval=num_classes,\n                                       dtype=tf.dtypes.int32)\n    ds = tf.data.Dataset.from_tensor_slices((batched_features, batched_labels))\n    return ds\n\n\nclass BertClassifierModelSpecTest(tf.test.TestCase, parameterized.TestCase):\n\n  @parameterized.parameters(\n      ('https://tfhub.dev/tensorflow/bert_en_uncased_L-12_H-768_A-12/1', True),\n      ('https://tfhub.dev/google/bert_uncased_L-12_H-768_A-12/1', False),\n  )\n  def test_bert(self, uri, is_tf2):\n    model_spec = text_spec.BertClassifierModelSpec(\n        uri, is_tf2=is_tf2, distribution_strategy='off', seq_len=3)\n    self._test_convert_examples_to_features(model_spec)\n    self._test_run_classifier(model_spec)\n    self._test_create_model_without_compilation(model_spec)\n\n  def _test_convert_examples_to_features(self, model_spec):\n    examples = _gen_examples()\n    tfrecord_file = os.path.join(self.get_temp_dir(), 'tmp.tfrecord')\n    model_spec.convert_examples_to_features(examples, tfrecord_file,\n                                            ['pos', 'neg'])\n\n    ds = _get_dataset_from_tfrecord(tfrecord_file,\n                                    model_spec.get_name_to_features())\n    expected_features = []\n    expected_features.append({\n        'input_ids': [101, 2428, 102],\n        'input_mask': [1, 1, 1],\n        'segment_ids': [0, 0, 0],\n        'label_ids': 0\n    })\n    expected_features.append({\n        'input_ids': [101, 2061, 102],\n        'input_mask': [1, 1, 1],\n        'segment_ids': [0, 0, 0],\n        'label_ids': 1\n    })\n    for i, sample in enumerate(ds):\n      for k, v in expected_features[i].items():\n        self.assertTrue((sample[k].numpy() == v).all())\n\n  def _test_run_classifier(self, model_spec):\n    num_classes = 2\n    model = model_spec.run_classifier(\n        train_ds=self._gen_random_ds(model_spec.seq_len, num_classes),\n        validation_ds=self._gen_random_ds(model_spec.seq_len, num_classes),\n        epochs=1,\n        steps_per_epoch=1,\n        num_classes=num_classes)\n    self.assertIsInstance(model, tf.keras.Model)\n\n  def _test_create_model_without_compilation(self, model_spec):\n    num_classes = 2\n    model = model_spec.create_model(\n        num_classes=num_classes, with_loss_and_metrics=False)\n    with self.assertRaises(RuntimeError):\n      model.fit(\n          self._gen_random_ds(model_spec.seq_len, num_classes),\n          validation_data=self._gen_random_ds(model_spec.seq_len, num_classes),\n          epochs=1,\n          steps_per_epoch=1)\n\n  def _gen_random_ds(self, seq_len, num_classes, data_size=1, batch_size=1):\n\n    batched_input_ids = tf.random.uniform((data_size, batch_size, seq_len),\n                                          minval=0,\n                                          maxval=2,\n                                          dtype=tf.dtypes.int32)\n    batched_input_mask = tf.random.uniform((data_size, batch_size, seq_len),\n                                           minval=0,\n                                           maxval=2,\n                                           dtype=tf.dtypes.int32)\n    batched_segment_ids = tf.random.uniform((data_size, batch_size, seq_len),\n                                            minval=0,\n                                            maxval=2,\n                                            dtype=tf.dtypes.int32)\n\n    batched_labels = tf.random.uniform((data_size, batch_size),\n                                       minval=0,\n                                       maxval=num_classes,\n                                       dtype=tf.dtypes.int32)\n    x = {\n        'input_word_ids': batched_input_ids,\n        'input_mask': batched_input_mask,\n        'input_type_ids': batched_segment_ids\n    }\n    y = batched_labels\n\n    ds = tf.data.Dataset.from_tensor_slices((x, y))\n    return ds\n\n\nif __name__ == '__main__':\n  # Load compressed models from tensorflow_hub\n  os.environ['TFHUB_MODEL_LOAD_FORMAT'] = 'COMPRESSED'\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/model_spec/util.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Utilities for model specification.\"\"\"\n\nimport inspect\nimport re\n\nimport tensorflow as tf\n\n\ndef dict_with_default(default_dict, **updates):\n  default_dict.update(updates)\n  return default_dict\n\n\ndef create_int_feature(values):\n  feature = tf.train.Feature(int64_list=tf.train.Int64List(value=list(values)))\n  return feature\n\n\ndef get_num_gpus(num_gpus):\n  try:\n    tot_num_gpus = len(tf.config.experimental.list_physical_devices('GPU'))\n  except (tf.errors.NotFoundError, tf.errors.InternalError):\n    tot_num_gpus = max(0, num_gpus)\n  if num_gpus > tot_num_gpus or num_gpus == -1:\n    num_gpus = tot_num_gpus\n  return num_gpus\n\n\ndef wrap_doc(func_or_class, short_desciption):\n  \"\"\"Wrap doc string of function or class, and replace short description.\"\"\"\n  if inspect.isfunction(func_or_class):\n    doc = func_or_class.__doc__\n  elif inspect.isclass(func_or_class):\n    doc = func_or_class.__init__.__doc__\n  else:\n    raise ValueError('Only support function or classtion, but got: {}.'.format(\n        func_or_class))\n\n  if not doc:\n    doc = ''\n  # Replace the first line to the new short description.\n  doc = re.sub(r'^.*', short_desciption, doc, count=1)\n  return doc\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/model_util.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Utilities for keras models.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport json\nimport os\nimport tempfile\n\nimport numpy as np\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflowjs.converters import converter as tfjs_converter\n# pylint: disable=g-direct-tensorflow-import\nfrom ai_edge_litert import interpreter as tfl_interpreter\n# pylint: enable=g-direct-tensorflow-import\nfrom tflite_support import metadata as _metadata\n\nDEFAULT_SCALE, DEFAULT_ZERO_POINT = 0, 0\nESTIMITED_STEPS_PER_EPOCH = 1000\n\n\ndef set_batch_size(model, batch_size):\n  \"\"\"Sets batch size for the model.\"\"\"\n  for model_input in model.inputs:\n    new_shape = [batch_size] + model_input.shape[1:]\n    model_input.set_shape(new_shape)\n\n\ndef get_steps_per_epoch(steps_per_epoch=None, batch_size=None, train_data=None):\n  \"\"\"Gets the estimated training steps per epoch.\n\n  1. If `steps_per_epoch` is set, returns `steps_per_epoch` directly.\n  2. Else if we can get the length of training data successfully, returns\n     `train_data_length // batch_size`.\n  3. Else if it fails to get the length of training data, return None.\n\n  Args:\n    steps_per_epoch: int, training steps per epoch.\n    batch_size: int, batch size.\n    train_data: training data.\n\n  Returns:\n    Estimated training steps per epoch.\n  \"\"\"\n  if steps_per_epoch is not None:\n    # steps_per_epoch is set by users manually.\n    return steps_per_epoch\n  else:\n    # Gets the steps by the length of the training data.\n    try:\n      return len(train_data) // batch_size\n    except TypeError:\n      return None\n\n\ndef _create_temp_dir(convert_from_saved_model):\n  \"\"\"Creates temp dir, if True is given.\"\"\"\n  if convert_from_saved_model:\n    return tempfile.TemporaryDirectory()\n  else:\n    return DummyContextManager()\n\n\nclass DummyContextManager(object):\n\n  def __enter__(self):\n    pass\n\n  def __exit__(self, *args):\n    pass\n\n\ndef export_labels(filepath, index_to_label):\n  with tf.io.gfile.GFile(filepath, 'w') as f:\n    f.write('\\n'.join(index_to_label))\n\n\ndef export_saved_model(model,\n                       filepath,\n                       overwrite=True,\n                       include_optimizer=True,\n                       save_format=None,\n                       signatures=None,\n                       options=None):\n  \"\"\"Saves the model to Tensorflow SavedModel or a single HDF5 file.\n\n  Args:\n    model: Instance of a Keras model.\n    filepath: String, path to SavedModel or H5 file to save the model.\n    overwrite: Whether to silently overwrite any existing file at the target\n      location, or provide the user with a manual prompt.\n    include_optimizer: If True, save optimizer's state together.\n    save_format: Either 'tf' or 'h5', indicating whether to save the model to\n      Tensorflow SavedModel or HDF5. Defaults to 'tf' in TF 2.X, and 'h5' in TF\n      1.X.\n    signatures: Signatures to save with the SavedModel. Applicable to the 'tf'\n      format only. Please see the `signatures` argument in `tf.saved_model.save`\n      for details.\n    options: Optional `tf.saved_model.SaveOptions` object that specifies options\n      for saving to SavedModel.\n  \"\"\"\n  if filepath is None:\n    raise ValueError(\n        \"SavedModel filepath couldn't be None when exporting to SavedModel.\")\n  model.save(\n      filepath,\n      overwrite=overwrite,\n      include_optimizer=include_optimizer,\n      save_format=save_format,\n      signatures=signatures,\n      options=options)\n\n\ndef export_tflite(model,\n                  tflite_filepath,\n                  quantization_config=None,\n                  convert_from_saved_model_tf2=False,\n                  preprocess=None,\n                  supported_ops=(tf.lite.OpsSet.TFLITE_BUILTINS,)):\n  \"\"\"Converts the retrained model to tflite format and saves it.\n\n  Args:\n    model: model to be converted to tflite.\n    tflite_filepath: File path to save tflite model.\n    quantization_config: Configuration for post-training quantization.\n    convert_from_saved_model_tf2: Convert to TFLite from saved_model in TF 2.x.\n    preprocess: A preprocess function to apply on the dataset.\n        # TODO(wangtz): Remove when preprocess is split off from CustomModel.\n    supported_ops: A list of supported ops in the converted TFLite file.\n  \"\"\"\n  if tflite_filepath is None:\n    raise ValueError(\n        \"TFLite filepath couldn't be None when exporting to tflite.\")\n\n  if compat.get_tf_behavior() == 1:\n    lite = tf.compat.v1.lite\n  else:\n    lite = tf.lite\n\n  convert_from_saved_model = (\n      compat.get_tf_behavior() == 1 or convert_from_saved_model_tf2)\n  with _create_temp_dir(convert_from_saved_model) as temp_dir_name:\n    if temp_dir_name:\n      save_path = os.path.join(temp_dir_name, 'saved_model')\n      model.save(save_path, include_optimizer=False, save_format='tf')\n      converter = lite.TFLiteConverter.from_saved_model(save_path)\n    else:\n      converter = lite.TFLiteConverter.from_keras_model(model)\n      # TODO(b/191205988): Explicitly disable saved model lowering in\n      #                    the conversion.\n      converter.experimental_lower_to_saved_model = False\n\n    if quantization_config:\n      converter = quantization_config.get_converter_with_quantization(\n          converter, preprocess=preprocess)\n\n    converter.target_spec.supported_ops = supported_ops\n    tflite_model = converter.convert()\n\n  with tf.io.gfile.GFile(tflite_filepath, 'wb') as f:\n    f.write(tflite_model)\n\n\ndef get_lite_runner(tflite_filepath, model_spec=None):\n  \"\"\"Gets `LiteRunner` from file path to TFLite model and `model_spec`.\"\"\"\n  # Gets the functions to handle the input & output indexes if exists.\n  reorder_input_details_fn = None\n  if hasattr(model_spec, 'reorder_input_details'):\n    reorder_input_details_fn = model_spec.reorder_input_details\n\n  reorder_output_details_fn = None\n  if hasattr(model_spec, 'reorder_output_details'):\n    reorder_output_details_fn = model_spec.reorder_output_details\n\n  lite_runner = LiteRunner(tflite_filepath, reorder_input_details_fn,\n                           reorder_output_details_fn)\n  return lite_runner\n\n\ndef _get_input_tensor(input_tensors, input_details, i):\n  \"\"\"Gets input tensor in `input_tensors` that maps `input_detail[i]`.\"\"\"\n  if isinstance(input_tensors, dict):\n    # Gets the mapped input tensor.\n    input_detail = input_details[i]\n    for input_tensor_name, input_tensor in input_tensors.items():\n      if input_tensor_name in input_detail['name']:\n        return input_tensor\n    raise ValueError('Input tensors don\\'t contains a tensor that mapped the '\n                     'input detail %s' % str(input_detail))\n  else:\n    return input_tensors[i]\n\n\nclass LiteRunner(object):\n  \"\"\"Runs inference with the TFLite model.\"\"\"\n\n  def __init__(self,\n               tflite_filepath,\n               reorder_input_details_fn=None,\n               reorder_output_details_fn=None):\n    \"\"\"Initializes Lite runner with tflite model file.\n\n    Args:\n      tflite_filepath: File path to the TFLite model.\n      reorder_input_details_fn: Function to reorder the input details to map the\n        order of keras model.\n      reorder_output_details_fn: Function to reorder the output details to map\n        the order of keras model.\n    \"\"\"\n    with tf.io.gfile.GFile(tflite_filepath, 'rb') as f:\n      tflite_model = f.read()\n    self.interpreter = tfl_interpreter.Interpreter(model_content=tflite_model)\n    self.interpreter.allocate_tensors()\n\n    # Gets the indexed of the input tensors.\n    self.input_details = self.interpreter.get_input_details()\n    if reorder_input_details_fn is not None:\n      self.input_details = reorder_input_details_fn(self.input_details)\n\n    self.output_details = self.interpreter.get_output_details()\n    if reorder_output_details_fn is not None:\n      self.output_details = reorder_output_details_fn(self.output_details)\n\n  def run(self, input_tensors):\n    \"\"\"Runs inference with the TFLite model.\n\n    Args:\n      input_tensors: List / Dict of the input tensors of the TFLite model. The\n        order should be the same as the keras model if it's a list. It also\n        accepts tensor directly if the model has only 1 input.\n\n    Returns:\n      List of the output tensors for multi-output models, otherwise just\n        the output tensor. The order should be the same as the keras model.\n    \"\"\"\n\n    if not isinstance(input_tensors, list) and \\\n       not isinstance(input_tensors, tuple) and \\\n       not isinstance(input_tensors, dict):\n      input_tensors = [input_tensors]\n\n    interpreter = self.interpreter\n\n    # Reshape inputs\n    for i, input_detail in enumerate(self.input_details):\n      input_tensor = _get_input_tensor(input_tensors, self.input_details, i)\n      interpreter.resize_tensor_input(input_detail['index'], input_tensor.shape)\n    interpreter.allocate_tensors()\n\n    # Feed input to the interpreter\n    for i, input_detail in enumerate(self.input_details):\n      input_tensor = _get_input_tensor(input_tensors, self.input_details, i)\n      if input_detail['quantization'] != (DEFAULT_SCALE, DEFAULT_ZERO_POINT):\n        # Quantize the input\n        scale, zero_point = input_detail['quantization']\n        input_tensor = input_tensor / scale + zero_point\n        input_tensor = np.array(input_tensor, dtype=input_detail['dtype'])\n      interpreter.set_tensor(input_detail['index'], input_tensor)\n\n    interpreter.invoke()\n\n    output_tensors = []\n    for output_detail in self.output_details:\n      output_tensor = interpreter.get_tensor(output_detail['index'])\n      if output_detail['quantization'] != (DEFAULT_SCALE, DEFAULT_ZERO_POINT):\n        # Dequantize the output\n        scale, zero_point = output_detail['quantization']\n        output_tensor = output_tensor.astype(np.float32)\n        output_tensor = (output_tensor - zero_point) * scale\n      output_tensors.append(output_tensor)\n\n    if len(output_tensors) == 1:\n      return output_tensors[0]\n    return output_tensors\n\n\ndef export_tfjs(keras_or_saved_model,\n                output_dir,\n                tflite_filepath=None,\n                **kwargs):\n  \"\"\"Exports saved model to tfjs.\n\n  https://www.tensorflow.org/js/guide/conversion?hl=en\n\n  Args:\n    keras_or_saved_model: Keras or saved model.\n    output_dir: Output TF.js model dir.\n    tflite_filepath: str, file path to existing tflite model. If set, the\n      metadata is extracted to the TF.js model.\n    **kwargs: Other options.\n  \"\"\"\n  # For Keras model, creates a saved model first in a temp dir. Otherwise,\n  # convert directly.\n  is_keras = isinstance(keras_or_saved_model, tf.keras.Model)\n  with _create_temp_dir(is_keras) as temp_dir_name:\n    # Export keras model to saved model and then convert to TFJS.\n    if is_keras:\n      keras_or_saved_model.save(\n          temp_dir_name, include_optimizer=False, save_format='tf')\n      path = temp_dir_name\n    else:\n      path = keras_or_saved_model\n\n    # Extract metadata if tflite_filepath is provided.\n    if tflite_filepath:\n      metadata_json = extract_tflite_metadata_json(tflite_filepath)\n      metadata = json.loads(metadata_json)\n      kwargs.update(metadata=metadata)\n\n    tfjs_converter.dispatch_keras_saved_model_to_tensorflowjs_conversion(\n        path, output_dir, **kwargs)\n\n\ndef load_tfjs_keras_model(model_path):\n  \"\"\"Loads tfjs keras model from path.\"\"\"\n  return tfjs_converter.keras_tfjs_loader.load_keras_model(\n      model_path, load_weights=True)\n\n\ndef extract_tflite_metadata_json(tflite_filepath):\n  \"\"\"Extracts metadata from tflite model filepath.\n\n  Args:\n    tflite_filepath: str, path to tflite model file.\n\n  Returns:\n    str: tflite metadata json string.\n  \"\"\"\n  displayer = _metadata.MetadataDisplayer.with_model_file(tflite_filepath)\n  return displayer.get_metadata_json()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/model_util_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\n\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core import test_util\nfrom tensorflow_examples.lite.model_maker.core.task import configs\nfrom tensorflow_examples.lite.model_maker.core.task import model_util\n\n\ndef _get_quantization_config_list(input_dim, num_classes, max_input_value):\n  # Configuration for dynamic range quantization.\n  config1 = configs.QuantizationConfig.for_dynamic()\n\n  representative_data = test_util.get_dataloader(\n      data_size=1,\n      input_shape=[input_dim],\n      num_classes=num_classes,\n      max_input_value=max_input_value)\n  # Configuration for full integer quantization with integer only.\n  config2 = configs.QuantizationConfig.for_int8(\n      representative_data=representative_data, quantization_steps=1)\n\n  # Configuration for full integer quantization with float fallback.\n  config3 = configs.QuantizationConfig.for_float16()\n  return [config1, config2, config3]\n\n\ndef _mock_gen_dataset(data, batch_size=1, is_training=False):  # pylint: disable=unused-argument\n  ds = data.dataset\n  ds = ds.batch(batch_size)\n  return ds\n\n\nclass ModelUtilTest(tf.test.TestCase):\n\n  @test_util.test_in_tf_1and2\n  def test_export_tflite(self):\n    input_dim = 4\n    model = test_util.build_model(input_shape=[input_dim], num_classes=2)\n    tflite_file = os.path.join(self.get_temp_dir(), 'model.tflite')\n    model_util.export_tflite(model, tflite_file)\n    self._test_tflite(model, tflite_file, input_dim)\n\n  @test_util.test_in_tf_1and2\n  def test_export_tflite_quantized(self):\n    input_dim = 4000\n    num_classes = 2\n    max_input_value = 5\n    model = test_util.build_model([input_dim], num_classes)\n    tflite_file = os.path.join(self.get_temp_dir(), 'model_quantized.tflite')\n\n    quant_configs = _get_quantization_config_list(input_dim, num_classes,\n                                                  max_input_value)\n    model_sizes = (9088, 9600, 17280)\n    for config, model_size in zip(quant_configs, model_sizes):\n      model_util.export_tflite(model, tflite_file, config, _mock_gen_dataset)\n      self._test_tflite(\n          model, tflite_file, input_dim, max_input_value, atol=1e-01)\n      self.assertNear(os.path.getsize(tflite_file), model_size, 300)\n\n  def _test_tflite(self,\n                   keras_model,\n                   tflite_model_file,\n                   input_dim,\n                   max_input_value=1000,\n                   atol=1e-04):\n    np.random.seed(0)\n    random_input = np.random.uniform(\n        low=0, high=max_input_value, size=(1, input_dim)).astype(np.float32)\n\n    self.assertTrue(\n        test_util.is_same_output(\n            tflite_model_file, keras_model, random_input, atol=atol))\n\n  @test_util.test_in_tf_1and2\n  def test_export_tfjs(self):\n    input_dim = 4000\n    num_classes = 2\n    model = test_util.build_model([input_dim], num_classes)\n\n    output_dir = os.path.join(self.get_temp_dir(), 'tfjs')\n    model_util.export_tfjs(model, output_dir)\n    self.assertTrue(os.path.exists(output_dir))\n    expected_model_json = os.path.join(output_dir, 'model.json')\n    self.assertTrue(os.path.exists(expected_model_json))\n\n  @test_util.test_in_tf_1and2\n  def test_export_tfjs_saved_model(self):\n    input_dim = 4000\n    num_classes = 2\n    model = test_util.build_model([input_dim], num_classes)\n\n    saved_model_dir = os.path.join(self.get_temp_dir(), 'saved_model_for_js')\n    model.save(saved_model_dir)\n\n    output_dir = os.path.join(self.get_temp_dir(), 'tfjs')\n    model_util.export_tfjs(saved_model_dir, output_dir)\n    self.assertTrue(os.path.exists(output_dir))\n    expected_model_json = os.path.join(output_dir, 'model.json')\n    self.assertTrue(os.path.exists(expected_model_json))\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/model_util_v1_test.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core.task import model_util_test\n\n\nclass ModelUtilTest(model_util_test.ModelUtilTest):\n  \"\"\"Share text tests of the base class, but in tf v1 behavior.\"\"\"\n\n\nif __name__ == '__main__':\n  compat.setup_tf_behavior(tf_version=1)\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/object_detector.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"APIs to train an object detection model.\"\"\"\n\nimport os\nimport tempfile\nfrom typing import Dict, Optional, Tuple, TypeVar\n\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core.api.api_util import mm_export\nfrom tensorflow_examples.lite.model_maker.core.data_util import object_detector_dataloader\nfrom tensorflow_examples.lite.model_maker.core.export_format import ExportFormat\nfrom tensorflow_examples.lite.model_maker.core.task import configs\nfrom tensorflow_examples.lite.model_maker.core.task import custom_model\nfrom tensorflow_examples.lite.model_maker.core.task import model_spec as ms\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import object_detector_spec\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import label_util\nfrom tflite_support.metadata_writers import object_detector as metadata_writer\nfrom tflite_support.metadata_writers import writer_utils\n\nT = TypeVar('T', bound='ObjectDetector')\n\n\n@mm_export('object_detector.ObjectDetector')\nclass ObjectDetector(custom_model.CustomModel):\n  \"\"\"ObjectDetector class for inference and exporting to tflite.\"\"\"\n\n  ALLOWED_EXPORT_FORMAT = (ExportFormat.TFLITE, ExportFormat.SAVED_MODEL,\n                           ExportFormat.LABEL)\n\n  def __init__(\n      self,\n      model_spec: object_detector_spec.EfficientDetModelSpec,\n      label_map: Dict[int, str],\n      representative_data: Optional[\n          object_detector_dataloader.DataLoader] = None\n  ) -> None:\n    \"\"\"Initializes the ObjectDetector class.\n\n    Args:\n      model_spec: Specification for the model.\n      label_map:  Dict, map label integer ids to string label names such as {1:\n        'person', 2: 'notperson'}. 0 is the reserved key for `background` and\n          doesn't need to be included in `label_map`. Label names can't be\n          duplicated.\n      representative_data:  Representative dataset for full integer\n        quantization. Used when converting the keras model to the TFLite model\n        with full interger quantization.\n    \"\"\"\n    super().__init__(model_spec, shuffle=None)\n    if model_spec.config.label_map and model_spec.config.label_map != label_map:\n      tf.compat.v1.logging.warn(\n          'Label map is not the same as the previous label_map in model_spec.')\n    model_spec.config.label_map = label_map\n    # TODO(yuqili): num_classes = 1 have some issues during training. Thus we\n    # make minimum num_classes=2 for now.\n    model_spec.config.num_classes = max(2, max(label_map.keys()))\n    self.representative_data = representative_data\n\n  def create_model(self) -> tf.keras.Model:\n    self.model = self.model_spec.create_model()\n    return self.model\n\n  def _get_dataset_and_steps(\n      self,\n      data: object_detector_dataloader.DataLoader,\n      batch_size: int,\n      is_training: bool,\n  ) -> Tuple[Optional[tf.data.Dataset], int, Optional[str]]:\n    \"\"\"Gets dataset, steps and annotations json file.\"\"\"\n    if not data:\n      return None, 0, None\n    # TODO(b/171449557): Put this into DataLoader.\n    dataset = data.gen_dataset(\n        self.model_spec, batch_size, is_training=is_training)\n    steps = len(data) // batch_size\n    return dataset, steps, data.annotations_json_file\n\n  def train(self,\n            train_data: object_detector_dataloader.DataLoader,\n            validation_data: Optional[\n                object_detector_dataloader.DataLoader] = None,\n            epochs: Optional[int] = None,\n            batch_size: Optional[int] = None) -> tf.keras.Model:\n    \"\"\"Feeds the training data for training.\"\"\"\n    if not self.model_spec.config.drop_remainder:\n      raise ValueError('Must set `drop_remainder=True` during training. '\n                       'Otherwise it will fail.')\n\n    batch_size = batch_size if batch_size else self.model_spec.batch_size\n    # TODO(b/171449557): Upstream this to the parent class.\n    if len(train_data) < batch_size:\n      raise ValueError('The size of the train_data (%d) couldn\\'t be smaller '\n                       'than batch_size (%d). To solve this problem, set '\n                       'the batch_size smaller or increase the size of the '\n                       'train_data.' % (len(train_data), batch_size))\n    if validation_data and len(validation_data) < batch_size:\n      tf.compat.v1.logging.warn(\n          'The size of the validation_data (%d) is smaller than batch_size '\n          '(%d). Ignore the validation_data.' %\n          (len(validation_data), batch_size))\n      validation_data = None\n\n    with self.model_spec.ds_strategy.scope():\n      self.create_model()\n      train_ds, steps_per_epoch, _ = self._get_dataset_and_steps(\n          train_data, batch_size, is_training=True)\n      validation_ds, validation_steps, val_json_file = self._get_dataset_and_steps(\n          validation_data, batch_size, is_training=False)\n      return self.model_spec.train(self.model, train_ds, steps_per_epoch,\n                                   validation_ds, validation_steps, epochs,\n                                   batch_size, val_json_file)\n\n  def evaluate(self,\n               data: object_detector_dataloader.DataLoader,\n               batch_size: Optional[int] = None) -> Dict[str, float]:\n    \"\"\"Evaluates the model.\"\"\"\n    batch_size = batch_size if batch_size else self.model_spec.batch_size\n    # Not to drop the smaller batch to evaluate the whole dataset.\n    self.model_spec.config.drop_remainder = False\n    ds = data.gen_dataset(self.model_spec, batch_size, is_training=False)\n    steps = (len(data) + batch_size - 1) // batch_size\n    # TODO(b/171449557): Upstream this to the parent class.\n    if steps <= 0:\n      raise ValueError('The size of the validation_data (%d) couldn\\'t be '\n                       'smaller than batch_size (%d). To solve this problem, '\n                       'set the batch_size smaller or increase the size of the '\n                       'validation_data.' % (len(data), batch_size))\n\n    eval_metrics = self.model_spec.evaluate(self.model, ds, steps,\n                                            data.annotations_json_file)\n    # Set back drop_remainder=True since it must be True during training.\n    # Otherwise it will fail.\n    self.model_spec.config.drop_remainder = True\n    return eval_metrics\n\n  def evaluate_tflite(\n      self, tflite_filepath: str,\n      data: object_detector_dataloader.DataLoader) -> Dict[str, float]:\n    \"\"\"Evaluate the TFLite model.\"\"\"\n    ds = data.gen_dataset(self.model_spec, batch_size=1, is_training=False)\n    return self.model_spec.evaluate_tflite(tflite_filepath, ds, len(data),\n                                           data.annotations_json_file)\n\n  def _export_saved_model(self, saved_model_dir: str) -> None:\n    \"\"\"Saves the model to Tensorflow SavedModel.\"\"\"\n    self.model_spec.export_saved_model(self.model, saved_model_dir)\n\n  def _export_tflite(\n      self,\n      tflite_filepath: str,\n      quantization_config: configs.QuantizationConfigType = 'default',\n      with_metadata: bool = True,\n      export_metadata_json_file: bool = False) -> None:\n    \"\"\"Converts the retrained model to tflite format and saves it.\n\n    Args:\n      tflite_filepath: File path to save tflite model.\n      quantization_config: Configuration for post-training quantization. If\n        'default', sets the `quantization_config` by default according to\n        `self.model_spec`. If None, exports the float tflite model without\n        quantization.\n      with_metadata: Whether the output tflite model contains metadata.\n      export_metadata_json_file: Whether to export metadata in json file. If\n        True, export the metadata in the same directory as tflite model.Used\n        only if `with_metadata` is True.\n    \"\"\"\n    if quantization_config == 'default':\n      quantization_config = self.model_spec.get_default_quantization_config(\n          self.representative_data)\n\n    self.model_spec.export_tflite(self.model, tflite_filepath,\n                                  quantization_config)\n\n    if with_metadata:\n      with tempfile.TemporaryDirectory() as temp_dir:\n        tf.compat.v1.logging.info(\n            'Label file is inside the TFLite model with metadata.')\n        label_filepath = os.path.join(temp_dir, 'labelmap.txt')\n        self._export_labels(label_filepath)\n        writer = metadata_writer.MetadataWriter.create_for_inference(\n            writer_utils.load_file(tflite_filepath),\n            [self.model_spec.config.mean_rgb],\n            [self.model_spec.config.stddev_rgb], [label_filepath])\n        writer_utils.save_file(writer.populate(), tflite_filepath)\n\n        if export_metadata_json_file:\n          metadata_json = writer.get_populated_metadata_json()\n          export_json_path = os.path.splitext(tflite_filepath)[0] + '.json'\n          with open(export_json_path, 'w') as f:\n            f.write(metadata_json)\n\n  def _export_labels(self, label_filepath: str) -> None:\n    \"\"\"Export labels to label_filepath.\"\"\"\n    tf.compat.v1.logging.info('Saving labels in %s.', label_filepath)\n    num_classes = self.model_spec.config.num_classes\n    label_map = label_util.get_label_map(self.model_spec.config.label_map)\n    with tf.io.gfile.GFile(label_filepath, 'w') as f:\n      # Ignores label_map[0] that's the background. The labels in the label file\n      # for TFLite metadata should start from the actual labels without the\n      # background.\n      for i in range(num_classes):\n        label = label_map[i + 1] if i + 1 in label_map else '???'\n        f.write(label + '\\n')\n\n  @classmethod\n  def create(cls,\n             train_data: object_detector_dataloader.DataLoader,\n             model_spec: object_detector_spec.EfficientDetModelSpec,\n             validation_data: Optional[\n                 object_detector_dataloader.DataLoader] = None,\n             epochs: Optional[object_detector_dataloader.DataLoader] = None,\n             batch_size: Optional[int] = None,\n             train_whole_model: bool = False,\n             do_train: bool = True) -> T:\n    \"\"\"Loads data and train the model for object detection.\n\n    Args:\n      train_data: Training data.\n      model_spec: Specification for the model.\n      validation_data: Validation data. If None, skips validation process.\n      epochs: Number of epochs for training.\n      batch_size: Batch size for training.\n      train_whole_model: Boolean, False by default. If true, train the whole\n        model. Otherwise, only train the layers that are not match\n        `model_spec.config.var_freeze_expr`.\n      do_train: Whether to run training.\n\n    Returns:\n      An instance based on ObjectDetector.\n    \"\"\"\n    model_spec = ms.get(model_spec)\n    if epochs is not None:\n      model_spec.config.num_epochs = epochs\n    if batch_size is not None:\n      model_spec.config.batch_size = batch_size\n    if train_whole_model:\n      model_spec.config.var_freeze_expr = None\n    if compat.get_tf_behavior() not in model_spec.compat_tf_versions:\n      raise ValueError('Incompatible versions. Expect {}, but got {}.'.format(\n          model_spec.compat_tf_versions, compat.get_tf_behavior()))\n\n    object_detector = cls(model_spec, train_data.label_map, train_data)\n\n    if do_train:\n      tf.compat.v1.logging.info('Retraining the models...')\n      object_detector.train(train_data, validation_data, epochs, batch_size)\n    else:\n      object_detector.create_model()\n\n    return object_detector\n\n\n# Shortcut function.\ncreate = ObjectDetector.create\nmm_export('object_detector.create').export_constant(__name__, 'create')\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/object_detector_test.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport filecmp\nimport os\n\nfrom absl import logging\nimport tensorflow.compat.v2 as tf\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core import test_util\nfrom tensorflow_examples.lite.model_maker.core.data_util import object_detector_dataloader\nfrom tensorflow_examples.lite.model_maker.core.export_format import ExportFormat\nfrom tensorflow_examples.lite.model_maker.core.task import model_spec\nfrom tensorflow_examples.lite.model_maker.core.task import object_detector\n\n\nclass ObjectDetectorTest(tf.test.TestCase):\n\n  def testEfficientDetLite0(self):\n    # Gets model specification.\n    spec = model_spec.get('efficientdet_lite0')\n\n    # Prepare data.\n    images_dir, annotations_dir, label_map = test_util.create_pascal_voc(\n        self.get_temp_dir())\n    data = object_detector_dataloader.DataLoader.from_pascal_voc(\n        images_dir, annotations_dir, label_map)\n\n    # Train the model.\n    task = object_detector.create(data, spec, batch_size=1, epochs=1)\n    self.assertEqual(spec.config.num_classes, 2)\n\n    # Evaluate trained model\n    metrics = task.evaluate(data)\n    self.assertIsInstance(metrics, dict)\n    self.assertGreaterEqual(metrics['AP'], 0)\n\n    # Export the model to saved model.\n    output_path = os.path.join(self.get_temp_dir(), 'saved_model')\n    task.export(self.get_temp_dir(), export_format=ExportFormat.SAVED_MODEL)\n    self.assertTrue(os.path.isdir(output_path))\n    self.assertNotEqual(len(os.listdir(output_path)), 0)\n\n    # Export the model to the float TFLite model.\n    output_path = os.path.join(self.get_temp_dir(), 'float.tflite')\n    task.export(\n        self.get_temp_dir(),\n        tflite_filename='float.tflite',\n        quantization_config=None,\n        export_format=ExportFormat.TFLITE,\n        with_metadata=True,\n        export_metadata_json_file=True)\n    # Checks the sizes of the float32 TFLite model files in bytes.\n    model_size = 13476379\n    self.assertNear(os.path.getsize(output_path), model_size, 50000)\n\n    json_output_file = os.path.join(self.get_temp_dir(), 'float.json')\n    self.assertTrue(os.path.isfile(json_output_file))\n    self.assertGreater(os.path.getsize(json_output_file), 0)\n    expected_json_file = test_util.get_test_data_path(\n        'efficientdet_lite0_metadata.json')\n    self.assertTrue(filecmp.cmp(json_output_file, expected_json_file))\n\n    # Evaluate the TFLite model.\n    task.evaluate_tflite(output_path, data)\n    self.assertIsInstance(metrics, dict)\n    self.assertGreaterEqual(metrics['AP'], 0)\n\n    # Tests the default quantized model.\n    filename = 'model_quant.tflite'\n    output_path = os.path.join(self.get_temp_dir(), filename)\n    task.export(\n        self.get_temp_dir(),\n        tflite_filename=filename,\n        export_format=ExportFormat.TFLITE)\n    model_size = 4312187\n    err = model_size * 0.05\n    self.assertTrue(os.path.isfile(output_path))\n    self.assertNear(os.path.getsize(output_path), model_size, err)\n\n\nif __name__ == '__main__':\n  logging.set_verbosity(logging.ERROR)\n  # Load compressed models from tensorflow_hub\n  os.environ['TFHUB_MODEL_LOAD_FORMAT'] = 'COMPRESSED'\n  compat.setup_tf_behavior(tf_version=2)\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/question_answer.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"APIs to train a model that can answer questions based on a predefined text.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nimport tempfile\n\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core.api import mm_export\nfrom tensorflow_examples.lite.model_maker.core.export_format import ExportFormat\nfrom tensorflow_examples.lite.model_maker.core.task import custom_model\nfrom tensorflow_examples.lite.model_maker.core.task import model_spec as ms\nfrom tensorflow_examples.lite.model_maker.core.task import model_util\nfrom tensorflow_examples.lite.model_maker.core.task.metadata_writers.bert.question_answerer import metadata_writer_for_bert_question_answerer as metadata_writer\n\n\ndef _get_model_info(model_spec, vocab_file):\n  \"\"\"Gets the specific info for the question answer model.\"\"\"\n  return metadata_writer.QuestionAnswererInfo(\n      name=model_spec.name + ' Question and Answerer',\n      version='v1',\n      description=metadata_writer.DEFAULT_DESCRIPTION,\n      input_names=metadata_writer.bert_qa_inputs(\n          ids_name=model_spec.tflite_input_name['ids'],\n          mask_name=model_spec.tflite_input_name['mask'],\n          segment_ids_name=model_spec.tflite_input_name['segment_ids']),\n      output_names=metadata_writer.bert_qa_outputs(\n          start_logits_name=model_spec.tflite_output_name['start_logits'],\n          end_logits_name=model_spec.tflite_output_name['end_logits']),\n      tokenizer_type=metadata_writer.Tokenizer.BERT_TOKENIZER,\n      vocab_file=vocab_file)\n\n\n@mm_export('question_answer.QuestionAnswer')\nclass QuestionAnswer(custom_model.CustomModel):\n  \"\"\"QuestionAnswer class for inference and exporting to tflite.\"\"\"\n\n  DEFAULT_EXPORT_FORMAT = (ExportFormat.TFLITE, ExportFormat.VOCAB)\n  ALLOWED_EXPORT_FORMAT = (ExportFormat.TFLITE, ExportFormat.VOCAB,\n                           ExportFormat.SAVED_MODEL)\n\n  def train(self,\n            train_data,\n            epochs=None,\n            batch_size=None,\n            steps_per_epoch=None):\n    \"\"\"Feeds the training data for training.\"\"\"\n    if batch_size is None:\n      batch_size = self.model_spec.default_batch_size\n\n    if len(train_data) < batch_size:\n      raise ValueError('The size of the train_data (%d) couldn\\'t be smaller '\n                       'than batch_size (%d). To solve this problem, set '\n                       'the batch_size smaller or increase the size of the '\n                       'train_data.' % (len(train_data), batch_size))\n\n    train_ds = train_data.gen_dataset(batch_size, is_training=True)\n    steps_per_epoch = model_util.get_steps_per_epoch(steps_per_epoch,\n                                                     batch_size, train_data)\n    if steps_per_epoch is not None:\n      train_ds = train_ds.take(steps_per_epoch)\n    self.model = self.model_spec.train(\n        train_ds=train_ds, epochs=epochs, steps_per_epoch=steps_per_epoch)\n\n    return self.model\n\n  def create_model(self):\n    self.model = self.model_spec.create_model()\n\n  def evaluate(self,\n               data,\n               max_answer_length=30,\n               null_score_diff_threshold=0.0,\n               verbose_logging=False,\n               output_dir=None):\n    \"\"\"Evaluate the model.\n\n    Args:\n      data: Data to be evaluated.\n      max_answer_length: The maximum length of an answer that can be generated.\n        This is needed because the start and end predictions are not conditioned\n        on one another.\n      null_score_diff_threshold: If null_score - best_non_null is greater than\n        the threshold, predict null. This is only used for SQuAD v2.\n      verbose_logging: If true, all of the warnings related to data processing\n        will be printed. A number of warnings are expected for a normal SQuAD\n        evaluation.\n      output_dir: The output directory to save output to json files:\n        predictions.json, nbest_predictions.json, null_odds.json. If None, skip\n        saving to json files.\n\n    Returns:\n      A dict contains two metrics: Exact match rate and F1 score.\n    \"\"\"\n    predict_batch_size = self.model_spec.predict_batch_size\n    ds = data.gen_dataset(predict_batch_size, is_training=False)\n    num_steps = int(len(data) / predict_batch_size)\n    return self.model_spec.evaluate(\n        self.model, None, ds, num_steps, data.examples, data.features,\n        data.squad_file, data.version_2_with_negative, max_answer_length,\n        null_score_diff_threshold, verbose_logging, output_dir)\n\n  def evaluate_tflite(self,\n                      tflite_filepath,\n                      data,\n                      max_answer_length=30,\n                      null_score_diff_threshold=0.0,\n                      verbose_logging=False,\n                      output_dir=None):\n    \"\"\"Evaluate the model.\n\n    Args:\n      tflite_filepath: File path to the TFLite model.\n      data: Data to be evaluated.\n      max_answer_length: The maximum length of an answer that can be generated.\n        This is needed because the start and end predictions are not conditioned\n        on one another.\n      null_score_diff_threshold: If null_score - best_non_null is greater than\n        the threshold, predict null. This is only used for SQuAD v2.\n      verbose_logging: If true, all of the warnings related to data processing\n        will be printed. A number of warnings are expected for a normal SQuAD\n        evaluation.\n      output_dir: The output directory to save output to json files:\n        predictions.json, nbest_predictions.json, null_odds.json. If None, skip\n        saving to json files.\n\n    Returns:\n      A dict contains two metrics: Exact match rate and F1 score.\n    \"\"\"\n    ds = data.gen_dataset(batch_size=1, is_training=False)\n    return self.model_spec.evaluate(\n        None, tflite_filepath, ds, len(data), data.examples, data.features,\n        data.squad_file, data.version_2_with_negative, max_answer_length,\n        null_score_diff_threshold, verbose_logging, output_dir)\n\n  def _export_tflite(self,\n                     tflite_filepath,\n                     quantization_config='default',\n                     with_metadata=True,\n                     export_metadata_json_file=False):\n    \"\"\"Converts the retrained model to tflite format and saves it.\n\n    Args:\n      tflite_filepath: File path to save tflite model.\n      quantization_config: Configuration for post-training quantization. If\n        'default', sets the `quantization_config` by default according to\n        `self.model_spec`. If None, exports the float tflite model without\n        quantization.\n      with_metadata: Whether the output tflite model contains metadata.\n      export_metadata_json_file: Whether to export metadata in json file. If\n        True, export the metadata in the same directory as tflite model.Used\n        only if `with_metadata` is True.\n    \"\"\"\n    if quantization_config == 'default':\n      quantization_config = self.model_spec.get_default_quantization_config()\n\n    # Sets batch size from None to 1 when converting to tflite.\n    model_util.set_batch_size(self.model, batch_size=1)\n    model_util.export_tflite(self.model, tflite_filepath, quantization_config,\n                             self.model_spec.convert_from_saved_model_tf2)\n    # Sets batch size back to None to support retraining later.\n    model_util.set_batch_size(self.model, batch_size=None)\n\n    if with_metadata:\n      with tempfile.TemporaryDirectory() as temp_dir:\n        tf.compat.v1.logging.info(\n            'Vocab file is inside the TFLite model with metadata.')\n        vocab_filepath = os.path.join(temp_dir, 'vocab.txt')\n        self.model_spec.save_vocab(vocab_filepath)\n        model_info = _get_model_info(self.model_spec, vocab_filepath)\n        export_dir = os.path.dirname(tflite_filepath)\n        populator = metadata_writer.MetadataPopulatorForBertQuestionAndAnswer(\n            tflite_filepath, export_dir, model_info)\n        populator.populate(export_metadata_json_file)\n\n  @classmethod\n  def create(cls,\n             train_data,\n             model_spec,\n             batch_size=None,\n             epochs=2,\n             steps_per_epoch=None,\n             shuffle=False,\n             do_train=True):\n    \"\"\"Loads data and train the model for question answer.\n\n    Args:\n      train_data: Training data.\n      model_spec: Specification for the model.\n      batch_size: Batch size for training.\n      epochs: Number of epochs for training.\n      steps_per_epoch: Integer or None. Total number of steps (batches of\n        samples) before declaring one epoch finished and starting the next\n        epoch. If `steps_per_epoch` is None, the epoch will run until the input\n        dataset is exhausted.\n      shuffle: Whether the data should be shuffled.\n      do_train: Whether to run training.\n\n    Returns:\n      An instance based on QuestionAnswer.\n    \"\"\"\n    model_spec = ms.get(model_spec)\n    if compat.get_tf_behavior() not in model_spec.compat_tf_versions:\n      raise ValueError('Incompatible versions. Expect {}, but got {}.'.format(\n          model_spec.compat_tf_versions, compat.get_tf_behavior()))\n\n    model = cls(model_spec, shuffle=shuffle)\n\n    if do_train:\n      tf.compat.v1.logging.info('Retraining the models...')\n      model.train(train_data, epochs, batch_size, steps_per_epoch)\n    else:\n      model.create_model()\n\n    return model\n\n\n# Shortcut function.\ncreate = QuestionAnswer.create\nmm_export('question_answer.create').export_constant(__name__, 'create')\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/question_answer_test.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport filecmp\nimport os\n\nfrom absl.testing import parameterized\nimport numpy as np\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core import test_util\nfrom tensorflow_examples.lite.model_maker.core.data_util import text_dataloader\nfrom tensorflow_examples.lite.model_maker.core.export_format import ExportFormat\nfrom tensorflow_examples.lite.model_maker.core.task import model_spec as ms\nfrom tensorflow_examples.lite.model_maker.core.task import question_answer\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import text_spec\n\n\ndef _get_data(model_spec, version):\n  path = test_util.get_test_data_path('squad_testdata')\n  train_data_path = os.path.join(path, 'train-v%s.json' % version)\n  validation_data_path = os.path.join(path, 'dev-v%s.json' % version)\n  version_2_with_negative = version.startswith('2')\n  train_data = text_dataloader.QuestionAnswerDataLoader.from_squad(\n      train_data_path,\n      model_spec,\n      is_training=True,\n      version_2_with_negative=version_2_with_negative)\n  validation_data = text_dataloader.QuestionAnswerDataLoader.from_squad(\n      validation_data_path,\n      model_spec,\n      is_training=False,\n      version_2_with_negative=version_2_with_negative)\n  return train_data, validation_data\n\n\nclass QuestionAnswerTest(tf.test.TestCase, parameterized.TestCase):\n\n  @test_util.test_in_tf_1\n  def test_bert_model_v1_incompatible(self):\n    with self.assertRaisesRegex(ValueError, 'Incompatible versions'):\n      text_spec.BertQAModelSpec(trainable=False)\n\n  @test_util.test_in_tf_2\n  def test_bert_model(self):\n    # Only test squad1.1 since it takes too long time for this.\n    version = '1.1'\n    model_spec = text_spec.BertQAModelSpec(\n        trainable=False, predict_batch_size=1)\n    train_data, validation_data = _get_data(model_spec, version)\n    model = question_answer.create(\n        train_data, model_spec=model_spec, epochs=1, batch_size=1)\n    self._test_f1_score(model, validation_data, 0.0)\n    self._test_export_vocab(model)\n    self._test_export_to_tflite(model, validation_data)\n    self._test_export_to_saved_model(model)\n    # Comments this due to Out of Memory Error.\n    # self._test_model_without_training(model_spec, train_data, validation_data)\n\n  def _test_model_without_training(self, model_spec, train_data,\n                                   validation_data):\n    # Test without retraining.\n    model = question_answer.create(\n        train_data, model_spec=model_spec, do_train=False)\n    self._test_f1_score(model, validation_data, 0.0)\n    self._test_export_to_tflite(model, validation_data)\n\n  @parameterized.parameters(\n      ('mobilebert_qa', False),\n      ('mobilebert_qa_squad', True),\n  )\n  @test_util.test_in_tf_2\n  def test_mobilebert_model(self, spec, trainable):\n    # Only test squad1.1 since it takes too long time for this.\n    version = '1.1'\n    model_spec = ms.get(spec)\n    model_spec.trainable = trainable\n    model_spec.predict_batch_size = 1\n    train_data, validation_data = _get_data(model_spec, version)\n    model = question_answer.create(\n        train_data, model_spec=model_spec, epochs=1, batch_size=1)\n    self._test_f1_score(model, validation_data, 0.0)\n    self._test_export_to_tflite(model, validation_data, atol=1e-02)\n\n  def _test_f1_score(self, model, validation_data, threshold):\n    metric = model.evaluate(validation_data)\n    self.assertGreaterEqual(metric['final_f1'], threshold)\n\n  def _test_export_vocab(self, model):\n    vocab_output_file = os.path.join(self.get_temp_dir(), 'vocab.txt')\n    model.export(self.get_temp_dir(), export_format=ExportFormat.VOCAB)\n\n    self.assertTrue(os.path.isfile(vocab_output_file))\n    self.assertGreater(os.path.getsize(vocab_output_file), 0)\n\n  def _test_export_to_tflite(self,\n                             model,\n                             validation_data,\n                             threshold=0.0,\n                             atol=1e-04,\n                             expected_json_file=None):\n    tflite_output_file = os.path.join(self.get_temp_dir(), 'model.tflite')\n    model.export(\n        self.get_temp_dir(),\n        export_format=ExportFormat.TFLITE,\n        quantization_config=None,\n        export_metadata_json_file=expected_json_file is not None)\n\n    self.assertTrue(os.path.isfile(tflite_output_file))\n    self.assertGreater(os.path.getsize(tflite_output_file), 0)\n\n    metric = model.evaluate_tflite(tflite_output_file, validation_data)\n    self.assertGreaterEqual(metric['final_f1'], threshold)\n\n    spec = model.model_spec\n    input_word_ids = np.random.randint(\n        low=0,\n        high=len(spec.tokenizer.vocab),\n        size=(1, spec.seq_len),\n        dtype=np.int32)\n    input_mask = np.random.randint(\n        low=0, high=2, size=(1, spec.seq_len), dtype=np.int32)\n    input_type_ids = np.random.randint(\n        low=0, high=2, size=(1, spec.seq_len), dtype=np.int32)\n    random_inputs = (input_word_ids, input_mask, input_type_ids)\n\n    self.assertTrue(\n        test_util.is_same_output(\n            tflite_output_file,\n            model.model,\n            random_inputs,\n            model.model_spec,\n            atol=atol))\n\n    if expected_json_file is not None:\n      json_output_file = os.path.join(self.get_temp_dir(), 'model.json')\n      self.assertTrue(os.path.isfile(json_output_file))\n      self.assertGreater(os.path.getsize(json_output_file), 0)\n      expected_json_file = test_util.get_test_data_path(expected_json_file)\n      self.assertTrue(filecmp.cmp(json_output_file, expected_json_file))\n\n  def _test_export_to_saved_model(self, model):\n    save_model_output_path = os.path.join(self.get_temp_dir(), 'saved_model')\n    model.export(self.get_temp_dir(), export_format=ExportFormat.SAVED_MODEL)\n\n    self.assertTrue(os.path.isdir(save_model_output_path))\n    self.assertNotEmpty(os.listdir(save_model_output_path))\n\n\nif __name__ == '__main__':\n  # Load compressed models from tensorflow_hub\n  os.environ['TFHUB_MODEL_LOAD_FORMAT'] = 'COMPRESSED'\n  compat.setup_tf_behavior(tf_version=2)\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/question_answer_v1_test.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core.task import question_answer_test\n\n\nclass QuestionAnswerV1Test(question_answer_test.QuestionAnswerTest):\n  \"\"\"Share text tests of the base class, but in tf v1 behavior.\"\"\"\n\n\nif __name__ == '__main__':\n  compat.setup_tf_behavior(tf_version=1)\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/recommendation.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"APIs to train an on-device recommendation model.\"\"\"\nimport collections\nimport tempfile\n\nimport numpy as np\nfrom packaging import version\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.core.api import mm_export\nfrom tensorflow_examples.lite.model_maker.core.data_util import data_util\nfrom tensorflow_examples.lite.model_maker.core.data_util import recommendation_config\nfrom tensorflow_examples.lite.model_maker.core.export_format import ExportFormat\nfrom tensorflow_examples.lite.model_maker.core.task import custom_model\nfrom tensorflow_examples.lite.model_maker.core.task import model_util\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import recommendation_spec\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import input_pipeline\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import metrics as _metrics\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import recommendation_model_launcher as _launcher\n\n\n@mm_export('recommendation.Recommendation')\nclass Recommendation(custom_model.CustomModel):\n  \"\"\"Recommendation task class.\"\"\"\n\n  DEFAULT_EXPORT_FORMAT = (ExportFormat.TFLITE,)\n  ALLOWED_EXPORT_FORMAT = (ExportFormat.LABEL, ExportFormat.TFLITE,\n                           ExportFormat.SAVED_MODEL)\n\n  # ID = 0 means a placeholder to OOV. Used for padding.\n  OOV_ID = 0\n\n  def __init__(self,\n               model_spec,\n               model_dir,\n               shuffle=True,\n               learning_rate=0.1,\n               gradient_clip_norm=1.0):\n    \"\"\"Init recommendation model.\n\n    Args:\n      model_spec: recommendation model spec.\n      model_dir: str, path to export model checkpoints and summaries.\n      shuffle: boolean, whether the training data should be shuffled.\n      learning_rate: float, learning rate.\n      gradient_clip_norm: float, clip threshold (<= 0 meaning no clip).\n    \"\"\"\n    if not isinstance(model_spec, recommendation_spec.RecommendationSpec):\n      raise ValueError(\n          'Expect RecommendationSpec but got model_spec: {}'.format(model_spec))\n    self._model_dir = model_dir\n    self._learning_rate = learning_rate\n    self._gradient_clip_norm = gradient_clip_norm\n    super(Recommendation, self).__init__(model_spec, shuffle=shuffle)\n\n  @property\n  def input_spec(self) -> recommendation_config.InputSpec:\n    return self.model_spec.input_spec\n\n  @property\n  def model_hparams(self) -> recommendation_config.ModelHParams:\n    return self.model_spec.model_hparams\n\n  def create_model(self, do_train=True):\n    \"\"\"Creates a model.\n\n    Args:\n      do_train: boolean. Whether to train the model.\n\n    Returns:\n      Keras model.\n    \"\"\"\n    self.model = self.model_spec.create_model()\n    if do_train:\n      _launcher.compile_model(self.model, self.model_hparams.eval_top_k,\n                              self._learning_rate, self._gradient_clip_norm)\n\n  def train(self,\n            train_data,\n            validation_data=None,\n            batch_size=16,\n            steps_per_epoch=100,\n            epochs=1):\n    \"\"\"Feeds the training data for training.\n\n    Args:\n      train_data: Training dataset.\n      validation_data: Validation data. If None, skips validation process.\n      batch_size: int, the batch size.\n      steps_per_epoch: int, the step of each epoch.\n      epochs: int, number of epochs.\n\n    Returns:\n      History from model.fit().\n    \"\"\"\n    batch_size = batch_size if batch_size else self.model_spec.batch_size\n\n    train_ds = train_data.gen_dataset(\n        batch_size, is_training=True, shuffle=self.shuffle)\n    if validation_data:\n      validation_ds = validation_data.gen_dataset(batch_size, is_training=False)\n    else:\n      validation_ds = None\n\n    self.create_model(do_train=True)\n    history = self.model.fit(\n        x=train_ds,\n        validation_data=validation_ds,\n        steps_per_epoch=steps_per_epoch,\n        epochs=epochs,\n        callbacks=self._keras_callbacks(self._model_dir))\n    tf.get_logger().info(history)\n    return history\n\n  def evaluate(self, data, batch_size=10):\n    \"\"\"Evaluate the model.\n\n    Args:\n      data: Evaluation data.\n      batch_size: int, batch size for evaluation.\n\n    Returns:\n      History from model.evaluate().\n    \"\"\"\n    batch_size = batch_size if batch_size else self.model_spec.batch_size\n    eval_ds = data.gen_dataset(batch_size, is_training=False)\n    history = self.model.evaluate(eval_ds)\n    tf.get_logger().info(history)\n    return history\n\n  def _keras_callbacks(self, model_dir):\n    \"\"\"Returns a list of default keras callbacks for `model.fit`.\"\"\"\n    return _launcher.get_callbacks(self.model, model_dir)\n\n  def _get_serve_fn(self, keras_model):\n    \"\"\"Gets serve fn for exporting model.\"\"\"\n    input_specs = input_pipeline.get_serving_input_specs(self.input_spec)\n    return keras_model.serve.get_concrete_function(**input_specs)\n\n  def _export_tflite(self, tflite_filepath):\n    \"\"\"Exports tflite model.\"\"\"\n    serve_fn = self._get_serve_fn(self.model)\n    # Providing trackable objects is now recommended since it will make the\n    # concrete function conversion API be based on the new SavedModel importer,\n    # which will enable new TensorFlow Lite features including variable support,\n    # resources and variant tensor, and signature concept.\n    if version.parse(tf.__version__) >= version.parse('2.7'):\n      converter = tf.lite.TFLiteConverter.from_concrete_functions([serve_fn],\n                                                                  self.model)\n    else:\n      converter = tf.lite.TFLiteConverter.from_concrete_functions([serve_fn])\n    tflite_model = converter.convert()\n    with tf.io.gfile.GFile(tflite_filepath, 'wb') as f:\n      f.write(tflite_model)\n\n  def _export_saved_model(self, filepath):\n    serve_fn = self._get_serve_fn(self.model)\n    signatures = {tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY: serve_fn}\n    tf.saved_model.save(self.model, export_dir=filepath, signatures=signatures)\n\n  def evaluate_tflite(self, tflite_filepath, data):\n    \"\"\"Evaluates the tflite model.\n\n    The data is padded to required length, and multiple metrics are evaluated.\n\n    Args:\n      tflite_filepath: File path to the TFLite model.\n      data: Data to be evaluated.\n\n    Returns:\n      Dict of (metric, value), evaluation result of TFLite model.\n    \"\"\"\n    label_name = self.input_spec.label_feature.feature_name\n    lite_runner = model_util.get_lite_runner(tflite_filepath, self.model_spec)\n    ds = data.gen_dataset(batch_size=1, is_training=False)\n\n    max_output_size = data.max_vocab_id + 1  # +1 because 0 is reserved for OOV.\n    eval_top_k = self.model_hparams.eval_top_k\n    metrics = [\n        _metrics.GlobalRecall(top_k=k, name=f'Global_Recall/Recall_{k}')\n        for k in eval_top_k\n    ]\n    for feature, y_true in data_util.generate_elements(ds):\n      feature.pop(label_name)\n      x = feature\n      ids, scores = lite_runner.run(x)\n\n      # y_true: shape [1, 1]\n      # y_pred: shape [1, max_output_size]; fill only scores with top-k ids.\n      y_pred = np.zeros([1, max_output_size])\n      for i, score in zip(ids, scores):\n        if i in data.vocab:  # Only set if id is in vocab.\n          y_pred[0, i] = score\n\n      # Update metrics.\n      for m in metrics:\n        m.update_state(y_true, y_pred)\n    result = collections.OrderedDict([(m.name, m.result()) for m in metrics])\n    return result\n\n  @classmethod\n  def create(cls,\n             train_data,\n             model_spec: recommendation_spec.RecommendationSpec,\n             model_dir: str = None,\n             validation_data=None,\n             batch_size: int = 16,\n             steps_per_epoch: int = 10000,\n             epochs: int = 1,\n             learning_rate: float = 0.1,\n             gradient_clip_norm: float = 1.0,\n             shuffle: bool = True,\n             do_train: bool = True):\n    \"\"\"Loads data and train the model for recommendation.\n\n    Args:\n      train_data: Training data.\n      model_spec: ModelSpec, Specification for the model.\n      model_dir: str, path to export model checkpoints and summaries.\n      validation_data: Validation data.\n      batch_size: Batch size for training.\n      steps_per_epoch: int, Number of step per epoch.\n      epochs: int, Number of epochs for training.\n      learning_rate: float, learning rate.\n      gradient_clip_norm: float, clip threshold (<= 0 meaning no clip).\n      shuffle: boolean, whether the training data should be shuffled.\n      do_train: boolean, whether to run training.\n\n    Returns:\n      An instance based on Recommendation.\n    \"\"\"\n    # Use model_dir or a temp folder to store intermediate checkpoints, etc.\n    if model_dir is None:\n      model_dir = tempfile.mkdtemp()\n\n    recommendation = cls(\n        model_spec,\n        model_dir=model_dir,\n        shuffle=shuffle,\n        learning_rate=learning_rate,\n        gradient_clip_norm=gradient_clip_norm)\n\n    if do_train:\n      tf.compat.v1.logging.info('Training recommendation model...')\n      recommendation.train(\n          train_data,\n          validation_data,\n          batch_size=batch_size,\n          steps_per_epoch=steps_per_epoch,\n          epochs=epochs)\n    else:\n      recommendation.create_model(do_train=False)\n    return recommendation\n\n\n# Shortcut function.\ncreate = Recommendation.create\nmm_export('recommendation.create').export_constant(__name__, 'create')\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/recommendation_test.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Tests for recommendation task.\"\"\"\nimport os\n\nfrom absl.testing import parameterized\nimport tensorflow.compat.v2 as tf\n\nfrom tensorflow_examples.lite.model_maker.core.data_util import recommendation_dataloader as _dl\nfrom tensorflow_examples.lite.model_maker.core.data_util import recommendation_testutil as _testutil\nfrom tensorflow_examples.lite.model_maker.core.export_format import ExportFormat\nfrom tensorflow_examples.lite.model_maker.core.task import model_spec as ms\nfrom tensorflow_examples.lite.model_maker.core.task import recommendation\n\n\nclass RecommendationTest(parameterized.TestCase, tf.test.TestCase):\n\n  def setUp(self):\n    super().setUp()\n    _testutil.setup_fake_testdata(self)\n    self.input_spec = _testutil.get_input_spec()\n    self.model_hparams = _testutil.get_model_hparams()\n    self.train_loader = _dl.RecommendationDataLoader.from_movielens(\n        self.dataset_dir, 'train', self.input_spec)\n    self.test_loader = _dl.RecommendationDataLoader.from_movielens(\n        self.dataset_dir, 'test', self.input_spec)\n\n  @parameterized.parameters(\n      ('bow'),\n      ('cnn'),\n      ('lstm'),\n  )\n  def test_create(self, encoder_type):\n    model_dir = os.path.join(self.test_tempdir, 'recommendation_create')\n    input_spec = _testutil.get_input_spec(encoder_type)\n\n    model_spec = ms.get(\n        'recommendation',\n        input_spec=input_spec,\n        model_hparams=self.model_hparams)\n    model = recommendation.create(\n        self.train_loader,\n        model_spec=model_spec,\n        model_dir=model_dir,\n        steps_per_epoch=1)\n    self.assertIsNotNone(model.model)\n\n  def test_evaluate(self):\n    model_dir = os.path.join(self.test_tempdir, 'recommendation_evaluate')\n    model_spec = ms.get(\n        'recommendation',\n        input_spec=self.input_spec,\n        model_hparams=self.model_hparams)\n    model = recommendation.create(\n        self.train_loader,\n        model_spec=model_spec,\n        model_dir=model_dir,\n        steps_per_epoch=1)\n    history = model.evaluate(self.test_loader)\n    self.assertIsInstance(history, list)\n    self.assertTrue(history)  # Non-empty list.\n\n  def test_export_and_evaluation(self):\n    model_dir = os.path.join(self.test_tempdir, 'recommendation_export')\n    model_spec = ms.get(\n        'recommendation',\n        input_spec=self.input_spec,\n        model_hparams=self.model_hparams)\n    model = recommendation.create(\n        self.train_loader,\n        model_spec=model_spec,\n        model_dir=model_dir,\n        steps_per_epoch=1)\n    export_format = [\n        ExportFormat.TFLITE,\n        ExportFormat.SAVED_MODEL,\n    ]\n    model.export(model_dir, export_format=export_format)\n    # Expect tflite file.\n    expected_tflite = os.path.join(model_dir, 'model.tflite')\n    self.assertTrue(os.path.exists(expected_tflite))\n    self.assertGreater(os.path.getsize(expected_tflite), 0)\n\n    # Expect saved model.\n    expected_saved_model = os.path.join(model_dir, 'saved_model',\n                                        'saved_model.pb')\n    self.assertTrue(os.path.exists(expected_saved_model))\n    self.assertGreater(os.path.getsize(expected_saved_model), 0)\n\n    # Evaluate tflite model.\n    self._test_evaluate_tflite(model, expected_tflite)\n\n  def _test_evaluate_tflite(self, model, tflite_filepath):\n    result = model.evaluate_tflite(tflite_filepath, self.test_loader)\n    self.assertIsInstance(result, dict)\n    self.assertTrue(result)  # Not empty.\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/searcher.py",
    "content": "# Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"APIs to train a model that can search Searcher Task.\"\"\"\n\nimport dataclasses\nimport enum\nimport logging\nimport os\nimport tempfile\nfrom typing import AnyStr, List, Optional\n\nimport flatbuffers\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core.api.api_util import mm_export\nfrom tensorflow_lite_support.metadata import metadata_schema_py_generated as _metadata_fb\nfrom tensorflow_lite_support.metadata import schema_py_generated as _schema_fb\nfrom tensorflow_lite_support.metadata.python import metadata as _metadata\nfrom tensorflow_examples.lite.model_maker.core.data_util import searcher_dataloader\nfrom tensorflow_examples.lite.model_maker.core.utils import ondevice_scann_builder\nfrom tensorflow_examples.lite.model_maker.core.utils import scann_converter\n\n# Expected index of the response encoding output tensor in Universal Sentence\n# Encoder models.\n_USE_RESPONSE_ENCODING_INDEX = 1\n\n\n@mm_export(\"searcher.ExportFormat\")\n@enum.unique\nclass ExportFormat(enum.Enum):\n  TFLITE = \"TFLITE\"\n  SCANN_INDEX_FILE = \"SCANN_INDEX_FILE\"\n\n\n@mm_export(\"searcher.Tree\")\n@dataclasses.dataclass\nclass Tree:\n  \"\"\"K-Means partitioning tree configuration.\n\n  In ScaNN, we use single layer K-Means tree to partition the database (index)\n  as a way to reduce search space.\n\n  Attributes:\n    num_leaves: How many leaves (partitions) to have on the K-Means tree. In\n      general, a good starting point would be the square root of the database\n      size.\n    num_leaves_to_search: During inference ScaNN will compare the query vector\n      against all the partition centroids and select the closest\n      `num_leaves_to_search` ones to search in. The more leaves to search, the\n      better the retrieval quality, and higher computational cost.\n    training_sample_size: How many database embeddings to sample for the K-Means\n      training. Generally, you want to use a large enough sample of the database\n      to train K-Means so that it's representative enough. However, large sample\n      can also lead to longer training time. A good starting value would be\n      100k, or the whole dataset if it's smaller than that.\n    min_partition_size: Smallest allowable cluster size. Any clusters smaller\n      than this will be removed, and its data points will be merged with other\n      clusters. Recommended to be 1/10 of average cluster size (size of database\n      divided by `num_leaves`)\n    training_iterations: How many itrations to train K-Means.\n    spherical: If true, L2 normalize the K-Means centroids.\n    quantize_centroids: If true, quantize centroids to int8.\n    random_init: If true, use random init. Otherwise use K-Means++.\n  \"\"\"\n  num_leaves: int\n  num_leaves_to_search: int\n  training_sample_size: int = 100000\n  min_partition_size: int = 50\n  training_iterations: int = 12\n  spherical: bool = False\n  quantize_centroids: bool = False\n  random_init: bool = True\n\n\n@mm_export(\"searcher.ScoreAH\")\n@dataclasses.dataclass\nclass ScoreAH:\n  \"\"\"Product Quantization (PQ) based in-partition scoring configuration.\n\n  In ScaNN we use PQ to compress the database embeddings, but not the query\n  embedding. We called it Asymmetric Hashing. See\n  https://research.google/pubs/pub41694/\n\n  Attributes:\n    dimensions_per_block: How many dimensions in each PQ block. If the embedding\n      vector dimensionality is a multiple of this value, there will be\n      `number_of_dimensions / dimensions_per_block` PQ blocks. Otherwise, the\n      last block will be the remainder. For example, if a vector has 12\n      dimensions, and `dimensions_per_block` is 2, then there will be 6\n      2-dimension blocks. However, if the vector has 13 dimensions and\n      `dimensions_per_block` is still 2, there will be 6 2-dimension blocks and\n      one 1-dimension block.\n    anisotropic_quantization_threshold: If this value is set, we will penalize\n      the quantization error that's parallel to the original vector differently\n      than the orthogonal error. A generally recommended value for this\n      parameter would be 0.2. For more details, please look at ScaNN's 2020 ICML\n      paper https://arxiv.org/abs/1908.10396 and the Google AI Blog post\n      https://ai.googleblog.com/2020/07/announcing-scann-efficient-vector.html\n    training_sample_size: How many database points to sample for training the\n      K-Means for PQ centers. A good starting value would be 100k or the whole\n      dataset if it's smaller than that.\n    training_iterations: How many iterations to run K-Means for PQ.\n  \"\"\"\n  dimensions_per_block: int\n  anisotropic_quantization_threshold: float = float(\"nan\")\n  training_sample_size: int = 100000\n\n  training_iterations: int = 10\n\n\n@mm_export(\"searcher.ScoreBruteForce\")\n@dataclasses.dataclass\nclass ScoreBruteForce:\n  \"\"\"Bruce force in-partition scoring configuration.\n\n  There'll be no compression or quantization applied to the database\n  embeddings or query embeddings.\n  \"\"\"\n\n\n@mm_export(\"searcher.ScaNNOptions\")\n@dataclasses.dataclass\nclass ScaNNOptions:\n  \"\"\"Options to build ScaNN.\n\n  ScaNN\n  (https://ai.googleblog.com/2020/07/announcing-scann-efficient-vector.html) is\n  a highly efficient and scalable vector nearest neighbor retrieval\n  library from Google Research. We use ScaNN to build the on-device search\n  index, and do on-device retrieval with a simplified implementation.\n  TODO(b/231134703) Add a link to the README\n\n  Attributes:\n    distance_measure: How to compute the distance. Allowed values are\n      'dot_product' and 'squared_l2'. Please note that when distance is\n      'dot_product', we actually compute the negative dot product between query\n      and database vectors, to preserve the notion that \"smaller is closer\".\n    tree: Configure partitioning. If not set, no partitioning is performed.\n    score_ah: Configure asymmetric hashing. Must defined this or\n      `score_brute_force`.\n    score_brute_force: Configure bruce force. Must defined this or `score_ah`.\n  \"\"\"\n  distance_measure: str\n  tree: Optional[Tree] = None\n  score_ah: Optional[ScoreAH] = None\n  score_brute_force: Optional[ScoreBruteForce] = None\n\n\n@mm_export(\"searcher.Searcher\")\nclass Searcher(object):\n  \"\"\"Creates the similarity search model with ScaNN.\"\"\"\n\n  def __init__(self,\n               serialized_scann_path: str,\n               metadata: List[AnyStr],\n               embedder_path: Optional[str] = None) -> None:\n    \"\"\"Initializes the Searcher object.\n\n    Args:\n      serialized_scann_path: Path to the dir that contains the ScaNN's\n        artifacts.\n      metadata: The metadata for each of the embeddings in the database. Passed\n        in the same order as the embeddings in ScaNN.\n      embedder_path: Path to the TFLite Embedder model file.\n    \"\"\"\n    self._serialized_scann_path = serialized_scann_path\n    self._metadata = metadata\n    self._embedder_path = embedder_path\n\n  @classmethod\n  def create_from_server_scann(\n      cls,\n      serialized_scann_path: str,\n      metadata: List[AnyStr],\n      embedder_path: Optional[str] = None) -> \"Searcher\":\n    \"\"\"Creates the instance from the serialized serving scann directory.\n\n    Args:\n      serialized_scann_path: Path to the dir that contains the ScaNN's\n        artifacts.\n      metadata: The metadata for each of the embeddings in the database. Passed\n        in the same order as the embeddings in ScaNN.\n      embedder_path: Path to the TFLite Embedder model file.\n\n    Returns:\n      A Searcher instance.\n    \"\"\"\n    return cls(serialized_scann_path, metadata, embedder_path)\n\n  @classmethod\n  def create_from_data(cls,\n                       data: searcher_dataloader.DataLoader,\n                       scann_options: ScaNNOptions,\n                       cache_dir: Optional[str] = None) -> \"Searcher\":\n    \"\"\"\"Creates the instance from data.\n\n    Args:\n      data: Data used to create scann.\n      scann_options: Options to build the ScaNN index file.\n      cache_dir: The cache directory to save serialized ScaNN and/or the tflite\n        model. When cache_dir is not set, a temporary folder will be created and\n        will **not** be removed automatically which makes it can be used later.\n\n    Returns:\n      A Searcher instance.\n    \"\"\"\n    # Gets the ScaNN builder.\n    builder = ondevice_scann_builder.builder(\n        data.dataset,\n        num_neighbors=10,  # This parameter is not used in on-device.\n        distance_measure=scann_options.distance_measure)\n    if scann_options.tree:\n      builder = builder.tree(**dataclasses.asdict(scann_options.tree))\n    if scann_options.score_ah:\n      # We only support LUT256 for on-device.\n      builder = builder.score_ah(\n          hash_type=\"lut256\", **dataclasses.asdict(scann_options.score_ah))\n    if scann_options.score_brute_force:\n      builder = builder.score_brute_force(\n          **dataclasses.asdict(scann_options.score_brute_force))\n\n    if cache_dir is None:\n      cache_dir = tempfile.mkdtemp()\n    if not tf.io.gfile.exists(cache_dir):\n      tf.io.gfile.makedirs(cache_dir)\n    logging.info(\"Cache will be stored in %s\", cache_dir)\n\n    # Builds, serializes and saves the ScaNN model.\n    scann = builder.build()\n    serialized_scann_path = os.path.join(cache_dir, \"serialized_scann\")\n    if not tf.io.gfile.exists(serialized_scann_path):\n      tf.io.gfile.makedirs(serialized_scann_path)\n    scann.serialize(serialized_scann_path)\n\n    return cls(serialized_scann_path, data.metadata, data.embedder_path)\n\n  def export(self,\n             export_format: ExportFormat,\n             export_filename: str,\n             userinfo: AnyStr,\n             compression: bool = True):\n    \"\"\"Export the searcher model.\n\n    Args:\n      export_format: Export format that could be tflite or on-device ScaNN index\n        file, must be `ExportFormat.TFLITE` or `ExportFormat.SCANN_INDEX_FILE`.\n      export_filename: File name to save the exported file. The exported file\n        can be TFLite model or on-device ScaNN index file.\n      userinfo: A special field in the index file that can be an arbitrary\n        string supplied by the user.\n      compression: Whether to snappy compress the index file.\n    \"\"\"\n    export_dir = os.path.dirname(export_filename)\n    if not tf.io.gfile.exists(export_dir):\n      tf.io.gfile.makedirs(export_dir)\n\n    if export_format is ExportFormat.SCANN_INDEX_FILE:\n      output_scann_path = export_filename\n    elif export_format is ExportFormat.TFLITE:\n      tmpdir = tempfile.mkdtemp()\n      output_scann_path = os.path.join(tmpdir, \"on_device_scann_index.ldb\")\n    else:\n      raise ValueError(\"Unsupported export format: \", export_format)\n\n    # Creates the on-device ScaNN index file and saves it in output_scann_path.\n    artifacts = scann_converter.convert_serialized_to_on_device(\n        self._serialized_scann_path)\n    scann_converter.convert_artifacts_to_leveldb(\n        output_scann_path,\n        metadata=self._metadata,\n        artifacts=artifacts,\n        userinfo=userinfo,\n        compression=compression)\n\n    # Associates the scann index file with the tflite model file.\n    if export_format is ExportFormat.TFLITE:\n      # Creates the metadata populator.\n      if self._embedder_path is None:\n        raise ValueError(\"Can't export the tflite model since embedder model \"\n                         \"is not provided.\")\n      with tf.io.gfile.GFile(self._embedder_path, \"rb\") as f:\n        model_buffer = f.read()\n        populator = _metadata.MetadataPopulator.with_model_buffer(model_buffer)\n\n      # Gets the output index by the number of input tensors.\n      model = _schema_fb.Model.GetRootAsModel(model_buffer, 0)\n      num_input_tensors = model.Subgraphs(0).InputsLength()\n\n      # Extracts the metadata.\n      metadata_buffer = _metadata.get_metadata_buffer(model_buffer)\n      if metadata_buffer:\n        metadata = _metadata_fb.ModelMetadataT.InitFromObj(\n            _metadata_fb.ModelMetadata.GetRootAsModelMetadata(\n                metadata_buffer, 0))\n      else:\n        # Creates the empty metadata.\n        input_metadata = [\n            _metadata_fb.TensorMetadataT() for i in range(num_input_tensors)\n        ]\n        num_output_tensors = model.Subgraphs(0).OutputsLength()\n        output_metadata = [\n            _metadata_fb.TensorMetadataT() for i in range(num_output_tensors)\n        ]\n\n        subgraph_metadata = _metadata_fb.SubGraphMetadataT()\n        subgraph_metadata.inputTensorMetadata = input_metadata\n        subgraph_metadata.outputTensorMetadata = output_metadata\n\n        metadata = _metadata_fb.ModelMetadataT()\n        metadata.subgraphMetadata = [subgraph_metadata]\n\n      if num_input_tensors == 3 and (\n          not metadata.subgraphMetadata[0].inputProcessUnits):\n        # Assume Universal Sentence Encoder-based model.\n        output_index = _USE_RESPONSE_ENCODING_INDEX\n      else:\n        output_index = 0\n\n      # Updates the metadata with the scann associated file.\n      scann_file = _metadata_fb.AssociatedFileT()\n      scann_file.name = os.path.basename(output_scann_path)\n      scann_file.description = \"On-device Scann Index file with LevelDB format.\"\n      scann_file.type = _metadata_fb.AssociatedFileType.SCANN_INDEX_FILE\n      output_metadata = metadata.subgraphMetadata[0].outputTensorMetadata[\n          output_index]\n      if output_metadata.associatedFiles is None:\n        output_metadata.associatedFiles = [scann_file]\n      else:\n        output_metadata.associatedFiles.append(scann_file)\n\n      # Saves the updated metadata and the scann associated file along with the\n      # model buffer.\n      buffer_builder = flatbuffers.Builder(0)\n      buffer_builder.Finish(\n          metadata.Pack(buffer_builder),\n          _metadata.MetadataPopulator.METADATA_FILE_IDENTIFIER)\n      updated_metadata_buffer = buffer_builder.Output()\n      populator.load_metadata_buffer(updated_metadata_buffer)\n      populator.load_associated_files([output_scann_path])\n      populator.populate()\n\n      output_tflite_path = export_filename\n      with tf.io.gfile.GFile(output_tflite_path, \"wb\") as f:\n        f.write(populator.get_model_buffer())\n      tf.io.gfile.rmtree(tmpdir)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/searcher_test.py",
    "content": "# Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Tests for searcher.\"\"\"\nimport os\nimport tempfile\n\nfrom absl.testing import parameterized\nimport numpy as np\nimport requests\nimport tensorflow as tf\nfrom tensorflow_lite_support.metadata.python import metadata as _metadata\nfrom tensorflow_examples.lite.model_maker.core.data_util import image_searcher_dataloader\nfrom tensorflow_examples.lite.model_maker.core.data_util import searcher_dataloader\nfrom tensorflow_examples.lite.model_maker.core.task import searcher\nfrom tensorflow_lite_support.python.task.text import text_searcher as task_text_searcher\nfrom tensorflow_lite_support.python.task.vision import image_searcher as task_image_searcher\nfrom tensorflow_lite_support.python.task.vision.core import tensor_image\nfrom tensorflow_examples.lite.model_maker.core import test_util\n\n\nclass SearcherTest(tf.test.TestCase, parameterized.TestCase):\n\n  def test_searcher_with_image_embedder(self):\n    tflite_path = test_util.get_test_data_path(\n        \"mobilenet_v2_035_96_embedder_with_metadata.tflite\")\n    image_folder = test_util.get_test_data_path(\"animals\")\n\n    # Creates the image searcher data loader.\n    data_loader = image_searcher_dataloader.DataLoader.create(tflite_path)\n    data_loader.load_from_folder(image_folder)\n    # Expands the data by 50x.\n    repeated_times = 500\n    data_loader._dataset = np.repeat(\n        data_loader.dataset, repeated_times, axis=0)\n    data_loader._metadata = repeated_times * data_loader.metadata\n\n    # Creates searcher model with ScaNN options including `score_ah`.\n    scann_options = searcher.ScaNNOptions(\n        distance_measure=\"dot_product\",\n        tree=searcher.Tree(num_leaves=10, num_leaves_to_search=2),\n        score_ah=searcher.ScoreAH(2, anisotropic_quantization_threshold=0.2))\n    model = searcher.Searcher.create_from_data(data_loader, scann_options)\n\n    # Exports the model to on-device ScaNN index file.\n    with tempfile.TemporaryDirectory() as tmpdir:\n      # Exports the standalone on-device ScaNN index file.\n      output_scann_path = os.path.join(tmpdir, \"on_device_scann_index.ldb\")\n      model.export(\n          export_filename=output_scann_path,\n          userinfo=b\"userinfo\\x00\\x11\\x22\\x34\",\n          export_format=searcher.ExportFormat.SCANN_INDEX_FILE)\n\n      self.assertTrue(os.path.exists(output_scann_path))\n      self.assertGreater(os.path.getsize(output_scann_path), 0)\n\n      test_image = tensor_image.TensorImage.create_from_file(\n          test_util.get_test_data_path(\"sparrow.png\"))\n      searcher_infer = task_image_searcher.ImageSearcher.create_from_file(\n          tflite_path, output_scann_path)\n\n      result1 = searcher_infer.search(test_image)\n      # Don't check the expected result directly since the result changes with\n      # `score_ah` options.\n      self.assertLen(result1.nearest_neighbors, 5)\n\n      # Exports the TFLite with on-device ScaNN index file as the associate\n      # file.\n      output_tflite_path = os.path.join(tmpdir, \"model.tflite\")\n      model.export(\n          export_filename=output_tflite_path,\n          userinfo=b\"userinfo\\x00\\x11\\x22\\x34\",\n          export_format=searcher.ExportFormat.TFLITE)\n\n      self.assertTrue(os.path.exists(output_tflite_path))\n      self.assertGreater(os.path.getsize(output_tflite_path), 0)\n\n      displayer = _metadata.MetadataDisplayer.with_model_file(\n          output_tflite_path)\n      actual_json = displayer.get_metadata_json()\n      expected_json_file = test_util.get_test_data_path(\n          \"mobilenet_v3_small_100_224_embedder_scann.json\")\n      with open(expected_json_file, \"r\") as f:\n        expected_json = f.read()\n      self.assertEqual(actual_json, expected_json)\n\n      searcher_infer = task_image_searcher.ImageSearcher.create_from_file(\n          output_tflite_path)\n      result2 = searcher_infer.search(test_image)\n      self.assertLen(result2.nearest_neighbors, 5)\n\n  def test_searcher_with_text_embedder(self):\n    # Dumpy text embedder TFLite model with string as input which doesn't need\n    # additional preprocessing in the task library.\n    tflite_path = test_util.get_test_data_path(\"dummy_sp_text_embedder.tflite\")\n\n    np.random.seed(123)\n    tf.random.set_seed(123)\n\n    # Creates the base SearcherDataLoader initialized from tflite file, dataset\n    # and metadata.\n    dataset_size = 1000\n    dim = 8\n    data_loader = searcher_dataloader.DataLoader(\n        embedder_path=tflite_path,\n        dataset=np.random.rand(dataset_size, dim),\n        metadata=[\"0\"] * dataset_size)\n\n    # Creates searcher model with ScaNN options including `score_brute_force`.\n    scann_options = searcher.ScaNNOptions(\n        distance_measure=\"dot_product\",\n        tree=searcher.Tree(num_leaves=10, num_leaves_to_search=2),\n        score_brute_force=searcher.ScoreBruteForce())\n    model = searcher.Searcher.create_from_data(data_loader, scann_options)\n\n    with tempfile.TemporaryDirectory() as tmpdir:\n      # Exports the standalone on-device ScaNN index file.\n      output_scann_path = os.path.join(tmpdir, \"text_on_device_scann_index.ldb\")\n      model.export(\n          export_filename=output_scann_path,\n          userinfo=b\"userinfo\\x00\\x11\\x22\\x34\",\n          export_format=searcher.ExportFormat.SCANN_INDEX_FILE)\n\n      self.assertTrue(os.path.exists(output_scann_path))\n      self.assertGreater(os.path.getsize(output_scann_path), 0)\n\n      searcher_infer = task_text_searcher.TextSearcher.create_from_file(\n          tflite_path, output_scann_path)\n      test_text = \"Test text.\"\n      result = searcher_infer.search(test_text)\n      # TODO(b/231393039): Changes to check the expected result if fix the\n      # inconsistency issue between the internal and external result.\n      self.assertLen(result.nearest_neighbors, 5)\n\n      # Exports the TFLite with on-device ScaNN index file as the associate\n      # file.\n      output_tflite_path = os.path.join(tmpdir, \"text_model.tflite\")\n      model.export(\n          export_filename=output_tflite_path,\n          userinfo=\"\",\n          export_format=searcher.ExportFormat.TFLITE)\n\n      self.assertTrue(os.path.exists(output_tflite_path))\n      self.assertGreater(os.path.getsize(output_tflite_path), 0)\n\n      searcher_infer = task_text_searcher.TextSearcher.create_from_file(\n          output_tflite_path)\n      result = searcher_infer.search(test_text)\n      self.assertLen(result.nearest_neighbors, 5)\n\n  @parameterized.parameters(\n      (\"universal_sentence_encoder_embedder.tflite\",\n       \"https://tfhub.dev/google/lite-model/universal-sentence-encoder-qa-ondevice/1?lite-format=tflite\",\n       100),\n      (\"mobilebert_embedding_with_metadata.tflite\",\n       \" https://storage.googleapis.com/download.tensorflow.org/models/tflite_support/bert_embedder/mobilebert_embedding_with_metadata.tflite\",\n       512),\n  )\n  def test_searcher_with_3_inputs_models(self, tflite_filename, url, dim):\n    # Tests seacher with the universal sentence encoder model or bert model.\n    # Gets the path to the tflite embedder model.\n    try:\n      tflite_path = test_util.get_test_data_path(tflite_filename)\n    except ValueError:\n      # Used for external test, download the tflite model in the tensorflow\n      # hub.\n      r = requests.get(url)\n      tflite_path = os.path.join(self.get_temp_dir(), \"embedder.tflite\")\n      with open(tflite_path, \"wb\") as f:\n        f.write(r.content)\n\n    # Gets the data loader.\n    data_loader = searcher_dataloader.DataLoader(\n        embedder_path=tflite_path,\n        dataset=np.random.rand(200, dim),\n        metadata=[\"1\"] * 200)\n\n    # Creates searcher model with ScaNN options.\n    scann_options = searcher.ScaNNOptions(\n        distance_measure=\"dot_product\",\n        tree=searcher.Tree(num_leaves=10, num_leaves_to_search=2),\n        score_brute_force=searcher.ScoreBruteForce())\n    model = searcher.Searcher.create_from_data(data_loader, scann_options)\n\n    # Exports the standalone on-device ScaNN index file.\n    output_scann_path = os.path.join(self.get_temp_dir(), \"scann_index.ldb\")\n    model.export(\n        export_filename=output_scann_path,\n        userinfo=\"\",\n        export_format=searcher.ExportFormat.SCANN_INDEX_FILE)\n\n    self.assertTrue(os.path.exists(output_scann_path))\n    self.assertGreater(os.path.getsize(output_scann_path), 0)\n\n    # Runs the inference to see if it works.\n    searcher_infer = task_text_searcher.TextSearcher.create_from_file(\n        tflite_path, output_scann_path)\n    result = searcher_infer.search(\"The weather is good.\")\n    self.assertLen(result.nearest_neighbors, 5)\n\n    # Exports the TFLite with on-device ScaNN index file as the associate\n    # file.\n    output_tflite_path = os.path.join(self.get_temp_dir(), \"searcher.tflite\")\n    model.export(\n        export_filename=output_tflite_path,\n        userinfo=\"\",\n        export_format=searcher.ExportFormat.TFLITE)\n\n    self.assertTrue(os.path.exists(output_tflite_path))\n    self.assertGreater(os.path.getsize(output_tflite_path), 0)\n\n    # Runs the inference to see if it works.\n    searcher_infer = task_text_searcher.TextSearcher.create_from_file(\n        output_tflite_path)\n    result = searcher_infer.search(\"The weather is so bad.\")\n    self.assertLen(result.nearest_neighbors, 5)\n\nif __name__ == \"__main__\":\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/testdata/average_word_vec_metadata.json",
    "content": "{\n  \"name\": \"AverageWordVec text classifier\",\n  \"description\": \"Classify text into predefined categories.\",\n  \"version\": \"v1\",\n  \"subgraph_metadata\": [\n    {\n      \"input_tensor_metadata\": [\n        {\n          \"name\": \"input_text\",\n          \"description\": \"Embedding vectors representing the input text to be classified. The input need to be converted from raw text to embedding vectors using the attached dictionary file.\",\n          \"content\": {\n            \"content_properties_type\": \"FeatureProperties\",\n            \"content_properties\": {\n            }\n          },\n          \"process_units\": [\n            {\n              \"options_type\": \"RegexTokenizerOptions\",\n              \"options\": {\n                \"delim_regex_pattern\": \"[^\\\\w\\\\']+\",\n                \"vocab_file\": [\n                  {\n                    \"name\": \"vocab.txt\",\n                    \"description\": \"Vocabulary file to convert natural language words to embedding vectors.\",\n                    \"type\": \"VOCABULARY\"\n                  }\n                ]\n              }\n            }\n          ]\n        }\n      ],\n      \"output_tensor_metadata\": [\n        {\n          \"name\": \"probability\",\n          \"description\": \"Probabilities of the labels respectively.\",\n          \"content\": {\n            \"content_properties_type\": \"FeatureProperties\",\n            \"content_properties\": {\n            }\n          },\n          \"stats\": {\n            \"max\": [\n              1.0\n            ],\n            \"min\": [\n              0.0\n            ]\n          },\n          \"associated_files\": [\n            {\n              \"name\": \"labels.txt\",\n              \"description\": \"Labels for the categories that the model can classify.\",\n              \"type\": \"TENSOR_AXIS_LABELS\"\n            }\n          ]\n        }\n      ]\n    }\n  ],\n  \"author\": \"TensorFlow Lite Model Maker\",\n  \"license\": \"Apache License. Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.\",\n  \"min_parser_version\": \"1.2.1\"\n}\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/testdata/bert_classifier_metadata.json",
    "content": "{\n  \"name\": \"Bert text classifier\",\n  \"description\": \"Classifies the input string based on the known catergories. To integrate the model into your app, try the `BertNLClassifier` API in the TensorFlow Lite Task library. `BertNLClassifier` takes an input string, and returns the classified label with probability. It encapsulates the processing logic of inputs and outputs and runs the inference with the best practice.\",\n  \"version\": \"v1\",\n  \"subgraph_metadata\": [\n    {\n      \"input_tensor_metadata\": [\n        {\n          \"name\": \"ids\",\n          \"description\": \"Tokenized ids of input text.\",\n          \"content\": {\n            \"content_properties_type\": \"FeatureProperties\",\n            \"content_properties\": {\n            }\n          }\n        },\n        {\n          \"name\": \"mask\",\n          \"description\": \"Mask with 1 for real tokens and 0 for padding tokens.\",\n          \"content\": {\n            \"content_properties_type\": \"FeatureProperties\",\n            \"content_properties\": {\n            }\n          }\n        },\n        {\n          \"name\": \"segment_ids\",\n          \"description\": \"0 for the first sequence, 1 for the second sequence if exists.\",\n          \"content\": {\n            \"content_properties_type\": \"FeatureProperties\",\n            \"content_properties\": {\n            }\n          }\n        }\n      ],\n      \"output_tensor_metadata\": [\n        {\n          \"name\": \"probability\",\n          \"description\": \"Probabilities of labels respectively.\",\n          \"content\": {\n            \"content_properties_type\": \"FeatureProperties\",\n            \"content_properties\": {\n            }\n          },\n          \"associated_files\": [\n            {\n              \"name\": \"labels.txt\",\n              \"description\": \"Labels for classification categories.\",\n              \"type\": \"TENSOR_AXIS_LABELS\"\n            }\n          ]\n        }\n      ],\n      \"input_process_units\": [\n        {\n          \"options_type\": \"BertTokenizerOptions\",\n          \"options\": {\n            \"vocab_file\": [\n              {\n                \"name\": \"vocab.txt\",\n                \"description\": \"Vocabulary file for the BertTokenizer.\",\n                \"type\": \"VOCABULARY\"\n              }\n            ]\n          }\n        }\n      ]\n    }\n  ],\n  \"author\": \"TensorFlow Lite Model Maker\",\n  \"license\": \"Apache License. Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.\",\n  \"min_parser_version\": \"1.1.0\"\n}\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/testdata/bert_qa_metadata.json",
    "content": "{\n  \"name\": \"Bert Question and Answerer\",\n  \"description\": \"Answers questions based on the content of a given passage. To integrate the model into your app, try the `BertQuestionAnswerer` API in the TensorFlow Lite Task library. `BertQuestionAnswerer` takes a passage string and a query string, and returns the answer strings. It encapsulates the processing logic of inputs and outputs and runs the inference with the best practice.\",\n  \"version\": \"v1\",\n  \"subgraph_metadata\": [\n    {\n      \"input_tensor_metadata\": [\n        {\n          \"name\": \"ids\",\n          \"description\": \"Tokenized ids of input text.\",\n          \"content\": {\n            \"content_properties_type\": \"FeatureProperties\",\n            \"content_properties\": {\n            }\n          }\n        },\n        {\n          \"name\": \"mask\",\n          \"description\": \"Mask with 1 for real tokens and 0 for padding tokens.\",\n          \"content\": {\n            \"content_properties_type\": \"FeatureProperties\",\n            \"content_properties\": {\n            }\n          }\n        },\n        {\n          \"name\": \"segment_ids\",\n          \"description\": \"0 for the first sequence, 1 for the second sequence if exists.\",\n          \"content\": {\n            \"content_properties_type\": \"FeatureProperties\",\n            \"content_properties\": {\n            }\n          }\n        }\n      ],\n      \"output_tensor_metadata\": [\n        {\n          \"name\": \"end_logits\",\n          \"description\": \"logits over the sequence which indicates the end position of the answer span with closed interval.\",\n          \"content\": {\n            \"content_properties_type\": \"FeatureProperties\",\n            \"content_properties\": {\n            }\n          }\n        },\n        {\n          \"name\": \"start_logits\",\n          \"description\": \"logits over the sequence which indicates the start position of the answer span with closed interval.\",\n          \"content\": {\n            \"content_properties_type\": \"FeatureProperties\",\n            \"content_properties\": {\n            }\n          }\n        }\n      ],\n      \"input_process_units\": [\n        {\n          \"options_type\": \"BertTokenizerOptions\",\n          \"options\": {\n            \"vocab_file\": [\n              {\n                \"name\": \"vocab.txt\",\n                \"description\": \"Vocabulary file for the BertTokenizer.\",\n                \"type\": \"VOCABULARY\"\n              }\n            ]\n          }\n        }\n      ]\n    }\n  ],\n  \"author\": \"TensorFlow Lite Model Maker\",\n  \"license\": \"Apache License. Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.\",\n  \"min_parser_version\": \"1.1.0\"\n}\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/testdata/efficientdet_lite0_metadata.json",
    "content": "{\n  \"name\": \"ObjectDetector\",\n  \"description\": \"Identify which of a known set of objects might be present and provide information about their positions within the given image or a video stream.\",\n  \"subgraph_metadata\": [\n    {\n      \"input_tensor_metadata\": [\n        {\n          \"name\": \"image\",\n          \"description\": \"Input image to be detected.\",\n          \"content\": {\n            \"content_properties_type\": \"ImageProperties\",\n            \"content_properties\": {\n              \"color_space\": \"RGB\"\n            }\n          },\n          \"process_units\": [\n            {\n              \"options_type\": \"NormalizationOptions\",\n              \"options\": {\n                \"mean\": [\n                  127.0\n                ],\n                \"std\": [\n                  128.0\n                ]\n              }\n            }\n          ],\n          \"stats\": {\n            \"max\": [\n              1.0\n            ],\n            \"min\": [\n              -0.992188\n            ]\n          }\n        }\n      ],\n      \"output_tensor_metadata\": [\n        {\n          \"name\": \"score\",\n          \"description\": \"The scores of the detected boxes.\",\n          \"content\": {\n            \"content_properties_type\": \"FeatureProperties\",\n            \"content_properties\": {\n            },\n            \"range\": {\n              \"min\": 2,\n              \"max\": 2\n            }\n          },\n          \"stats\": {\n          }\n        },\n        {\n          \"name\": \"location\",\n          \"description\": \"The locations of the detected boxes.\",\n          \"content\": {\n            \"content_properties_type\": \"BoundingBoxProperties\",\n            \"content_properties\": {\n              \"index\": [\n                1,\n                0,\n                3,\n                2\n              ],\n              \"type\": \"BOUNDARIES\"\n            },\n            \"range\": {\n              \"min\": 2,\n              \"max\": 2\n            }\n          },\n          \"stats\": {\n          }\n        },\n        {\n          \"name\": \"number of detections\",\n          \"description\": \"The number of the detected boxes.\",\n          \"content\": {\n            \"content_properties_type\": \"FeatureProperties\",\n            \"content_properties\": {\n            }\n          },\n          \"stats\": {\n          }\n        },\n        {\n          \"name\": \"category\",\n          \"description\": \"The categories of the detected boxes.\",\n          \"content\": {\n            \"content_properties_type\": \"FeatureProperties\",\n            \"content_properties\": {\n            },\n            \"range\": {\n              \"min\": 2,\n              \"max\": 2\n            }\n          },\n          \"stats\": {\n          },\n          \"associated_files\": [\n            {\n              \"name\": \"labelmap.txt\",\n              \"description\": \"Labels for categories that the model can recognize.\",\n              \"type\": \"TENSOR_VALUE_LABELS\"\n            }\n          ]\n        }\n      ],\n      \"output_tensor_groups\": [\n        {\n          \"name\": \"detection_result\",\n          \"tensor_names\": [\n            \"location\",\n            \"category\",\n            \"score\"\n          ]\n        }\n      ]\n    }\n  ],\n  \"min_parser_version\": \"1.2.0\"\n}\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/testdata/efficientnet_lite0_metadata.json",
    "content": "{\n  \"name\": \"efficientnet_lite0\",\n  \"description\": \"Identify the most prominent object in the image from a set of 3 categories.\",\n  \"version\": \"v1\",\n  \"subgraph_metadata\": [\n    {\n      \"input_tensor_metadata\": [\n        {\n          \"name\": \"image\",\n          \"description\": \"Input image to be classified. The expected image is 224 x 224, with three channels (red, blue, and green) per pixel. Each value in the tensor is a single byte between 0 and 1.\",\n          \"content\": {\n            \"content_properties_type\": \"ImageProperties\",\n            \"content_properties\": {\n              \"color_space\": \"RGB\"\n            }\n          },\n          \"process_units\": [\n            {\n              \"options_type\": \"NormalizationOptions\",\n              \"options\": {\n                \"mean\": [\n                  0.0\n                ],\n                \"std\": [\n                  255.0\n                ]\n              }\n            }\n          ],\n          \"stats\": {\n            \"max\": [\n              1.0\n            ],\n            \"min\": [\n              0.0\n            ]\n          }\n        }\n      ],\n      \"output_tensor_metadata\": [\n        {\n          \"name\": \"probability\",\n          \"description\": \"Probabilities of the 3 labels respectively.\",\n          \"content\": {\n            \"content_properties_type\": \"FeatureProperties\"\n          },\n          \"stats\": {\n            \"max\": [\n              1.0\n            ],\n            \"min\": [\n              0.0\n            ]\n          },\n          \"associated_files\": [\n            {\n              \"name\": \"labels.txt\",\n              \"description\": \"Labels for objects that the model can recognize.\",\n              \"type\": \"TENSOR_AXIS_LABELS\"\n            }\n          ]\n        }\n      ]\n    }\n  ],\n  \"author\": \"TensorFlow Lite Model Maker\",\n  \"license\": \"Apache License. Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.\",\n  \"min_parser_version\": \"1.0.0\"\n}\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/testdata/hub_module_v1_mini/tfhub_module.pb",
    "content": "\b\u0003"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/testdata/hub_module_v1_mini_train/tfhub_module.pb",
    "content": "\b\u0003"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/testdata/hub_module_v1_mini_train/variables/variables.data-00000-of-00001",
    "content": "¿"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/testdata/mobilenet_v3_small_100_224_embedder_scann.json",
    "content": "{\n  \"name\": \"ImageEmbedder\",\n  \"description\": \"Performs dense feature vector extraction on image.\",\n  \"subgraph_metadata\": [\n    {\n      \"input_tensor_metadata\": [\n        {\n          \"name\": \"image\",\n          \"description\": \"Input image.\",\n          \"content\": {\n            \"content_properties_type\": \"ImageProperties\",\n            \"content_properties\": {\n              \"color_space\": \"RGB\"\n            }\n          },\n          \"process_units\": [\n            {\n              \"options_type\": \"NormalizationOptions\",\n              \"options\": {\n                \"mean\": [\n                  127.5\n                ],\n                \"std\": [\n                  127.5\n                ]\n              }\n            }\n          ],\n          \"stats\": {\n            \"max\": [\n              1.0\n            ],\n            \"min\": [\n              -1.0\n            ]\n          }\n        }\n      ],\n      \"output_tensor_metadata\": [\n        {\n          \"name\": \"StatefulPartitionedCall:0\",\n          \"associated_files\": [\n            {\n              \"name\": \"on_device_scann_index.ldb\",\n              \"description\": \"On-device Scann Index file with LevelDB format.\",\n              \"type\": \"SCANN_INDEX_FILE\"\n            }\n          ]\n        }\n      ]\n    }\n  ],\n  \"min_parser_version\": \"1.4.0\"\n}\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/text_classifier.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"APIs to train a text classification model.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nimport tempfile\n\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core.api import mm_export\nfrom tensorflow_examples.lite.model_maker.core.export_format import ExportFormat\nfrom tensorflow_examples.lite.model_maker.core.task import classification_model\nfrom tensorflow_examples.lite.model_maker.core.task import model_spec as ms\nfrom tensorflow_examples.lite.model_maker.core.task import model_util\nfrom tensorflow_examples.lite.model_maker.core.task.metadata_writers.bert.text_classifier import metadata_writer_for_bert_text_classifier as bert_metadata_writer\nfrom tensorflow_examples.lite.model_maker.core.task.metadata_writers.text_classifier import metadata_writer_for_text_classifier as metadata_writer\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import text_spec\n\n\ndef _get_bert_model_info(model_spec, vocab_file, label_file):\n  return bert_metadata_writer.ClassifierSpecificInfo(\n      name=model_spec.name + ' text classifier',\n      version='v1',\n      description=bert_metadata_writer.DEFAULT_DESCRIPTION,\n      input_names=bert_metadata_writer.bert_qa_inputs(\n          ids_name=model_spec.tflite_input_name['ids'],\n          mask_name=model_spec.tflite_input_name['mask'],\n          segment_ids_name=model_spec.tflite_input_name['segment_ids']),\n      tokenizer_type=bert_metadata_writer.Tokenizer.BERT_TOKENIZER,\n      vocab_file=vocab_file,\n      label_file=label_file)\n\n\ndef _get_model_info(model_name):\n  return metadata_writer.ModelSpecificInfo(\n      name=model_name + ' text classifier',\n      description='Classify text into predefined categories.',\n      version='v1')\n\n\n@mm_export('text_classifier.TextClassifier')\nclass TextClassifier(classification_model.ClassificationModel):\n  \"\"\"TextClassifier class for inference and exporting to tflite.\"\"\"\n\n  DEFAULT_EXPORT_FORMAT = (ExportFormat.TFLITE, ExportFormat.LABEL,\n                           ExportFormat.VOCAB)\n  ALLOWED_EXPORT_FORMAT = (ExportFormat.TFLITE, ExportFormat.LABEL,\n                           ExportFormat.VOCAB, ExportFormat.SAVED_MODEL,\n                           ExportFormat.TFJS)\n\n  def __init__(self,\n               model_spec,\n               index_to_label,\n               shuffle=True):\n    \"\"\"Init function for TextClassifier class.\n\n    Args:\n      model_spec: Specification for the model.\n      index_to_label: A list that map from index to label class name.\n      shuffle: Whether the data should be shuffled.\n    \"\"\"\n    super(TextClassifier, self).__init__(\n        model_spec,\n        index_to_label,\n        shuffle,\n        train_whole_model=True)\n\n  def create_model(self, with_loss_and_metrics=True):\n    self.model = self.model_spec.create_model(\n        self.num_classes, with_loss_and_metrics=with_loss_and_metrics)\n\n  def train(self,\n            train_data,\n            validation_data=None,\n            epochs=None,\n            batch_size=None,\n            steps_per_epoch=None):\n    \"\"\"Feeds the training data for training.\"\"\"\n    if batch_size is None:\n      batch_size = self.model_spec.default_batch_size\n\n    if len(train_data) < batch_size:\n      raise ValueError('The size of the train_data (%d) couldn\\'t be smaller '\n                       'than batch_size (%d). To solve this problem, set '\n                       'the batch_size smaller or increase the size of the '\n                       'train_data.' % (len(train_data), batch_size))\n\n    train_ds = train_data.gen_dataset(batch_size, is_training=True)\n    validation_ds = validation_data.gen_dataset(\n        batch_size, is_training=False) if validation_data else None\n\n    steps_per_epoch = model_util.get_steps_per_epoch(steps_per_epoch,\n                                                     batch_size, train_data)\n    if steps_per_epoch is not None:\n      train_ds = train_ds.take(steps_per_epoch)\n    self.model = self.model_spec.run_classifier(\n        train_ds=train_ds,\n        validation_ds=validation_ds,\n        epochs=epochs,\n        steps_per_epoch=steps_per_epoch,\n        num_classes=self.num_classes,\n        callbacks=self._keras_callbacks(model_dir=self.model_spec.model_dir))\n\n    return self.model\n\n  def _export_tflite(self,\n                     tflite_filepath,\n                     quantization_config='default',\n                     with_metadata=True,\n                     export_metadata_json_file=False):\n    \"\"\"Converts the retrained model to tflite format and saves it.\n\n    Args:\n      tflite_filepath: File path to save tflite model.\n      quantization_config: Configuration for post-training quantization. If\n        'default', sets the `quantization_config` by default according to\n        `self.model_spec`. If None, exports the float tflite model without\n        quantization.\n      with_metadata: Whether the output tflite model contains metadata.\n      export_metadata_json_file: Whether to export metadata in json file. If\n        True, export the metadata in the same directory as tflite model.Used\n        only if `with_metadata` is True.\n    \"\"\"\n    if quantization_config == 'default':\n      quantization_config = self.model_spec.get_default_quantization_config()\n\n    # Sets batch size from None to 1 when converting to tflite.\n    model_util.set_batch_size(self.model, batch_size=1)\n    model_util.export_tflite(self.model, tflite_filepath, quantization_config,\n                             self.model_spec.convert_from_saved_model_tf2)\n    # Sets batch size back to None to support retraining later.\n    model_util.set_batch_size(self.model, batch_size=None)\n\n    if with_metadata:\n      with tempfile.TemporaryDirectory() as temp_dir:\n        tf.compat.v1.logging.info('Vocab file and label file are inside the '\n                                  'TFLite model with metadata.')\n        vocab_filepath = os.path.join(temp_dir, 'vocab.txt')\n        self.model_spec.save_vocab(vocab_filepath)\n        label_filepath = os.path.join(temp_dir, 'labels.txt')\n        self._export_labels(label_filepath)\n\n        export_dir = os.path.dirname(tflite_filepath)\n        if isinstance(self.model_spec, text_spec.BertClassifierModelSpec):\n          model_info = _get_bert_model_info(self.model_spec, vocab_filepath,\n                                            label_filepath)\n          populator = bert_metadata_writer.MetadataPopulatorForBertTextClassifier(\n              tflite_filepath, export_dir, model_info)\n        elif isinstance(self.model_spec, text_spec.AverageWordVecModelSpec):\n          model_info = _get_model_info(self.model_spec.name)\n          populator = metadata_writer.MetadataPopulatorForTextClassifier(\n              tflite_filepath, export_dir, model_info, label_filepath,\n              vocab_filepath)\n        else:\n          raise ValueError('Model Specification is not supported to writing '\n                           'metadata into TFLite. Please set '\n                           '`with_metadata=False` or write metadata by '\n                           'yourself.')\n        populator.populate(export_metadata_json_file)\n\n  @classmethod\n  def create(cls,\n             train_data,\n             model_spec='average_word_vec',\n             validation_data=None,\n             batch_size=None,\n             epochs=3,\n             steps_per_epoch=None,\n             shuffle=False,\n             do_train=True):\n    \"\"\"Loads data and train the model for test classification.\n\n    Args:\n      train_data: Training data.\n      model_spec: Specification for the model.\n      validation_data: Validation data. If None, skips validation process.\n      batch_size: Batch size for training.\n      epochs: Number of epochs for training.\n      steps_per_epoch: Integer or None. Total number of steps (batches of\n        samples) before declaring one epoch finished and starting the next\n        epoch. If `steps_per_epoch` is None, the epoch will run until the input\n        dataset is exhausted.\n      shuffle: Whether the data should be shuffled.\n      do_train: Whether to run training.\n\n    Returns:\n      An instance based on TextClassifier.\n    \"\"\"\n    model_spec = ms.get(model_spec)\n    if compat.get_tf_behavior() not in model_spec.compat_tf_versions:\n      raise ValueError('Incompatible versions. Expect {}, but got {}.'.format(\n          model_spec.compat_tf_versions, compat.get_tf_behavior()))\n\n    text_classifier = cls(\n        model_spec, train_data.index_to_label, shuffle=shuffle)\n\n    if do_train:\n      tf.compat.v1.logging.info('Retraining the models...')\n      text_classifier.train(train_data, validation_data, epochs, batch_size,\n                            steps_per_epoch)\n    else:\n      text_classifier.create_model()\n\n    return text_classifier\n\n\n# Shortcut function.\ncreate = TextClassifier.create\nmm_export('text_classifier.create').export_constant(__name__, 'create')\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/text_classifier_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport filecmp\nimport os\n\nimport numpy as np\nimport tensorflow.compat.v2 as tf\n\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core import test_util\nfrom tensorflow_examples.lite.model_maker.core.data_util import text_dataloader\nfrom tensorflow_examples.lite.model_maker.core.export_format import ExportFormat\nfrom tensorflow_examples.lite.model_maker.core.task import text_classifier\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import text_spec\n\n\nclass TextClassifierTest(tf.test.TestCase):\n  TEST_LABELS_AND_TEXT = (('pos', 'super good'), ('neg', 'really bad.'))\n\n  def _gen_text_dir(self, text_per_class=1):\n    text_dir = os.path.join(self.get_temp_dir(), 'random_text_dir')\n    if os.path.exists(text_dir):\n      return text_dir\n    os.mkdir(text_dir)\n\n    for class_name, text in self.TEST_LABELS_AND_TEXT:\n      class_subdir = os.path.join(text_dir, class_name)\n      os.mkdir(class_subdir)\n      for i in range(text_per_class):\n        with open(os.path.join(class_subdir, '%d.txt' % i), 'w') as f:\n          f.write(text)\n    return text_dir\n\n  def setUp(self):\n    super(TextClassifierTest, self).setUp()\n    self.text_dir = self._gen_text_dir()\n    self.tiny_text_dir = self._gen_text_dir(text_per_class=1)\n\n  @test_util.test_in_tf_1\n  def test_average_wordvec_model_create_v1_incompatible(self):\n    with self.assertRaisesRegex(ValueError, 'Incompatible versions'):\n      model_spec = text_spec.AverageWordVecModelSpec(seq_len=2)\n      all_data = text_dataloader.TextClassifierDataLoader.from_folder(\n          self.text_dir, model_spec=model_spec)\n      _ = text_classifier.create(\n          all_data,\n          model_spec=model_spec,\n      )\n\n  @test_util.test_in_tf_2\n  def test_bert_model(self):\n    model_spec = text_spec.BertClassifierModelSpec(seq_len=2, trainable=False)\n    all_data = text_dataloader.TextClassifierDataLoader.from_folder(\n        self.tiny_text_dir, model_spec=model_spec)\n    # Splits data, 50% data for training, 50% for testing\n    self.train_data, self.test_data = all_data.split(0.5)\n\n    model = text_classifier.create(\n        self.train_data,\n        model_spec=model_spec,\n        epochs=1,\n        batch_size=1,\n        shuffle=True)\n    self._test_accuracy(model, 0.0)\n    self._test_export_to_tflite(model, threshold=0.0)\n    self._test_model_without_training(model_spec)\n\n  @test_util.test_in_tf_2\n  def test_mobilebert_model(self):\n    model_spec = text_spec.mobilebert_classifier_spec(\n        seq_len=2, trainable=False, default_batch_size=1)\n    all_data = text_dataloader.TextClassifierDataLoader.from_folder(\n        self.tiny_text_dir, model_spec=model_spec)\n    # Splits data, 50% data for training, 50% for testing\n    self.train_data, self.test_data = all_data.split(0.5)\n\n    model = text_classifier.create(\n        self.train_data,\n        model_spec=model_spec,\n        epochs=1,\n        shuffle=True)\n    self._test_accuracy(model, 0.0)\n    self._test_export_to_tflite(model, threshold=0.0, atol=1e-2)\n    self._test_export_to_tflite_quant(model, model_size=25555047)\n\n  @test_util.test_in_tf_2\n  def test_mobilebert_model_without_training_for_tfjs(self):\n    model_spec = text_spec.mobilebert_classifier_spec(\n        seq_len=2, trainable=False, default_batch_size=1)\n    all_data = text_dataloader.TextClassifierDataLoader.from_folder(\n        self.text_dir, model_spec=model_spec)\n    self.train_data, self.test_data = all_data.split(0.5)\n    with self.assertRaises(Exception):  # Raise an error when reloading model.\n      self._test_model_without_training(model_spec)\n\n  @test_util.test_in_tf_2\n  def test_average_wordvec_model(self):\n    model_spec = text_spec.AverageWordVecModelSpec(seq_len=2)\n    all_data = text_dataloader.TextClassifierDataLoader.from_folder(\n        self.text_dir, model_spec=model_spec)\n    # Splits data, 90% data for training, 10% for testing\n    self.train_data, self.test_data = all_data.split(0.5)\n\n    model = text_classifier.create(\n        self.train_data,\n        model_spec=model_spec,\n        epochs=1,\n        batch_size=1,\n        shuffle=True)\n    self._test_accuracy(model, threshold=0.0)\n    self._test_predict_top_k(model)\n    self._test_export_to_tflite(\n        model,\n        threshold=0.0,\n        expected_json_file='average_word_vec_metadata.json')\n    self._test_export_to_saved_model(model)\n    self._test_export_labels(model)\n    self._test_export_vocab(model)\n    self._test_model_without_training(model_spec)\n\n  def _test_model_without_training(self, model_spec):\n    # Test without retraining.\n    model = text_classifier.create(\n        self.train_data, model_spec=model_spec, do_train=False)\n    self._test_accuracy(model, threshold=0.0)\n    self._test_export_to_tflite(model, threshold=0.0)\n    self._test_export_to_tfjs(model)\n\n  def _test_accuracy(self, model, threshold=1.0):\n    _, accuracy = model.evaluate(self.test_data)\n    self.assertGreaterEqual(accuracy, threshold)\n\n  def _test_predict_top_k(self, model):\n    topk = model.predict_top_k(self.test_data, batch_size=1)\n    for i in range(len(self.test_data)):\n      predict_label, predict_prob = topk[i][0][0], topk[i][0][1]\n      self.assertIn(predict_label, model.index_to_label)\n      self.assertGreater(predict_prob, 0.5)\n\n  def _load_vocab(self, filepath):\n    with tf.io.gfile.GFile(filepath, 'r') as f:\n      return [vocab.strip('\\n').split() for vocab in f]\n\n  def _load_labels(self, filepath):\n    with tf.io.gfile.GFile(filepath, 'r') as f:\n      return [label.strip('\\n') for label in f]\n\n  def _test_export_labels(self, model):\n    labels_output_file = os.path.join(self.get_temp_dir(), 'labels.txt')\n    model.export(self.get_temp_dir(), export_format=ExportFormat.LABEL)\n\n    labels = self._load_labels(labels_output_file)\n    self.assertEqual(labels, ['neg', 'pos'])\n\n  def _test_export_vocab(self, model):\n    vocab_output_file = os.path.join(self.get_temp_dir(), 'vocab.txt')\n    model.export(self.get_temp_dir(), export_format=ExportFormat.VOCAB)\n\n    word_index = self._load_vocab(vocab_output_file)\n    expected_predefined = [['<PAD>', '0'], ['<START>', '1'], ['<UNKNOWN>', '2']]\n    self.assertEqual(word_index[:3], expected_predefined)\n\n    expected_vocab = ['bad', 'good', 'really', 'super']\n    actual_vocab = sorted([word for word, index in word_index[3:]])\n    self.assertEqual(actual_vocab, expected_vocab)\n\n    expected_index = ['3', '4', '5', '6']\n    actual_index = [index for word, index in word_index[3:]]\n    self.assertEqual(actual_index, expected_index)\n\n  def _test_export_to_tflite(self,\n                             model,\n                             threshold=1.0,\n                             atol=1e-04,\n                             expected_json_file=None):\n    tflite_output_file = os.path.join(self.get_temp_dir(), 'model.tflite')\n    model.export(\n        self.get_temp_dir(),\n        export_format=ExportFormat.TFLITE,\n        quantization_config=None,\n        export_metadata_json_file=expected_json_file is not None)\n\n    self.assertTrue(tf.io.gfile.exists(tflite_output_file))\n    self.assertGreater(os.path.getsize(tflite_output_file), 0)\n\n    result = model.evaluate_tflite(tflite_output_file, self.test_data)\n    self.assertGreaterEqual(result['accuracy'], threshold)\n\n    spec = model.model_spec\n    if isinstance(spec, text_spec.AverageWordVecModelSpec):\n      random_inputs = np.random.randint(\n          low=0, high=len(spec.vocab), size=(1, spec.seq_len), dtype=np.int32)\n    elif isinstance(spec, text_spec.BertClassifierModelSpec):\n      input_word_ids = np.random.randint(\n          low=0,\n          high=len(spec.tokenizer.vocab),\n          size=(1, spec.seq_len),\n          dtype=np.int32)\n      input_mask = np.random.randint(\n          low=0, high=2, size=(1, spec.seq_len), dtype=np.int32)\n      input_type_ids = np.random.randint(\n          low=0, high=2, size=(1, spec.seq_len), dtype=np.int32)\n      random_inputs = (input_word_ids, input_mask, input_type_ids)\n    else:\n      raise ValueError('Unsupported model_spec type: %s' % str(type(spec)))\n\n    self.assertTrue(\n        test_util.is_same_output(\n            tflite_output_file, model.model, random_inputs, spec, atol=atol))\n\n    if expected_json_file is not None:\n      json_output_file = os.path.join(self.get_temp_dir(), 'model.json')\n      self.assertTrue(os.path.isfile(json_output_file))\n      self.assertGreater(os.path.getsize(json_output_file), 0)\n      expected_json_file = test_util.get_test_data_path(expected_json_file)\n      self.assertTrue(filecmp.cmp(json_output_file, expected_json_file))\n\n  def _test_export_to_saved_model(self, model):\n    save_model_output_path = os.path.join(self.get_temp_dir(), 'saved_model')\n    model.export(self.get_temp_dir(), export_format=ExportFormat.SAVED_MODEL)\n\n    self.assertTrue(os.path.isdir(save_model_output_path))\n    self.assertNotEmpty(os.listdir(save_model_output_path))\n\n  def _test_export_to_tfjs(self, model):\n    output_path = os.path.join(self.get_temp_dir(), 'tfjs')\n    model.export(\n        self.get_temp_dir(),\n        export_format=[ExportFormat.TFLITE, ExportFormat.TFJS])\n\n    self.assertTrue(os.path.isdir(output_path))\n    self.assertNotEmpty(os.listdir(output_path))\n\n  def _test_export_to_tflite_quant(self, model, model_size, err_ratio=0.08):\n    tflite_filename = 'model_quant.tflite'\n    tflite_output_file = os.path.join(self.get_temp_dir(), tflite_filename)\n    model.export(\n        self.get_temp_dir(),\n        tflite_filename=tflite_filename,\n        export_format=ExportFormat.TFLITE)\n\n    self.assertTrue(tf.io.gfile.exists(tflite_output_file))\n    err = model_size * err_ratio\n    self.assertNear(os.path.getsize(tflite_output_file), model_size, err)\n\n\nif __name__ == '__main__':\n  # Load compressed models from tensorflow_hub\n  os.environ['TFHUB_MODEL_LOAD_FORMAT'] = 'COMPRESSED'\n  compat.setup_tf_behavior(tf_version=2)\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/text_classifier_v1_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core.task import text_classifier_test\n\n\nclass TextClassifierV1Test(text_classifier_test.TextClassifierTest):\n  \"\"\"Share text tests of the base class, but in tf v1 behavior.\"\"\"\n\n\nif __name__ == '__main__':\n  compat.setup_tf_behavior(tf_version=1)\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/task/train_image_classifier_lib.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Library to retrain image classifier models.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport collections\nimport logging\nimport os\nimport tempfile\n\nimport tensorflow.compat.v2 as tf\nfrom tensorflow_examples.lite.model_maker.core.optimization import warmup\nfrom tensorflow_examples.lite.model_maker.core.task import make_image_classifier\nfrom tensorflow_examples.lite.model_maker.core.task import model_util\n\nDEFAULT_DECAY_SAMPLES = 10000 * 256\nDEFAULT_WARMUP_EPOCHS = 2\n\n\ndef add_params(hparams, **kwargs):\n  param_dict = {k: v for k, v in kwargs.items() if v is not None}\n\n  return hparams._replace(**param_dict)\n\n\nclass HParams(\n    collections.namedtuple(\n        \"HParams\",\n        make_image_classifier.HParams._fields + (\"warmup_steps\", \"model_dir\"),\n    )\n):\n  \"\"\"The hyperparameters for make_image_classifier.\n\n  train_epochs: Training will do this many iterations over the dataset.\n  do_fine_tuning: If true, the Hub module is trained together with the\n    classification layer on top.\n  batch_size: Each training step samples a batch of this many images.\n  learning_rate: Base learning rate when train batch size is 256. Linear to the\n    batch size.\n  dropout_rate: The fraction of the input units to drop, used in dropout layer.\n  warmup_steps: Number of warmup steps for warmup schedule on learning rate.\n  model_dir: The location of the model checkpoint files.\n  \"\"\"\n\n  @classmethod\n  def get_hparams(cls, **kwargs):\n    \"\"\"Gets the hyperparameters for `train_image_classifier_lib`.\"\"\"\n    hparams = get_default_hparams()\n    return add_params(hparams, **kwargs)\n\n\ndef get_default_hparams():\n  \"\"\"Returns a fresh HParams object initialized to default values.\"\"\"\n  default_hub_hparams = make_image_classifier.get_default_hparams()\n  as_dict = default_hub_hparams._asdict()\n  as_dict.update(\n      train_epochs=10,\n      do_fine_tuning=False,\n      batch_size=64,\n      learning_rate=0.004,\n      dropout_rate=0.2,\n      warmup_steps=None,\n      model_dir=tempfile.mkdtemp(),\n  )\n  default_hparams = HParams(**as_dict)\n  return default_hparams\n\n\ndef create_optimizer(init_lr, num_decay_steps, num_warmup_steps):\n  \"\"\"Creates an optimizer with learning rate schedule.\"\"\"\n  # Leverages cosine decay of the learning rate.\n  learning_rate_fn = tf.keras.experimental.CosineDecay(\n      initial_learning_rate=init_lr, decay_steps=num_decay_steps, alpha=0.0)\n  if num_warmup_steps:\n    learning_rate_fn = warmup.WarmUp(\n        initial_learning_rate=init_lr,\n        decay_schedule_fn=learning_rate_fn,\n        warmup_steps=num_warmup_steps)\n  if hasattr(tf.keras.optimizers, \"legacy\"):\n    optimizer = tf.keras.optimizers.legacy.RMSprop(\n        learning_rate=learning_rate_fn, rho=0.9, momentum=0.9, epsilon=0.001)\n  else:\n    optimizer = tf.keras.optimizers.RMSprop(\n        learning_rate=learning_rate_fn, rho=0.9, momentum=0.9, epsilon=0.001)\n  return optimizer\n\n\ndef get_default_callbacks(model_dir):\n  \"\"\"Gets default callbacks.\"\"\"\n  summary_dir = os.path.join(model_dir, \"summaries\")\n  summary_callback = tf.keras.callbacks.TensorBoard(summary_dir)\n  # Save checkpoint every 20 epochs.\n\n  checkpoint_path = os.path.join(model_dir, \"checkpoint\")\n  checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(\n      checkpoint_path, save_weights_only=True, period=20)\n  return [summary_callback, checkpoint_callback]\n\n\ndef hub_train_model(model, hparams, train_ds, validation_ds, steps_per_epoch):\n  \"\"\"Trains model with the given data and hyperparameters.\n\n  If using a DistributionStrategy, call this under its `.scope()`.\n  Args:\n    model: The tf.keras.Model from _build_model().\n    hparams: A namedtuple of hyperparameters. This function expects\n      .train_epochs: a Python integer with the number of passes over the\n        training dataset;\n      .learning_rate: a Python float forwarded to the optimizer;\n      .momentum: a Python float forwarded to the optimizer;\n      .batch_size: a Python integer, the number of examples returned by each\n        call to the generators.\n    train_ds: tf.data.Dataset, training data to be fed in tf.keras.Model.fit().\n    validation_ds: tf.data.Dataset, validation data to be fed in\n      tf.keras.Model.fit().\n    steps_per_epoch: Integer or None. Total number of steps (batches of samples)\n      before declaring one epoch finished and starting the next epoch. If\n      `steps_per_epoch` is None, the epoch will run until the input dataset is\n      exhausted.\n\n  Returns:\n    The tf.keras.callbacks.History object returned by tf.keras.Model.fit().\n  \"\"\"\n  loss = tf.keras.losses.CategoricalCrossentropy(\n      label_smoothing=hparams.label_smoothing)\n  if hasattr(tf.keras.optimizers, \"legacy\"):\n    optimizer = tf.keras.optimizers.legacy.SGD(\n        learning_rate=hparams.learning_rate, momentum=hparams.momentum)\n  else:\n    optimizer = tf.keras.optimizers.SGD(\n        learning_rate=hparams.learning_rate, momentum=hparams.momentum)\n  model.compile(optimizer=optimizer, loss=loss, metrics=[\"accuracy\"])\n\n  return model.fit(\n      train_ds,\n      epochs=hparams.train_epochs,\n      steps_per_epoch=steps_per_epoch,\n      validation_data=validation_ds)\n\n\ndef train_model(model, hparams, train_ds, validation_ds, steps_per_epoch):\n  \"\"\"Trains model with the given data and hyperparameters.\n\n  Args:\n    model: The tf.keras.Model from _build_model().\n    hparams: A namedtuple of hyperparameters. This function expects\n      .train_epochs: a Python integer with the number of passes over the\n        training dataset;\n      .learning_rate: a Python float forwarded to the optimizer; Base learning\n        rate when train batch size is 256. Linear to the batch size;\n      .batch_size: a Python integer, the number of examples returned by each\n        call to the generators;\n      .warmup_steps: a Python integer, the number of warmup steps for warmup\n        schedule on learning rate. If None, default warmup_steps is used;\n      .model_dir: a Python string, the location of the model checkpoint files.\n    train_ds: tf.data.Dataset, training data to be fed in tf.keras.Model.fit().\n    validation_ds: tf.data.Dataset, validation data to be fed in\n      tf.keras.Model.fit().\n    steps_per_epoch: Integer or None. Total number of steps (batches of samples)\n      before declaring one epoch finished and starting the next epoch. If\n      `steps_per_epoch` is None, the epoch will run until the input dataset is\n      exhausted.\n\n  Returns:\n    The tf.keras.callbacks.History object returned by tf.keras.Model.fit().\n  \"\"\"\n  if steps_per_epoch is None:\n    logging.info(\n        \"steps_per_epoch is None, use %d as the estimated steps_per_epoch\",\n        model_util.ESTIMITED_STEPS_PER_EPOCH)\n    steps_per_epoch = model_util.ESTIMITED_STEPS_PER_EPOCH\n\n  # Learning rate is linear to batch size.\n  learning_rate = hparams.learning_rate * hparams.batch_size / 256\n\n  # Gets decay steps.\n  total_training_steps = steps_per_epoch * hparams.train_epochs\n  default_decay_steps = DEFAULT_DECAY_SAMPLES // hparams.batch_size\n  decay_steps = max(total_training_steps, default_decay_steps)\n\n  warmup_steps = hparams.warmup_steps\n  if warmup_steps is None:\n    warmup_steps = DEFAULT_WARMUP_EPOCHS * steps_per_epoch\n  optimizer = create_optimizer(learning_rate, decay_steps, warmup_steps)\n\n  loss = tf.keras.losses.CategoricalCrossentropy(label_smoothing=0.1)\n  model.compile(optimizer=optimizer, loss=loss, metrics=[\"accuracy\"])\n  callbacks = get_default_callbacks(hparams.model_dir)\n\n  # Trains the models.\n  return model.fit(\n      train_ds,\n      epochs=hparams.train_epochs,\n      validation_data=validation_ds,\n      callbacks=callbacks)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/test_util.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Test util for model maker.\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport functools\nimport os\nimport shutil\nimport tempfile\n\nfrom absl import flags\nimport numpy as np\nimport PIL.Image\n\nimport tensorflow.compat.v2 as tf\nfrom tensorflow_examples.lite.model_maker.core import compat\nfrom tensorflow_examples.lite.model_maker.core.data_util import dataloader\nfrom tensorflow_examples.lite.model_maker.core.task import model_util\n\nFLAGS = flags.FLAGS\n\n\ndef test_srcdir():\n  \"\"\"Returns the path where to look for test data files.\"\"\"\n  if \"test_srcdir\" in flags.FLAGS:\n    return flags.FLAGS[\"test_srcdir\"].value\n  elif \"TEST_SRCDIR\" in os.environ:\n    return os.environ[\"TEST_SRCDIR\"]\n  else:\n    raise RuntimeError(\"Missing TEST_SRCDIR environment.\")\n\n\ndef get_test_data_path(file_or_dirname):\n  \"\"\"Return full test data path.\"\"\"\n  for (directory, subdirs, files) in tf.io.gfile.walk(test_srcdir()):\n    for f in subdirs + files:\n      if f.endswith(file_or_dirname):\n        return os.path.join(directory, f)\n  raise ValueError(\"No %s in test directory\" % file_or_dirname)\n\n\ndef get_cache_dir(temp_dir, filename):\n  \"\"\"Gets `cache_dir` for `tf.keras.utils.get_file` function.\"\"\"\n  # Copies SST-2.zip in testdata folder to a temp folder since\n  # `tf.keras.utils.get_file` needed writability of the path.\n  try:\n    src_path = get_test_data_path(filename)\n    dest_path = os.path.join(temp_dir, \"datasets\")\n    if not tf.io.gfile.exists(dest_path):\n      tf.io.gfile.mkdir(dest_path)\n    shutil.copy2(src_path, dest_path)\n    return temp_dir\n  except ValueError:  # There's no testdata.\n    return None\n\n\ndef test_in_tf_1(fn):\n  \"\"\"Decorator to test in tf 1 behaviors.\"\"\"\n\n  @functools.wraps(fn)\n  def decorator(*args, **kwargs):\n    if compat.get_tf_behavior() != 1:\n      tf.compat.v1.logging.info(\"Skip function {} for test_in_tf_1\".format(\n          fn.__name__))\n      return\n    fn(*args, **kwargs)\n\n  return decorator\n\n\ndef test_in_tf_2(fn):\n  \"\"\"Decorator to test in tf 2 behaviors.\"\"\"\n\n  @functools.wraps(fn)\n  def decorator(*args, **kwargs):\n    if compat.get_tf_behavior() != 2:\n      tf.compat.v1.logging.info(\"Skip function {} for test_in_tf_2\".format(\n          fn.__name__))\n      return\n    fn(*args, **kwargs)\n\n  return decorator\n\n\ndef test_in_tf_1and2(fn):\n  \"\"\"Decorator to test in tf 1 and 2 behaviors.\"\"\"\n\n  @functools.wraps(fn)\n  def decorator(*args, **kwargs):\n    if compat.get_tf_behavior() not in [1, 2]:\n      tf.compat.v1.logging.info(\"Skip function {} for test_in_tf_1and2\".format(\n          fn.__name__))\n      return\n    fn(*args, **kwargs)\n\n  return decorator\n\n\ndef build_model(input_shape, num_classes):\n  \"\"\"Builds a simple model for test.\"\"\"\n  inputs = tf.keras.layers.Input(shape=input_shape)\n  if len(input_shape) == 3:  # Image inputs.\n    outputs = tf.keras.layers.GlobalAveragePooling2D()(inputs)\n    outputs = tf.keras.layers.Dense(num_classes, activation=\"softmax\")(outputs)\n  elif len(input_shape) == 1:  # Text inputs.\n    outputs = tf.keras.layers.Dense(num_classes, activation=\"softmax\")(inputs)\n  else:\n    raise ValueError(\"Model inputs should be 2D tensor or 4D tensor.\")\n\n  model = tf.keras.Model(inputs=inputs, outputs=outputs)\n  return model\n\n\ndef get_dataloader(data_size, input_shape, num_classes, max_input_value=1000):\n  \"\"\"Gets a simple `DataLoader` object for test.\"\"\"\n  features = tf.random.uniform(\n      shape=[data_size] + input_shape,\n      minval=0,\n      maxval=max_input_value,\n      dtype=tf.float32)\n\n  labels = tf.random.uniform(\n      shape=[data_size], minval=0, maxval=num_classes, dtype=tf.int32)\n\n  ds = tf.data.Dataset.from_tensor_slices((features, labels))\n  data = dataloader.DataLoader(ds, data_size)\n  return data\n\n\ndef create_pascal_voc(temp_dir=None):\n  \"\"\"Creates test data with PASCAL VOC format.\"\"\"\n  if temp_dir is None or not tf.io.gfile.exists(temp_dir):\n    temp_dir = tempfile.mkdtemp()\n\n  # Saves the image into images_dir.\n  image_file_name = \"2012_12.jpg\"\n  image_data = np.random.rand(256, 256, 3)\n  images_dir = os.path.join(temp_dir, \"images\")\n  os.mkdir(images_dir)\n  save_path = os.path.join(images_dir, image_file_name)\n  image = PIL.Image.fromarray(image_data, \"RGB\")\n  image.save(save_path)\n\n  # Gets the annonation path.\n  annotations_path = get_test_data_path(\"2012_12.xml\")\n  annotations_dir = os.path.dirname(annotations_path)\n\n  label_map = {\n      1: \"person\",\n      2: \"notperson\",\n  }\n  return images_dir, annotations_dir, label_map\n\n\ndef is_same_output(tflite_file,\n                   keras_model,\n                   input_tensors,\n                   model_spec=None,\n                   atol=1e-04):\n  \"\"\"Whether the output of TFLite model is the same as keras model.\"\"\"\n  # Gets output from lite model.\n  lite_runner = model_util.get_lite_runner(tflite_file, model_spec)\n  lite_output = lite_runner.run(input_tensors)\n\n  # Gets output from keras model.\n  keras_output = keras_model.predict_on_batch(input_tensors)\n\n  return np.allclose(lite_output, keras_output, atol=atol)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/utils/__init__.py",
    "content": "# Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/utils/ondevice_scann_builder.py",
    "content": "# Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"ScannBuilder class for on-device applications.\"\"\"\n\nfrom google.protobuf import text_format\nfrom scann.proto import scann_pb2\nfrom scann.scann_ops.py import scann_builder\nfrom scann.scann_ops.py import scann_ops_pybind\n\n\ndef builder(db, num_neighbors, distance_measure):\n  \"\"\"pybind analogue of builder() in scann_ops.py for the on-device use case.\"\"\"\n\n  def builder_lambda(db, config, training_threads, **kwargs):\n    return scann_ops_pybind.create_searcher(db, config, training_threads,\n                                           **kwargs)\n\n  return OndeviceScannBuilder(\n      db, num_neighbors, distance_measure).set_builder_lambda(builder_lambda)\n\n\nclass OndeviceScannBuilder(scann_builder.ScannBuilder):\n  \"\"\"ScannBuilder for on-device applications.\"\"\"\n\n  def create_config(self):\n    \"\"\"Creates the config.\"\"\"\n    config = super().create_config()\n    config_proto = scann_pb2.ScannConfig()\n    text_format.Parse(config, config_proto)\n    # We don't support residual quantization on device so we need to disable\n    # use_residual_quantization.\n    if config_proto.hash.asymmetric_hash.use_residual_quantization:\n      config_proto.hash.asymmetric_hash.use_residual_quantization = False\n    return text_format.MessageToString(config_proto)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/utils/ondevice_scann_builder_test.py",
    "content": "# Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Tests for ondevice_scann_builder.\"\"\"\n\nimport numpy as np\nimport tensorflow as tf\nfrom google.protobuf import text_format\nfrom scann.proto import scann_pb2\nfrom tensorflow_examples.lite.model_maker.core.utils import ondevice_scann_builder\n\n\nclass OndeviceScannBuilderTest(tf.test.TestCase):\n\n  def test_create_from_config_bruteforce(self):\n    dataset = np.random.random(size=(1000, 1024))\n    builder = ondevice_scann_builder.builder(\n        dataset, num_neighbors=10, distance_measure=\"dot_product\")\n    builder.score_brute_force()\n    config = builder.create_config()\n    config_proto = scann_pb2.ScannConfig()\n    text_format.Parse(config, config_proto)\n    expected_config_proto = \"\"\"num_neighbors: 10\n        distance_measure {\n          distance_measure: \"DotProductDistance\"\n        }\n        brute_force {\n          fixed_point {\n            enabled: false\n          }\n        }\"\"\"\n    self.assertProtoEquals(expected_config_proto, config_proto)\n\n  def test_create_from_config_ah(self):\n    dataset = np.random.random(size=(1000, 1024))\n    builder = ondevice_scann_builder.builder(\n        dataset, num_neighbors=5, distance_measure=\"dot_product\")\n    builder.tree(num_leaves=10, num_leaves_to_search=2)\n    builder.score_ah(\n        dimensions_per_block=1,\n        anisotropic_quantization_threshold=0.2,\n        hash_type=\"lut256\")\n    config = builder.create_config()\n    config_proto = scann_pb2.ScannConfig()\n    text_format.Parse(config, config_proto)\n    # Changes use_residual_quantization = False.\n    expected_config_proto = \"\"\"num_neighbors: 5\n        distance_measure {\n          distance_measure: \"DotProductDistance\"\n        }\n        partitioning {\n          num_children: 10\n          max_clustering_iterations: 12\n          min_cluster_size: 50.0\n          partitioning_distance {\n            distance_measure: \"SquaredL2Distance\"\n          }\n          query_spilling {\n            spilling_type: FIXED_NUMBER_OF_CENTERS\n            max_spill_centers: 2\n          }\n          partitioning_type: GENERIC\n          query_tokenization_distance_override {\n            distance_measure: \"DotProductDistance\"\n          }\n          query_tokenization_type: FLOAT\n          expected_sample_size: 100000\n          single_machine_center_initialization: RANDOM_INITIALIZATION\n        }\n        hash {\n          asymmetric_hash {\n            projection {\n              projection_type: CHUNK\n              num_blocks: 1024\n              num_dims_per_block: 1\n              input_dim: 1024\n            }\n            num_clusters_per_block: 256\n            max_clustering_iterations: 10\n            quantization_distance {\n              distance_measure: \"SquaredL2Distance\"\n            }\n            min_cluster_size: 100.0\n            lookup_type: INT8\n            use_residual_quantization: false\n            fixed_point_lut_conversion_options {\n              float_to_int_conversion_method: ROUND\n            }\n            noise_shaping_threshold: 0.2\n            expected_sample_size: 100000\n            use_global_topn: false\n          }\n        }\"\"\"\n    self.assertProtoEquals(expected_config_proto, config_proto)\n\n\nif __name__ == \"__main__\":\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/utils/scann_converter.py",
    "content": "# Copyright 2022 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Util for converting ScaNN artifacts to on-device format.\"\"\"\nimport os\nfrom typing import NamedTuple, Optional, List, AnyStr\n\nimport numpy as np\nimport tensorflow as tf\n\nfrom tensorflow_lite_support.scann_ondevice.cc.core import serialized_searcher_pb2\nfrom scann.data_format import features_pb2\nfrom scann.partitioning import partitioner_pb2\nfrom scann.proto import centers_pb2\nfrom scann.proto import hash_pb2\nfrom scann.proto import scann_pb2\nfrom tensorflow_lite_support.scann_ondevice.cc.python import index_builder\n\n_DISTANCE_MAP = {\n    'SquaredL2Distance': serialized_searcher_pb2.SQUARED_L2_DISTANCE,\n    'DotProductDistance': serialized_searcher_pb2.DOT_PRODUCT,\n}\n_LOOKUP_TYPE_MAP = {\n    hash_pb2.AsymmetricHasherConfig.FLOAT:\n        serialized_searcher_pb2.AsymmetricHashingProto.FLOAT,\n    hash_pb2.AsymmetricHasherConfig.INT8:\n        serialized_searcher_pb2.AsymmetricHashingProto.INT8,\n    hash_pb2.AsymmetricHasherConfig.INT16:\n        serialized_searcher_pb2.AsymmetricHashingProto.INT16,\n    hash_pb2.AsymmetricHasherConfig.INT8_LUT16:\n        serialized_searcher_pb2.AsymmetricHashingProto.INT8_LUT16,\n}\n\n\nclass OnDeviceArtifacts(NamedTuple):\n  ondevice_config: serialized_searcher_pb2.ScannOnDeviceConfig\n  hashed_dataset: Optional[np.ndarray]\n  float_dataset: Optional[np.ndarray]\n  partition_assignments: Optional[np.ndarray]\n\n\ndef get_distance_measure(\n    distance_measure_str: str) -> serialized_searcher_pb2.DistanceMeasure:\n  \"\"\"Maps a distance measure string to a DistanceMeasure proto.\"\"\"\n  return _DISTANCE_MAP.get(distance_measure_str,\n                           serialized_searcher_pb2.UNSPECIFIED)\n\n\ndef get_indexer(\n    on_device_distance: serialized_searcher_pb2.DistanceMeasure,\n    lookup_type: hash_pb2.AsymmetricHasherConfig.LookupType,\n    ah_codebook: centers_pb2.CentersForAllSubspaces\n) -> serialized_searcher_pb2.IndexerProto:\n  \"\"\"Helper util that builds an indexer for `convert_artifacts_to_leveldb`.\n\n  Args:\n    on_device_distance: DistanceMeasure used in NN search.\n    lookup_type: Type of lookup table used for asymmetric distance NN search.\n    ah_codebook: ScaNN AsymmetricHashing hasher.\n\n  Returns:\n    An IndexerProto.\n\n  Raises:\n    ValueError: If a subspace center in `ah_codebook` uses a feature type other\n    than double or float.\n  \"\"\"\n  indexer = serialized_searcher_pb2.IndexerProto()\n  od_ah_codebook = indexer.asymmetric_hashing\n  od_ah_codebook.query_distance = on_device_distance\n  od_ah_codebook.lookup_type = _LOOKUP_TYPE_MAP[lookup_type]\n  for subspace in ah_codebook.subspace_centers:\n    od_subspace = od_ah_codebook.subspace.add()\n    for center in subspace.center:\n      entry = od_subspace.entry.add()\n      feature_type = center.feature_type\n      if feature_type == features_pb2.GenericFeatureVector.FeatureType.FLOAT:\n        entry.dimension.extend(center.feature_value_float)\n      elif feature_type == features_pb2.GenericFeatureVector.FeatureType.DOUBLE:\n        entry.dimension.extend(center.feature_value_double)\n      else:\n        raise ValueError(\n            f'ah_codebook has unsupported feature_type {feature_type}')\n  return indexer\n\n\ndef get_partitioner(\n    on_device_distance: serialized_searcher_pb2.DistanceMeasure,\n    search_fraction: float,\n    partition_centroids: partitioner_pb2.SerializedPartitioner\n) -> serialized_searcher_pb2.PartitionerProto:\n  \"\"\"Helper util that builds a partitioner for `convert_artifacts_to_leveldb`.\n\n  Args:\n    on_device_distance: DistanceMeasure used in NN search.\n    search_fraction: Fraction of partitions searched.\n    partition_centroids: ScaNN partitioner.\n\n  Returns:\n    A PartitionerProto.\n  \"\"\"\n  od_partitioner = serialized_searcher_pb2.PartitionerProto()\n  od_partitioner.query_distance = on_device_distance\n  od_partitioner.search_fraction = search_fraction\n  for i, center in enumerate(\n      partition_centroids.kmeans.kmeans_tree.root.centers):\n    assert partition_centroids.kmeans.kmeans_tree.root.children[i].leaf_id == i\n    leaf = od_partitioner.leaf.add()\n    leaf.dimension.extend(center.dimension)\n  return od_partitioner\n\n\ndef convert_serialized_to_on_device(serialized_path: str) -> OnDeviceArtifacts:\n  \"\"\"Converts ScaNN's serialized artifacts to on-device format.\n\n  Args:\n    serialized_path: Path to the dir that contains the ScaNN's artifacts.\n\n  Returns:\n    A named tuple containing the on-device ScannOnDeviceConfig as well as the\n    database (maybe compressed) and partition assignment (if partitioning is\n    enabled) in numpy format.\n  \"\"\"\n  config_path = os.path.join(serialized_path, 'scann_config.pb')\n\n  # Load ScannConfig\n  with tf.io.gfile.GFile(config_path, 'rb') as config_pb:\n    scann_config = scann_pb2.ScannConfig.FromString(config_pb.read())\n\n  if scann_config.HasField('exact_reordering'):\n    raise ValueError('exact_reordering is not supported on-device')\n\n  on_device_config = serialized_searcher_pb2.ScannOnDeviceConfig()\n  on_device_distance = get_distance_measure(\n      scann_config.distance_measure.distance_measure)\n  on_device_config.query_distance = on_device_distance\n\n  # Indexed dataset parts\n  ah_quantized_dataset = None\n  float_dataset = None\n  partition_assignments = None\n\n  # Load AH centers\n  if scann_config.HasField('hash'):\n    with tf.io.gfile.GFile(\n        os.path.join(serialized_path, 'ah_codebook.pb'), 'rb') as ah_pb:\n      ah_codebook = centers_pb2.CentersForAllSubspaces.FromString(ah_pb.read())\n      on_device_config.indexer.CopyFrom(\n          get_indexer(on_device_distance,\n                      scann_config.hash.asymmetric_hash.lookup_type,\n                      ah_codebook))\n    ah_quantized_dataset = np.load(\n        os.path.join(serialized_path, 'hashed_dataset.npy'))\n\n  # Load partition centroids\n  if scann_config.HasField('partitioning'):\n    with tf.io.gfile.GFile(\n        os.path.join(serialized_path, 'serialized_partitioner.pb'),\n        'rb') as partition_centroid_pb:\n      partition_centroids = partitioner_pb2.SerializedPartitioner.FromString(\n          partition_centroid_pb.read())\n      on_device_config.partitioner.CopyFrom(\n          get_partitioner(\n              on_device_distance,\n              (scann_config.partitioning.query_spilling.max_spill_centers /\n               scann_config.partitioning.num_children), partition_centroids))\n    partition_assignments = np.load(\n        os.path.join(serialized_path, 'datapoint_to_token.npy'))\n\n  # Load brute force datasets\n  if scann_config.brute_force.fixed_point.enabled:\n    raise NotImplementedError(\n        'Fixed point int8 quantization is not yet supported on-device')\n  elif scann_config.HasField('brute_force'):\n    float_dataset = np.load(os.path.join(serialized_path, 'dataset.npy'))\n\n  return OnDeviceArtifacts(on_device_config, ah_quantized_dataset,\n                           float_dataset, partition_assignments)\n\n\ndef convert_artifacts_to_leveldb(output_file_path: str,\n                                 metadata: List[AnyStr],\n                                 userinfo: AnyStr,\n                                 artifacts: OnDeviceArtifacts,\n                                 compression: bool = True) -> None:\n  \"\"\"Converts artifacts to the index file.\n\n  Raises exception if the input is invalid or failed to create the file.\n\n  Args:\n    output_file_path: Path to the levelDB index file.\n    metadata: The metadata for each of the embeddings in the database. Passed in\n      the same order as the embeddings in artifacts.\n    userinfo: A special field in the index file that can be an arbitrary string\n      supplied by the user.\n    artifacts: Artifacts parsed by the convert_serialized_to_on_device\n    compression: Whether to snappy compress the index file.\n  \"\"\"\n  hashed_dataset = None\n  float_dataset = None\n  if artifacts.hashed_dataset is not None:\n    embedding_dim = int(artifacts.hashed_dataset.shape[1])\n    hashed_dataset = artifacts.hashed_dataset.reshape((-1,))\n  if artifacts.float_dataset is not None:\n    embedding_dim = int(artifacts.float_dataset.shape[1])\n    float_dataset = artifacts.float_dataset.reshape((-1,))\n\n  if len(artifacts.partition_assignments.shape) != 1:\n    raise ValueError('Partition assignment array has to be 1D')\n\n  # Raises exception if both hashed_dataset and float_dataset are not\n  # None, or both are None\n  serialized_index_file = index_builder.create_serialized_index_file(\n      embedding_dim,\n      artifacts.ondevice_config.SerializeToString(),\n      userinfo,\n      artifacts.partition_assignments,\n      metadata,\n      compression=compression,\n      hashed_database=hashed_dataset,\n      float_database=float_dataset)\n  with tf.io.gfile.GFile(output_file_path, 'w') as f:\n    f.write(serialized_index_file)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/core/utils/scann_converter_test.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Tests for scann_converter.\"\"\"\n\nimport os\nimport shutil\n\nfrom absl import flags\nfrom absl.testing import parameterized\nimport numpy as np\nimport tensorflow as tf\n\nfrom google.protobuf import text_format\nfrom tensorflow_lite_support.scann_ondevice.cc.core import serialized_searcher_pb2\nfrom scann.partitioning import partitioner_pb2\nfrom scann.proto import centers_pb2\nfrom scann.scann_ops.py import scann_ops_pybind\nfrom tensorflow_examples.lite.model_maker.core.utils import scann_converter\nfrom tensorflow_lite_support.scann_ondevice.cc.test.python import leveldb_testing_utils\n\nFLAGS = flags.FLAGS\n\nDIMENSIONS = 20\nNUM_NEIGHBORS = 10\n\n\nclass ScannConverterTest(tf.test.TestCase, parameterized.TestCase):\n\n  def setUp(self):\n    super().setUp()\n    self.artifact_dir = os.path.join(FLAGS.test_tmpdir, 'serialized_searcher')\n    os.mkdir(self.artifact_dir)\n\n  def tearDown(self):\n    super().tearDown()\n    shutil.rmtree(self.artifact_dir)\n\n  @parameterized.named_parameters(\n      dict(\n          testcase_name='tree+ah+dot_product',\n          distance='dot_product',\n          tree=True,\n          ah=True,\n          brute_force=False),\n      dict(\n          testcase_name='tree+ah+squared_l2',\n          distance='squared_l2',\n          tree=True,\n          ah=True,\n          brute_force=False),\n      dict(\n          testcase_name='tree+bf+dot_product',\n          distance='dot_product',\n          tree=True,\n          ah=False,\n          brute_force=True),\n      dict(\n          testcase_name='tree+bf+squared_l2',\n          distance='squared_l2',\n          tree=True,\n          ah=False,\n          brute_force=True),\n      dict(\n          testcase_name='ah+dot_product',\n          distance='dot_product',\n          tree=False,\n          ah=True,\n          brute_force=False),\n      dict(\n          testcase_name='ah+squared_l2',\n          distance='squared_l2',\n          tree=False,\n          ah=True,\n          brute_force=False),\n      dict(\n          testcase_name='bf+dot_product',\n          distance='dot_product',\n          tree=False,\n          ah=False,\n          brute_force=True),\n      dict(\n          testcase_name='bf+squared_l2',\n          distance='squared_l2',\n          tree=False,\n          ah=False,\n          brute_force=True),\n  )\n  def test_converts_files_properly(self, distance: str, tree: bool, ah: bool,\n                                   brute_force: bool):\n    dataset_size = 10000\n\n    dataset = np.random.random(size=(dataset_size, DIMENSIONS))\n    builder = scann_ops_pybind.builder(dataset, NUM_NEIGHBORS, distance)\n    if tree:\n      builder = builder.tree(\n          num_leaves=10, num_leaves_to_search=2, training_sample_size=1000)\n    if ah:\n      builder = builder.score_ah(2, anisotropic_quantization_threshold=0.2)\n    if brute_force:\n      builder = builder.score_brute_force(quantize=False)\n    searcher = builder.build()\n    searcher.serialize(self.artifact_dir)\n\n    converted_artifacts = scann_converter.convert_serialized_to_on_device(\n        self.artifact_dir)\n\n    if distance == 'dot_product':\n      self.assertEqual(converted_artifacts.ondevice_config.query_distance,\n                       serialized_searcher_pb2.DOT_PRODUCT)\n    if distance == 'squared_l2':\n      self.assertEqual(converted_artifacts.ondevice_config.query_distance,\n                       serialized_searcher_pb2.SQUARED_L2_DISTANCE)\n\n    if ah:\n      self.assertIsNotNone(converted_artifacts.hashed_dataset)\n      self.assertEqual(converted_artifacts.hashed_dataset.shape,\n                       (dataset_size, (DIMENSIONS + 1) // 2))\n      self.assertEqual(converted_artifacts.hashed_dataset.dtype, np.uint8)\n      with tf.io.gfile.GFile(\n          os.path.join(self.artifact_dir, 'ah_codebook.pb'), 'rb') as ah_pb:\n        ah_codebook = centers_pb2.CentersForAllSubspaces.FromString(\n            ah_pb.read())\n        self._verify_ah_centers(\n            converted_artifacts.ondevice_config.indexer.asymmetric_hashing,\n            ah_codebook)\n\n    if brute_force:\n      self.assertIsNotNone(converted_artifacts.float_dataset)\n      np.testing.assert_allclose(converted_artifacts.float_dataset, dataset)\n\n    if tree:\n      self.assertIsNotNone(converted_artifacts.partition_assignments)\n      self.assertEqual(converted_artifacts.partition_assignments.shape,\n                       (dataset_size,))\n      self.assertEqual(converted_artifacts.partition_assignments.dtype,\n                       np.int32)\n      np.testing.assert_array_less(converted_artifacts.partition_assignments,\n                                   dataset_size)\n      with tf.io.gfile.GFile(\n          os.path.join(self.artifact_dir, 'serialized_partitioner.pb'),\n          'rb') as partition_centroid_pb:\n        partition_centroids = partitioner_pb2.SerializedPartitioner.FromString(\n            partition_centroid_pb.read())\n        self._verify_partition_centroids(\n            converted_artifacts.ondevice_config.partitioner,\n            partition_centroids)\n\n  def _verify_ah_centers(\n      self, device_ah_proto: serialized_searcher_pb2.AsymmetricHashingProto,\n      desktop_ah_proto: centers_pb2.CentersForAllSubspaces) -> None:\n    self.assertEqual(\n        len(device_ah_proto.subspace), len(desktop_ah_proto.subspace_centers))\n    for device_subspace, desktop_subspace in zip(\n        device_ah_proto.subspace, desktop_ah_proto.subspace_centers):\n      if device_ah_proto.lookup_type == device_ah_proto.INT8_LUT16:\n        self.assertLen(device_subspace.entry, 16)\n      else:\n        self.assertLen(device_subspace.entry, 256)\n      self.assertEqual(len(device_subspace.entry), len(desktop_subspace.center))\n      for device_entry, desktop_entry in zip(device_subspace.entry,\n                                             desktop_subspace.center):\n        self.assertEqual(\n            len(device_entry.dimension), len(desktop_entry.feature_value_float))\n        self.assertListEqual(\n            list(device_entry.dimension),\n            list(desktop_entry.feature_value_float))\n\n  def _verify_partition_centroids(\n      self, device_partition_proto: serialized_searcher_pb2.PartitionerProto,\n      desktop_partition_proto: partitioner_pb2.SerializedPartitioner):\n    self.assertEqual(\n        len(device_partition_proto.leaf),\n        len(desktop_partition_proto.kmeans.kmeans_tree.root.centers))\n    for device_centroid, deesktop_centroid in zip(\n        device_partition_proto.leaf,\n        desktop_partition_proto.kmeans.kmeans_tree.root.centers):\n      self.assertEqual(\n          len(device_centroid.dimension), len(deesktop_centroid.dimension))\n      self.assertListEqual(\n          list(device_centroid.dimension), list(deesktop_centroid.dimension))\n\n  @parameterized.named_parameters(\n      dict(testcase_name='hashed_compressed', hashed=True, compressed=True),\n      dict(testcase_name='hashed_uncompressed', hashed=True, compressed=False),\n      dict(testcase_name='float_compressed', hashed=False, compressed=True),\n      dict(testcase_name='float_uncompressed', hashed=False, compressed=False),\n  )\n  def test_generates_index_leveldb_file(self, hashed: bool, compressed: bool):\n    dataset_size = 10\n\n    config = serialized_searcher_pb2.ScannOnDeviceConfig()\n    text_format.Parse(\"\"\"\n    partitioner: {\n      leaf: { dimension: 1 dimension: 1 dimension: 1 }\n      leaf: { dimension: 2 dimension: 2 dimension: 2 }\n      leaf: { dimension: 3 dimension: 3 dimension: 3 }\n    }\n    \"\"\", config)\n    if hashed:\n      hashed_dataset = np.array(\n          [i // DIMENSIONS for i in range(dataset_size * DIMENSIONS)],\n          dtype=np.uint8).reshape((dataset_size, DIMENSIONS))\n      float_dataset = None\n    else:\n      float_dataset = np.array(\n          [i // DIMENSIONS for i in range(dataset_size * DIMENSIONS)],\n          dtype=np.float32).reshape((dataset_size, DIMENSIONS))\n      hashed_dataset = None\n    partition_assignments = np.array([0, 1, 2, 0, 1, 2, 0, 1, 2, 0])\n    metadata = ['0', '1', '2', '3', '你好', b'\\x00\\x22\\x33', '6', '7', '8', '9']\n\n    output_file_path = os.path.join(self.artifact_dir, 'index_file')\n    scann_converter.convert_artifacts_to_leveldb(\n        output_file_path=output_file_path,\n        metadata=metadata,\n        userinfo=b'userinfo\\x00\\x11\\x22\\x34',\n        artifacts=scann_converter.OnDeviceArtifacts(\n            ondevice_config=config,\n            hashed_dataset=hashed_dataset,\n            float_dataset=float_dataset,\n            partition_assignments=partition_assignments),\n        compression=compressed)\n\n    db_contents = dict(\n        leveldb_testing_utils.leveldb_table_to_pair_list(\n            output_file_path, compressed))\n\n    partition_2 = np.frombuffer(\n        db_contents[b'E_2'],\n        dtype=np.uint8 if hashed else np.float32).reshape((3, DIMENSIONS))\n\n    if hashed:\n      expected_dataset = hashed_dataset\n    else:\n      expected_dataset = float_dataset\n    np.testing.assert_array_equal(partition_2[0], expected_dataset[2])\n    np.testing.assert_array_equal(partition_2[1], expected_dataset[5])\n    np.testing.assert_array_equal(partition_2[2], expected_dataset[8])\n\n    config = serialized_searcher_pb2.ScannOnDeviceConfig()\n\n    self.assertEqual(db_contents[b'USER_INFO'], b'userinfo\\x00\\x11\\x22\\x34')\n\n    # Original embedding index after reordering by partitions\n    # [0, 3, 6, 9, 1, 4, 7, 2, 5, 8]\n    self.assertEqual(db_contents[b'M_1'], b'3')\n    self.assertEqual(db_contents[b'M_5'], '你好'.encode('utf-8'))\n    self.assertEqual(db_contents[b'M_8'], b'\\x00\\x22\\x33')\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/demo/__init__.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/demo/audio_classification_demo.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Audio classification demo code of Model Maker for TFLite.\n\nExample usage:\npython audio_classification_demo.py --export_dir=/tmp\n\nSample output:\nDownloading data from\nhttps://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip\n182083584/182082353 [==============================] - 4s 0us/step\n182091776/182082353 [==============================] - 4s 0us/step\nDataset has been downloaded to\n/usr/local/google/home/wangtz/.keras/datasets/mini_speech_commands\nProcessing audio files:\n8000/8000 [==============================] - 354s 44ms/file\nCached 7178 audio samples.\nTraining the model\n5742/5742 [==============================] - 29s 5ms/step - loss: 3.2289 - acc:\n0.8029 - val_loss: 0.6229 - val_acc: 0.9638\nEvaluating the model\n15/15 [==============================] - 2s 12ms/step - loss: 1.3569 - acc:\n0.9270\nTest accuracy: 0.927039\n\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\nimport tensorflow as tf\nfrom tflite_model_maker import audio_classifier\nfrom tflite_model_maker import ExportFormat\nfrom tflite_model_maker import model_spec\n\nFLAGS = flags.FLAGS\n\n\ndef _define_flags():\n  \"\"\"Define CLI flags and their default values.\"\"\"\n  flags.DEFINE_string('export_dir', None,\n                      'The directory to save exported files.')\n  flags.DEFINE_string('data_dir', None, 'The directory to load dataset from.')\n  flags.DEFINE_string('spec', 'audio_browser_fft',\n                      'Name of the model spec to use.')\n  flags.DEFINE_string(\n      'dataset', 'mini_speech_command',\n      'Which dataset to use. Supports: `mini_speech_command`, '\n      '`bird` and `esc50`')\n  flags.mark_flag_as_required('export_dir')\n\n\ndef _download_dataset(filename, url, extracted_folder_name, **kwargs):\n  \"\"\"Downloads the dataset, and returns path to the extracted directory.\"\"\"\n  tf.compat.v1.logging.info('Downloading the dataset.')\n  # ${HOME}/.keras/datasets/FILENAME\n  filepath = tf.keras.utils.get_file(\n      filename, url, cache_subdir='datasets', extract=True, **kwargs)\n  # ${HOME}/.keras/datasets/EXTRACTED_FOLDER_NAME\n  folder_path = os.path.dirname(filepath)\n  folder_path = os.path.join(folder_path, extracted_folder_name)\n  print(f'Dataset has been downloaded to {folder_path}')\n  return folder_path\n\n\ndef download_bird_dataset(**kwargs):\n  \"\"\"Downloads the bird dataset, and returns path to the directory.\"\"\"\n  return _download_dataset(\n      'birds_dataset.zip',\n      'https://storage.googleapis.com/laurencemoroney-blog.appspot.com/birds_dataset.zip',\n      'small_birds_dataset', **kwargs)\n\n\ndef download_speech_commands_dataset(**kwargs):\n  \"\"\"Downloads demo dataset, and returns directory path.\"\"\"\n  return _download_dataset(\n      'mini_speech_commands.zip',\n      'https://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip',\n      'mini_speech_commands', **kwargs)\n\n\ndef download_esc50_dataset(**kwargs):\n  \"\"\"Downloads ESC50 dataset, and returns directory path.\"\"\"\n  return _download_dataset(\n      'esc-50.zip', 'https://github.com/karoldvl/ESC-50/archive/master.zip',\n      'ESC-50-master', **kwargs)\n\n\ndef run(spec,\n        data_dir,\n        dataset_type,\n        export_dir,\n        epochs=5,\n        batch_size=32,\n        **kwargs):\n  \"\"\"Runs demo.\"\"\"\n  spec = model_spec.get(spec)\n\n  if dataset_type == 'esc50':\n    # Limit to 2 categories to speed up the demo\n    categories = ['dog', 'cat']\n    train_data = audio_classifier.DataLoader.from_esc50(\n        spec, data_dir, folds=[0, 1, 2, 3], categories=categories)\n    validation_data = audio_classifier.DataLoader.from_esc50(\n        spec, data_dir, folds=[\n            4,\n        ], categories=categories, cache=True)\n    test_data = audio_classifier.DataLoader.from_esc50(\n        spec, data_dir, folds=[\n            5,\n        ], categories=categories, cache=True)\n  elif dataset_type == 'bird':\n    if isinstance(spec, audio_classifier.YamNetSpec):\n      # In some files, two consecutive bird sounds might be too far apart, so\n      # increase the window size to have a higher probability of capturing the\n      # bird sound.\n      spec = audio_classifier.YamNetSpec(\n          keep_yamnet_and_custom_heads=True,\n          frame_step=3 * audio_classifier.YamNetSpec.EXPECTED_WAVEFORM_LENGTH,\n          frame_length=6 * audio_classifier.YamNetSpec.EXPECTED_WAVEFORM_LENGTH)\n    else:\n      raise ValueError('Bird dataset can only be used with YAMNet model.')\n    batch_size = 128\n    epochs = 100\n    train_data = audio_classifier.DataLoader.from_folder(\n        spec, os.path.join(data_dir, 'train'), cache=True)\n    train_data, validation_data = train_data.split(0.8)\n    test_data = audio_classifier.DataLoader.from_folder(\n        spec, os.path.join(data_dir, 'test'), cache=True)\n\n  else:\n    data = audio_classifier.DataLoader.from_folder(spec, data_dir, cache=True)\n    train_data, rest_data = data.split(0.8)\n    validation_data, test_data = rest_data.split(0.5)\n\n  print('\\nTraining the model')\n  model = audio_classifier.create(\n      train_data,\n      spec,\n      validation_data,\n      batch_size=batch_size,\n      epochs=epochs,\n      **kwargs)\n\n  print('\\nEvaluating the model')\n  model.evaluate(test_data)\n\n  print('\\nConfusion matrix: ')\n  print(model.confusion_matrix(test_data))\n  print('labels: ', test_data.index_to_label)\n\n  print('\\nExporing the Saved model and TFLite model to {}'.format(export_dir))\n  model.export(\n      export_dir, export_format=(ExportFormat.TFLITE, ExportFormat.SAVED_MODEL))\n\n\ndef main(_):\n  logging.set_verbosity(logging.INFO)\n\n  export_dir = os.path.expanduser(FLAGS.export_dir)\n\n  if not FLAGS.data_dir:\n    if FLAGS.dataset == 'esc50':\n      data_dir = download_esc50_dataset()\n    elif FLAGS.dataset == 'bird':\n      data_dir = download_bird_dataset()\n    elif FLAGS.dataset == 'mini_speech_command':\n      data_dir = download_speech_commands_dataset()\n    else:\n      raise ValueError('Unsupported dataset type: ', FLAGS.dataset)\n  else:\n    data_dir = os.path.expanduser(FLAGS.data_dir)\n\n  run(FLAGS.spec, data_dir, FLAGS.dataset, export_dir=export_dir)\n\n\nif __name__ == '__main__':\n  _define_flags()\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/demo/audio_classification_demo_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nimport tempfile\nimport unittest\n\nfrom absl.testing import parameterized\nfrom packaging import version\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.core import test_util\nfrom tensorflow_examples.lite.model_maker.demo import audio_classification_demo\nfrom tflite_model_maker import audio_classifier\n\nfrom_folder_fn = audio_classifier.DataLoader.from_folder\n\n\ndef patch_data_loader():\n  \"\"\"Patch to train partial dataset rather than all of them.\"\"\"\n\n  def side_effect(*args, **kwargs):\n    tf.compat.v1.logging.info('Train on partial dataset')\n    # This takes around 8 mins as it caches all files in the folder.\n    # We should be able to address this issue once the dataset is lazily loaded.\n    data_loader = from_folder_fn(*args, **kwargs)\n    if len(data_loader) > 10:  # Trim dataset to at most 10.\n      data_loader._size = 10\n      # TODO(b/171449557): Change this once the dataset is lazily loaded.\n      data_loader._dataset = data_loader._dataset.take(10)\n    return data_loader\n\n  return unittest.mock.patch.object(\n      audio_classifier.DataLoader, 'from_folder', side_effect=side_effect)\n\n\n@unittest.skipIf(\n    version.parse(tf.__version__) < version.parse('2.5'),\n    'Audio Classification requires TF 2.5 or later')\nclass AudioClassificationDemoTest(tf.test.TestCase, parameterized.TestCase):\n\n  @parameterized.parameters(\n      ('audio_browser_fft', 'mini_speech_command'),\n      ('audio_yamnet', 'mini_speech_command'),\n  )\n  def test_audio_classification_demo(self, spec, dataset):\n    with patch_data_loader():\n      with tempfile.TemporaryDirectory() as temp_dir:\n        # Use cached training data if exists.\n        data_dir = audio_classification_demo.download_speech_commands_dataset(\n            cache_dir=test_util.get_cache_dir(temp_dir,\n                                              'mini_speech_commands.zip'),\n            file_hash='4b8a67bae2973844e84fa7ac988d1a44')\n\n        audio_classification_demo.run(\n            spec, data_dir, dataset, temp_dir, epochs=1, batch_size=1)\n\n        tflite_filename = os.path.join(temp_dir, 'model.tflite')\n        self.assertTrue(tf.io.gfile.exists(tflite_filename))\n        self.assertGreater(os.path.getsize(tflite_filename), 0)\n\n\nif __name__ == '__main__':\n  # Load compressed models from tensorflow_hub\n  os.environ['TFHUB_MODEL_LOAD_FORMAT'] = 'COMPRESSED'\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/demo/custom_model_demo.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"A demo of user defined task - use XOR problem as an example.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport abc\nimport os\nimport tempfile\n\nfrom absl import app\nfrom absl import flags\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core.data_util import data_util\nfrom tensorflow_examples.lite.model_maker.core.data_util import dataloader\nfrom tensorflow_examples.lite.model_maker.core.task import custom_model\nfrom tensorflow_examples.lite.model_maker.core.task import model_util\nfrom tflite_model_maker import config\n\nfrom official.nlp import optimization\n\n# pylint: disable=g-import-not-at-top,bare-except\ntry:\n  from official.common import distribute_utils\nexcept:\n  from official.utils.misc import distribution_utils as distribute_utils\n# pylint: enable=g-import-not-at-top,bare-except\n\nFLAGS = flags.FLAGS\n\n\ndef define_flags():\n  flags.DEFINE_string(\n      'export_dir', None,\n      'The directory to save exported TfLite/saved_model files.')\n  flags.mark_flag_as_required('export_dir')\n\n\nclass DataLoader(dataloader.DataLoader):\n  \"\"\"DataLoader for XOR problem.\"\"\"\n\n  @classmethod\n  def create(cls, spec, is_training=True, shuffle=False):\n    inputs = tf.data.Dataset.from_tensor_slices([(0., 0), (0, 1), (1, 0),\n                                                 (1, 1)])\n    outputs = tf.data.Dataset.from_tensor_slices([0, 1, 1, 0])\n    ds = tf.data.Dataset.zip((inputs, outputs))\n    ds = ds.repeat(10)\n\n    if shuffle:\n      # Add some randomness in train/validation data split.\n      ds = ds.shuffle(len(ds))\n\n    ds = spec.preprocess_ds(ds, is_training=is_training)\n    return cls(ds, len(ds))\n\n\nclass BinaryClassificationBaseSpec(abc.ABC):\n  \"\"\"Model spec for binary classification.\"\"\"\n\n  compat_tf_versions = (2,)\n\n  def __init__(self, model_dir=None, strategy=None):\n    self.model_dir = model_dir\n    if not model_dir:\n      self.model_dir = tempfile.mkdtemp()\n    tf.compat.v1.logging.info('Checkpoints are stored in %s', self.model_dir)\n    self.strategy = strategy or tf.distribute.get_strategy()\n\n  @abc.abstractmethod\n  def create_model(self):\n    pass\n\n  @abc.abstractmethod\n  def run_classifier(self, model, epochs, train_ds, train_steps, validation_ds,\n                     validation_steps, **kwargs):\n    pass\n\n  def preprocess_ds(self, ds, is_training=False):\n    del is_training\n    return ds\n\n\nclass Spec(BinaryClassificationBaseSpec):\n  \"\"\"Spec for XOR problem, contains a model with a single hidden layer.\"\"\"\n\n  def preprocess_ds(self, ds, is_training=False):\n\n    @tf.function\n    def _add_noise(x, y):\n      \"\"\"Add some random noise on the input sample.\"\"\"\n      return tf.random.normal(tf.shape(x), stddev=0.1) + x, y\n\n    if is_training:\n      ds = ds.map(_add_noise)\n    return ds\n\n  def create_model(self):\n    model = tf.keras.Sequential()\n    model.add(tf.keras.Input((2,)))\n    model.add(tf.keras.layers.Dense(4, activation='relu'))\n    model.add(tf.keras.layers.Dense(1, activation='sigmoid'))\n    return model\n\n  def run_classifier(self, model, epochs, train_ds, train_steps, validation_ds,\n                     **kwargs):\n    initial_lr = 0.1\n    total_steps = train_steps * epochs\n    warmup_steps = int(epochs * train_steps * 0.1)\n    optimizer = optimization.create_optimizer(initial_lr, total_steps,\n                                              warmup_steps)\n    model.compile(\n        optimizer=optimizer, loss='binary_crossentropy', metrics=['accuracy'])\n\n    hist = model.fit(\n        train_ds,\n        steps_per_epoch=train_steps,\n        validation_data=validation_ds,\n        epochs=epochs,\n        **kwargs)\n    return hist\n\n\nclass BinaryClassifier(custom_model.CustomModel):\n  \"\"\"BinaryClassifier for train/inference and model export.\"\"\"\n\n  def __init__(self, spec, shuffle=True):\n    assert isinstance(spec, BinaryClassificationBaseSpec)\n    super(BinaryClassifier, self).__init__(spec, shuffle)\n\n  def train(self, train_data, validation_data, epochs=10, batch_size=4):\n    if len(train_data) < batch_size:\n      raise ValueError('The size of the train_data (%d) couldn\\'t be smaller '\n                       'than batch_size (%d). To solve this problem, set '\n                       'the batch_size smaller or increase the size of the '\n                       'train_data.' % (len(train_data), batch_size))\n\n    with distribute_utils.get_strategy_scope(self.model_spec.strategy):\n      train_ds = train_data.gen_dataset(batch_size, is_training=True)\n      train_steps = len(train_data) // batch_size\n      validation_ds = validation_data.gen_dataset(\n          batch_size, is_training=False) if validation_data else None\n\n      self.model = self.model_spec.create_model()\n      return self.model_spec.run_classifier(\n          self.model,\n          epochs,\n          train_ds,\n          train_steps,\n          validation_ds,\n          callbacks=self._keras_callbacks(self.model_spec.model_dir))\n\n  def evaluate(self, data, batch_size=4):\n    ds = data.gen_dataset(batch_size, is_training=False)\n    return self.model.evaluate(ds, return_dict=True)\n\n  def evaluate_tflite(self, tflite_filepath, data):\n    ds = data.gen_dataset(batch_size=1, is_training=False)\n\n    predictions, labels = [], []\n\n    lite_runner = model_util.get_lite_runner(tflite_filepath, self.model_spec)\n    for i, (feature, label) in enumerate(data_util.generate_elements(ds)):\n      log_steps = 1000\n      tf.compat.v1.logging.log_every_n(tf.compat.v1.logging.INFO,\n                                       'Processing example: #%d\\n%s', log_steps,\n                                       i, feature)\n\n      probability = lite_runner.run(feature)  # Shape: (batch=1, 1)\n      probability = probability.flatten()[0]  # Get sclar value\n      predictions.append(probability > 0.5)\n\n      label = label[0]\n      labels.append(label)\n\n    predictions = np.array(predictions).astype(int)\n    labels = np.array(labels).astype(int)\n\n    return {'accuracy': (predictions == labels).mean()}\n\n\ndef train_xor_model(export_dir):\n  \"\"\"Use deep learning to solve XOR problem and return the test accuracy.\"\"\"\n  spec = Spec(model_dir=export_dir)\n\n  data = DataLoader.create(spec, is_training=True)\n  train_data, validation_data = data.split(0.8)\n\n  classifier = BinaryClassifier(spec)\n  classifier.train(train_data, validation_data)\n\n  test_data = DataLoader.create(spec, is_training=False)\n  eval_result = classifier.evaluate(test_data)\n  eval_acc = eval_result['accuracy']\n  print('Test accuracy: %f' % eval_acc)\n\n  # Convert and quantize the model to tflite format.\n  quantization = config.QuantizationConfig.for_int8(\n      representative_data=test_data, quantization_steps=len(test_data))\n  classifier.export(\n      export_dir,\n      quantization_config=quantization,\n  )\n  classifier.export(\n      export_dir, export_format=(config.ExportFormat.SAVED_MODEL,))\n\n  # Evaluate quantized tflite model.\n  result = classifier.evaluate_tflite(\n      os.path.join(export_dir, 'model.tflite'), test_data)\n  test_acc = result['accuracy']\n  print('Test accuracy on quantized TfLite model: %f' % test_acc)\n  return test_acc\n\n\ndef main(_):\n  train_xor_model(FLAGS.export_dir)\n\n\nif __name__ == '__main__':\n  define_flags()\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/demo/custom_model_demo_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nimport tempfile\n\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.demo import custom_model_demo\n\n\nclass DemoTest(tf.test.TestCase):\n\n  def test_demo(self):\n    with tempfile.TemporaryDirectory() as temp_dir:\n      tflite_filename = os.path.join(temp_dir, 'model.tflite')\n      saved_model_filename = os.path.join(temp_dir,\n                                          'saved_model/saved_model.pb')\n\n      seed = 100\n      tf.random.set_seed(seed)\n      acc = custom_model_demo.train_xor_model(temp_dir)\n      self.assertEqual(acc, 1.)\n\n      def exists(filename):\n        self.assertTrue(tf.io.gfile.exists(filename))\n        self.assertGreater(os.path.getsize(filename), 0)\n\n      exists(tflite_filename)\n      exists(saved_model_filename)\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/demo/image_classification.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"h2q27gKz1H20\"\n      },\n      \"source\": [\n        \"##### Copyright 2019 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"TUfAcER1oUS6\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Gb7qyhNL1yWt\"\n      },\n      \"source\": [\n        \"# Image classification with TensorFlow Lite Model Maker with TensorFlow 2.0\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"nDABAblytltI\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://www.tensorflow.org/lite/tutorials/model_maker_image_classification\\\"><img src=\\\"https://www.tensorflow.org/images/tf_logo_32px.png\\\" />View on TensorFlow.org</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/tutorials/model_maker_image_classification.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/tutorials/model_maker_image_classification.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a href=\\\"https://storage.googleapis.com/tensorflow_docs/tensorflow/tensorflow/lite/g3doc/tutorials/model_maker_image_classification.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/download_logo_32px.png\\\" />Download notebook</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"m86-Nh4pMHqY\"\n      },\n      \"source\": [\n        \"This notebook has been moved [here](https://www.tensorflow.org/lite/tutorials/model_maker_image_classification).\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"image_classification.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/demo/image_classification_demo.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Image classification demo code of Model Maker for TFLite.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\n\nimport tensorflow as tf\n\nfrom tflite_model_maker import image_classifier\nfrom tflite_model_maker import model_spec\nfrom tflite_model_maker.config import ExportFormat\n\nFLAGS = flags.FLAGS\n\n\ndef define_flags():\n  flags.DEFINE_string('export_dir', None,\n                      'The directory to save exported files.')\n  flags.DEFINE_string('spec', 'efficientnet_lite0',\n                      'The image classifier to run.')\n  flags.mark_flag_as_required('export_dir')\n\n\ndef download_demo_data(**kwargs):\n  \"\"\"Downloads demo data, and returns directory path.\"\"\"\n  data_dir = tf.keras.utils.get_file(\n      fname='flower_photos.tgz',\n      origin='https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',\n      extract=True,\n      **kwargs)\n  return os.path.join(os.path.dirname(data_dir), 'flower_photos')  # folder name\n\n\ndef run(data_dir, export_dir, spec='efficientnet_lite0', **kwargs):\n  \"\"\"Runs demo.\"\"\"\n  spec = model_spec.get(spec)\n  data = image_classifier.DataLoader.from_folder(data_dir)\n  train_data, rest_data = data.split(0.8)\n  validation_data, test_data = rest_data.split(0.5)\n\n  model = image_classifier.create(\n      train_data, model_spec=spec, validation_data=validation_data, **kwargs)\n\n  _, acc = model.evaluate(test_data)\n  print('Test accuracy: %f' % acc)\n\n  # Exports to TFLite and SavedModel, with label file.\n  export_format = [\n      ExportFormat.TFLITE,\n      ExportFormat.SAVED_MODEL,\n  ]\n  model.export(export_dir, export_format=export_format)\n\n\ndef main(_):\n  logging.set_verbosity(logging.INFO)\n  data_dir = download_demo_data()\n  export_dir = os.path.expanduser(FLAGS.export_dir)\n  run(data_dir, export_dir, spec=FLAGS.spec)\n\n\nif __name__ == '__main__':\n  define_flags()\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/demo/image_classification_demo_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nimport tempfile\nfrom unittest.mock import patch\n\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.core import test_util\nfrom tensorflow_examples.lite.model_maker.demo import image_classification_demo\nfrom tflite_model_maker import image_classifier\n\nfrom_folder_fn = image_classifier.DataLoader.from_folder\n\n\ndef patch_data_loader():\n  \"\"\"Patch to train partial dataset rather than all of them.\"\"\"\n\n  def side_effect(*args, **kwargs):\n    tf.compat.v1.logging.info('Train on partial dataset')\n    data_loader = from_folder_fn(*args, **kwargs)\n    if len(data_loader) > 10:  # Trim dataset to at most 10.\n      data_loader._size = 10\n      # TODO(b/171449557): Change this once the dataset is lazily loaded.\n      data_loader._dataset = data_loader._dataset.take(10)\n    return data_loader\n\n  return patch.object(\n      image_classifier.DataLoader, 'from_folder', side_effect=side_effect)\n\n\nclass ImageClassificationDemoTest(tf.test.TestCase):\n\n  def test_image_classification_demo(self):\n    with patch_data_loader():\n      with tempfile.TemporaryDirectory() as temp_dir:\n        # Use cached training data if exists.\n        data_dir = image_classification_demo.download_demo_data(\n            cache_dir=test_util.get_cache_dir(temp_dir, 'flower_photos.tgz'),\n            file_hash='6f87fb78e9cc9ab41eff2015b380011d')\n\n        tflite_filename = os.path.join(temp_dir, 'model.tflite')\n        label_filename = os.path.join(temp_dir, 'labels.txt')\n        image_classification_demo.run(\n            data_dir,\n            temp_dir,\n            spec='efficientnet_lite0',\n            epochs=1,\n            batch_size=1)\n\n        self.assertTrue(tf.io.gfile.exists(tflite_filename))\n        self.assertGreater(os.path.getsize(tflite_filename), 0)\n\n        self.assertFalse(tf.io.gfile.exists(label_filename))\n\n\nif __name__ == '__main__':\n  # Load compressed models from tensorflow_hub\n  os.environ['TFHUB_MODEL_LOAD_FORMAT'] = 'COMPRESSED'\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/demo/question_answer_demo.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Text classification demo code of model customization for TFLite.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\n\nimport tensorflow.compat.v2 as tf\nfrom tflite_model_maker import model_spec\nfrom tflite_model_maker import question_answer\n\nFLAGS = flags.FLAGS\n\n\ndef define_flags():\n  flags.DEFINE_string('export_dir', None,\n                      'The directory to save exported files.')\n  flags.mark_flag_as_required('export_dir')\n  flags.DEFINE_string('spec', 'bert_qa', 'The QA model to run.')\n\n\ndef download_demo_data(**kwargs):\n  \"\"\"Downloads demo data, and returns directory path.\"\"\"\n  train_data_path = tf.keras.utils.get_file(\n      fname='train-v1.1.json',\n      origin='https://rajpurkar.github.io/SQuAD-explorer/dataset/train-v1.1.json',\n      **kwargs)\n  validation_data_path = tf.keras.utils.get_file(\n      fname='dev-v1.1.json',\n      origin='https://rajpurkar.github.io/SQuAD-explorer/dataset/dev-v1.1.json',\n      **kwargs)\n  return train_data_path, validation_data_path\n\n\ndef run(train_data_path,\n        validation_data_path,\n        export_dir,\n        spec='bert_qa',\n        **kwargs):\n  \"\"\"Runs demo.\"\"\"\n  # Chooses model specification that represents model.\n  spec = model_spec.get(spec)\n\n  # Gets training data and validation data.\n  train_data = question_answer.DataLoader.from_squad(\n      train_data_path, spec, is_training=True)\n  validation_data = question_answer.DataLoader.from_squad(\n      validation_data_path, spec, is_training=False)\n\n  # Fine-tunes the model.\n  model = question_answer.create(train_data, model_spec=spec, **kwargs)\n\n  # Gets evaluation results.\n  metric = model.evaluate(validation_data)\n  tf.compat.v1.logging.info('Eval F1 score:%f' % metric['final_f1'])\n\n  # Exports to TFLite format.\n  model.export(export_dir)\n\n\ndef main(_):\n  logging.set_verbosity(logging.INFO)\n\n  train_data_path, validation_data_path = download_demo_data()\n  export_dir = os.path.expanduser(FLAGS.export_dir)\n\n  run(train_data_path, validation_data_path, export_dir, spec=FLAGS.spec)\n\n\nif __name__ == '__main__':\n  define_flags()\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/demo/recommendation_demo.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Recommendation demo code of Model Maker for TFLite.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\n\nfrom tflite_model_maker import model_spec as ms\nfrom tflite_model_maker import recommendation\n\nFLAGS = flags.FLAGS\n\n\ndef define_flags():\n  flags.DEFINE_string('data_dir', None, 'The directory to save dataset.')\n  flags.DEFINE_string('export_dir', None,\n                      'The directory to save exported files.')\n  flags.DEFINE_string('encoder_type', 'bow',\n                      'The recommendation encoder to run. (bow, cnn, lstm)')\n  flags.mark_flag_as_required('export_dir')\n\n\ndef download_data(download_dir):\n  \"\"\"Downloads demo data, and returns directory path.\"\"\"\n  return recommendation.DataLoader.download_and_extract_movielens(download_dir)\n\n\ndef get_input_spec(encoder_type: str,\n                   num_classes: int) -> recommendation.spec.InputSpec:\n  \"\"\"Gets input spec (for test).\n\n  Input spec defines how the input features are extracted.\n\n  Args:\n    encoder_type: str, case-insensitive {'CNN', 'LSTM', 'BOW'}.\n    num_classes: int, num of classes in vocabulary.\n\n  Returns:\n    InputSpec.\n  \"\"\"\n  etype = encoder_type.upper()\n  if etype not in {'CNN', 'LSTM', 'BOW'}:\n    raise ValueError('Not support encoder_type: {}'.format(etype))\n\n  return recommendation.spec.InputSpec(\n      activity_feature_groups=[\n          # Group #1: defines how features are grouped in the first Group.\n          dict(\n              features=[\n                  # First feature.\n                  dict(\n                      feature_name='context_movie_id',  # Feature name\n                      feature_type='INT',  # Feature type\n                      vocab_size=num_classes,  # ID size (number of IDs)\n                      embedding_dim=8,  # Projected feature embedding dim\n                      feature_length=10,  # History length of 10.\n                  ),\n                  # Maybe more features...\n              ],\n              encoder_type='CNN',  # CNN encoder (e.g. CNN, LSTM, BOW)\n          ),\n          # Maybe more groups...\n      ],\n      label_feature=dict(\n          feature_name='label_movie_id',  # Label feature name\n          feature_type='INT',  # Label type\n          vocab_size=num_classes,  # Label size (number of classes)\n          embedding_dim=8,  # Label embedding demension\n          feature_length=1,  # Exactly 1 label\n      ),\n  )\n\n\ndef get_model_hparams() -> recommendation.spec.ModelHParams:\n  \"\"\"Gets model hparams (for test).\n\n  ModelHParams defines the model architecture.\n\n  Returns:\n    ModelHParams.\n  \"\"\"\n  return recommendation.spec.ModelHParams(\n      hidden_layer_dims=[32, 32],  # Hidden layers dimension.\n      eval_top_k=[1, 5],  # Eval top 1 and top 5.\n      conv_num_filter_ratios=[2, 4],  # For CNN encoder, conv filter mutipler.\n      conv_kernel_size=16,  # For CNN encoder, base kernel size.\n      lstm_num_units=16,  # For LSTM/RNN, num units.\n      num_predictions=10,  # Number of output predictions. Select top 10.\n  )\n\n\ndef run(data_dir, export_dir, batch_size=16, epochs=5, encoder_type='bow'):\n  \"\"\"Runs demo.\"\"\"\n  meta = recommendation.DataLoader.generate_movielens_dataset(data_dir)\n  num_classes = recommendation.DataLoader.get_num_classes(meta)\n\n  input_spec = get_input_spec(encoder_type, num_classes)\n  train_data = recommendation.DataLoader.from_movielens(data_dir, 'train',\n                                                        input_spec)\n  test_data = recommendation.DataLoader.from_movielens(data_dir, 'test',\n                                                       input_spec)\n\n  model_spec = ms.get(\n      'recommendation',\n      input_spec=input_spec,\n      model_hparams=get_model_hparams())\n  # Create a model and train.\n  model = recommendation.create(\n      train_data,\n      model_spec=model_spec,\n      model_dir=export_dir,\n      validation_data=test_data,\n      batch_size=batch_size,\n      epochs=epochs)\n\n  # Evaluate with test_data.\n  history = model.evaluate(test_data)\n  print('Test metrics from Keras model: %s' % history)\n\n  # Export tflite model.\n  model.export(export_dir)\n\n  # Evaluate tflite model.\n  tflite_model = os.path.join(export_dir, 'model.tflite')\n  history = model.evaluate_tflite(tflite_model, test_data)\n  print('Test metrics from TFLite model: %s' % history)\n\n\ndef main(_):\n  logging.set_verbosity(logging.INFO)\n\n  export_dir = os.path.expanduser(FLAGS.export_dir)\n  data_dir = os.path.expanduser(FLAGS.data_dir)\n\n  extracted_dir = download_data(data_dir)\n  run(extracted_dir, export_dir, encoder_type=FLAGS.encoder_type)\n\n\nif __name__ == '__main__':\n  define_flags()\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/demo/recommendation_demo_test.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Recommendation demo test.\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nimport shutil\nimport tempfile\nfrom unittest import mock\nimport zipfile\n\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.core import test_util\nfrom tensorflow_examples.lite.model_maker.core.data_util import recommendation_testutil as _rt\nfrom tensorflow_examples.lite.model_maker.demo import recommendation_demo\nfrom tflite_model_maker import recommendation\n\n\ndef setup_testdata(instance):\n  \"\"\"Setup testdata under download_dir, and unzip data to dataset_dir.\"\"\"\n  if not hasattr(instance, 'test_tempdir'):\n    instance.test_tempdir = tempfile.mkdtemp()\n  instance.download_dir = os.path.join(instance.test_tempdir, 'download')\n\n  # Copy zip file and unzip.\n  os.makedirs(instance.download_dir, exist_ok=True)\n  # Use existing copy of data, if exists; otherwise, download it.\n  try:\n    path = test_util.get_test_data_path('recommendation_movielens')\n    zip_file = os.path.join(path, 'ml-1m.zip')\n    shutil.copy(zip_file, instance.download_dir)\n    with zipfile.ZipFile(zip_file, 'r') as zfile:\n      zfile.extractall(instance.download_dir)  # Will generate at 'ml-1m'.\n    instance.dataset_dir = os.path.join(instance.download_dir, 'ml-1m')\n  except ValueError:\n    instance.dataset_dir = recommendation_demo.download_data(\n        instance.download_dir)\n\n\ndef patch_data_loader():\n  \"\"\"Patch to train/eval partial dataset rather than all of them.\"\"\"\n\n  def mocked_init(self, dataset, size, vocab):\n    \"\"\"Mocked init function with a smaller dataset.\"\"\"\n    size = 16  # small size for dataset.\n    self._dataset = dataset.take(size)\n    self._size = size\n    self.vocab = vocab\n    self.max_vocab_id = max(self.vocab.keys())\n\n  return mock.patch.object(recommendation.DataLoader, '__init__', mocked_init)\n\n\nclass RecommendationDemoTest(tf.test.TestCase):\n\n  def setUp(self):\n    super().setUp()\n    setup_testdata(self)\n\n  def test_recommendation_demo(self):\n    with _rt.patch_download_and_extract_data(self.dataset_dir):\n      data_dir = recommendation_demo.download_data(self.download_dir)\n    self.assertEqual(data_dir, self.dataset_dir)\n\n    export_dir = os.path.join(self.test_tempdir, 'export')\n    tflite_filename = os.path.join(export_dir, 'model.tflite')\n    with patch_data_loader():\n      recommendation_demo.run(data_dir, export_dir, epochs=1)\n\n    self.assertTrue(tf.io.gfile.exists(tflite_filename))\n    self.assertGreater(os.path.getsize(tflite_filename), 0)\n\n\nif __name__ == '__main__':\n  # Load compressed models from tensorflow_hub\n  os.environ['TFHUB_MODEL_LOAD_FORMAT'] = 'COMPRESSED'\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/demo/text_classification.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"h2q27gKz1H20\"\n      },\n      \"source\": [\n        \"##### Copyright 2019 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"cellView\": \"form\",\n        \"id\": \"TUfAcER1oUS6\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Gb7qyhNL1yWt\"\n      },\n      \"source\": [\n        \"# Text classification with TensorFlow Lite Model Maker with TensorFlow 2.0\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Fw5Y7snSuG51\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://www.tensorflow.org/lite/tutorials/model_maker_text_classification\\\"><img src=\\\"https://www.tensorflow.org/images/tf_logo_32px.png\\\" />View on TensorFlow.org</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/tutorials/model_maker_text_classification.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/tutorials/model_maker_text_classification.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a href=\\\"https://storage.googleapis.com/tensorflow_docs/tensorflow/tensorflow/lite/g3doc/tutorials/model_maker_text_classification.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/download_logo_32px.png\\\" />Download notebook</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"sr3q-gvm3cI8\"\n      },\n      \"source\": [\n        \"This notebook has been moved [here](https://www.tensorflow.org/lite/tutorials/model_maker_text_classification).\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"text_classification.ipynb\",\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/demo/text_classification_demo.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Text classification demo code of Model Maker for TFLite.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\n\nimport tensorflow as tf\n\nfrom tflite_model_maker import model_spec\nfrom tflite_model_maker import text_classifier\nfrom tflite_model_maker.config import ExportFormat\n\nFLAGS = flags.FLAGS\n\n\ndef define_flags():\n  flags.DEFINE_string('export_dir', None,\n                      'The directory to save exported files.')\n  flags.DEFINE_string('spec', 'mobilebert_classifier',\n                      'The text classifier to run.')\n  flags.mark_flag_as_required('export_dir')\n\n\ndef download_demo_data(**kwargs):\n  \"\"\"Downloads demo data, and returns directory path.\"\"\"\n  data_path = tf.keras.utils.get_file(\n      fname='SST-2.zip',\n      origin='https://dl.fbaipublicfiles.com/glue/data/SST-2.zip',\n      extract=True,\n      **kwargs)\n  return os.path.join(os.path.dirname(data_path), 'SST-2')  # folder name\n\n\ndef run(data_dir, export_dir, spec='mobilebert_classifier', **kwargs):\n  \"\"\"Runs demo.\"\"\"\n  # Chooses model specification that represents model.\n  spec = model_spec.get(spec)\n\n  # Gets training data and validation data.\n  train_data = text_classifier.DataLoader.from_csv(\n      filename=os.path.join(os.path.join(data_dir, 'train.tsv')),\n      text_column='sentence',\n      label_column='label',\n      model_spec=spec,\n      delimiter='\\t',\n      is_training=True)\n  validation_data = text_classifier.DataLoader.from_csv(\n      filename=os.path.join(os.path.join(data_dir, 'dev.tsv')),\n      text_column='sentence',\n      label_column='label',\n      model_spec=spec,\n      delimiter='\\t',\n      is_training=False)\n\n  # Fine-tunes the model.\n  model = text_classifier.create(\n      train_data, model_spec=spec, validation_data=validation_data, **kwargs)\n\n  # Gets evaluation results.\n  _, acc = model.evaluate(validation_data)\n  print('Eval accuracy: %f' % acc)\n\n  # Exports to TFLite and SavedModel, with label and vocab files.\n  export_format = [\n      ExportFormat.TFLITE,\n      ExportFormat.SAVED_MODEL,\n  ]\n  model.export(export_dir, export_format=export_format)\n\n\ndef main(_):\n  logging.set_verbosity(logging.INFO)\n  data_dir = download_demo_data()\n  export_dir = os.path.expanduser(FLAGS.export_dir)\n\n  run(data_dir, export_dir, spec=FLAGS.spec)\n\n\nif __name__ == '__main__':\n  define_flags()\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/demo/text_classification_demo_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nimport tempfile\nfrom unittest.mock import patch\n\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.core import test_util\nfrom tensorflow_examples.lite.model_maker.demo import text_classification_demo\nfrom tflite_model_maker import text_classifier\n\nfrom_csv_fn = text_classifier.DataLoader.from_csv\n\n\ndef patch_data_loader():\n  \"\"\"Patch to train partial dataset rather than all of them.\"\"\"\n\n  def side_effect(*args, **kwargs):\n    tf.compat.v1.logging.info('Train on partial dataset')\n    data_loader = from_csv_fn(*args, **kwargs)\n    if len(data_loader) > 2:  # Trim dataset to at most 2.\n      data_loader._size = 2\n      # TODO(b/171449557): Change this once dataset is lazily loaded.\n      data_loader._dataset = data_loader._dataset.take(2)\n    return data_loader\n\n  return patch.object(\n      text_classifier.DataLoader, 'from_csv', side_effect=side_effect)\n\n\nclass TextClassificationDemoTest(tf.test.TestCase):\n\n  def test_text_classification_demo(self):\n    with patch_data_loader():\n      with tempfile.TemporaryDirectory() as temp_dir:\n        # Use cached training data if exists.\n        data_dir = text_classification_demo.download_demo_data(\n            cache_dir=test_util.get_cache_dir(temp_dir, 'SST-2.zip'),\n            file_hash='9f81648d4199384278b86e315dac217c')\n\n        tflite_filename = os.path.join(temp_dir, 'model.tflite')\n        label_filename = os.path.join(temp_dir, 'labels.txt')\n        vocab_filename = os.path.join(temp_dir, 'vocab')\n        # TODO(b/150597348): Bert model is out of memory when export to tflite.\n        # Changed to a smaller bert models like mobilebert later for unittest.\n        text_classification_demo.run(\n            data_dir, temp_dir, spec='average_word_vec', epochs=1, batch_size=1)\n\n        self.assertTrue(tf.io.gfile.exists(tflite_filename))\n        self.assertGreater(os.path.getsize(tflite_filename), 0)\n\n        self.assertFalse(tf.io.gfile.exists(label_filename))\n        self.assertFalse(tf.io.gfile.exists(vocab_filename))\n\n\nif __name__ == '__main__':\n  # Load compressed models from tensorflow_hub\n  os.environ['TFHUB_MODEL_LOAD_FORMAT'] = 'COMPRESSED'\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/pip_package/create_venv.sh",
    "content": "#!/bin/bash\n# Create virtual environment.\n\nset -e\nset -x\n\nif [[ \"$1\" == \"--nightly\" ]]; then\n  VENV_HOME=/tmp/venv_nightly/\nelse\n  VENV_HOME=/tmp/venv/\nfi\nPIP_FLAG=\"--user\"\n\nfunction create_venv_or_activate {\n  local PY=\"$(which python3.8)\"\n  local PIP=\"$(which pip3.8)\"\n  # Test whether pip exists.\n  if [[ \"${PY}\" == \"\" ]]; then\n    echo \"python is not available.\"\n    exit 1\n  fi\n\n  if [[ ! -d \"${VENV_HOME}\" ]]; then\n    # Install virtualenv and create VENV_HOME\n    \"${PIP?}\" install virtualenv ${PIP_FLAG}\n    \"${PY?}\" -m venv \"${VENV_HOME}\"\n    source \"${VENV_HOME}/bin/activate\"\n\n    # Install required package: twine, wheel\n    PY=\"$(which python3.8)\"\n    \"${PY?}\" -m pip install twine==3.7.1 wheel\n  else\n    source \"${VENV_HOME}/bin/activate\"\n  fi\n}\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/pip_package/golden_api_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Test all golden APIs.\n\nRun this test, after `tflite_model_maker` package is installed.\n\npython golden_api_test.py\n\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport importlib\nfrom typing import List\n\nfrom absl.testing import parameterized\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.core.api import api_gen\n\nGOLDEN = api_gen.load_golden('golden_api.json')\n\n\nclass GoldenApiTest(tf.test.TestCase, parameterized.TestCase):\n\n  @parameterized.parameters(GOLDEN.items())\n  def test_golden_apis(self, package: str, import_lines: List[str]):\n    \"\"\"Test all golden API symbols.\"\"\"\n    import tflite_model_maker  # pylint: disable=g-import-not-at-top\n\n    for line in import_lines:\n      if not package:\n        continue\n\n      parts = package.split('.')\n      # Get `c`, for `from a import c` or `from a import b as c`.\n      name = line.split()[-1]\n      parts.append(name)\n\n      # Assert there is no error for golden API.\n      symbol = tflite_model_maker\n      for p in parts:\n        symbol = getattr(symbol, p)\n\n  @parameterized.parameters(\n      # Golden APIs:\n      ('tflite_model_maker'),\n      ('tflite_model_maker.question_answer'),\n      # Internal packages:\n      ('tflite_model_maker.python'),\n      ('tflite_model_maker.python.cli.cli'),\n      ('tflite_model_maker.python.core.export_format'),\n      ('tflite_model_maker.python.demo.question_answer_demo'),\n  )\n  def test_absolute_import(self, name: str):\n    \"\"\"Tests absolute import (internal python.* are subject to change).\"\"\"\n    importlib.import_module(name)\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/pip_package/setup.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Setup for Model Maker.\n\nTo build package:\n  python setup.py sdist bdist_wheel\n\nTo install directly:\n  pip install -e .\n\nTo uninstall:\n  pip uninstall tflite-model-maker\n\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport datetime\nimport os\nimport pathlib\nimport sys\nimport setup_util\n\nfrom setuptools import setup\n\nnightly = False\nif '--nightly' in sys.argv:\n  nightly = True\n  sys.argv.remove('--nightly')\n\nproject_name = 'tflite-model-maker'\ndatestring = datetime.datetime.now().strftime('%Y%m%d%H%M')\nclassifiers = [\n    'Intended Audience :: Developers',\n    'License :: OSI Approved :: Apache Software License',\n    'Topic :: Scientific/Engineering',\n    'Topic :: Scientific/Engineering :: Artificial Intelligence',\n    'Topic :: Scientific/Engineering :: Mathematics',\n    'Topic :: Software Development',\n    'Topic :: Software Development :: Libraries',\n    'Topic :: Software Development :: Libraries :: Python Modules',\n]\n\n# Set package version.\nif nightly:\n  project_name = '{}-nightly'.format(project_name)\n  version = '0.4.4'  # Version prefix, usually major.minor.micro.\n  version = '{:s}.dev{:s}'.format(version, datestring)\n  classifiers += [\n      'Development Status :: 4 - Beta',\n  ]\nelse:\n  # LINT.IfChange(model_maker_pip_version)\n  version = '0.4.3'\n  # LINT.ThenChange(../public/__init__.py, ../RELEASE.md)\n\n# Path to model_maker dir: <repo>/tensorflow_examples/lite/model_maker\nBASE_DIR = pathlib.Path(os.path.abspath(__file__)).parents[1]\n# Path to root dir: <repo>\nROOT_DIR = BASE_DIR.parents[2]\n# Original namespace of the lib.\nLIB_NAMESPACE = 'tensorflow_examples.lite.model_maker'\n# Official package namespace for API. Used as code name.\nAPI_NAMESPACE = 'tflite_model_maker'\n# Internal package tflite_model_maker.python mapping internal packages.\nINTERNAL_NAME = 'python'\nMODEL_MAKER_CONSOLE = 'tflite_model_maker=tflite_model_maker.python.cli.cli:main'\n\n# Build dir `pip_package/src`: copy all source code and create a package.\nSRC_NAME = 'src'\nBUILD_DIR = BASE_DIR.joinpath('pip_package').joinpath(SRC_NAME)\n\n# Setup options.\nsetup_options = {\n    'package_dir': {\n        '': SRC_NAME\n    },\n    'entry_points': {\n        'console_scripts': [MODEL_MAKER_CONSOLE,],\n    },\n}\n\nDESCRIPTION = ('TFLite Model Maker: a model customization library for on-device'\n               ' applications.')\nwith BASE_DIR.joinpath('README.md').open() as readme_file:\n  LONG_DESCRIPTION = readme_file.read()\n\n\ndef _read_required_packages(fpath):\n  with fpath.open() as f:\n    required_pkgs = [l.strip() for l in f.read().splitlines()]\n    required_pkgs = list(\n        filter(lambda line: line and not line.startswith('#'), required_pkgs))\n  return required_pkgs\n\n\ndef get_required_packages():\n  \"\"\"Gets packages inside requirements.txt.\"\"\"\n  # Gets model maker's required packages\n  filename = 'requirements_nightly.txt' if nightly else 'requirements.txt'\n  fpath = BASE_DIR.joinpath(filename)\n  required_pkgs = _read_required_packages(fpath)\n\n  return required_pkgs\n\n\nextra_options = setup_util.PackageGen(BASE_DIR, ROOT_DIR, BUILD_DIR, nightly,\n                                      version, LIB_NAMESPACE, API_NAMESPACE,\n                                      INTERNAL_NAME).run()\nsetup_options.update(extra_options)\n\nsetup(\n    name=project_name,\n    version=version,\n    description=DESCRIPTION,\n    long_description=LONG_DESCRIPTION,\n    long_description_content_type='text/markdown',\n    author='Google LLC',\n    author_email='packages@tensorflow.org',\n    url='http://github.com/tensorflow/examples',\n    download_url='https://github.com/tensorflow/examples/tags',\n    license='Apache 2.0',\n    scripts=[],\n    install_requires=get_required_packages(),\n    classifiers=classifiers,\n    keywords=['tensorflow', 'lite', 'model customization', 'transfer learning'],\n    **setup_options)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/pip_package/setup_util.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Util for setup.\"\"\"\nimport os\nimport shutil\nimport sys\n\nfrom setuptools import find_namespace_packages\n\n\nclass PackageGen:\n  \"\"\"Organizes package and generates APIs structure.\"\"\"\n\n  def __init__(\n      self,\n      base_dir,\n      root_dir,\n      build_dir,\n      nightly,\n      version,\n      lib_ns,\n      api_ns,\n      internal_name,\n  ):\n    \"\"\"Init.\n\n    Args:\n      base_dir: Path, path to the base dir. (e.g., Path('model_maker'))\n      root_dir: Path, path to the root dir. (e.g., Path('<github repo>'))\n      build_dir: Path, path to the build dir, where we copy all source code and\n        create a package. (e.g. Path('pip_package/src')).\n      nightly: boolean, whether it is nightly.\n      version: str, package version.\n      lib_ns: str, original namespace of the lib (e.g.\n        'tensorflow_examples.lite.model_maker').\n      api_ns: str, official package namespace for API. Used as code name. (e.g.,\n        'tflite_model_maker').\n      internal_name: str, internal package name (e.g., 'python') that creates\n        <api_ns>.<internal_name> to map internal packages.\n    \"\"\"\n    self.base_dir = base_dir\n    self.root_dir = root_dir\n    self.build_dir = build_dir\n    self.nightly = nightly\n    self.version = version\n    self.lib_ns = lib_ns\n    self.api_ns = api_ns\n    self.internal_name = internal_name\n\n    # Add the root_dir to the PYTHONPATH (used to look up for api_gen).\n    root = str(self.root_dir)\n    if root not in sys.path:\n      sys.path.insert(0, root)\n\n  def run(self):\n    \"\"\"Generate build folder, and returns packages with dir mapping.\n\n    Returns:\n      A dict: extra kwargs for setup.\n    \"\"\"\n    from tensorflow_examples.lite.model_maker.core.api import api_util  # pylint: disable=g-import-not-at-top\n\n    # Cleanup if `src` folder exists\n    if self.build_dir.exists():\n      shutil.rmtree(str(self.build_dir), ignore_errors=True)\n\n    lib_names = api_util.split_name(self.lib_ns)\n    lib_pkg = self.build_dir.joinpath(*lib_names)\n\n    # Prepare __init__.py.\n    api_util.make_dirs_or_not(lib_pkg)\n    for i in range(len(lib_names) + 1):\n      dirpath = self.build_dir.joinpath(*lib_names[:i])\n      init_file = dirpath.joinpath('__init__.py')\n      if not init_file.exists():\n        doc = api_util.generate_package_doc(lib_names[:i])\n        api_util.write_python_file(init_file, doc, None)\n\n    # Copy static files.\n    static_files = [\n        'README.md', 'RELEASE.md', 'requirements.txt',\n        'requirements_nightly.txt'\n    ]\n    for f in static_files:\n      shutil.copy2(\n          str(self.base_dir.joinpath(f)), str(self.build_dir.joinpath(f)))\n\n    # Copy .py files.\n    files = self.base_dir.rglob(r'*')\n    include_extentions = {'.py', '.json'}\n\n    script_pys = []\n    init_pys = []\n    public_api = os.path.join('public')\n    for path in files:\n      name = str(path)\n      if path.is_dir():\n        continue\n      if path.suffix not in include_extentions:\n        continue\n      if ('pip_package' in name) or ('_test.py' in name) or (public_api\n                                                             in name):\n        continue\n\n      target_path = lib_pkg.joinpath(path.relative_to(self.base_dir))\n      api_util.make_dirs_or_not(target_path.parent)\n      shutil.copy2(str(path), str(target_path))\n\n      if path.suffix == '.py':\n        relative = target_path.relative_to(lib_pkg)\n        if path.stem != '__init__':\n          # Get a path like: a/b.py\n          script_pys.append(relative)\n        else:\n          # Get a path like: a/__init__.py\n          init_pys.append(relative)\n\n    # Add APIs files.\n    target_api_dir = self.build_dir.joinpath(self.api_ns)\n    shutil.copytree(self.base_dir.joinpath(public_api), target_api_dir)\n    # Set version in package.\n    api_util.overwrite_version_in_package(target_api_dir, self.version)\n\n    # Create API's namespace mapping.\n    internal_names = api_util.split_name(self.api_ns) + api_util.split_name(\n        self.internal_name)\n    internal_pkg = self.build_dir.joinpath(*internal_names)\n    for path in script_pys + init_pys:\n      official_py = internal_pkg.joinpath(path)\n      if path.stem != '__init__':\n        # For example, `a/b.py` becomes `a.b`\n        ns = str(path.with_suffix('')).replace(os.sep, '.')\n      else:\n        # For example, `a/__init__.py` becomes `a`\n        ns = str(path.parent).replace(os.sep, '.')\n\n      if ns == '.':  # Replace dir '.' with empty namespace.\n        ns = ''\n      real_ns = api_util.as_package(\n          api_util.split_name(self.lib_ns) + api_util.split_name(ns))\n      doc = api_util.generate_package_doc(real_ns)\n      content = 'from {} import *'.format(real_ns)\n      api_util.make_dirs_or_not(official_py.parent)\n      api_util.write_python_file(official_py, doc, [content])\n\n    package_data = {\n        '': ['*.txt', '*.md', '*.json'],\n    }\n\n    # Return package.\n    namespace_packages = find_namespace_packages(where=self.build_dir)\n\n    return {\n        'packages': namespace_packages,\n        'package_data': package_data,\n    }\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/pip_package/test_pip_package.sh",
    "content": "#!/bin/bash\n# Test Model Maker's pip package.\n# bash test_pip_package.sh\n\nset -e\nset -x\n\nPATH=$PATH:/home/kbuilder/.local/bin\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" >/dev/null 2>&1 && pwd)\"\nWORKSPACE_DIR=\"$(realpath \"${SCRIPT_DIR}/../../../../\")\"\nMODEL_MAKER_VERSION=\"$1\"   # \"--nightly\", or empty (for stable)\n\nsource \"${SCRIPT_DIR}/create_venv.sh\" ${MODEL_MAKER_VERSION}\ncreate_venv_or_activate  # Use virtualenv and activate.\n\nPYTHON_BIN=\"$(which python3.8)\"\nPIP_BIN=\"$(which pip3.8)\"\nCLI_BIN=\"tflite_model_maker\"\nNUM_PROCESSES=30   # Run tests in parallel. Adjust this to your own machine.\n\n\nfunction build_pip_and_install {\n  # Build and install pip package.\n  if [[ \"${PYTHON_BIN}\" == \"\" ]]; then\n    echo \"python is not available.\"\n    exit 1\n  fi\n\n  local ver=\"\"\n  local pkg=\"tflite_model_maker\"\n  if [[ \"${MODEL_MAKER_VERSION}\" == \"--nightly\" ]]; then\n    pkg=\"${pkg}_nightly\"\n    ver=\"--nightly\"\n  fi\n\n  echo \"------ build pip and install -----\"\n  pushd \"${SCRIPT_DIR}\" > /dev/null\n\n  # Upgrade pip\n  ${PIP_BIN} install --upgrade pip\n\n  rm -r -f dist   # Clean up distributions.\n  ${PYTHON_BIN} setup.py ${ver?} sdist bdist_wheel\n  local dist_pkg=\"$(ls dist/${pkg}*.whl)\"\n  ${PIP_BIN} install ${dist_pkg?} --ignore-installed\n  ${PIP_BIN} install pycocotools pyparsing==2.4.7\n\n  popd > /dev/null\n  echo\n}\n\nfunction uninstall_pip {\n  # Uninstall pip package.\n  echo \"------ uninstall pip -----\"\n\n  local pip_pkg=\"tflite-model-maker\"\n  if [[ \"${MODEL_MAKER_VERSION}\" == \"--nightly\" ]]; then\n    pip_pkg=\"${pip_pkg}-nightly\"\n  fi\n\n  yes | ${PIP_BIN} uninstall ${pip_pkg?}\n  echo\n}\n\nfunction test_import {\n  # Test whether import is successful\n  echo \"------ Test import -----\"\n  ${PYTHON_BIN} -c \"import tflite_model_maker; print(tflite_model_maker.__version__)\"\n  echo\n}\n\nfunction test_cli {\n  # Test CLI\n  echo \"------ Test CLI -----\"\n  yes | ${CLI_BIN}\n}\n\nfunction test_unittest {\n  TEST_DIR=\"${WORKSPACE_DIR}/tensorflow_examples/lite/model_maker\"\n\n  echo \"=== BEGIN UNIT TESTS FOR: ${TEST_DIR} ===\"\n\n  # Tests are excluded from pip, so need to be in root folder to test.\n  pushd \"${WORKSPACE_DIR}\" > /dev/null\n\n  # Set environment variables: test_srcdir for unit tests; and then run tests\n  # one by one.\n  export TEST_SRCDIR=${TEST_DIR}\n  # Tests all, excluding \"*v1_test\" and \"tensorflowjs\".\n  find \"${TEST_DIR}\" -name \"*_test.py\" \\\n      ! -name \"*v1_test.py\" \\\n      ! -wholename \"*tfjs*\" \\\n      -print0 | \\\n  xargs -0 -P ${NUM_PROCESSES?} -I{} ${PYTHON_BIN?} {}\n\n  popd > /dev/null\n  echo \"=== END UNIT TESTS: ${TEST_DIR} ===\"\n  echo\n  echo\n}\n\nfunction test_model_maker {\n  if [[ \"${MODEL_MAKER_VERSION}\" == \"--nightly\" ]]; then\n    echo \"===== Test Model Maker (nightly) =====\"\n  else\n    echo \"===== Test Model Maker (stable) =====\"\n  fi\n\n  build_pip_and_install\n  test_import\n  test_cli\n  test_unittest\n  uninstall_pip\n  echo\n}\n\ntest_model_maker\ndeactivate  # deactivate virtualenv.\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/public/__init__.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# pylint: disable=g-bad-import-order,redefined-builtin\n\"\"\"Public APIs for TFLite Model Maker, a transfer learning library to train custom TFLite models.\n\nYou can install the package with\n\n```bash\npip install tflite-model-maker\n```\n\nTypical usage of Model Maker is to create a model in a few lines of code, e.g.:\n\n```python\n# Load input data specific to an on-device ML app.\ndata = DataLoader.from_folder('flower_photos/')\ntrain_data, test_data = data.split(0.9)\n\n# Customize the TensorFlow model.\nmodel = image_classifier.create(train_data)\n\n# Evaluate the model.\naccuracy = model.evaluate(test_data)\n\n# Export to Tensorflow Lite model and label file in `export_dir`.\nmodel.export(export_dir='/tmp/')\n```\n\nFor more details, please refer to our guide:\nhttps://www.tensorflow.org/lite/guide/model_maker.\n\"\"\"\n\nfrom tflite_model_maker import audio_classifier\nfrom tflite_model_maker import config\nfrom tflite_model_maker import image_classifier\nfrom tflite_model_maker import model_spec\nfrom tflite_model_maker import object_detector\nfrom tflite_model_maker import question_answer\nfrom tflite_model_maker import recommendation\nfrom tflite_model_maker import searcher\nfrom tflite_model_maker import text_classifier\n\n# Deprecated imports are kept for backward compatiblity and to be removed in\n# future versions. Please refer to public APIs for replacement:\n# https://www.tensorflow.org/lite/api_docs/python/tflite_model_maker\n# pylint: disable=g-bad-import-order\nfrom tensorflow_examples.lite.model_maker.core.data_util.image_dataloader import ImageClassifierDataLoader\nfrom tensorflow_examples.lite.model_maker.core.export_format import ExportFormat\nfrom tensorflow_examples.lite.model_maker.core.task import configs\n# pylint: enable=g-bad-import-order\n\n__version__ = '0.4.3'\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/public/audio_classifier/__init__.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# pylint: disable=g-bad-import-order,redefined-builtin\n\"\"\"APIs to train an audio classification model.\n\nTutorial:\nhttps://colab.research.google.com/github/googlecodelabs/odml-pathways/blob/main/audio_classification/colab/model_maker_audio_colab.ipynb\n\nDemo code:\nhttps://github.com/tensorflow/examples/blob/master/tensorflow_examples/lite/model_maker/demo/audio_classification_demo.py\n\"\"\"\n\nfrom tensorflow_examples.lite.model_maker.core.data_util.audio_dataloader import DataLoader\nfrom tensorflow_examples.lite.model_maker.core.task.audio_classifier import AudioClassifier\nfrom tensorflow_examples.lite.model_maker.core.task.audio_classifier import create\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.audio_spec import BrowserFFTSpec as BrowserFftSpec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.audio_spec import YAMNetSpec as YamNetSpec\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/public/config/__init__.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# pylint: disable=g-bad-import-order,redefined-builtin\n\"\"\"APIs for the config of TFLite Model Maker.\"\"\"\n\nfrom tensorflow_examples.lite.model_maker.core.export_format import ExportFormat\nfrom tensorflow_examples.lite.model_maker.core.task.configs import QuantizationConfig\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/public/image_classifier/__init__.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# pylint: disable=g-bad-import-order,redefined-builtin\n\"\"\"APIs to train an image classification model.\n\nTask guide:\nhttps://www.tensorflow.org/lite/tutorials/model_maker_image_classification.\n\"\"\"\n\nfrom tensorflow_examples.lite.model_maker.core.data_util.image_dataloader import ImageClassifierDataLoader as DataLoader\nfrom tensorflow_examples.lite.model_maker.core.task.image_classifier import create\nfrom tensorflow_examples.lite.model_maker.core.task.image_classifier import ImageClassifier\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.image_spec import efficientnet_lite0_spec as EfficientNetLite0Spec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.image_spec import efficientnet_lite1_spec as EfficientNetLite1Spec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.image_spec import efficientnet_lite2_spec as EfficientNetLite2Spec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.image_spec import efficientnet_lite3_spec as EfficientNetLite3Spec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.image_spec import efficientnet_lite4_spec as EfficientNetLite4Spec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.image_spec import ImageModelSpec as ModelSpec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.image_spec import mobilenet_v2_spec as MobileNetV2Spec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.image_spec import resnet_50_spec as Resnet50Spec\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/public/model_spec/__init__.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# pylint: disable=g-bad-import-order,redefined-builtin\n\"\"\"APIs for the model spec of TFLite Model Maker.\"\"\"\n\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import AUDIO_CLASSIFICATION_MODELS\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import get\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import IMAGE_CLASSIFICATION_MODELS\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import OBJECT_DETECTION_MODELS\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import QUESTION_ANSWER_MODELS\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import RECOMMENDATION_MODELS\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec import TEXT_CLASSIFICATION_MODELS\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/public/object_detector/__init__.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# pylint: disable=g-bad-import-order,redefined-builtin\n\"\"\"APIs to train an object detection model.\"\"\"\n\nfrom tensorflow_examples.lite.model_maker.core.data_util.object_detector_dataloader import DataLoader\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.object_detector_spec import efficientdet_lite0_spec as EfficientDetLite0Spec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.object_detector_spec import efficientdet_lite1_spec as EfficientDetLite1Spec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.object_detector_spec import efficientdet_lite2_spec as EfficientDetLite2Spec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.object_detector_spec import efficientdet_lite3_spec as EfficientDetLite3Spec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.object_detector_spec import efficientdet_lite4_spec as EfficientDetLite4Spec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.object_detector_spec import EfficientDetModelSpec as EfficientDetSpec\nfrom tensorflow_examples.lite.model_maker.core.task.object_detector import create\nfrom tensorflow_examples.lite.model_maker.core.task.object_detector import ObjectDetector\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/public/question_answer/__init__.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# pylint: disable=g-bad-import-order,redefined-builtin\n\"\"\"APIs to train a model that can answer questions based on a predefined text.\n\nTask guide:\nhttps://www.tensorflow.org/lite/tutorials/model_maker_question_answer.\n\"\"\"\n\nfrom tensorflow_examples.lite.model_maker.core.data_util.text_dataloader import QuestionAnswerDataLoader as DataLoader\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.text_spec import BertQAModelSpec as BertQaSpec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.text_spec import mobilebert_qa_spec as MobileBertQaSpec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.text_spec import mobilebert_qa_squad_spec as MobileBertQaSquadSpec\nfrom tensorflow_examples.lite.model_maker.core.task.question_answer import create\nfrom tensorflow_examples.lite.model_maker.core.task.question_answer import QuestionAnswer\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/public/recommendation/__init__.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# pylint: disable=g-bad-import-order,redefined-builtin\n\"\"\"APIs to train an on-device recommendation model.\n\nDemo code:\nhttps://github.com/tensorflow/examples/blob/master/tensorflow_examples/lite/model_maker/demo/recommendation_demo.py\n\"\"\"\n\nfrom tflite_model_maker.recommendation import spec\nfrom tensorflow_examples.lite.model_maker.core.data_util.recommendation_dataloader import RecommendationDataLoader as DataLoader\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.recommendation_spec import RecommendationSpec as ModelSpec\nfrom tensorflow_examples.lite.model_maker.core.task.recommendation import create\nfrom tensorflow_examples.lite.model_maker.core.task.recommendation import Recommendation\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/public/recommendation/spec/__init__.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# pylint: disable=g-bad-import-order,redefined-builtin\n\"\"\"APIs for recommendation specifications.\n\nExample:\n```python\ninput_spec = recommendation.spec.InputSpec(\n    activity_feature_groups=[\n        # Group #1: defines how features are grouped in the first Group.\n        dict(\n            features=[\n                # First feature.\n                dict(\n                    feature_name='context_movie_id',  # Feature name\n                    feature_type='INT',  # Feature type\n                    vocab_size=3953,     # ID size (number of IDs)\n                    embedding_dim=8,     # Projected feature embedding dim\n                    feature_length=10,   # History length of 10.\n                ),\n                # Maybe more features...\n            ],\n            encoder_type='CNN',  # CNN encoder (e.g. CNN, LSTM, BOW)\n        ),\n        # Maybe more groups...\n    ],\n    label_feature=dict(\n        feature_name='label_movie_id',  # Label feature name\n        feature_type='INT',  # Label type\n        vocab_size=3953,   # Label size (number of classes)\n        embedding_dim=8,   # label embedding demension\n        feature_length=1,  # Exactly 1 label\n    ),\n)\n\nmodel_hparams = recommendation.spec.ModelHParams(\n    hidden_layer_dims=[32, 32],  # Hidden layers dimension.\n    eval_top_k=[1, 5],           # Eval top 1 and top 5.\n    conv_num_filter_ratios=[2, 4],  # For CNN encoder, conv filter mutipler.\n    conv_kernel_size=16,            # For CNN encoder, base kernel size.\n    lstm_num_units=16,              # For LSTM/RNN, num units.\n    num_predictions=10,          # Number of output predictions. Select top 10.\n)\n\nspec = recommendation.ModelSpec(\n    input_spec=input_spec, model_hparams=model_hparams)\n# Or:\nspec = model_spec.get(\n    'recommendation', input_spec=input_spec, model_hparams=model_hparams)\n```\n\"\"\"\n\nfrom tensorflow_examples.lite.model_maker.core.data_util.recommendation_config import EncoderType\nfrom tensorflow_examples.lite.model_maker.core.data_util.recommendation_config import Feature\nfrom tensorflow_examples.lite.model_maker.core.data_util.recommendation_config import FeatureGroup\nfrom tensorflow_examples.lite.model_maker.core.data_util.recommendation_config import FeatureType\nfrom tensorflow_examples.lite.model_maker.core.data_util.recommendation_config import InputSpec\nfrom tensorflow_examples.lite.model_maker.core.data_util.recommendation_config import ModelHParams\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/public/searcher/__init__.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# pylint: disable=g-bad-import-order,redefined-builtin\n\"\"\"APIs to create the searcher model.\n\nTask guide:\nhttps://www.tensorflow.org/lite/tutorials/model_maker_text_searcher.\n\"\"\"\n\nfrom tensorflow_examples.lite.model_maker.core.data_util.image_searcher_dataloader import DataLoader as ImageDataLoader\nfrom tensorflow_examples.lite.model_maker.core.data_util.metadata_loader import MetadataType\nfrom tensorflow_examples.lite.model_maker.core.data_util.searcher_dataloader import DataLoader\nfrom tensorflow_examples.lite.model_maker.core.data_util.text_searcher_dataloader import DataLoader as TextDataLoader\nfrom tensorflow_examples.lite.model_maker.core.task.searcher import ExportFormat\nfrom tensorflow_examples.lite.model_maker.core.task.searcher import ScaNNOptions\nfrom tensorflow_examples.lite.model_maker.core.task.searcher import ScoreAH\nfrom tensorflow_examples.lite.model_maker.core.task.searcher import ScoreBruteForce\nfrom tensorflow_examples.lite.model_maker.core.task.searcher import Searcher\nfrom tensorflow_examples.lite.model_maker.core.task.searcher import Tree\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/public/text_classifier/__init__.py",
    "content": "# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# pylint: disable=g-bad-import-order,redefined-builtin\n\"\"\"APIs to train a text classification model.\n\nTask guide:\nhttps://www.tensorflow.org/lite/tutorials/model_maker_text_classification.\n\"\"\"\n\nfrom tensorflow_examples.lite.model_maker.core.data_util.text_dataloader import TextClassifierDataLoader as DataLoader\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.text_spec import AverageWordVecModelSpec as AverageWordVecSpec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.text_spec import BertClassifierModelSpec as BertClassifierSpec\nfrom tensorflow_examples.lite.model_maker.core.task.model_spec.text_spec import mobilebert_classifier_spec as MobileBertClassifierSpec\nfrom tensorflow_examples.lite.model_maker.core.task.text_classifier import create\nfrom tensorflow_examples.lite.model_maker.core.task.text_classifier import TextClassifier\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/requirements.txt",
    "content": "tf-models-official==2.3.0\n# tensorflow-hub is to load Hub model. Specific version is required by TFJS.\ntensorflow-hub>=0.7.0,<0.10; python_version < \"3\"\ntensorflow-hub>=0.7.0,<0.13; python_version >= \"3\"\nnumpy>=1.17.3,<1.23.4\npillow>=7.0.0\nsentencepiece>=0.1.91\ntensorflow-datasets>=2.1.0\nfire>=0.3.1\nflatbuffers>=2.0\nabsl-py>=0.10.0\nurllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1\ntflite-support>=0.4.2\ntensorflowjs>=2.4.0,<3.19.0\ntensorflow>=2.6.0\n# b/196287362: This Numba + Librosa combination works for numpy 1.19, introduced\n# by TensorFlow 2.6.0.\nnumba>=0.53\nlibrosa==0.8.1\nlxml>=4.6.1\nPyYAML>=5.1\n# The following are the requirements of efficientdet.\nmatplotlib>=3.0.3,<3.5.0\nsix>=1.12.0\ntensorflow-addons>=0.11.2\nneural-structured-learning>=1.3.1\ntensorflow-model-optimization>=0.5\nCython>=0.29.13\nscann==1.2.6\nai-edge-litert>=1.0.1"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/requirements_nightly.txt",
    "content": "# TODO(b/177629432): Change to tf-models-nightly and tensorflow once\n# NotFoundError is fixed with tensorflow-text-nightly.\ntf-models-official==2.3.0\n# tensorflow-hub is to load Hub model. Specific version is required by TFJS.\ntensorflow-hub>=0.7.0,<0.10; python_version < \"3\"\ntensorflow-hub>=0.7.0,<0.13; python_version >= \"3\"\nnumpy>=1.17.3,<1.23.4\npillow>=7.0.0\nsentencepiece>=0.1.91\ntensorflow-datasets>=2.1.0\nfire>=0.3.1\nflatbuffers>=2.0\nabsl-py>=0.10.0\nurllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1\ntflite-support>=0.4.2\ntensorflow>=2.6.0\n# b/196287362: This Numba + Librosa combination works for numpy 1.19, introduced\n# by TensorFlow 2.6.0.\nnumba>=0.53\nlibrosa>=0.8.1\nlxml>=4.6.1\nPyYAML>=5.1\n# The following are the requirements of efficientdet.\nmatplotlib>=3.0.3,<3.5.0\nsix>=1.12.0\ntfa-nightly\nneural-structured-learning>=1.3.1\ntensorflow-model-optimization>=0.5\nCython>=0.29.13\nscann==1.2.6\ntensorflowjs>=2.4.0,<3.19.0\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/Det-AdvProp.md",
    "content": "# Det-AdvProp\n[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.sandbox.google.com/github/google/automl/blob/master/efficientdet/det_advprop_tutorial.ipynb)\n\n[1] Xiangning Chen, Cihang Xie, Mingxing Tan, Li Zhang, Cho-Jui Hsieh, Boqing\nGong. CVPR 2021. Arxiv link: https://arxiv.org/abs/2103.13886\n\nDet-AdvProp is a data augmentation technique specifically designed for the\nfine-tuning process of object detectors. It can consistently and substantially\noutperform the vanilla training and AutoAugment under various settings. The\nobtained detector is not only more accurate on clean images, but also more\nrobust to image distortions and domain shift.\n\n<p align=\"center\">\n<img src=\"./g3doc/Det-AdvProp.png\" width=\"100%\" />\n</p>\n\n## 1. Accurate on Clean Images\n\nThe following table includes a list of models trained with Det-AdvProp +\nAutoAugment (AA):\n\nModel                                                                                                                                                                                                                          | AP<sup>test</sup> | AP<sub>50</sub> | AP<sub>75</sub> | AP<sub>S</sub> | AP<sub>M</sub> | AP<sub>L</sub> | AP<sup>val</sup> |     | #params | #FLOPs\n------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------- | --------------- | --------------- | -------------- | -------------- | -------------- | ---------------- | --- | ------- | :----:\nEfficientDet-D0 + Det-AdvProp + AA ([ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/advprop/efficientdet-d0.tar.gz), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/advprop/d0.txt)) | 35.3              | 54.1            | 37.8            | 12.7           | 39.9           | 53.2           | 35.1             |     | 3.9M    | 2.54B\nEfficientDet-D1 + Det-AdvProp + AA ([ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/advprop/efficientdet-d1.tar.gz), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/advprop/d1.txt)) | 40.9              | 60.0            | 44.1            | 19.1           | 45.6           | 57.2           | 40.8             |     | 6.6M    | 6.10B\nEfficientDet-D2 + Det-AdvProp + AA ([ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/advprop/efficientdet-d2.tar.gz), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/advprop/d2.txt)) | 44.3              | 63.5            | 47.9            | 23.5           | 48.5           | 59.9           | 44.3             |     | 8.1M    | 11.0B\nEfficientDet-D3 + Det-AdvProp + AA ([ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/advprop/efficientdet-d3.tar.gz), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/advprop/d3.txt)) | 48.0              | 67.1            | 52.2            | 28.1           | 51.8           | 62.8           | 47.7             |     | 12.0M   | 24.9B\nEfficientDet-D4 + Det-AdvProp + AA ([ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/advprop/efficientdet-d4.tar.gz), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/advprop/d4.txt)) | 50.4              | 69.5            | 54.9            | 30.9           | 54.3           | 64.4           | 50.4             |     | 20.7M   | 55.2B\nEfficientDet-D5 + Det-AdvProp + AA ([ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/advprop/efficientdet-d5.tar.gz), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/advprop/d5.txt)) | 52.5              | 71.8            | 57.2            | 34.6           | 55.9           | 65.2           | 52.2             |     | 33.7M   | 130B\n\n<sup>Unlike the vanilla EfficientDet that scales the image with mean and std,\nhere we scale the input to the range of [-1, 1] to make it easier for performing\nadversarial attack. Please see [this Colab](https://github.com/google/automl/blob/master/efficientdet/det_advprop_tutorial.ipynb) for reproducing the\nresults.</sup>\n\n## 2. Robust Against Common Corruptions\n\nWe test the detectors' robustness against common image corruptions (e.g.,\nGaussian Noise, Snow, etc.) based on the COCO-C dataset in\n[this paper](https://arxiv.org/abs/1907.07484). The table below shows the\ncomparison between vanilla training and Det-AdvProp + AutoAugment (AA):\n\nModel                  | mAP\n---------------------- | ---------------\nEfficientDet-D0        | 21.4\n**+ Det-AdvProp + AA** | **22.7 (+1.3)**\nEfficientDet-D1        | 24.4\n**+ Det-AdvProp + AA** | **26.7 (+2.3)**\nEfficientDet-D2        | 26.7\n**+ Det-AdvProp + AA** | **28.9 (+2.2)**\nEfficientDet-D3        | 28.8\n**+ Det-AdvProp + AA** | **32.0 (+3.2)**\nEfficientDet-D4        | 30.1\n**+ Det-AdvProp + AA** | **33.9 (+3.8)**\nEfficientDet-D5        | 31.4\n**+ Det-AdvProp + AA** | **35.0 (+3.6)**\n\n## 3. Robust Against Domain Shift\n\nPASCAL VOC 2012 only contains 20 classes, which are much smaller than the 80\nlabeled classes in COCO. The underlying distributions of the two datasets are\nalso different in the image content or the bounding box sizes and locations. We\nuse the trained detectors to run inference directly on the VOC dataset to test\ntheir transferibility. We maintain the COCO evaluation metrics in this\nexperiment:\n\nModel                  | mAP             | AP<sub>50</sub> | AP<sub>75</sub>\n---------------------- | --------------- | --------------- | ---------------\nEfficientDet-D0        | 55.6            | 77.6            | 61.4\n**+ Det-AdvProp + AA** | **56.2 (+0.6)** | **78.3 (+0.7)** | **62.3 (+0.9)**\nEfficientDet-D1        | 60.8            | 82.0            | 66.7\n**+ Det-AdvProp + AA** | **61.3 (+0.5)** | **82.5 (+0.5)** | **67.6 (+0.9)**\nEfficientDet-D2        | 63.3            | 83.6            | 69.3\n**+ Det-AdvProp + AA** | **63.6 (+0.3)** | **84.0 (+0.4)** | **70.0 (+0.7)**\nEfficientDet-D3        | 65.7            | 85.3            | 71.8\n**+ Det-AdvProp + AA** | **66.4 (+0.7)** | **85.9 (+0.6)** | **72.8 (+1.0)**\nEfficientDet-D4        | 67.0            | 86.0            | 73.0\n**+ Det-AdvProp + AA** | **67.8 (+0.8)** | **87.0 (+1.0)** | **74.3 (+1.3)**\nEfficientDet-D5        | 67.4            | 86.9            | 73.8\n**+ Det-AdvProp + AA** | **68.7 (+1.3)** | **88.0 (+1.1)** | **75.4 (+1.6)**\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/README.md",
    "content": "# EfficientDet\n[![Paper](http://img.shields.io/badge/Paper-arXiv.1911.09070-B3181B?logo=arXiv)](https://arxiv.org/abs/1911.09070)\n[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.sandbox.google.com/github/google/automl/blob/master/efficientdet/tutorial.ipynb)\n[![TensorFlow Hub](https://img.shields.io/badge/TF%20Hub-Models-FF6F00?logo=tensorflow)](https://tfhub.dev/s?network-architecture=efficientdet)\n\n\n\n[1] Mingxing Tan, Ruoming Pang, Quoc V. Le. EfficientDet: Scalable and Efficient Object Detection. CVPR 2020.\nArxiv link: https://arxiv.org/abs/1911.09070\n\nUpdates:\n\n  - May10/2021: Added EfficientDet-lite checkpoints (by Yuqi and TFLite team)\n  - Mar25/2021: Added [Det-AdvProp](https://arxiv.org/abs/2103.13886) model checkpoints ([see this page](./Det-AdvProp.md)).\n  - Jul20/2020: Added keras/TF2 and new SOTA D7x: 55.1mAP with 153ms.\n  - Apr22/2020: Sped up end-to-end latency: D0 has up to >200 FPS throughput on Tesla V100.\n    * A great collaboration with [@fsx950223](https://github.com/fsx950223).\n  - Apr1/2020: Updated results for test-dev and added EfficientDet-D7.\n  - Mar26/2020: Fixed a few bugs and updated all checkpoints/results.\n  - Mar24/2020: Added tutorial with visualization and coco eval.\n  - Mar13/2020: Released the initial code and models.\n\n**Quick start tutorial: [tutorial.ipynb](tutorial.ipynb)**\n\n**Quick install dependencies: ```pip install -r requirements.txt```**\n\n## 1. About EfficientDet Models\n\nEfficientDets are a family of object detection models, which achieve state-of-the-art 55.1mAP on COCO test-dev, yet being 4x - 9x smaller and using 13x - 42x fewer FLOPs than previous detectors. Our models also run 2x - 4x faster on GPU, and 5x - 11x faster on CPU than other detectors.\n\n\nEfficientDets are developed based on the advanced backbone, a new BiFPN, and a new scaling technique:\n\n<p align=\"center\">\n<img src=\"./g3doc/network.png\" width=\"800\" />\n</p>\n\n  * **Backbone**: we employ [EfficientNets](https://arxiv.org/abs/1905.11946) as our backbone networks.\n  * **BiFPN**: we propose BiFPN, a bi-directional feature network enhanced with fast normalization, which enables easy and fast feature fusion.\n  * **Scaling**: we use a single compound scaling factor to govern the depth, width, and resolution for all backbone, feature & prediction networks.\n\nOur model family starts from EfficientDet-D0, which has comparable accuracy as [YOLOv3](https://arxiv.org/abs/1804.02767). Then we scale up this baseline model using our compound scaling method to obtain a list of detection models EfficientDet-D1 to D6, with different trade-offs between accuracy and model complexity.\n\n\n<table border=\"0\">\n<tr>\n    <td>\n    <img src=\"./g3doc/flops.png\" width=\"100%\" />\n    </td>\n    <td>\n    <img src=\"./g3doc/params.png\", width=\"100%\" />\n    </td>\n</tr>\n</table>\n\n** For simplicity, we compare the whole detectors here. For more comparison on FPN/NAS-FPN/BiFPN, please see Table 4 of our [paper](https://arxiv.org/abs/1911.09070).\n\n\n\n## 2. Pretrained EfficientDet Checkpoints\n\nWe have provided a list of EfficientDet checkpoints and results as follows:\n\n|       Model    | AP<sup>test</sup>    |  AP<sub>50</sub> | AP<sub>75</sub> |AP<sub>S</sub>   |  AP<sub>M</sub>    |  AP<sub>L</sub>   |  AP<sup>val</sup> | | #params | #FLOPs |\n|----------     |------ |------ |------ | -------- | ------| ------| ------ |------ |------ |  :------: |\n|     EfficientDet-D0 ([h5](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d0.h5), [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d0.tar.gz), [val](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/val/d0_coco_val.txt), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/testdev/d0_coco_test-dev2017.txt))    | 34.6 | 53.0 | 37.1 | 12.4 | 39.0 | 52.7 | 34.3 |  | 3.9M | 2.54B  |\n|     EfficientDet-D1 ([h5](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d1.h5), [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d1.tar.gz), [val](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/val/d1_coco_val.txt), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/testdev/d1_coco_test-dev2017.txt))    | 40.5 | 59.1 | 43.7 | 18.3 | 45.0 | 57.5 | 40.2 | | 6.6M | 6.10B |\n|     EfficientDet-D2 ([h5](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d2.h5), [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d2.tar.gz), [val](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/val/d2_coco_val.txt), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/testdev/d2_coco_test-dev2017.txt))    | 43.9 | 62.7 | 47.6 | 22.9 | 48.1 | 59.5 | 43.5 | | 8.1M | 11.0B |\n|     EfficientDet-D3 ([h5](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d3.h5), [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d3.tar.gz), [val](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/val/d3_coco_val.txt), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/testdev/d3_coco_test-dev2017.txt))    | 47.2 | 65.9 | 51.2 | 27.2 | 51.0 | 62.1 | 46.8 | | 12.0M | 24.9B |\n|     EfficientDet-D4 ([h5](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d4.h5), [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d4.tar.gz), [val](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/val/d4_coco_val.txt), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/testdev/d4_coco_test-dev2017.txt))    | 49.7 | 68.4 | 53.9 | 30.7 | 53.2 | 63.2 | 49.3 |  | 20.7M | 55.2B |\n|     EfficientDet-D5 ([h5](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d5.h5), [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d5.tar.gz), [val](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/val/d5_coco_val.txt), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/testdev/d5_coco_test-dev2017.txt))    | 51.5 | 70.5 | 56.1 | 33.9 | 54.7 | 64.1 | 51.2 |  | 33.7M | 130B |\n|     EfficientDet-D6 ([h5](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d6.h5), [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d6.tar.gz), [val](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/val/d6_coco_val.txt), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/testdev/d6_coco_test-dev2017.txt))    | 52.6 | 71.5 | 57.2 | 34.9 | 56.0 | 65.4 | 52.1 | | 51.9M  |  226B  |\n|     EfficientDet-D7 ([h5](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d7.h5), [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d7.tar.gz), [val](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/val/d7_coco_val.txt), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/testdev/d7_coco_test-dev2017.txt))    | 53.7 | 72.4 | 58.4 | 35.8 | 57.0 | 66.3 | 53.4 | | 51.9M  |  325B  |\n|     EfficientDet-D7x ([h5](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d7x.h5), [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d7x.tar.gz), [val](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/val/d7x_coco_val.txt), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/testdev/d7x_coco_test-dev2017.txt))    | 55.1 | 74.3 | 59.9 | 37.2 | 57.9 | 68.0 | 54.4 | | 77.0M  |  410B  |\n\n<sup><em>val</em> denotes validation results, <em>test-dev</em> denotes test-dev2017 results. AP<sup>val</sup> is for validation accuracy, all other AP results in the table are for COCO test-dev2017. All accuracy numbers are for single-model single-scale without ensemble or test-time augmentation.  EfficientDet-D0 to D6 are trained for 300 epochs and D7/D7x are trained for 600 epochs.</sup>\n\nFor more accurate and robust EfficientDet, please see [this page](./Det-AdvProp.md), which contains a list of models trained with Det-AdvProp + AutoAugment (AA) described in [this paper](https://arxiv.org/abs/2103.13886). The obatined model is not only more accurate on clean images, but also much more robust against various corruptions and domain shift.\n\nIn addition, the following table includes a list of models trained with fixed 640x640 image sizes (see appendix of [this paper](https://arxiv.org/abs/1911.09070)):\n\n\n|       Model    |   mAP | Latency |\n| ------ | ------ | ------  |\n| D2(640) [h5](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco640/efficientdet-d2-640.h5), [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco640/efficientdet-d2-640.tar.gz) |  41.7 | 14.8ms |\n| D3(640) [h5](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco640/efficientdet-d3-640.h5), [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco640/efficientdet-d3-640.tar.gz) |  44.0 | 18.7ms |\n| D4(640) [h5](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco640/efficientdet-d4-640.h5), [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco640/efficientdet-d4-640.tar.gz) |  45.7 | 21.7ms |\n| D5(640) [h5](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco640/efficientdet-d5-640.h5), [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco640/efficientdet-d5-640.tar.gz) |  46.6 | 26.6ms |\n| D6(640) [h5](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco640/efficientdet-d6-640.h5), [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco640/efficientdet-d6-640.tar.gz) |  47.9 | 33.8ms |\n\nWe have also provided a list of mobile-size lite models:\n\n|       Model    |   mAP (float) | Quantized mAP (int8) | Prameters | Mobile latency |\n| ------ | :------: | :------:  | :------: | :------:  |\n| EfficientDet-lite0,  [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco/efficientdet-lite0.tgz)  |  26.41 | 26.10 | 4.3M  |  36ms |\n| EfficientDet-lite1,  [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco/efficientdet-lite1.tgz)  |  31.50 | 31.12 |  5.8M |  49ms |\n| EfficientDet-lite2,  [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco/efficientdet-lite2.tgz)  |  35.06 | 34.69 |  7.2M | 69ms |\n| EfficientDet-lite3,  [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco/efficientdet-lite3.tgz)  |  38.77 | 38.42 |  11M  | 116ms |\n| EfficientDet-lite3x, [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco/efficientdet-lite3x.tgz) |  42.64 | 41.87 |  12M  | 208ms  |\n| EfficientDet-lite4,  [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco/efficientdet-lite4.tgz)  |  43.18 | 42.83 |   20M | 260ms  |\n\n\n## 3. Export SavedModel, frozen graph, tensort models, or tflite.\n\n\nRun the following command line to export models:\n\n    !rm  -rf savedmodeldir\n    !python model_inspect.py --runmode=saved_model --model_name=efficientdet-d0 \\\n      --ckpt_path=efficientdet-d0 --saved_model_dir=savedmodeldir \\\n      --tensorrt=FP32  --tflite_path=efficientdet-d0.tflite \\\n      --hparams=voc_config.yaml\n\nThen you will get:\n\n - saved model under `savedmodeldir/`\n - frozen graph with name `savedmodeldir/efficientdet-d0_frozen.pb`\n - TensorRT saved model under `savedmodeldir/tensorrt_fp32/`\n - tflite file with name `efficientdet-d0.tflite`\n\nNotably,\n --tflite_path only works after 2.3.0-dev20200521 ,\n --ckpt_path=xx/archive is the folder for exporting the best model.\n\n\n## 4. Benchmark model latency.\n\n\nThere are two types of latency: network latency and end-to-end latency.\n\n(1) To measure the network latency (from the first conv to the last class/box\nprediction output), use the following command:\n\n    !python model_inspect.py --runmode=bm --model_name=efficientdet-d0\n\nadd --hparams=\"mixed_precision=True\" if running on V100.\n\nOn single Tesla V100 without TensorRT, our D0 network (no pre/post-processing)\nhas 134 FPS (frame per second) for batch size 1, and 238 FPS for batch size 8.\n\n(2) To measure the end-to-end latency (from the input image to the final rendered\nnew image, including: image preprocessing, network, postprocessing and NMS),\nuse the following command:\n\n    !rm  -rf /tmp/benchmark/\n    !python model_inspect.py --runmode=saved_model --model_name=efficientdet-d0 \\\n      --ckpt_path=efficientdet-d0 --saved_model_dir=/tmp/benchmark/ \\\n      --hparams=mixed_precision=true\n\n    !python model_inspect.py --runmode=saved_model_benchmark \\\n      --saved_model_dir=/tmp/benchmark/efficientdet-d0_frozen.pb \\\n      --model_name=efficientdet-d0  --input_image=testdata/img1.jpg  \\\n      --output_image_dir=/tmp/\n\nOn single Tesla V100 without using TensorRT, our end-to-end\nlatency and throughput are:\n\n\n|       Model    |   mAP | batch1 latency |  batch1 throughput |  batch8 throughput |\n| ------ | ------ | ------  | ------ | ------ |\n| EfficientDet-D0 |  34.6 | 10.2ms | 97 fps | 209 fps |\n| EfficientDet-D1 |  40.5 | 13.5ms | 74 fps | 140 fps |\n| EfficientDet-D2 |  43.0 | 17.7ms | 57 fps | 97 fps  |\n| EfficientDet-D3 |  47.5 | 28.0ms | 36 fps | 58 fps  |\n| EfficientDet-D4 |  49.7 | 42.8ms | 23 fps | 35 fps  |\n| EfficientDet-D5 |  51.5 | 72.5ms | 14 fps | 18 fps  |\n| EfficientDet-D6 |  52.6 | 92.8ms | 11 fps | - fps  |\n| EfficientDet-D7 |  53.7 | 122ms  | 8.2 fps | - fps  |\n| EfficientDet-D7x |  55.1 | 153ms  | 6.5 fps | - fps  |\n\n** FPS means frames per second (or images/second).\n\n## 5. Inference for images.\n\n    # Step0: download model and testing image.\n    !export MODEL=efficientdet-d0\n    !export CKPT_PATH=efficientdet-d0\n    !wget https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco/${MODEL}.tar.gz\n    !wget https://user-images.githubusercontent.com/11736571/77320690-099af300-6d37-11ea-9d86-24f14dc2d540.png -O img.png\n    !tar xf ${MODEL}.tar.gz\n\n    # Step 1: export saved model.\n    !python model_inspect.py --runmode=saved_model \\\n      --model_name=efficientdet-d0 --ckpt_path=efficientdet-d0 \\\n      --hparams=\"image_size=1920x1280\" \\\n      --saved_model_dir=/tmp/saved_model\n\n    # Step 2: do inference with saved model.\n    !python model_inspect.py --runmode=saved_model_infer \\\n      --model_name=efficientdet-d0  \\\n      --saved_model_dir=/tmp/saved_model  \\\n      --input_image=img.png --output_image_dir=/tmp/\n    # you can visualize the output /tmp/0.jpg\n\n\nAlternatively, if you want to do inference using frozen graph instead of saved model, you can run\n\n    # Step 0 and 1 is the same as before.\n    # Step 2: do inference with frozen graph.\n    !python model_inspect.py --runmode=saved_model_infer \\\n      --model_name=efficientdet-d0  \\\n      --saved_model_dir=/tmp/saved_model/efficientdet-d0_frozen.pb  \\\n      --input_image=img.png --output_image_dir=/tmp/\n\nLastly, if you only have one image and just want to run a quick test, you can also run the following command (it is slow because it needs to construct the graph from scratch):\n\n    # Run inference for a single image.\n    !python model_inspect.py --runmode=infer --model_name=$MODEL \\\n      --hparams=\"image_size=1920x1280\"  --max_boxes_to_draw=100   --min_score_thresh=0.4 \\\n      --ckpt_path=$CKPT_PATH --input_image=img.png --output_image_dir=/tmp\n    # you can visualize the output /tmp/0.jpg\n\nHere is an example of EfficientDet-D0 visualization: more on [tutorial](tutorial.ipynb)\n\n<p align=\"center\">\n<img src=\"./g3doc/street.jpg\" width=\"800\" />\n</p>\n\n## 6. Inference for videos.\n\nYou can run inference for a video and show the results online:\n\n    # step 0: download the example video.\n    !wget https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/data/video480p.mov -O input.mov\n\n    # step 1: export saved model.\n    !python model_inspect.py --runmode=saved_model \\\n      --model_name=efficientdet-d0 --ckpt_path=efficientdet-d0 \\\n      --saved_model_dir=/tmp/savedmodel --hparams=voc_config.yaml\n\n    # step 2: inference video using saved_model_video.\n    !python model_inspect.py --runmode=saved_model_video \\\n      --model_name=efficientdet-d0 \\\n      --saved_model_dir=/tmp/savedmodel --input_video=input.mov\n\n    # alternative step 2: inference video and save the result.\n    !python model_inspect.py --runmode=saved_model_video \\\n      --model_name=efficientdet-d0   \\\n      --saved_model_dir=/tmp/savedmodel --input_video=input.mov  \\\n      --output_video=output.mov\n\n## 7. Eval on COCO 2017 val or test-dev.\n\n    // Download coco data.\n    !wget http://images.cocodataset.org/zips/val2017.zip\n    !wget http://images.cocodataset.org/annotations/annotations_trainval2017.zip\n    !unzip val2017.zip\n    !unzip annotations_trainval2017.zip\n\n    // convert coco data to tfrecord.\n    !mkdir tfrecord\n    !PYTHONPATH=\".:$PYTHONPATH\"  python dataset/create_coco_tfrecord.py \\\n        --image_dir=val2017 \\\n        --caption_annotations_file=annotations/captions_val2017.json \\\n        --output_file_prefix=tfrecord/val \\\n        --num_shards=32\n\n    // Run eval.\n    !python main.py --mode=eval  \\\n        --model_name=${MODEL}  --model_dir=${CKPT_PATH}  \\\n        --val_file_pattern=tfrecord/val*  \\\n        --val_json_file=annotations/instances_val2017.json\n\nYou can also run eval on test-dev set with the following command:\n\n    !wget http://images.cocodataset.org/zips/test2017.zip\n    !unzip -q test2017.zip\n    !wget http://images.cocodataset.org/annotations/image_info_test2017.zip\n    !unzip image_info_test2017.zip\n\n    !mkdir tfrecord\n    !PYTHONPATH=\".:$PYTHONPATH\"  python dataset/create_coco_tfrecord.py \\\n          --image_dir=test2017 \\\n          --image_info_file=annotations/image_info_test-dev2017.json \\\n          --output_file_prefix=tfrecord/testdev \\\n          --num_shards=32\n\n    # Eval on test-dev: testdev_dir must be set.\n    # Also, test-dev has 20288 images rather than val 5000 images.\n    !python main.py --mode=eval  \\\n        --model_name=${MODEL}  --model_dir=${CKPT_PATH}  \\\n        --val_file_pattern=tfrecord/testdev*  \\\n        --testdev_dir='testdev_output' --eval_samples=20288\n    # Now you can submit testdev_output/detections_test-dev2017_test_results.json to\n    # coco server: https://competitions.codalab.org/competitions/20794#participate\n\n## 8. Finetune on PASCAL VOC 2012 with detector COCO ckpt.\n\nDownload data and checkpoints.\n\n    # Download and convert pascal data.\n    !wget http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar\n    !tar xf VOCtrainval_11-May-2012.tar\n    !mkdir tfrecord\n    !PYTHONPATH=\".:$PYTHONPATH\"  python dataset/create_pascal_tfrecord.py  \\\n        --data_dir=VOCdevkit --year=VOC2012  --output_path=tfrecord/pascal\n\n    # Download backbone checkopints.\n    !wget https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco/efficientdet-d0.tar.gz\n    !tar xf efficientdet-d0.tar.gz\n\nCreate a config file for the PASCAL VOC dataset called voc_config.yaml and put this in it.\n\n      num_classes: 21\n      var_freeze_expr: '(efficientnet|fpn_cells|resample_p6)'\n      label_map: {1: aeroplane, 2: bicycle, 3: bird, 4: boat, 5: bottle, 6: bus, 7: car, 8: cat, 9: chair, 10: cow, 11: diningtable, 12: dog, 13: horse, 14: motorbike, 15: person, 16: pottedplant, 17: sheep, 18: sofa, 19: train, 20: tvmonitor}\n\nFinetune needs to use --ckpt rather than --backbone_ckpt.\n\n    !python main.py --mode=train_and_eval \\\n        --train_file_pattern=tfrecord/pascal*.tfrecord \\\n        --val_file_pattern=tfrecord/pascal*.tfrecord \\\n        --model_name=efficientdet-d0 \\\n        --model_dir=/tmp/efficientdet-d0-finetune  \\\n        --ckpt=efficientdet-d0  \\\n        --train_batch_size=64 \\\n        --eval_batch_size=64 --eval_samples=1024 \\\n        --num_examples_per_epoch=5717 --num_epochs=50  \\\n        --hparams=voc_config.yaml\n\nIf you want to continue to train the model, simply re-run the above command because the `num_epochs` is a maximum number of epochs. For example, to reproduce the result of efficientdet-d0, set `--num_epochs=300` then run the command multiple times until the training is finished.\n\nIf you want to do inference for custom data, you can run\n\n    # Setting hparams-flag is needed sometimes.\n    !python model_inspect.py --runmode=infer \\\n      --model_name=efficientdet-d0   --ckpt_path=efficientdet-d0 \\\n      --hparams=voc_config.yaml  \\\n      --input_image=img.png --output_image_dir=/tmp/\n\nYou should check more details of runmode which is written in caption-4.\n\n## 9. Train on multi GPUs.\n\nCreate a config file for the PASCAL VOC dataset called voc_config.yaml and put this in it.\n\n      num_classes: 21\n      var_freeze_expr: '(efficientnet|fpn_cells|resample_p6)'\n      label_map: {1: aeroplane, 2: bicycle, 3: bird, 4: boat, 5: bottle, 6: bus, 7: car, 8: cat, 9: chair, 10: cow, 11: diningtable, 12: dog, 13: horse, 14: motorbike, 15: person, 16: pottedplant, 17: sheep, 18: sofa, 19: train, 20: tvmonitor}\n\nDownload efficientdet coco checkpoint.\n\n    !wget https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco/efficientdet-d0.tar.gz\n    !tar xf efficientdet-d0.tar.gz\n\nFinetune needs to use --ckpt rather than --backbone_ckpt.\n\n    python main.py --mode=train \\\n        --train_file_pattern=tfrecord/pascal*.tfrecord \\\n        --val_file_pattern=tfrecord/pascal*.tfrecord \\\n        --model_name=efficientdet-d0 \\\n        --model_dir=/tmp/efficientdet-d0-finetune  \\\n        --ckpt=efficientdet-d0  \\\n        --train_batch_size=64 \\\n        --eval_batch_size=64 --eval_samples=1024 \\\n        --num_examples_per_epoch=5717 --num_epochs=50  \\\n        --hparams=voc_config.yaml\n        --strategy=gpus\n\nIf you want to do inference for custom data, you can run\n\n    # Setting hparams-flag is needed sometimes.\n    !python model_inspect.py --runmode=infer \\\n      --model_name=efficientdet-d0   --ckpt_path=efficientdet-d0 \\\n      --hparams=voc_config.yaml  \\\n      --input_image=img.png --output_image_dir=/tmp/\n\nYou should check more details of runmode which is written in caption-4.\n\n## 10. Training EfficientDets on TPUs.\n\nTo train this model on Cloud TPU, you will need:\n\n   * A GCE VM instance with an associated Cloud TPU resource.\n   * A GCS bucket to store your training checkpoints (the \"model directory\").\n   * Install latest TensorFlow for both GCE VM and Cloud.\n\nThen train the model:\n\n    !export PYTHONPATH=\"$PYTHONPATH:/path/to/models\"\n    !python main.py --tpu=TPU_NAME --train_file_pattern=DATA_DIR/*.tfrecord --model_dir=MODEL_DIR --strategy=tpu\n\n    # TPU_NAME is the name of the TPU node, the same name that appears when you run gcloud compute tpus list, or ctpu ls.\n    # MODEL_DIR is a GCS location (a URL starting with gs:// where both the GCE VM and the associated Cloud TPU have write access.\n    # DATA_DIR is a GCS location to which both the GCE VM and associated Cloud TPU have read access.\n\n\nFor more instructions about training on TPUs, please refer to the following tutorials:\n\n  * EfficientNet tutorial: https://cloud.google.com/tpu/docs/tutorials/efficientnet\n\n## 11. Reducing Memory Usage when Training EfficientDets on GPU.\n\nEfficientDets use a lot of GPU memory for a few reasons:\n\n* Large input resolution: because resolution is one of the scaling dimension, our resolution tends to be higher, which significantly increase activations (although no parameter increase).\n* Large internal activations for backbone: our backbone uses a relatively large expansion ratio (6), causing the large expanded activations.\n* Deep BiFPN: our BiFPN has multiple top-down and bottom-up paths, which leads to a lot of intermediate memory usage during training.\n\nTo train this model on GPU with low memory there is an experimental option grad_checkpoint.\n\nCheck these links for a high-level idea of what gradient checkpointing is doing:\n1. https://medium.com/tensorflow/fitting-larger-networks-into-memory-583e3c758ff9\n\n**grad_checkpoint: True**\n\nIf set to True, keras model uses ```tf.recompute_grad``` to achieve gradient checkpoints.\n\nTesting shows that:\n* It also allows to train a d6 network with batch size of 2 by main.py on a 11Gb (1080Ti) GPU\n\n## 12. Visualize TF-Records.\n\nYou can visualize tf-records with following commands:\n\nTo visualize training tfrecords with input dataloader use.\n```\npython dataset/inspect_tfrecords.py --file_pattern dataset/sample.record\\ \n--model_name \"efficientdet-d0\" --samples 10\\ \n--save_samples_dir train_samples/  -hparams=\"label_map={1:'label1'}, autoaugmentation_policy=v3\"\n\n```\n\nTo visualize evaluation tfrecords use.\n```\npython dataset/inspect_tfrecords.py --file_pattern dataset/sample.record\\ \n--model_name \"efficientdet-d0\" --samples 10\\ \n--save_samples_dir train_samples/  -eval\\\n-hparams=\"label_map={1:'label1'}\"\n\n```\n* samples: random samples to visualize.\n* model_name: model name will be used to get image_size.\n* save_samples_dir: save dir.\n* eval: flag for eval data.\n\nNOTE: this is not an official Google product.\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/__init__.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/aug/__init__.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/aug/autoaugment.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"AutoAugment.\n\n[1] Barret, et al. Learning Data Augmentation Strategies for Object Detection.\n    Arxiv: https://arxiv.org/abs/1906.11172\n\"\"\"\nimport inspect\nimport math\nfrom absl import logging\nimport tensorflow.compat.v1 as tf\nfrom tensorflow_addons import image as image_ops\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import hparams_config\n\n# This signifies the max integer that the controller RNN could predict for the\n# augmentation scheme.\n_MAX_LEVEL = 10.\n\n# Represents an invalid bounding box that is used for checking for padding\n# lists of bounding box coordinates for a few augmentation operations\n_INVALID_BOX = [[-1.0, -1.0, -1.0, -1.0]]\n\n\ndef policy_v0():\n  \"\"\"Autoaugment policy that was used in AutoAugment Detection Paper.\"\"\"\n  # Each tuple is an augmentation operation of the form\n  # (operation, probability, magnitude). Each element in policy is a\n  # sub-policy that will be applied sequentially on the image.\n  policy = [\n      [('TranslateX_BBox', 0.6, 4), ('Equalize', 0.8, 10)],\n      [('TranslateY_Only_BBoxes', 0.2, 2), ('Cutout', 0.8, 8)],\n      [('Sharpness', 0.0, 8), ('ShearX_BBox', 0.4, 0)],\n      [('ShearY_BBox', 1.0, 2), ('TranslateY_Only_BBoxes', 0.6, 6)],\n      [('Rotate_BBox', 0.6, 10), ('Color', 1.0, 6)],\n  ]\n  return policy\n\n\ndef policy_v1():\n  \"\"\"Autoaugment policy that was used in AutoAugment Detection Paper.\"\"\"\n  # Each tuple is an augmentation operation of the form\n  # (operation, probability, magnitude). Each element in policy is a\n  # sub-policy that will be applied sequentially on the image.\n  policy = [\n      [('TranslateX_BBox', 0.6, 4), ('Equalize', 0.8, 10)],\n      [('TranslateY_Only_BBoxes', 0.2, 2), ('Cutout', 0.8, 8)],\n      [('Sharpness', 0.0, 8), ('ShearX_BBox', 0.4, 0)],\n      [('ShearY_BBox', 1.0, 2), ('TranslateY_Only_BBoxes', 0.6, 6)],\n      [('Rotate_BBox', 0.6, 10), ('Color', 1.0, 6)],\n      [('Color', 0.0, 0), ('ShearX_Only_BBoxes', 0.8, 4)],\n      [('ShearY_Only_BBoxes', 0.8, 2), ('Flip_Only_BBoxes', 0.0, 10)],\n      [('Equalize', 0.6, 10), ('TranslateX_BBox', 0.2, 2)],\n      [('Color', 1.0, 10), ('TranslateY_Only_BBoxes', 0.4, 6)],\n      [('Rotate_BBox', 0.8, 10), ('Contrast', 0.0, 10)],\n      [('Cutout', 0.2, 2), ('Brightness', 0.8, 10)],\n      [('Color', 1.0, 6), ('Equalize', 1.0, 2)],\n      [('Cutout_Only_BBoxes', 0.4, 6), ('TranslateY_Only_BBoxes', 0.8, 2)],\n      [('Color', 0.2, 8), ('Rotate_BBox', 0.8, 10)],\n      [('Sharpness', 0.4, 4), ('TranslateY_Only_BBoxes', 0.0, 4)],\n      [('Sharpness', 1.0, 4), ('SolarizeAdd', 0.4, 4)],\n      [('Rotate_BBox', 1.0, 8), ('Sharpness', 0.2, 8)],\n      [('ShearY_BBox', 0.6, 10), ('Equalize_Only_BBoxes', 0.6, 8)],\n      [('ShearX_BBox', 0.2, 6), ('TranslateY_Only_BBoxes', 0.2, 10)],\n      [('SolarizeAdd', 0.6, 8), ('Brightness', 0.8, 10)],\n  ]\n  return policy\n\n\ndef policy_vtest():\n  \"\"\"Autoaugment test policy for debugging.\"\"\"\n  # Each tuple is an augmentation operation of the form\n  # (operation, probability, magnitude). Each element in policy is a\n  # sub-policy that will be applied sequentially on the image.\n  policy = [\n      [('TranslateX_BBox', 1.0, 4), ('Equalize', 1.0, 10)],\n  ]\n  return policy\n\n\ndef policy_v2():\n  \"\"\"Additional policy that performs well on object detection.\"\"\"\n  # Each tuple is an augmentation operation of the form\n  # (operation, probability, magnitude). Each element in policy is a\n  # sub-policy that will be applied sequentially on the image.\n  policy = [\n      [('Color', 0.0, 6), ('Cutout', 0.6, 8), ('Sharpness', 0.4, 8)],\n      [('Rotate_BBox', 0.4, 8), ('Sharpness', 0.4, 2),\n       ('Rotate_BBox', 0.8, 10)],\n      [('TranslateY_BBox', 1.0, 8), ('AutoContrast', 0.8, 2)],\n      [('AutoContrast', 0.4, 6), ('ShearX_BBox', 0.8, 8),\n       ('Brightness', 0.0, 10)],\n      [('SolarizeAdd', 0.2, 6), ('Contrast', 0.0, 10),\n       ('AutoContrast', 0.6, 0)],\n      [('Cutout', 0.2, 0), ('Solarize', 0.8, 8), ('Color', 1.0, 4)],\n      [('TranslateY_BBox', 0.0, 4), ('Equalize', 0.6, 8),\n       ('Solarize', 0.0, 10)],\n      [('TranslateY_BBox', 0.2, 2), ('ShearY_BBox', 0.8, 8),\n       ('Rotate_BBox', 0.8, 8)],\n      [('Cutout', 0.8, 8), ('Brightness', 0.8, 8), ('Cutout', 0.2, 2)],\n      [('Color', 0.8, 4), ('TranslateY_BBox', 1.0, 6), ('Rotate_BBox', 0.6, 6)],\n      [('Rotate_BBox', 0.6, 10), ('BBox_Cutout', 1.0, 4), ('Cutout', 0.2, 8)],\n      [('Rotate_BBox', 0.0, 0), ('Equalize', 0.6, 6), ('ShearY_BBox', 0.6, 8)],\n      [('Brightness', 0.8, 8), ('AutoContrast', 0.4, 2),\n       ('Brightness', 0.2, 2)],\n      [('TranslateY_BBox', 0.4, 8), ('Solarize', 0.4, 6),\n       ('SolarizeAdd', 0.2, 10)],\n      [('Contrast', 1.0, 10), ('SolarizeAdd', 0.2, 8), ('Equalize', 0.2, 4)],\n  ]\n  return policy\n\n\ndef policy_v3():\n  \"\"\"\"Additional policy that performs well on object detection.\"\"\"\n  # Each tuple is an augmentation operation of the form\n  # (operation, probability, magnitude). Each element in policy is a\n  # sub-policy that will be applied sequentially on the image.\n  policy = [\n      [('Posterize', 0.8, 2), ('TranslateX_BBox', 1.0, 8)],\n      [('BBox_Cutout', 0.2, 10), ('Sharpness', 1.0, 8)],\n      [('Rotate_BBox', 0.6, 8), ('Rotate_BBox', 0.8, 10)],\n      [('Equalize', 0.8, 10), ('AutoContrast', 0.2, 10)],\n      [('SolarizeAdd', 0.2, 2), ('TranslateY_BBox', 0.2, 8)],\n      [('Sharpness', 0.0, 2), ('Color', 0.4, 8)],\n      [('Equalize', 1.0, 8), ('TranslateY_BBox', 1.0, 8)],\n      [('Posterize', 0.6, 2), ('Rotate_BBox', 0.0, 10)],\n      [('AutoContrast', 0.6, 0), ('Rotate_BBox', 1.0, 6)],\n      [('Equalize', 0.0, 4), ('Cutout', 0.8, 10)],\n      [('Brightness', 1.0, 2), ('TranslateY_BBox', 1.0, 6)],\n      [('Contrast', 0.0, 2), ('ShearY_BBox', 0.8, 0)],\n      [('AutoContrast', 0.8, 10), ('Contrast', 0.2, 10)],\n      [('Rotate_BBox', 1.0, 10), ('Cutout', 1.0, 10)],\n      [('SolarizeAdd', 0.8, 6), ('Equalize', 0.8, 8)],\n  ]\n  return policy\n\n\ndef blend(image1, image2, factor):\n  \"\"\"Blend image1 and image2 using 'factor'.\n\n  Factor can be above 0.0.  A value of 0.0 means only image1 is used.\n  A value of 1.0 means only image2 is used.  A value between 0.0 and\n  1.0 means we linearly interpolate the pixel values between the two\n  images.  A value greater than 1.0 \"extrapolates\" the difference\n  between the two pixel values, and we clip the results to values\n  between 0 and 255.\n\n  Args:\n    image1: An image Tensor of type uint8.\n    image2: An image Tensor of type uint8.\n    factor: A floating point value above 0.0.\n\n  Returns:\n    A blended image Tensor of type uint8.\n  \"\"\"\n  if factor == 0.0:\n    return tf.convert_to_tensor(image1)\n  if factor == 1.0:\n    return tf.convert_to_tensor(image2)\n\n  image1 = tf.to_float(image1)\n  image2 = tf.to_float(image2)\n\n  difference = image2 - image1\n  scaled = factor * difference\n\n  # Do addition in float.\n  temp = tf.to_float(image1) + scaled\n\n  # Interpolate\n  if factor > 0.0 and factor < 1.0:\n    # Interpolation means we always stay within 0 and 255.\n    return tf.cast(temp, tf.uint8)\n\n  # Extrapolate:\n  #\n  # We need to clip and then cast.\n  return tf.cast(tf.clip_by_value(temp, 0.0, 255.0), tf.uint8)\n\n\ndef cutout(image, pad_size, replace=0):\n  \"\"\"Apply cutout (https://arxiv.org/abs/1708.04552) to image.\n\n  This operation applies a (2*pad_size x 2*pad_size) mask of zeros to\n  a random location within `img`. The pixel values filled in will be of the\n  value `replace`. The located where the mask will be applied is randomly\n  chosen uniformly over the whole image.\n\n  Args:\n    image: An image Tensor of type uint8.\n    pad_size: Specifies how big the zero mask that will be generated is that\n      is applied to the image. The mask will be of size\n      (2*pad_size x 2*pad_size).\n    replace: What pixel value to fill in the image in the area that has\n      the cutout mask applied to it.\n\n  Returns:\n    An image Tensor that is of type uint8.\n  \"\"\"\n  image_height = tf.shape(image)[0]\n  image_width = tf.shape(image)[1]\n\n  # Sample the center location in the image where the zero mask will be applied.\n  cutout_center_height = tf.random_uniform(\n      shape=[], minval=0, maxval=image_height,\n      dtype=tf.int32)\n\n  cutout_center_width = tf.random_uniform(\n      shape=[], minval=0, maxval=image_width,\n      dtype=tf.int32)\n\n  lower_pad = tf.maximum(0, cutout_center_height - pad_size)\n  upper_pad = tf.maximum(0, image_height - cutout_center_height - pad_size)\n  left_pad = tf.maximum(0, cutout_center_width - pad_size)\n  right_pad = tf.maximum(0, image_width - cutout_center_width - pad_size)\n\n  cutout_shape = [image_height - (lower_pad + upper_pad),\n                  image_width - (left_pad + right_pad)]\n  padding_dims = [[lower_pad, upper_pad], [left_pad, right_pad]]\n  mask = tf.pad(\n      tf.zeros(cutout_shape, dtype=image.dtype),\n      padding_dims, constant_values=1)\n  mask = tf.expand_dims(mask, -1)\n  mask = tf.tile(mask, [1, 1, 3])\n  image = tf.where(\n      tf.equal(mask, 0),\n      tf.ones_like(image, dtype=image.dtype) * replace,\n      image)\n  return image\n\n\ndef solarize(image, threshold=128):\n  # For each pixel in the image, select the pixel\n  # if the value is less than the threshold.\n  # Otherwise, subtract 255 from the pixel.\n  return tf.where(image < threshold, image, 255 - image)\n\n\ndef solarize_add(image, addition=0, threshold=128):\n  # For each pixel in the image less than threshold\n  # we add 'addition' amount to it and then clip the\n  # pixel value to be between 0 and 255. The value\n  # of 'addition' is between -128 and 128.\n  added_image = tf.cast(image, tf.int64) + addition\n  added_image = tf.cast(tf.clip_by_value(added_image, 0, 255), tf.uint8)\n  return tf.where(image < threshold, added_image, image)\n\n\ndef color(image, factor):\n  \"\"\"Equivalent of PIL Color.\"\"\"\n  degenerate = tf.image.grayscale_to_rgb(tf.image.rgb_to_grayscale(image))\n  return blend(degenerate, image, factor)\n\n\ndef contrast(image, factor):\n  \"\"\"Equivalent of PIL Contrast.\"\"\"\n  degenerate = tf.image.rgb_to_grayscale(image)\n  # Cast before calling tf.histogram.\n  degenerate = tf.cast(degenerate, tf.int32)\n\n  # Compute the grayscale histogram, then compute the mean pixel value,\n  # and create a constant image size of that value.  Use that as the\n  # blending degenerate target of the original image.\n  mean = tf.reduce_mean(tf.cast(degenerate, tf.float32))\n  degenerate = tf.ones_like(degenerate, dtype=tf.float32) * mean\n  degenerate = tf.clip_by_value(degenerate, 0.0, 255.0)\n  degenerate = tf.image.grayscale_to_rgb(tf.cast(degenerate, tf.uint8))\n  return blend(degenerate, image, factor)\n\n\ndef brightness(image, factor):\n  \"\"\"Equivalent of PIL Brightness.\"\"\"\n  degenerate = tf.zeros_like(image)\n  return blend(degenerate, image, factor)\n\n\ndef posterize(image, bits):\n  \"\"\"Equivalent of PIL Posterize.\"\"\"\n  shift = 8 - bits\n  return tf.bitwise.left_shift(tf.bitwise.right_shift(image, shift), shift)\n\n\ndef rotate(image, degrees, replace):\n  \"\"\"Rotates the image by degrees either clockwise or counterclockwise.\n\n  Args:\n    image: An image Tensor of type uint8.\n    degrees: Float, a scalar angle in degrees to rotate all images by. If\n      degrees is positive the image will be rotated clockwise otherwise it will\n      be rotated counterclockwise.\n    replace: A one or three value 1D tensor to fill empty pixels caused by\n      the rotate operation.\n\n  Returns:\n    The rotated version of image.\n  \"\"\"\n  # Convert from degrees to radians.\n  degrees_to_radians = math.pi / 180.0\n  radians = degrees * degrees_to_radians\n\n  # In practice, we should randomize the rotation degrees by flipping\n  # it negatively half the time, but that's done on 'degrees' outside\n  # of the function.\n  image = image_ops.rotate(wrap(image), radians)\n  return unwrap(image, replace)\n\n\ndef random_shift_bbox(image, bbox, pixel_scaling, replace,\n                      new_min_bbox_coords=None):\n  \"\"\"Move the bbox and the image content to a slightly new random location.\n\n  Args:\n    image: 3D uint8 Tensor.\n    bbox: 1D Tensor that has 4 elements (min_y, min_x, max_y, max_x)\n      of type float that represents the normalized coordinates between 0 and 1.\n      The potential values for the new min corner of the bbox will be between\n      [old_min - pixel_scaling * bbox_height/2,\n       old_min - pixel_scaling * bbox_height/2].\n    pixel_scaling: A float between 0 and 1 that specifies the pixel range\n      that the new bbox location will be sampled from.\n    replace: A one or three value 1D tensor to fill empty pixels.\n    new_min_bbox_coords: If not None, then this is a tuple that specifies the\n      (min_y, min_x) coordinates of the new bbox. Normally this is randomly\n      specified, but this allows it to be manually set. The coordinates are\n      the absolute coordinates between 0 and image height/width and are int32.\n\n  Returns:\n    The new image that will have the shifted bbox location in it along with\n    the new bbox that contains the new coordinates.\n  \"\"\"\n  # Obtains image height and width and create helper clip functions.\n  image_height = tf.to_float(tf.shape(image)[0])\n  image_width = tf.to_float(tf.shape(image)[1])\n  def clip_y(val):\n    return tf.clip_by_value(val, 0, tf.to_int32(image_height) - 1)\n  def clip_x(val):\n    return tf.clip_by_value(val, 0, tf.to_int32(image_width) - 1)\n\n  # Convert bbox to pixel coordinates.\n  min_y = tf.to_int32(image_height * bbox[0])\n  min_x = tf.to_int32(image_width * bbox[1])\n  max_y = clip_y(tf.to_int32(image_height * bbox[2]))\n  max_x = clip_x(tf.to_int32(image_width * bbox[3]))\n  bbox_height, bbox_width = (max_y - min_y + 1, max_x - min_x + 1)\n  image_height = tf.to_int32(image_height)\n  image_width = tf.to_int32(image_width)\n\n  # Select the new min/max bbox ranges that are used for sampling the\n  # new min x/y coordinates of the shifted bbox.\n  minval_y = clip_y(\n      min_y - tf.to_int32(pixel_scaling * tf.to_float(bbox_height) / 2.0))\n  maxval_y = clip_y(\n      min_y + tf.to_int32(pixel_scaling * tf.to_float(bbox_height) / 2.0))\n  minval_x = clip_x(\n      min_x - tf.to_int32(pixel_scaling * tf.to_float(bbox_width) / 2.0))\n  maxval_x = clip_x(\n      min_x + tf.to_int32(pixel_scaling * tf.to_float(bbox_width) / 2.0))\n\n  # Sample and calculate the new unclipped min/max coordinates of the new bbox.\n  if new_min_bbox_coords is None:\n    unclipped_new_min_y = tf.random_uniform(\n        shape=[], minval=minval_y, maxval=maxval_y,\n        dtype=tf.int32)\n    unclipped_new_min_x = tf.random_uniform(\n        shape=[], minval=minval_x, maxval=maxval_x,\n        dtype=tf.int32)\n  else:\n    unclipped_new_min_y, unclipped_new_min_x = (\n        clip_y(new_min_bbox_coords[0]), clip_x(new_min_bbox_coords[1]))\n  unclipped_new_max_y = unclipped_new_min_y + bbox_height - 1\n  unclipped_new_max_x = unclipped_new_min_x + bbox_width - 1\n\n  # Determine if any of the new bbox was shifted outside the current image.\n  # This is used for determining if any of the original bbox content should be\n  # discarded.\n  new_min_y, new_min_x, new_max_y, new_max_x = (\n      clip_y(unclipped_new_min_y), clip_x(unclipped_new_min_x),\n      clip_y(unclipped_new_max_y), clip_x(unclipped_new_max_x))\n  shifted_min_y = (new_min_y - unclipped_new_min_y) + min_y\n  shifted_max_y = max_y - (unclipped_new_max_y - new_max_y)\n  shifted_min_x = (new_min_x - unclipped_new_min_x) + min_x\n  shifted_max_x = max_x - (unclipped_new_max_x - new_max_x)\n\n  # Create the new bbox tensor by converting pixel integer values to floats.\n  new_bbox = tf.stack([\n      tf.to_float(new_min_y) / tf.to_float(image_height),\n      tf.to_float(new_min_x) / tf.to_float(image_width),\n      tf.to_float(new_max_y) / tf.to_float(image_height),\n      tf.to_float(new_max_x) / tf.to_float(image_width)])\n\n  # Copy the contents in the bbox and fill the old bbox location\n  # with gray (128).\n  bbox_content = image[shifted_min_y:shifted_max_y + 1,\n                       shifted_min_x:shifted_max_x + 1, :]\n\n  def mask_and_add_image(\n      min_y_, min_x_, max_y_, max_x_, mask, content_tensor, image_):\n    \"\"\"Applies mask to bbox region in image then adds content_tensor to it.\"\"\"\n    mask = tf.pad(mask,\n                  [[min_y_, (image_height - 1) - max_y_],\n                   [min_x_, (image_width - 1) - max_x_],\n                   [0, 0]], constant_values=1)\n    content_tensor = tf.pad(content_tensor,\n                            [[min_y_, (image_height - 1) - max_y_],\n                             [min_x_, (image_width - 1) - max_x_],\n                             [0, 0]], constant_values=0)\n    return image_ * mask + content_tensor\n\n  # Zero out original bbox location.\n  mask = tf.zeros_like(image)[min_y:max_y+1, min_x:max_x+1, :]\n  grey_tensor = tf.zeros_like(mask) + replace[0]\n  image = mask_and_add_image(min_y, min_x, max_y, max_x, mask,\n                             grey_tensor, image)\n\n  # Fill in bbox content to new bbox location.\n  mask = tf.zeros_like(bbox_content)\n  image = mask_and_add_image(new_min_y, new_min_x, new_max_y, new_max_x, mask,\n                             bbox_content, image)\n\n  return image, new_bbox\n\n\ndef _clip_bbox(min_y, min_x, max_y, max_x):\n  \"\"\"Clip bounding box coordinates between 0 and 1.\n\n  Args:\n    min_y: Normalized bbox coordinate of type float between 0 and 1.\n    min_x: Normalized bbox coordinate of type float between 0 and 1.\n    max_y: Normalized bbox coordinate of type float between 0 and 1.\n    max_x: Normalized bbox coordinate of type float between 0 and 1.\n\n  Returns:\n    Clipped coordinate values between 0 and 1.\n  \"\"\"\n  min_y = tf.clip_by_value(min_y, 0.0, 1.0)\n  min_x = tf.clip_by_value(min_x, 0.0, 1.0)\n  max_y = tf.clip_by_value(max_y, 0.0, 1.0)\n  max_x = tf.clip_by_value(max_x, 0.0, 1.0)\n  return min_y, min_x, max_y, max_x\n\n\ndef _check_bbox_area(min_y, min_x, max_y, max_x, delta=0.05):\n  \"\"\"Adjusts bbox coordinates to make sure the area is > 0.\n\n  Args:\n    min_y: Normalized bbox coordinate of type float between 0 and 1.\n    min_x: Normalized bbox coordinate of type float between 0 and 1.\n    max_y: Normalized bbox coordinate of type float between 0 and 1.\n    max_x: Normalized bbox coordinate of type float between 0 and 1.\n    delta: Float, this is used to create a gap of size 2 * delta between\n      bbox min/max coordinates that are the same on the boundary.\n      This prevents the bbox from having an area of zero.\n\n  Returns:\n    Tuple of new bbox coordinates between 0 and 1 that will now have a\n    guaranteed area > 0.\n  \"\"\"\n  height = max_y - min_y\n  width = max_x - min_x\n  def _adjust_bbox_boundaries(min_coord, max_coord):\n    # Make sure max is never 0 and min is never 1.\n    max_coord = tf.maximum(max_coord, 0.0 + delta)\n    min_coord = tf.minimum(min_coord, 1.0 - delta)\n    return min_coord, max_coord\n  min_y, max_y = tf.cond(tf.equal(height, 0.0),\n                         lambda: _adjust_bbox_boundaries(min_y, max_y),\n                         lambda: (min_y, max_y))\n  min_x, max_x = tf.cond(tf.equal(width, 0.0),\n                         lambda: _adjust_bbox_boundaries(min_x, max_x),\n                         lambda: (min_x, max_x))\n  return min_y, min_x, max_y, max_x\n\n\ndef _scale_bbox_only_op_probability(prob):\n  \"\"\"Reduce the probability of the bbox-only operation.\n\n  Probability is reduced so that we do not distort the content of too many\n  bounding boxes that are close to each other. The value of 3.0 was a chosen\n  hyper parameter when designing the autoaugment algorithm that we found\n  empirically to work well.\n\n  Args:\n    prob: Float that is the probability of applying the bbox-only operation.\n\n  Returns:\n    Reduced probability.\n  \"\"\"\n  return prob / 3.0\n\n\ndef _apply_bbox_augmentation(image, bbox, augmentation_func, *args):\n  \"\"\"Applies augmentation_func to the subsection of image indicated by bbox.\n\n  Args:\n    image: 3D uint8 Tensor.\n    bbox: 1D Tensor that has 4 elements (min_y, min_x, max_y, max_x)\n      of type float that represents the normalized coordinates between 0 and 1.\n    augmentation_func: Augmentation function that will be applied to the\n      subsection of image.\n    *args: Additional parameters that will be passed into augmentation_func\n      when it is called.\n\n  Returns:\n    A modified version of image, where the bbox location in the image will\n    have `ugmentation_func applied to it.\n  \"\"\"\n  image_height = tf.to_float(tf.shape(image)[0])\n  image_width = tf.to_float(tf.shape(image)[1])\n  min_y = tf.to_int32(image_height * bbox[0])\n  min_x = tf.to_int32(image_width * bbox[1])\n  max_y = tf.to_int32(image_height * bbox[2])\n  max_x = tf.to_int32(image_width * bbox[3])\n  image_height = tf.to_int32(image_height)\n  image_width = tf.to_int32(image_width)\n\n  # Clip to be sure the max values do not fall out of range.\n  max_y = tf.minimum(max_y, image_height - 1)\n  max_x = tf.minimum(max_x, image_width - 1)\n\n  # Get the sub-tensor that is the image within the bounding box region.\n  bbox_content = image[min_y:max_y + 1, min_x:max_x + 1, :]\n\n  # Apply the augmentation function to the bbox portion of the image.\n  augmented_bbox_content = augmentation_func(bbox_content, *args)\n\n  # Pad the augmented_bbox_content and the mask to match the shape of original\n  # image.\n  augmented_bbox_content = tf.pad(augmented_bbox_content,\n                                  [[min_y, (image_height - 1) - max_y],\n                                   [min_x, (image_width - 1) - max_x],\n                                   [0, 0]])\n\n  # Create a mask that will be used to zero out a part of the original image.\n  mask_tensor = tf.zeros_like(bbox_content)\n\n  mask_tensor = tf.pad(mask_tensor,\n                       [[min_y, (image_height - 1) - max_y],\n                        [min_x, (image_width - 1) - max_x],\n                        [0, 0]],\n                       constant_values=1)\n  # Replace the old bbox content with the new augmented content.\n  image = image * mask_tensor + augmented_bbox_content\n  return image\n\n\ndef _concat_bbox(bbox, bboxes):\n  \"\"\"Helper function that concats bbox to bboxes along the first dimension.\"\"\"\n\n  # Note if all elements in bboxes are -1 (_INVALID_BOX), then this means\n  # we discard bboxes and start the bboxes Tensor with the current bbox.\n  bboxes_sum_check = tf.reduce_sum(bboxes)\n  bbox = tf.expand_dims(bbox, 0)\n  # This check will be true when it is an _INVALID_BOX\n  bboxes = tf.cond(tf.equal(bboxes_sum_check, -4.0),\n                   lambda: bbox,\n                   lambda: tf.concat([bboxes, bbox], 0))\n  return bboxes\n\n\ndef _apply_bbox_augmentation_wrapper(image, bbox, new_bboxes, prob,\n                                     augmentation_func, func_changes_bbox,\n                                     *args):\n  \"\"\"Applies _apply_bbox_augmentation with probability prob.\n\n  Args:\n    image: 3D uint8 Tensor.\n    bbox: 1D Tensor that has 4 elements (min_y, min_x, max_y, max_x)\n      of type float that represents the normalized coordinates between 0 and 1.\n    new_bboxes: 2D Tensor that is a list of the bboxes in the image after they\n      have been altered by aug_func. These will only be changed when\n      func_changes_bbox is set to true. Each bbox has 4 elements\n      (min_y, min_x, max_y, max_x) of type float that are the normalized\n      bbox coordinates between 0 and 1.\n    prob: Float that is the probability of applying _apply_bbox_augmentation.\n    augmentation_func: Augmentation function that will be applied to the\n      subsection of image.\n    func_changes_bbox: Boolean. Does augmentation_func return bbox in addition\n      to image.\n    *args: Additional parameters that will be passed into augmentation_func\n      when it is called.\n\n  Returns:\n    A tuple. Fist element is a modified version of image, where the bbox\n    location in the image will have augmentation_func applied to it if it is\n    chosen to be called with probability `prob`. The second element is a\n    Tensor of Tensors of length 4 that will contain the altered bbox after\n    applying augmentation_func.\n  \"\"\"\n  should_apply_op = tf.cast(\n      tf.floor(tf.random_uniform([], dtype=tf.float32) + prob), tf.bool)\n  if func_changes_bbox:\n    augmented_image, bbox = tf.cond(\n        should_apply_op,\n        lambda: augmentation_func(image, bbox, *args),\n        lambda: (image, bbox))\n  else:\n    augmented_image = tf.cond(\n        should_apply_op,\n        lambda: _apply_bbox_augmentation(image, bbox, augmentation_func, *args),\n        lambda: image)\n  new_bboxes = _concat_bbox(bbox, new_bboxes)\n  return augmented_image, new_bboxes\n\n\ndef _apply_multi_bbox_augmentation(image, bboxes, prob, aug_func,\n                                   func_changes_bbox, *args):\n  \"\"\"Applies aug_func to the image for each bbox in bboxes.\n\n  Args:\n    image: 3D uint8 Tensor.\n    bboxes: 2D Tensor that is a list of the bboxes in the image. Each bbox\n      has 4 elements (min_y, min_x, max_y, max_x) of type float.\n    prob: Float that is the probability of applying aug_func to a specific\n      bounding box within the image.\n    aug_func: Augmentation function that will be applied to the\n      subsections of image indicated by the bbox values in bboxes.\n    func_changes_bbox: Boolean. Does augmentation_func return bbox in addition\n      to image.\n    *args: Additional parameters that will be passed into augmentation_func\n      when it is called.\n\n  Returns:\n    A modified version of image, where each bbox location in the image will\n    have augmentation_func applied to it if it is chosen to be called with\n    probability prob independently across all bboxes. Also the final\n    bboxes are returned that will be unchanged if func_changes_bbox is set to\n    false and if true, the new altered ones will be returned.\n  \"\"\"\n  # Will keep track of the new altered bboxes after aug_func is repeatedly\n  # applied. The -1 values are a dummy value and this first Tensor will be\n  # removed upon appending the first real bbox.\n  new_bboxes = tf.constant(_INVALID_BOX)\n\n  # If the bboxes are empty, then just give it _INVALID_BOX. The result\n  # will be thrown away.\n  bboxes = tf.cond(tf.equal(tf.shape(bboxes)[0], 0),\n                   lambda: tf.constant(_INVALID_BOX),\n                   lambda: bboxes)\n\n  bboxes = tf.ensure_shape(bboxes, (None, 4))\n\n  # pylint:disable=g-long-lambda\n  # pylint:disable=line-too-long\n  wrapped_aug_func = lambda _image, bbox, _new_bboxes: _apply_bbox_augmentation_wrapper(\n      _image, bbox, _new_bboxes, prob, aug_func, func_changes_bbox, *args)\n  # pylint:enable=g-long-lambda\n  # pylint:enable=line-too-long\n\n  # Setup the while_loop.\n  num_bboxes = tf.shape(bboxes)[0]  # We loop until we go over all bboxes.\n  idx = tf.constant(0)  # Counter for the while loop.\n\n  # Conditional function when to end the loop once we go over all bboxes\n  # images_and_bboxes contain (_image, _new_bboxes)\n  cond = lambda _idx, _images_and_bboxes: tf.less(_idx, num_bboxes)\n\n  # Shuffle the bboxes so that the augmentation order is not deterministic if\n  # we are not changing the bboxes with aug_func.\n  if not func_changes_bbox:\n    loop_bboxes = tf.random.shuffle(bboxes)\n  else:\n    loop_bboxes = bboxes\n\n  # Main function of while_loop where we repeatedly apply augmentation on the\n  # bboxes in the image.\n  # pylint:disable=g-long-lambda\n  body = lambda _idx, _images_and_bboxes: [\n      _idx + 1, wrapped_aug_func(_images_and_bboxes[0],\n                                 loop_bboxes[_idx],\n                                 _images_and_bboxes[1])]\n  # pylint:enable=g-long-lambda\n\n  _, (image, new_bboxes) = tf.while_loop(\n      cond, body, [idx, (image, new_bboxes)],\n      shape_invariants=[idx.get_shape(),\n                        (image.get_shape(), tf.TensorShape([None, 4]))])\n\n  # Either return the altered bboxes or the original ones depending on if\n  # we altered them in anyway.\n  if func_changes_bbox:\n    final_bboxes = new_bboxes\n  else:\n    final_bboxes = bboxes\n  return image, final_bboxes\n\n\ndef _apply_multi_bbox_augmentation_wrapper(image, bboxes, prob, aug_func,\n                                           func_changes_bbox, *args):\n  \"\"\"Checks to be sure num bboxes > 0 before calling inner function.\"\"\"\n  num_bboxes = tf.shape(bboxes)[0]\n  image, bboxes = tf.cond(\n      tf.equal(num_bboxes, 0),\n      lambda: (image, bboxes),\n      # pylint:disable=g-long-lambda\n      lambda: _apply_multi_bbox_augmentation(\n          image, bboxes, prob, aug_func, func_changes_bbox, *args))\n  # pylint:enable=g-long-lambda\n  return image, bboxes\n\n\ndef rotate_only_bboxes(image, bboxes, prob, degrees, replace):\n  \"\"\"Apply rotate to each bbox in the image with probability prob.\"\"\"\n  func_changes_bbox = False\n  prob = _scale_bbox_only_op_probability(prob)\n  return _apply_multi_bbox_augmentation_wrapper(\n      image, bboxes, prob, rotate, func_changes_bbox, degrees, replace)\n\n\ndef shear_x_only_bboxes(image, bboxes, prob, level, replace):\n  \"\"\"Apply shear_x to each bbox in the image with probability prob.\"\"\"\n  func_changes_bbox = False\n  prob = _scale_bbox_only_op_probability(prob)\n  return _apply_multi_bbox_augmentation_wrapper(\n      image, bboxes, prob, shear_x, func_changes_bbox, level, replace)\n\n\ndef shear_y_only_bboxes(image, bboxes, prob, level, replace):\n  \"\"\"Apply shear_y to each bbox in the image with probability prob.\"\"\"\n  func_changes_bbox = False\n  prob = _scale_bbox_only_op_probability(prob)\n  return _apply_multi_bbox_augmentation_wrapper(\n      image, bboxes, prob, shear_y, func_changes_bbox, level, replace)\n\n\ndef translate_x_only_bboxes(image, bboxes, prob, pixels, replace):\n  \"\"\"Apply translate_x to each bbox in the image with probability prob.\"\"\"\n  func_changes_bbox = False\n  prob = _scale_bbox_only_op_probability(prob)\n  return _apply_multi_bbox_augmentation_wrapper(\n      image, bboxes, prob, translate_x, func_changes_bbox, pixels, replace)\n\n\ndef translate_y_only_bboxes(image, bboxes, prob, pixels, replace):\n  \"\"\"Apply translate_y to each bbox in the image with probability prob.\"\"\"\n  func_changes_bbox = False\n  prob = _scale_bbox_only_op_probability(prob)\n  return _apply_multi_bbox_augmentation_wrapper(\n      image, bboxes, prob, translate_y, func_changes_bbox, pixels, replace)\n\n\ndef flip_only_bboxes(image, bboxes, prob):\n  \"\"\"Apply flip_lr to each bbox in the image with probability prob.\"\"\"\n  func_changes_bbox = False\n  prob = _scale_bbox_only_op_probability(prob)\n  return _apply_multi_bbox_augmentation_wrapper(\n      image, bboxes, prob, tf.image.flip_left_right, func_changes_bbox)\n\n\ndef solarize_only_bboxes(image, bboxes, prob, threshold):\n  \"\"\"Apply solarize to each bbox in the image with probability prob.\"\"\"\n  func_changes_bbox = False\n  prob = _scale_bbox_only_op_probability(prob)\n  return _apply_multi_bbox_augmentation_wrapper(\n      image, bboxes, prob, solarize, func_changes_bbox, threshold)\n\n\ndef equalize_only_bboxes(image, bboxes, prob):\n  \"\"\"Apply equalize to each bbox in the image with probability prob.\"\"\"\n  func_changes_bbox = False\n  prob = _scale_bbox_only_op_probability(prob)\n  return _apply_multi_bbox_augmentation_wrapper(\n      image, bboxes, prob, equalize, func_changes_bbox)\n\n\ndef cutout_only_bboxes(image, bboxes, prob, pad_size, replace):\n  \"\"\"Apply cutout to each bbox in the image with probability prob.\"\"\"\n  func_changes_bbox = False\n  prob = _scale_bbox_only_op_probability(prob)\n  return _apply_multi_bbox_augmentation_wrapper(\n      image, bboxes, prob, cutout, func_changes_bbox, pad_size, replace)\n\n\ndef _rotate_bbox(bbox, image_height, image_width, degrees):\n  \"\"\"Rotates the bbox coordinated by degrees.\n\n  Args:\n    bbox: 1D Tensor that has 4 elements (min_y, min_x, max_y, max_x)\n      of type float that represents the normalized coordinates between 0 and 1.\n    image_height: Int, height of the image.\n    image_width: Int, height of the image.\n    degrees: Float, a scalar angle in degrees to rotate all images by. If\n      degrees is positive the image will be rotated clockwise otherwise it will\n      be rotated counterclockwise.\n\n  Returns:\n    A tensor of the same shape as bbox, but now with the rotated coordinates.\n  \"\"\"\n  image_height, image_width = (\n      tf.to_float(image_height), tf.to_float(image_width))\n\n  # Convert from degrees to radians.\n  degrees_to_radians = math.pi / 180.0\n  radians = degrees * degrees_to_radians\n\n  # Translate the bbox to the center of the image and turn the normalized 0-1\n  # coordinates to absolute pixel locations.\n  # Y coordinates are made negative as the y axis of images goes down with\n  # increasing pixel values, so we negate to make sure x axis and y axis points\n  # are in the traditionally positive direction.\n  min_y = -tf.to_int32(image_height * (bbox[0] - 0.5))\n  min_x = tf.to_int32(image_width * (bbox[1] - 0.5))\n  max_y = -tf.to_int32(image_height * (bbox[2] - 0.5))\n  max_x = tf.to_int32(image_width * (bbox[3] - 0.5))\n  coordinates = tf.stack(\n      [[min_y, min_x], [min_y, max_x], [max_y, min_x], [max_y, max_x]])\n  coordinates = tf.cast(coordinates, tf.float32)\n  # Rotate the coordinates according to the rotation matrix clockwise if\n  # radians is positive, else negative\n  rotation_matrix = tf.stack(\n      [[tf.cos(radians), tf.sin(radians)],\n       [-tf.sin(radians), tf.cos(radians)]])\n  new_coords = tf.cast(\n      tf.matmul(rotation_matrix, tf.transpose(coordinates)), tf.int32)\n  # Find min/max values and convert them back to normalized 0-1 floats.\n  min_y = -(tf.to_float(tf.reduce_max(new_coords[0, :])) / image_height - 0.5)\n  min_x = tf.to_float(tf.reduce_min(new_coords[1, :])) / image_width + 0.5\n  max_y = -(tf.to_float(tf.reduce_min(new_coords[0, :])) / image_height - 0.5)\n  max_x = tf.to_float(tf.reduce_max(new_coords[1, :])) / image_width + 0.5\n\n  # Clip the bboxes to be sure the fall between [0, 1].\n  min_y, min_x, max_y, max_x = _clip_bbox(min_y, min_x, max_y, max_x)\n  min_y, min_x, max_y, max_x = _check_bbox_area(min_y, min_x, max_y, max_x)\n  return tf.stack([min_y, min_x, max_y, max_x])\n\n\ndef rotate_with_bboxes(image, bboxes, degrees, replace):\n  \"\"\"Equivalent of PIL Rotate that rotates the image and bbox.\n\n  Args:\n    image: 3D uint8 Tensor.\n    bboxes: 2D Tensor that is a list of the bboxes in the image. Each bbox\n      has 4 elements (min_y, min_x, max_y, max_x) of type float.\n    degrees: Float, a scalar angle in degrees to rotate all images by. If\n      degrees is positive the image will be rotated clockwise otherwise it will\n      be rotated counterclockwise.\n    replace: A one or three value 1D tensor to fill empty pixels.\n\n  Returns:\n    A tuple containing a 3D uint8 Tensor that will be the result of rotating\n    image by degrees. The second element of the tuple is bboxes, where now\n    the coordinates will be shifted to reflect the rotated image.\n  \"\"\"\n  # Rotate the image.\n  image = rotate(image, degrees, replace)\n\n  # Convert bbox coordinates to pixel values.\n  image_height = tf.shape(image)[0]\n  image_width = tf.shape(image)[1]\n  # pylint:disable=g-long-lambda\n  wrapped_rotate_bbox = lambda bbox: _rotate_bbox(\n      bbox, image_height, image_width, degrees)\n  # pylint:enable=g-long-lambda\n  bboxes = tf.map_fn(wrapped_rotate_bbox, bboxes)\n  return image, bboxes\n\n\ndef translate_x(image, pixels, replace):\n  \"\"\"Equivalent of PIL Translate in X dimension.\"\"\"\n  image = image_ops.translate(wrap(image), [-pixels, 0])\n  return unwrap(image, replace)\n\n\ndef translate_y(image, pixels, replace):\n  \"\"\"Equivalent of PIL Translate in Y dimension.\"\"\"\n  image = image_ops.translate(wrap(image), [0, -pixels])\n  return unwrap(image, replace)\n\n\ndef _shift_bbox(bbox, image_height, image_width, pixels, shift_horizontal):\n  \"\"\"Shifts the bbox coordinates by pixels.\n\n  Args:\n    bbox: 1D Tensor that has 4 elements (min_y, min_x, max_y, max_x)\n      of type float that represents the normalized coordinates between 0 and 1.\n    image_height: Int, height of the image.\n    image_width: Int, width of the image.\n    pixels: An int. How many pixels to shift the bbox.\n    shift_horizontal: Boolean. If true then shift in X dimension else shift in\n      Y dimension.\n\n  Returns:\n    A tensor of the same shape as bbox, but now with the shifted coordinates.\n  \"\"\"\n  pixels = tf.to_int32(pixels)\n  # Convert bbox to integer pixel locations.\n  min_y = tf.to_int32(tf.to_float(image_height) * bbox[0])\n  min_x = tf.to_int32(tf.to_float(image_width) * bbox[1])\n  max_y = tf.to_int32(tf.to_float(image_height) * bbox[2])\n  max_x = tf.to_int32(tf.to_float(image_width) * bbox[3])\n\n  if shift_horizontal:\n    min_x = tf.maximum(0, min_x - pixels)\n    max_x = tf.minimum(image_width, max_x - pixels)\n  else:\n    min_y = tf.maximum(0, min_y - pixels)\n    max_y = tf.minimum(image_height, max_y - pixels)\n\n  # Convert bbox back to floats.\n  min_y = tf.to_float(min_y) / tf.to_float(image_height)\n  min_x = tf.to_float(min_x) / tf.to_float(image_width)\n  max_y = tf.to_float(max_y) / tf.to_float(image_height)\n  max_x = tf.to_float(max_x) / tf.to_float(image_width)\n\n  # Clip the bboxes to be sure the fall between [0, 1].\n  min_y, min_x, max_y, max_x = _clip_bbox(min_y, min_x, max_y, max_x)\n  min_y, min_x, max_y, max_x = _check_bbox_area(min_y, min_x, max_y, max_x)\n  return tf.stack([min_y, min_x, max_y, max_x])\n\n\ndef translate_bbox(image, bboxes, pixels, replace, shift_horizontal):\n  \"\"\"Equivalent of PIL Translate in X/Y dimension that shifts image and bbox.\n\n  Args:\n    image: 3D uint8 Tensor.\n    bboxes: 2D Tensor that is a list of the bboxes in the image. Each bbox\n      has 4 elements (min_y, min_x, max_y, max_x) of type float with values\n      between [0, 1].\n    pixels: An int. How many pixels to shift the image and bboxes\n    replace: A one or three value 1D tensor to fill empty pixels.\n    shift_horizontal: Boolean. If true then shift in X dimension else shift in\n      Y dimension.\n\n  Returns:\n    A tuple containing a 3D uint8 Tensor that will be the result of translating\n    image by pixels. The second element of the tuple is bboxes, where now\n    the coordinates will be shifted to reflect the shifted image.\n  \"\"\"\n  if shift_horizontal:\n    image = translate_x(image, pixels, replace)\n  else:\n    image = translate_y(image, pixels, replace)\n\n  # Convert bbox coordinates to pixel values.\n  image_height = tf.shape(image)[0]\n  image_width = tf.shape(image)[1]\n  # pylint:disable=g-long-lambda\n  wrapped_shift_bbox = lambda bbox: _shift_bbox(\n      bbox, image_height, image_width, pixels, shift_horizontal)\n  # pylint:enable=g-long-lambda\n  bboxes = tf.map_fn(wrapped_shift_bbox, bboxes)\n  return image, bboxes\n\n\ndef shear_x(image, level, replace):\n  \"\"\"Equivalent of PIL Shearing in X dimension.\"\"\"\n  # Shear parallel to x axis is a projective transform\n  # with a matrix form of:\n  # [1  level\n  #  0  1].\n  image = image_ops.transform(\n      wrap(image), [1., level, 0., 0., 1., 0., 0., 0.])\n  return unwrap(image, replace)\n\n\ndef shear_y(image, level, replace):\n  \"\"\"Equivalent of PIL Shearing in Y dimension.\"\"\"\n  # Shear parallel to y axis is a projective transform\n  # with a matrix form of:\n  # [1  0\n  #  level  1].\n  image = image_ops.transform(\n      wrap(image), [1., 0., 0., level, 1., 0., 0., 0.])\n  return unwrap(image, replace)\n\n\ndef _shear_bbox(bbox, image_height, image_width, level, shear_horizontal):\n  \"\"\"Shifts the bbox according to how the image was sheared.\n\n  Args:\n    bbox: 1D Tensor that has 4 elements (min_y, min_x, max_y, max_x)\n      of type float that represents the normalized coordinates between 0 and 1.\n    image_height: Int, height of the image.\n    image_width: Int, height of the image.\n    level: Float. How much to shear the image.\n    shear_horizontal: If true then shear in X dimension else shear in\n      the Y dimension.\n\n  Returns:\n    A tensor of the same shape as bbox, but now with the shifted coordinates.\n  \"\"\"\n  image_height, image_width = (\n      tf.to_float(image_height), tf.to_float(image_width))\n\n  # Change bbox coordinates to be pixels.\n  min_y = tf.to_int32(image_height * bbox[0])\n  min_x = tf.to_int32(image_width * bbox[1])\n  max_y = tf.to_int32(image_height * bbox[2])\n  max_x = tf.to_int32(image_width * bbox[3])\n  coordinates = tf.stack(\n      [[min_y, min_x], [min_y, max_x], [max_y, min_x], [max_y, max_x]])\n  coordinates = tf.cast(coordinates, tf.float32)\n\n  # Shear the coordinates according to the translation matrix.\n  if shear_horizontal:\n    translation_matrix = tf.stack(\n        [[1, 0], [-level, 1]])\n  else:\n    translation_matrix = tf.stack(\n        [[1, -level], [0, 1]])\n  translation_matrix = tf.cast(translation_matrix, tf.float32)\n  new_coords = tf.cast(\n      tf.matmul(translation_matrix, tf.transpose(coordinates)), tf.int32)\n\n  # Find min/max values and convert them back to floats.\n  min_y = tf.to_float(tf.reduce_min(new_coords[0, :])) / image_height\n  min_x = tf.to_float(tf.reduce_min(new_coords[1, :])) / image_width\n  max_y = tf.to_float(tf.reduce_max(new_coords[0, :])) / image_height\n  max_x = tf.to_float(tf.reduce_max(new_coords[1, :])) / image_width\n\n  # Clip the bboxes to be sure the fall between [0, 1].\n  min_y, min_x, max_y, max_x = _clip_bbox(min_y, min_x, max_y, max_x)\n  min_y, min_x, max_y, max_x = _check_bbox_area(min_y, min_x, max_y, max_x)\n  return tf.stack([min_y, min_x, max_y, max_x])\n\n\ndef shear_with_bboxes(image, bboxes, level, replace, shear_horizontal):\n  \"\"\"Applies Shear Transformation to the image and shifts the bboxes.\n\n  Args:\n    image: 3D uint8 Tensor.\n    bboxes: 2D Tensor that is a list of the bboxes in the image. Each bbox\n      has 4 elements (min_y, min_x, max_y, max_x) of type float with values\n      between [0, 1].\n    level: Float. How much to shear the image. This value will be between\n      -0.3 to 0.3.\n    replace: A one or three value 1D tensor to fill empty pixels.\n    shear_horizontal: Boolean. If true then shear in X dimension else shear in\n      the Y dimension.\n\n  Returns:\n    A tuple containing a 3D uint8 Tensor that will be the result of shearing\n    image by level. The second element of the tuple is bboxes, where now\n    the coordinates will be shifted to reflect the sheared image.\n  \"\"\"\n  if shear_horizontal:\n    image = shear_x(image, level, replace)\n  else:\n    image = shear_y(image, level, replace)\n\n  # Convert bbox coordinates to pixel values.\n  image_height = tf.shape(image)[0]\n  image_width = tf.shape(image)[1]\n  # pylint:disable=g-long-lambda\n  wrapped_shear_bbox = lambda bbox: _shear_bbox(\n      bbox, image_height, image_width, level, shear_horizontal)\n  # pylint:enable=g-long-lambda\n  bboxes = tf.map_fn(wrapped_shear_bbox, bboxes)\n  return image, bboxes\n\n\ndef autocontrast(image):\n  \"\"\"Implements Autocontrast function from PIL using TF ops.\n\n  Args:\n    image: A 3D uint8 tensor.\n\n  Returns:\n    The image after it has had autocontrast applied to it and will be of type\n    uint8.\n  \"\"\"\n\n  def scale_channel(image):\n    \"\"\"Scale the 2D image using the autocontrast rule.\"\"\"\n    # A possibly cheaper version can be done using cumsum/unique_with_counts\n    # over the histogram values, rather than iterating over the entire image.\n    # to compute mins and maxes.\n    lo = tf.to_float(tf.reduce_min(image))\n    hi = tf.to_float(tf.reduce_max(image))\n\n    # Scale the image, making the lowest value 0 and the highest value 255.\n    def scale_values(im):\n      scale = 255.0 / (hi - lo)\n      offset = -lo * scale\n      im = tf.to_float(im) * scale + offset\n      im = tf.clip_by_value(im, 0.0, 255.0)\n      return tf.cast(im, tf.uint8)\n\n    result = tf.cond(hi > lo, lambda: scale_values(image), lambda: image)\n    return result\n\n  # Assumes RGB for now.  Scales each channel independently\n  # and then stacks the result.\n  s1 = scale_channel(image[:, :, 0])\n  s2 = scale_channel(image[:, :, 1])\n  s3 = scale_channel(image[:, :, 2])\n  image = tf.stack([s1, s2, s3], 2)\n  return image\n\n\ndef sharpness(image, factor):\n  \"\"\"Implements Sharpness function from PIL using TF ops.\"\"\"\n  orig_image = image\n  image = tf.cast(image, tf.float32)\n  # Make image 4D for conv operation.\n  image = tf.expand_dims(image, 0)\n  # SMOOTH PIL Kernel.\n  kernel = tf.constant(\n      [[1, 1, 1], [1, 5, 1], [1, 1, 1]], dtype=tf.float32,\n      shape=[3, 3, 1, 1]) / 13.\n  # Tile across channel dimension.\n  kernel = tf.tile(kernel, [1, 1, 3, 1])\n  strides = [1, 1, 1, 1]\n  with tf.device('/cpu:0'):\n    degenerate = tf.nn.depthwise_conv2d(\n        image, kernel, strides, padding='VALID', rate=[1, 1])\n  degenerate = tf.clip_by_value(degenerate, 0.0, 255.0)\n  degenerate = tf.squeeze(tf.cast(degenerate, tf.uint8), [0])\n\n  # For the borders of the resulting image, fill in the values of the\n  # original image.\n  mask = tf.ones_like(degenerate)\n  padded_mask = tf.pad(mask, [[1, 1], [1, 1], [0, 0]])\n  padded_degenerate = tf.pad(degenerate, [[1, 1], [1, 1], [0, 0]])\n  result = tf.where(tf.equal(padded_mask, 1), padded_degenerate, orig_image)\n\n  # Blend the final result.\n  return blend(result, orig_image, factor)\n\n\ndef equalize(image):\n  \"\"\"Implements Equalize function from PIL using TF ops.\"\"\"\n  def scale_channel(im, c):\n    \"\"\"Scale the data in the channel to implement equalize.\"\"\"\n    im = tf.cast(im[:, :, c], tf.int32)\n    # Compute the histogram of the image channel.\n    histo = tf.histogram_fixed_width(im, [0, 255], nbins=256)\n\n    # For the purposes of computing the step, filter out the nonzeros.\n    nonzero = tf.where(tf.not_equal(histo, 0))\n    nonzero_histo = tf.reshape(tf.gather(histo, nonzero), [-1])\n    step = (tf.reduce_sum(nonzero_histo) - nonzero_histo[-1]) // 255\n\n    def build_lut(histo, step):\n      # Compute the cumulative sum, shifting by step // 2\n      # and then normalization by step.\n      lut = (tf.cumsum(histo) + (step // 2)) // step\n      # Shift lut, prepending with 0.\n      lut = tf.concat([[0], lut[:-1]], 0)\n      # Clip the counts to be in range.  This is done\n      # in the C code for image.point.\n      return tf.clip_by_value(lut, 0, 255)\n\n    # If step is zero, return the original image.  Otherwise, build\n    # lut from the full histogram and step and then index from it.\n    result = tf.cond(tf.equal(step, 0),\n                     lambda: im,\n                     lambda: tf.gather(build_lut(histo, step), im))\n\n    return tf.cast(result, tf.uint8)\n\n  # Assumes RGB for now.  Scales each channel independently\n  # and then stacks the result.\n  s1 = scale_channel(image, 0)\n  s2 = scale_channel(image, 1)\n  s3 = scale_channel(image, 2)\n  image = tf.stack([s1, s2, s3], 2)\n  return image\n\n\ndef wrap(image):\n  \"\"\"Returns 'image' with an extra channel set to all 1s.\"\"\"\n  shape = tf.shape(image)\n  extended_channel = tf.ones([shape[0], shape[1], 1], image.dtype)\n  extended = tf.concat([image, extended_channel], 2)\n  return extended\n\n\ndef unwrap(image, replace):\n  \"\"\"Unwraps an image produced by wrap.\n\n  Where there is a 0 in the last channel for every spatial position,\n  the rest of the three channels in that spatial dimension are grayed\n  (set to 128).  Operations like translate and shear on a wrapped\n  Tensor will leave 0s in empty locations.  Some transformations look\n  at the intensity of values to do preprocessing, and we want these\n  empty pixels to assume the 'average' value, rather than pure black.\n\n\n  Args:\n    image: A 3D Image Tensor with 4 channels.\n    replace: A one or three value 1D tensor to fill empty pixels.\n\n  Returns:\n    image: A 3D image Tensor with 3 channels.\n  \"\"\"\n  image_shape = tf.shape(image)\n  # Flatten the spatial dimensions.\n  flattened_image = tf.reshape(image, [-1, image_shape[2]])\n\n  # Find all pixels where the last channel is zero.\n  alpha_channel = flattened_image[:, 3]\n\n  replace = tf.concat([replace, tf.ones([1], image.dtype)], 0)\n\n  # Where they are zero, fill them in with 'replace'.\n  flattened_image = tf.where(\n      tf.equal(alpha_channel, 0),\n      tf.ones_like(flattened_image, dtype=image.dtype) * replace,\n      flattened_image)\n\n  image = tf.reshape(flattened_image, image_shape)\n  image = tf.slice(image, [0, 0, 0], [image_shape[0], image_shape[1], 3])\n  return image\n\n\ndef _cutout_inside_bbox(image, bbox, pad_fraction):\n  \"\"\"Generates cutout mask and the mean pixel value of the bbox.\n\n  First a location is randomly chosen within the image as the center where the\n  cutout mask will be applied. Note this can be towards the boundaries of the\n  image, so the full cutout mask may not be applied.\n\n  Args:\n    image: 3D uint8 Tensor.\n    bbox: 1D Tensor that has 4 elements (min_y, min_x, max_y, max_x)\n      of type float that represents the normalized coordinates between 0 and 1.\n    pad_fraction: Float that specifies how large the cutout mask should be in\n      in reference to the size of the original bbox. If pad_fraction is 0.25,\n      then the cutout mask will be of shape\n      (0.25 * bbox height, 0.25 * bbox width).\n\n  Returns:\n    A tuple. Fist element is a tensor of the same shape as image where each\n    element is either a 1 or 0 that is used to determine where the image\n    will have cutout applied. The second element is the mean of the pixels\n    in the image where the bbox is located.\n  \"\"\"\n  image_height = tf.shape(image)[0]\n  image_width = tf.shape(image)[1]\n  # Transform from shape [1, 4] to [4].\n  bbox = tf.squeeze(bbox)\n\n  min_y = tf.to_int32(tf.to_float(image_height) * bbox[0])\n  min_x = tf.to_int32(tf.to_float(image_width) * bbox[1])\n  max_y = tf.to_int32(tf.to_float(image_height) * bbox[2])\n  max_x = tf.to_int32(tf.to_float(image_width) * bbox[3])\n\n  # Calculate the mean pixel values in the bounding box, which will be used\n  # to fill the cutout region.\n  mean = tf.reduce_mean(image[min_y:max_y + 1, min_x:max_x + 1],\n                        reduction_indices=[0, 1])\n\n  # Cutout mask will be size pad_size_heigh * 2 by pad_size_width * 2 if the\n  # region lies entirely within the bbox.\n  box_height = max_y - min_y + 1\n  box_width = max_x - min_x + 1\n  pad_size_height = tf.to_int32(pad_fraction * (box_height / 2))\n  pad_size_width = tf.to_int32(pad_fraction * (box_width / 2))\n\n  # Sample the center location in the image where the zero mask will be applied.\n  cutout_center_height = tf.random_uniform(\n      shape=[], minval=min_y, maxval=max_y+1,\n      dtype=tf.int32)\n\n  cutout_center_width = tf.random_uniform(\n      shape=[], minval=min_x, maxval=max_x+1,\n      dtype=tf.int32)\n\n  lower_pad = tf.maximum(\n      0, cutout_center_height - pad_size_height)\n  upper_pad = tf.maximum(\n      0, image_height - cutout_center_height - pad_size_height)\n  left_pad = tf.maximum(\n      0, cutout_center_width - pad_size_width)\n  right_pad = tf.maximum(\n      0, image_width - cutout_center_width - pad_size_width)\n\n  cutout_shape = [image_height - (lower_pad + upper_pad),\n                  image_width - (left_pad + right_pad)]\n  padding_dims = [[lower_pad, upper_pad], [left_pad, right_pad]]\n\n  mask = tf.pad(\n      tf.zeros(cutout_shape, dtype=image.dtype),\n      padding_dims, constant_values=1)\n\n  mask = tf.expand_dims(mask, 2)\n  mask = tf.tile(mask, [1, 1, 3])\n\n  return mask, mean\n\n\ndef bbox_cutout(image, bboxes, pad_fraction, replace_with_mean):\n  \"\"\"Applies cutout to the image according to bbox information.\n\n  This is a cutout variant that using bbox information to make more informed\n  decisions on where to place the cutout mask.\n\n  Args:\n    image: 3D uint8 Tensor.\n    bboxes: 2D Tensor that is a list of the bboxes in the image. Each bbox\n      has 4 elements (min_y, min_x, max_y, max_x) of type float with values\n      between [0, 1].\n    pad_fraction: Float that specifies how large the cutout mask should be in\n      in reference to the size of the original bbox. If pad_fraction is 0.25,\n      then the cutout mask will be of shape\n      (0.25 * bbox height, 0.25 * bbox width).\n    replace_with_mean: Boolean that specified what value should be filled in\n      where the cutout mask is applied. Since the incoming image will be of\n      uint8 and will not have had any mean normalization applied, by default\n      we set the value to be 128. If replace_with_mean is True then we find\n      the mean pixel values across the channel dimension and use those to fill\n      in where the cutout mask is applied.\n\n  Returns:\n    A tuple. First element is a tensor of the same shape as image that has\n    cutout applied to it. Second element is the bboxes that were passed in\n    that will be unchanged.\n  \"\"\"\n  def apply_bbox_cutout(image, bboxes, pad_fraction):\n    \"\"\"Applies cutout to a single bounding box within image.\"\"\"\n    # Choose a single bounding box to apply cutout to.\n    random_index = tf.random_uniform(\n        shape=[], maxval=tf.shape(bboxes)[0], dtype=tf.int32)\n    # Select the corresponding bbox and apply cutout.\n    chosen_bbox = tf.gather(bboxes, random_index)\n    mask, mean = _cutout_inside_bbox(image, chosen_bbox, pad_fraction)\n\n    # When applying cutout we either set the pixel value to 128 or to the mean\n    # value inside the bbox.\n    replace = mean if replace_with_mean else 128\n\n    # Apply the cutout mask to the image. Where the mask is 0 we fill it with\n    # `replace`.\n    image = tf.where(\n        tf.equal(mask, 0),\n        tf.cast(tf.ones_like(image, dtype=image.dtype) * replace,\n                dtype=image.dtype),\n        image)\n    return image\n\n  # Check to see if there are boxes, if so then apply boxcutout.\n  image = tf.cond(tf.equal(tf.shape(bboxes)[0], 0), lambda: image,\n                  lambda: apply_bbox_cutout(image, bboxes, pad_fraction))\n\n  return image, bboxes\n\n\nNAME_TO_FUNC = {\n    'AutoContrast': autocontrast,\n    'Equalize': equalize,\n    'Posterize': posterize,\n    'Solarize': solarize,\n    'SolarizeAdd': solarize_add,\n    'Color': color,\n    'Contrast': contrast,\n    'Brightness': brightness,\n    'Sharpness': sharpness,\n    'Cutout': cutout,\n    'BBox_Cutout': bbox_cutout,\n    'Rotate_BBox': rotate_with_bboxes,\n    # pylint:disable=g-long-lambda\n    'TranslateX_BBox': lambda image, bboxes, pixels, replace: translate_bbox(\n        image, bboxes, pixels, replace, shift_horizontal=True),\n    'TranslateY_BBox': lambda image, bboxes, pixels, replace: translate_bbox(\n        image, bboxes, pixels, replace, shift_horizontal=False),\n    'ShearX_BBox': lambda image, bboxes, level, replace: shear_with_bboxes(\n        image, bboxes, level, replace, shear_horizontal=True),\n    'ShearY_BBox': lambda image, bboxes, level, replace: shear_with_bboxes(\n        image, bboxes, level, replace, shear_horizontal=False),\n    # pylint:enable=g-long-lambda\n    'Rotate_Only_BBoxes': rotate_only_bboxes,\n    'ShearX_Only_BBoxes': shear_x_only_bboxes,\n    'ShearY_Only_BBoxes': shear_y_only_bboxes,\n    'TranslateX_Only_BBoxes': translate_x_only_bboxes,\n    'TranslateY_Only_BBoxes': translate_y_only_bboxes,\n    'Flip_Only_BBoxes': flip_only_bboxes,\n    'Solarize_Only_BBoxes': solarize_only_bboxes,\n    'Equalize_Only_BBoxes': equalize_only_bboxes,\n    'Cutout_Only_BBoxes': cutout_only_bboxes,\n}\n\n\ndef _randomly_negate_tensor(tensor):\n  \"\"\"With 50% prob turn the tensor negative.\"\"\"\n  should_flip = tf.cast(tf.floor(tf.random_uniform([]) + 0.5), tf.bool)\n  final_tensor = tf.cond(should_flip, lambda: tensor, lambda: -tensor)\n  return final_tensor\n\n\ndef _rotate_level_to_arg(level):\n  level = (level/_MAX_LEVEL) * 30.\n  level = _randomly_negate_tensor(level)\n  return (level,)\n\n\ndef _shrink_level_to_arg(level):\n  \"\"\"Converts level to ratio by which we shrink the image content.\"\"\"\n  if level == 0:\n    return (1.0,)  # if level is zero, do not shrink the image\n  # Maximum shrinking ratio is 2.9.\n  level = 2. / (_MAX_LEVEL / level) + 0.9\n  return (level,)\n\n\ndef _enhance_level_to_arg(level):\n  return ((level/_MAX_LEVEL) * 1.8 + 0.1,)\n\n\ndef _shear_level_to_arg(level):\n  level = (level/_MAX_LEVEL) * 0.3\n  # Flip level to negative with 50% chance.\n  level = _randomly_negate_tensor(level)\n  return (level,)\n\n\ndef _translate_level_to_arg(level, translate_const):\n  level = (level/_MAX_LEVEL) * float(translate_const)\n  # Flip level to negative with 50% chance.\n  level = _randomly_negate_tensor(level)\n  return (level,)\n\n\ndef _bbox_cutout_level_to_arg(level, hparams):\n  cutout_pad_fraction = (level/_MAX_LEVEL) * hparams.cutout_max_pad_fraction\n  return (cutout_pad_fraction,\n          hparams.cutout_bbox_replace_with_mean)\n\n\ndef level_to_arg(hparams):\n  return {\n      'AutoContrast': lambda level: (),\n      'Equalize': lambda level: (),\n      'Posterize': lambda level: (int((level/_MAX_LEVEL) * 4),),\n      'Solarize': lambda level: (int((level/_MAX_LEVEL) * 256),),\n      'SolarizeAdd': lambda level: (int((level/_MAX_LEVEL) * 110),),\n      'Color': _enhance_level_to_arg,\n      'Contrast': _enhance_level_to_arg,\n      'Brightness': _enhance_level_to_arg,\n      'Sharpness': _enhance_level_to_arg,\n      'Cutout': lambda level: (int((level/_MAX_LEVEL) * hparams.cutout_const),),\n      # pylint:disable=g-long-lambda\n      'BBox_Cutout': lambda level: _bbox_cutout_level_to_arg(\n          level, hparams),\n      'TranslateX_BBox': lambda level: _translate_level_to_arg(\n          level, hparams.translate_const),\n      'TranslateY_BBox': lambda level: _translate_level_to_arg(\n          level, hparams.translate_const),\n      # pylint:enable=g-long-lambda\n      'ShearX_BBox': _shear_level_to_arg,\n      'ShearY_BBox': _shear_level_to_arg,\n      'Rotate_BBox': _rotate_level_to_arg,\n      'Rotate_Only_BBoxes': _rotate_level_to_arg,\n      'ShearX_Only_BBoxes': _shear_level_to_arg,\n      'ShearY_Only_BBoxes': _shear_level_to_arg,\n      # pylint:disable=g-long-lambda\n      'TranslateX_Only_BBoxes': lambda level: _translate_level_to_arg(\n          level, hparams.translate_bbox_const),\n      'TranslateY_Only_BBoxes': lambda level: _translate_level_to_arg(\n          level, hparams.translate_bbox_const),\n      # pylint:enable=g-long-lambda\n      'Flip_Only_BBoxes': lambda level: (),\n      'Solarize_Only_BBoxes': lambda level: (int((level/_MAX_LEVEL) * 256),),\n      'Equalize_Only_BBoxes': lambda level: (),\n      # pylint:disable=g-long-lambda\n      'Cutout_Only_BBoxes': lambda level: (\n          int((level/_MAX_LEVEL) * hparams.cutout_bbox_const),),\n      # pylint:enable=g-long-lambda\n  }\n\n\ndef bbox_wrapper(func):\n  \"\"\"Adds a bboxes function argument to func and returns unchanged bboxes.\"\"\"\n  def wrapper(images, bboxes, *args, **kwargs):\n    return (func(images, *args, **kwargs), bboxes)\n  return wrapper\n\n\ndef _parse_policy_info(name, prob, level, replace_value, augmentation_hparams):\n  \"\"\"Return the function that corresponds to `name` and update `level` param.\"\"\"\n  func = NAME_TO_FUNC[name]\n  args = level_to_arg(augmentation_hparams)[name](level)\n\n  # Check to see if prob is passed into function. This is used for operations\n  # where we alter bboxes independently.\n  # pytype:disable=wrong-arg-types\n  if 'prob' in inspect.getfullargspec(func)[0]:\n    args = tuple([prob] + list(args))\n  # pytype:enable=wrong-arg-types\n\n  # Add in replace arg if it is required for the function that is being called.\n  if 'replace' in inspect.getfullargspec(func)[0]:\n    # Make sure replace is the final argument\n    assert 'replace' == inspect.getfullargspec(func)[0][-1]\n    args = tuple(list(args) + [replace_value])\n\n  # Add bboxes as the second positional argument for the function if it does\n  # not already exist.\n  if 'bboxes' not in inspect.getfullargspec(func)[0]:\n    func = bbox_wrapper(func)\n  return (func, prob, args)\n\n\ndef _apply_func_with_prob(func, image, args, prob, bboxes):\n  \"\"\"Apply `func` to image w/ `args` as input with probability `prob`.\"\"\"\n  assert isinstance(args, tuple)\n  assert 'bboxes' == inspect.getfullargspec(func)[0][1]\n\n  # If prob is a function argument, then this randomness is being handled\n  # inside the function, so make sure it is always called.\n  if 'prob' in inspect.getfullargspec(func)[0]:\n    prob = 1.0\n\n  # Apply the function with probability `prob`.\n  should_apply_op = tf.cast(\n      tf.floor(tf.random_uniform([], dtype=tf.float32) + prob), tf.bool)\n  augmented_image, augmented_bboxes = tf.cond(\n      should_apply_op,\n      lambda: func(image, bboxes, *args),\n      lambda: (image, bboxes))\n  return augmented_image, augmented_bboxes\n\n\ndef select_and_apply_random_policy(policies, image, bboxes):\n  \"\"\"Select a random policy from `policies` and apply it to `image`.\"\"\"\n  policy_to_select = tf.random_uniform([], maxval=len(policies), dtype=tf.int32)\n  # Note that using tf.case instead of tf.conds would result in significantly\n  # larger graphs and would even break export for some larger policies.\n  for (i, policy) in enumerate(policies):\n    image, bboxes = tf.cond(\n        tf.equal(i, policy_to_select),\n        lambda selected_policy=policy: selected_policy(image, bboxes),\n        lambda: (image, bboxes))\n  return (image, bboxes)\n\n\ndef build_and_apply_nas_policy(policies, image, bboxes,\n                               augmentation_hparams):\n  \"\"\"Build a policy from the given policies passed in and apply to image.\n\n  Args:\n    policies: list of lists of tuples in the form `(func, prob, level)`, `func`\n      is a string name of the augmentation function, `prob` is the probability\n      of applying the `func` operation, `level` is the input argument for\n      `func`.\n    image: tf.Tensor that the resulting policy will be applied to.\n    bboxes: tf.Tensor of shape [N, 4] representing ground truth boxes that are\n      normalized between [0, 1].\n    augmentation_hparams: Hparams associated with the NAS learned policy.\n\n  Returns:\n    A version of image that now has data augmentation applied to it based on\n    the `policies` pass into the function. Additionally, returns bboxes if\n    a value for them is passed in that is not None\n  \"\"\"\n  replace_value = [128, 128, 128]\n\n  # func is the string name of the augmentation function, prob is the\n  # probability of applying the operation and level is the parameter associated\n  # with the tf op.\n\n  # tf_policies are functions that take in an image and return an augmented\n  # image.\n  tf_policies = []\n  for policy in policies:\n    tf_policy = []\n    # Link string name to the correct python function and make sure the correct\n    # argument is passed into that function.\n    for policy_info in policy:\n      policy_info = list(policy_info) + [replace_value, augmentation_hparams]\n\n      tf_policy.append(_parse_policy_info(*policy_info))\n    # Now build the tf policy that will apply the augmentation procedue\n    # on image.\n    def make_final_policy(tf_policy_):\n      def final_policy(image_, bboxes_):\n        for func, prob, args in tf_policy_:\n          image_, bboxes_ = _apply_func_with_prob(\n              func, image_, args, prob, bboxes_)\n        return image_, bboxes_\n      return final_policy\n    tf_policies.append(make_final_policy(tf_policy))\n  augmented_images, augmented_bboxes = select_and_apply_random_policy(\n      tf_policies, image, bboxes)\n\n  # If no bounding boxes were specified, then just return the images.\n  return (augmented_images, augmented_bboxes)\n\n\n@tf.autograph.experimental.do_not_convert\ndef distort_image_with_autoaugment(image,\n                                   bboxes,\n                                   augmentation_name):\n  \"\"\"Applies the AutoAugment policy to `image` and `bboxes`.\n\n  Args:\n    image: `Tensor` of shape [height, width, 3] representing an image.\n    bboxes: `Tensor` of shape [N, 4] representing ground truth boxes that are\n      normalized between [0, 1].\n    augmentation_name: The name of the AutoAugment policy to use. The available\n      options are `v0`, `v1`, `v2`, `v3` and `test`. `v0` is the policy used for\n      all of the results in the paper and was found to achieve the best results\n      on the COCO dataset. `v1`, `v2` and `v3` are additional good policies\n      found on the COCO dataset that have slight variation in what operations\n      were used during the search procedure along with how many operations are\n      applied in parallel to a single image (2 vs 3).\n\n  Returns:\n    A tuple containing the augmented versions of `image` and `bboxes`.\n  \"\"\"\n  logging.info('Using autoaugmention policy: %s', augmentation_name)\n  available_policies = {'v0': policy_v0, 'v1': policy_v1, 'v2': policy_v2,\n                        'v3': policy_v3, 'test': policy_vtest}\n  if augmentation_name not in available_policies:\n    raise ValueError('Invalid augmentation_name: {}'.format(augmentation_name))\n\n  policy = available_policies[augmentation_name]()\n  # Hparams that will be used for AutoAugment.\n  augmentation_hparams = hparams_config.Config(dict(\n      cutout_max_pad_fraction=0.75,\n      cutout_bbox_replace_with_mean=False,\n      cutout_const=100,\n      translate_const=250,\n      cutout_bbox_const=50,\n      translate_bbox_const=120))\n\n  return build_and_apply_nas_policy(policy, image, bboxes,\n                                    augmentation_hparams)\n\n\ndef distort_image_with_randaugment(image, bboxes, num_layers, magnitude):\n  \"\"\"Applies the RandAugment to `image` and `bboxes`.\"\"\"\n  replace_value = [128, 128, 128]\n  tf.logging.info('Using RandAugment.')\n\n  augmentation_hparams = hparams_config.Config(\n      dict(\n          cutout_max_pad_fraction=0.75,\n          cutout_bbox_replace_with_mean=False,\n          cutout_const=100,\n          translate_const=250,\n          cutout_bbox_const=50,\n          translate_bbox_const=120))\n\n  available_ops = [\n      'Equalize', 'Solarize', 'Color', 'Cutout', 'SolarizeAdd',\n      'TranslateX_BBox', 'TranslateY_BBox', 'ShearX_BBox', 'ShearY_BBox',\n      'Rotate_BBox']\n\n  if bboxes is None:\n    bboxes = tf.constant(0.0)\n\n  for layer_num in range(num_layers):\n    op_to_select = tf.random_uniform(\n        [], maxval=len(available_ops), dtype=tf.int32)\n    random_magnitude = float(magnitude)\n    with tf.name_scope('randaug_layer_{}'.format(layer_num)):\n      for (i, op_name) in enumerate(available_ops):\n        prob = tf.random_uniform([], minval=0.2, maxval=0.8, dtype=tf.float32)\n        func, _, args = _parse_policy_info(op_name, prob, random_magnitude,\n                                           replace_value, augmentation_hparams)\n        image, bboxes = tf.cond(\n            tf.equal(i, op_to_select),\n            lambda fn=func, fn_args=args: fn(image, bboxes, *fn_args),\n            lambda: (image, bboxes))\n  return (image, bboxes)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/aug/gridmask.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Grid Masking Augmentation Reference: https://arxiv.org/abs/2001.04086.\"\"\"\nimport math\n\nimport tensorflow as tf\nfrom tensorflow_addons import image as image_ops\n\n\nclass GridMask(object):\n  \"\"\"GridMask class for grid masking augmentation.\"\"\"\n\n  def __init__(self,\n               prob=0.6,\n               ratio=0.6,\n               rotate=10,\n               gridmask_size_ratio=0.5,\n               fill=1,\n               interpolation=\"BILINEAR\"):\n    \"\"\"initialization.\n\n    Args:\n      prob: probablity of occurance.\n      ratio: grid mask ratio i.e if 0.5 grid and spacing will be equal.\n      rotate: Rotation of grid mesh.\n      gridmask_size_ratio: Grid mask size, grid to image size ratio.\n      fill: Fill value for grids.\n      interpolation: Interpolation method for rotation.\n    \"\"\"\n    self.prob = prob\n    self.ratio = ratio\n    self.rotate = rotate\n    self.gridmask_size_ratio = gridmask_size_ratio\n    self.fill = fill\n    self.interpolation = interpolation\n\n  @tf.function\n  def random_rotate(self, mask):\n    \"\"\"Randomly rotates mask on given range.\"\"\"\n\n    angle = self.rotate * tf.random.normal([], -1, 1)\n    angle = math.pi * angle / 180\n    return image_ops.rotate(mask, angle, interpolation=self.interpolation)\n\n  @staticmethod\n  def crop(mask, h, w):\n    \"\"\"crops in middle of mask and image corners.\"\"\"\n    ww = hh = tf.shape(mask)[0]\n    mask = mask[(hh - h) // 2:(hh - h) // 2 + h,\n                (ww - w) // 2:(ww - w) // 2 + w,]\n    return mask\n\n  @tf.function\n  def mask(self, h, w):\n    \"\"\"mask helper function for initializing grid mask of required size.\"\"\"\n    h = tf.cast(h, tf.float32)\n    w = tf.cast(w, tf.float32)\n    mask_w = mask_h = tf.cast(\n        tf.cast(\n            (self.gridmask_size_ratio + 1), tf.float32) * tf.math.maximum(h, w),\n        tf.int32)\n    self.mask_w = mask_w\n    mask = tf.zeros(shape=[mask_h, mask_w], dtype=tf.int32)\n    gridblock = tf.random.uniform(\n        shape=[],\n        minval=int(tf.math.minimum(h * 0.5, w * 0.3)),\n        maxval=int(tf.math.maximum(h * 0.5, w * 0.3)) + 1,\n        dtype=tf.int32)\n\n    if self.ratio == 1:\n      length = tf.random.uniform(\n          shape=[], minval=1, maxval=gridblock + 1, dtype=tf.int32)\n    else:\n      length = tf.cast(\n          tf.math.minimum(\n              tf.math.maximum(\n                  int(tf.cast(gridblock, tf.float32) * self.ratio + 0.5), 1),\n              gridblock - 1), tf.int32)\n\n    for _ in range(2):\n      start_w = tf.random.uniform(\n          shape=[], minval=0, maxval=gridblock + 1, dtype=tf.int32)\n      for i in range(mask_w // gridblock):\n        start = gridblock * i + start_w\n        end = tf.math.minimum(start + length, mask_w)\n        indices = tf.reshape(tf.range(start, end), [end - start, 1])\n        updates = (\n            tf.ones(shape=[end - start, mask_w], dtype=tf.int32) * self.fill)\n        mask = tf.tensor_scatter_nd_update(mask, indices, updates)\n      mask = tf.transpose(mask)\n\n    return mask\n\n  def __call__(self, image, label):\n    \"\"\"Masks input image tensor with random grid mask.\"\"\"\n    h = tf.shape(image)[0]\n    w = tf.shape(image)[1]\n    grid = self.mask(h, w)\n    grid = self.random_rotate(grid)\n    mask = self.crop(grid, h, w)\n    mask = tf.cast(mask, image.dtype)\n    mask = tf.reshape(mask, (h, w))\n    mask = (tf.expand_dims(mask, -1) if image._rank() != mask._rank() else mask)\n    occur = tf.random.normal([], 0, 1) < self.prob\n    image = tf.cond(occur, lambda: image * mask, lambda: image)\n    return image, label\n\n\ndef gridmask(image,\n             boxes,\n             prob=0.5,\n             ratio=0.6,\n             rotate=10,\n             gridmask_size_ratio=0.5,\n             fill=1):\n  \"\"\"Callable instance of GridMask and transforms input image.\"\"\"\n  gridmask_obj = GridMask(\n      prob=prob,\n      ratio=ratio,\n      rotate=rotate,\n      gridmask_size_ratio=gridmask_size_ratio,\n      fill=fill)\n  image, boxes = gridmask_obj(image, boxes)\n  return image, boxes\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/aug/mosaic.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Mosaic augmentation.\"\"\"\nimport functools\nfrom typing import Tuple\nfrom absl import logging\n\nimport tensorflow as tf\n\n\nclass Mosaic:\n  \"\"\"Mosaic Augmentation class.\n\n  1. Mosaic sub images will not be preserving aspect ratio of original images.\n  2. Tested on static graphs and eager  execution.\n  3. This Implementation of mosaic augmentation is tested in tf2.x.\n  \"\"\"\n\n  def __init__(self,\n               out_size: Tuple(int, int)=(680, 680),\n               n_images: int = 4,\n               _minimum_mosaic_image_dim: int = 25):\n    \"\"\"__init__.\n\n    Args:\n      out_size: output mosaic image size.\n      n_images: number images to make mosaic\n      _minimum_mosaic_image_dim: minimum percentage of out_size dimension\n        should the mosaic be. i.e if out_size is (680,680) and\n        _minimum_mosaic_image_dim is 25 , minimum mosaic sub images\n        dimension will be 25 % of 680.\n    \"\"\"\n    # TODO(someone) #MED #use n_images to build mosaic.\n    self._n_images = n_images\n    self._out_size = out_size\n    self._minimum_mosaic_image_dim = _minimum_mosaic_image_dim\n    assert (_minimum_mosaic_image_dim >\n            0), \"Minimum Mosaic image dimension should be above 0\"\n\n  @property\n  def n_images(self) -> int:\n    return self._n_images\n\n  @property\n  def out_size(self) -> int:\n    return self._out_size\n\n  def _mosaic_divide_points(self) -> Tuple(int, int):\n    \"\"\"Returns a  tuple of x and y which corresponds to mosaic divide points.\"\"\"\n    x_point = tf.random.uniform(\n        shape=[1],\n        minval=tf.cast(\n            self.out_size[0] * (self._minimum_mosaic_image_dim / 100),\n            tf.int32),\n        maxval=tf.cast(\n            self.out_size[0] * ((100 - self._minimum_mosaic_image_dim) / 100),\n            tf.int32,\n        ),\n        dtype=tf.int32,\n    )\n    y_point = tf.random.uniform(\n        shape=[1],\n        minval=tf.cast(\n            self.out_size[1] * (self._minimum_mosaic_image_dim / 100),\n            tf.int32),\n        maxval=tf.cast(\n            self.out_size[1] * ((100 - self._minimum_mosaic_image_dim) / 100),\n            tf.int32,\n        ),\n        dtype=tf.int32,\n    )\n    return x_point, y_point\n\n  @staticmethod\n  def _scale_box(box, image, mosaic_image):\n    \"\"\"scale boxes with mosaic sub image.\n\n    Args:\n      box: mosaic image box.\n      image: original image.\n      mosaic_image: mosaic sub image.\n\n    Returns:\n      Scaled bounding boxes.\n    \"\"\"\n    return [\n        box[0] * tf.shape(mosaic_image)[1] / tf.shape(image)[1],\n        box[1] * tf.shape(mosaic_image)[0] / tf.shape(image)[0],\n        box[2] * tf.shape(mosaic_image)[1] / tf.shape(image)[1],\n        box[-1] * tf.shape(mosaic_image)[0] / tf.shape(image)[0],\n    ]\n\n  def _scale_images(self, images, mosaic_divide_points):\n    \"\"\"Scale Sub Images.\n\n    Args:\n      images: original single images to make mosaic.\n      mosaic_divide_points: Points to build mosaic around on given output.\n\n    Returns:\n      A tuple of scaled Mosaic sub images.\n    \"\"\"\n    x, y = mosaic_divide_points[0][0], mosaic_divide_points[1][0]\n    mosaic_image_topleft = tf.image.resize(images[0], (x, y))\n    mosaic_image_topright = tf.image.resize(images[1],\n                                            (self.out_size[0] - x, y))\n    mosaic_image_bottomleft = tf.image.resize(images[2],\n                                              (x, self.out_size[1] - y))\n    mosaic_image_bottomright = tf.image.resize(\n        images[3], (self.out_size[0] - x, self.out_size[1] - y))\n    return (\n        mosaic_image_topleft,\n        mosaic_image_topright,\n        mosaic_image_bottomleft,\n        mosaic_image_bottomright,\n    )\n\n  @tf.function\n  def _mosaic(self, images, boxes, mosaic_divide_points):\n    \"\"\"Builds mosaic of provided images.\n\n    Args:\n      images: original single images to make mosaic.\n      boxes: corresponding bounding boxes to images.\n      mosaic_divide_points: Points to build mosaic around on given output size.\n\n    Returns:\n      A tuple of mosaic Image, Mosaic Boxes merged.\n    \"\"\"\n    (\n        mosaic_image_topleft,\n        mosaic_image_topright,\n        mosaic_image_bottomleft,\n        mosaic_image_bottomright,\n    ) = self._scale_images(images, mosaic_divide_points)\n\n    #####################################################\n    # Scale Boxes for TOP LEFT image.\n    # Note: Below function is complex because of TF item assignment restriction.\n    # Map_fn is replace with vectorized_map below for optimization purpose.\n    mosaic_box_topleft = tf.transpose(\n        tf.vectorized_map(\n            functools.partial(\n                self._scale_box,\n                image=images[0],\n                mosaic_image=mosaic_image_topleft),\n            boxes[0],\n        ))\n\n    # Scale and Pad Boxes for TOP RIGHT image.\n\n    mosaic_box_topright = tf.vectorized_map(\n        functools.partial(\n            self._scale_box,\n            image=images[1],\n            mosaic_image=mosaic_image_topright),\n        boxes[1],\n    )\n    num_boxes = boxes[1].shape[0]\n    idx_tp = tf.constant([[1], [3]])\n    update_tp = [\n        [tf.shape(mosaic_image_topleft)[0]] * num_boxes,\n        [tf.shape(mosaic_image_topleft)[0]] * num_boxes,\n    ]\n    mosaic_box_topright = tf.transpose(\n        tf.tensor_scatter_nd_add(mosaic_box_topright, idx_tp, update_tp))\n\n    # Scale and Pad Boxes for BOTTOM LEFT image.\n\n    mosaic_box_bottomleft = tf.vectorized_map(\n        functools.partial(\n            self._scale_box,\n            image=images[2],\n            mosaic_image=mosaic_image_bottomleft),\n        boxes[2],\n    )\n\n    num_boxes = boxes[2].shape[0]\n    idx_bl = tf.constant([[0], [2]])\n    update_bl = [\n        [tf.shape(mosaic_image_topleft)[1]] * num_boxes,\n        [tf.shape(mosaic_image_topleft)[1]] * num_boxes,\n    ]\n    mosaic_box_bottomleft = tf.transpose(\n        tf.tensor_scatter_nd_add(mosaic_box_bottomleft, idx_bl, update_bl))\n\n    # Scale and Pad Boxes for BOTTOM RIGHT image.\n    mosaic_box_bottomright = tf.vectorized_map(\n        functools.partial(\n            self._scale_box,\n            image=images[3],\n            mosaic_image=mosaic_image_bottomright),\n        boxes[3],\n    )\n\n    num_boxes = boxes[3].shape[0]\n    idx_br = tf.constant([[0], [2], [1], [3]])\n    update_br = [\n        [tf.shape(mosaic_image_topright)[1]] * num_boxes,\n        [tf.shape(mosaic_image_topright)[1]] * num_boxes,\n        [tf.shape(mosaic_image_bottomleft)[0]] * num_boxes,\n        [tf.shape(mosaic_image_bottomleft)[0]] * num_boxes,\n    ]\n    mosaic_box_bottomright = tf.transpose(\n        tf.tensor_scatter_nd_add(mosaic_box_bottomright, idx_br, update_br))\n\n    # Gather mosaic_sub_images and boxes.\n    mosaic_images = [\n        mosaic_image_topleft,\n        mosaic_image_topright,\n        mosaic_image_bottomleft,\n        mosaic_image_bottomright,\n    ]\n    mosaic_boxes = [\n        mosaic_box_topleft,\n        mosaic_box_topright,\n        mosaic_box_bottomleft,\n        mosaic_box_bottomright,\n    ]\n\n    return mosaic_images, mosaic_boxes\n\n  def __call__(self, images, boxes):\n    \"\"\"Builds mosaic with given images, boxes.\"\"\"\n    if images.shape[0] != 4:\n      err_msg = \"Currently Exact 4 Images are supported by Mosaic Aug.\"\n      logging.error(err_msg)\n      raise Exception(err_msg)\n\n    x, y = self._mosaic_divide_points()\n    mosaic_sub_images, mosaic_boxes = self._mosaic(\n        images, boxes, mosaic_divide_points=(x, y))\n\n    upper_stack = tf.concat([mosaic_sub_images[0], mosaic_sub_images[1]],\n                            axis=0)\n    lower_stack = tf.concat([mosaic_sub_images[2], mosaic_sub_images[3]],\n                            axis=0)\n    mosaic_image = tf.concat([upper_stack, lower_stack], axis=1)\n    return mosaic_image, mosaic_boxes\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/backbone/__init__.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/backbone/autoaugment.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"AutoAugment and RandAugment policies for enhanced image preprocessing.\n\nAutoAugment Reference: https://arxiv.org/abs/1805.09501\nRandAugment Reference: https://arxiv.org/abs/1909.13719\n\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport inspect\nimport math\nimport tensorflow.compat.v1 as tf\nfrom tensorflow.contrib import image as contrib_image\nfrom tensorflow.contrib import training as contrib_training\n\n\n# This signifies the max integer that the controller RNN could predict for the\n# augmentation scheme.\n_MAX_LEVEL = 10.\n\n\ndef policy_v0():\n  \"\"\"Autoaugment policy that was used in AutoAugment Paper.\"\"\"\n  # Each tuple is an augmentation operation of the form\n  # (operation, probability, magnitude). Each element in policy is a\n  # sub-policy that will be applied sequentially on the image.\n  policy = [\n      [('Equalize', 0.8, 1), ('ShearY', 0.8, 4)],\n      [('Color', 0.4, 9), ('Equalize', 0.6, 3)],\n      [('Color', 0.4, 1), ('Rotate', 0.6, 8)],\n      [('Solarize', 0.8, 3), ('Equalize', 0.4, 7)],\n      [('Solarize', 0.4, 2), ('Solarize', 0.6, 2)],\n      [('Color', 0.2, 0), ('Equalize', 0.8, 8)],\n      [('Equalize', 0.4, 8), ('SolarizeAdd', 0.8, 3)],\n      [('ShearX', 0.2, 9), ('Rotate', 0.6, 8)],\n      [('Color', 0.6, 1), ('Equalize', 1.0, 2)],\n      [('Invert', 0.4, 9), ('Rotate', 0.6, 0)],\n      [('Equalize', 1.0, 9), ('ShearY', 0.6, 3)],\n      [('Color', 0.4, 7), ('Equalize', 0.6, 0)],\n      [('Posterize', 0.4, 6), ('AutoContrast', 0.4, 7)],\n      [('Solarize', 0.6, 8), ('Color', 0.6, 9)],\n      [('Solarize', 0.2, 4), ('Rotate', 0.8, 9)],\n      [('Rotate', 1.0, 7), ('TranslateY', 0.8, 9)],\n      [('ShearX', 0.0, 0), ('Solarize', 0.8, 4)],\n      [('ShearY', 0.8, 0), ('Color', 0.6, 4)],\n      [('Color', 1.0, 0), ('Rotate', 0.6, 2)],\n      [('Equalize', 0.8, 4), ('Equalize', 0.0, 8)],\n      [('Equalize', 1.0, 4), ('AutoContrast', 0.6, 2)],\n      [('ShearY', 0.4, 7), ('SolarizeAdd', 0.6, 7)],\n      [('Posterize', 0.8, 2), ('Solarize', 0.6, 10)],\n      [('Solarize', 0.6, 8), ('Equalize', 0.6, 1)],\n      [('Color', 0.8, 6), ('Rotate', 0.4, 5)],\n  ]\n  return policy\n\n\ndef policy_vtest():\n  \"\"\"Autoaugment test policy for debugging.\"\"\"\n  # Each tuple is an augmentation operation of the form\n  # (operation, probability, magnitude). Each element in policy is a\n  # sub-policy that will be applied sequentially on the image.\n  policy = [\n      [('TranslateX', 1.0, 4), ('Equalize', 1.0, 10)],\n  ]\n  return policy\n\n\ndef blend(image1, image2, factor):\n  \"\"\"Blend image1 and image2 using 'factor'.\n\n  Factor can be above 0.0.  A value of 0.0 means only image1 is used.\n  A value of 1.0 means only image2 is used.  A value between 0.0 and\n  1.0 means we linearly interpolate the pixel values between the two\n  images.  A value greater than 1.0 \"extrapolates\" the difference\n  between the two pixel values, and we clip the results to values\n  between 0 and 255.\n\n  Args:\n    image1: An image Tensor of type uint8.\n    image2: An image Tensor of type uint8.\n    factor: A floating point value above 0.0.\n\n  Returns:\n    A blended image Tensor of type uint8.\n  \"\"\"\n  if factor == 0.0:\n    return tf.convert_to_tensor(image1)\n  if factor == 1.0:\n    return tf.convert_to_tensor(image2)\n\n  image1 = tf.to_float(image1)\n  image2 = tf.to_float(image2)\n\n  difference = image2 - image1\n  scaled = factor * difference\n\n  # Do addition in float.\n  temp = tf.to_float(image1) + scaled\n\n  # Interpolate\n  if factor > 0.0 and factor < 1.0:\n    # Interpolation means we always stay within 0 and 255.\n    return tf.cast(temp, tf.uint8)\n\n  # Extrapolate:\n  #\n  # We need to clip and then cast.\n  return tf.cast(tf.clip_by_value(temp, 0.0, 255.0), tf.uint8)\n\n\ndef cutout(image, pad_size, replace=0):\n  \"\"\"Apply cutout (https://arxiv.org/abs/1708.04552) to image.\n\n  This operation applies a (2*pad_size x 2*pad_size) mask of zeros to\n  a random location within `img`. The pixel values filled in will be of the\n  value `replace`. The located where the mask will be applied is randomly\n  chosen uniformly over the whole image.\n\n  Args:\n    image: An image Tensor of type uint8.\n    pad_size: Specifies how big the zero mask that will be generated is that\n      is applied to the image. The mask will be of size\n      (2*pad_size x 2*pad_size).\n    replace: What pixel value to fill in the image in the area that has\n      the cutout mask applied to it.\n\n  Returns:\n    An image Tensor that is of type uint8.\n  \"\"\"\n  image_height = tf.shape(image)[0]\n  image_width = tf.shape(image)[1]\n\n  # Sample the center location in the image where the zero mask will be applied.\n  cutout_center_height = tf.random_uniform(\n      shape=[], minval=0, maxval=image_height,\n      dtype=tf.int32)\n\n  cutout_center_width = tf.random_uniform(\n      shape=[], minval=0, maxval=image_width,\n      dtype=tf.int32)\n\n  lower_pad = tf.maximum(0, cutout_center_height - pad_size)\n  upper_pad = tf.maximum(0, image_height - cutout_center_height - pad_size)\n  left_pad = tf.maximum(0, cutout_center_width - pad_size)\n  right_pad = tf.maximum(0, image_width - cutout_center_width - pad_size)\n\n  cutout_shape = [image_height - (lower_pad + upper_pad),\n                  image_width - (left_pad + right_pad)]\n  padding_dims = [[lower_pad, upper_pad], [left_pad, right_pad]]\n  mask = tf.pad(\n      tf.zeros(cutout_shape, dtype=image.dtype),\n      padding_dims, constant_values=1)\n  mask = tf.expand_dims(mask, -1)\n  mask = tf.tile(mask, [1, 1, 3])\n  image = tf.where(\n      tf.equal(mask, 0),\n      tf.ones_like(image, dtype=image.dtype) * replace,\n      image)\n  return image\n\n\ndef solarize(image, threshold=128):\n  # For each pixel in the image, select the pixel\n  # if the value is less than the threshold.\n  # Otherwise, subtract 255 from the pixel.\n  return tf.where(image < threshold, image, 255 - image)\n\n\ndef solarize_add(image, addition=0, threshold=128):\n  # For each pixel in the image less than threshold\n  # we add 'addition' amount to it and then clip the\n  # pixel value to be between 0 and 255. The value\n  # of 'addition' is between -128 and 128.\n  added_image = tf.cast(image, tf.int64) + addition\n  added_image = tf.cast(tf.clip_by_value(added_image, 0, 255), tf.uint8)\n  return tf.where(image < threshold, added_image, image)\n\n\ndef color(image, factor):\n  \"\"\"Equivalent of PIL Color.\"\"\"\n  degenerate = tf.image.grayscale_to_rgb(tf.image.rgb_to_grayscale(image))\n  return blend(degenerate, image, factor)\n\n\ndef contrast(image, factor):\n  \"\"\"Equivalent of PIL Contrast.\"\"\"\n  degenerate = tf.image.rgb_to_grayscale(image)\n  # Cast before calling tf.histogram.\n  degenerate = tf.cast(degenerate, tf.int32)\n\n  # Compute the grayscale histogram, then compute the mean pixel value,\n  # and create a constant image size of that value.  Use that as the\n  # blending degenerate target of the original image.\n  hist = tf.histogram_fixed_width(degenerate, [0, 255], nbins=256)\n  mean = tf.reduce_sum(tf.cast(hist, tf.float32)) / 256.0\n  degenerate = tf.ones_like(degenerate, dtype=tf.float32) * mean\n  degenerate = tf.clip_by_value(degenerate, 0.0, 255.0)\n  degenerate = tf.image.grayscale_to_rgb(tf.cast(degenerate, tf.uint8))\n  return blend(degenerate, image, factor)\n\n\ndef brightness(image, factor):\n  \"\"\"Equivalent of PIL Brightness.\"\"\"\n  degenerate = tf.zeros_like(image)\n  return blend(degenerate, image, factor)\n\n\ndef posterize(image, bits):\n  \"\"\"Equivalent of PIL Posterize.\"\"\"\n  shift = 8 - bits\n  return tf.bitwise.left_shift(tf.bitwise.right_shift(image, shift), shift)\n\n\ndef rotate(image, degrees, replace):\n  \"\"\"Rotates the image by degrees either clockwise or counterclockwise.\n\n  Args:\n    image: An image Tensor of type uint8.\n    degrees: Float, a scalar angle in degrees to rotate all images by. If\n      degrees is positive the image will be rotated clockwise otherwise it will\n      be rotated counterclockwise.\n    replace: A one or three value 1D tensor to fill empty pixels caused by\n      the rotate operation.\n\n  Returns:\n    The rotated version of image.\n  \"\"\"\n  # Convert from degrees to radians.\n  degrees_to_radians = math.pi / 180.0\n  radians = degrees * degrees_to_radians\n\n  # In practice, we should randomize the rotation degrees by flipping\n  # it negatively half the time, but that's done on 'degrees' outside\n  # of the function.\n  image = contrib_image.rotate(wrap(image), radians)\n  return unwrap(image, replace)\n\n\ndef translate_x(image, pixels, replace):\n  \"\"\"Equivalent of PIL Translate in X dimension.\"\"\"\n  image = contrib_image.translate(wrap(image), [-pixels, 0])\n  return unwrap(image, replace)\n\n\ndef translate_y(image, pixels, replace):\n  \"\"\"Equivalent of PIL Translate in Y dimension.\"\"\"\n  image = contrib_image.translate(wrap(image), [0, -pixels])\n  return unwrap(image, replace)\n\n\ndef shear_x(image, level, replace):\n  \"\"\"Equivalent of PIL Shearing in X dimension.\"\"\"\n  # Shear parallel to x axis is a projective transform\n  # with a matrix form of:\n  # [1  level\n  #  0  1].\n  image = contrib_image.transform(\n      wrap(image), [1., level, 0., 0., 1., 0., 0., 0.])\n  return unwrap(image, replace)\n\n\ndef shear_y(image, level, replace):\n  \"\"\"Equivalent of PIL Shearing in Y dimension.\"\"\"\n  # Shear parallel to y axis is a projective transform\n  # with a matrix form of:\n  # [1  0\n  #  level  1].\n  image = contrib_image.transform(\n      wrap(image), [1., 0., 0., level, 1., 0., 0., 0.])\n  return unwrap(image, replace)\n\n\ndef autocontrast(image):\n  \"\"\"Implements Autocontrast function from PIL using TF ops.\n\n  Args:\n    image: A 3D uint8 tensor.\n\n  Returns:\n    The image after it has had autocontrast applied to it and will be of type\n    uint8.\n  \"\"\"\n\n  def scale_channel(image):\n    \"\"\"Scale the 2D image using the autocontrast rule.\"\"\"\n    # A possibly cheaper version can be done using cumsum/unique_with_counts\n    # over the histogram values, rather than iterating over the entire image.\n    # to compute mins and maxes.\n    lo = tf.to_float(tf.reduce_min(image))\n    hi = tf.to_float(tf.reduce_max(image))\n\n    # Scale the image, making the lowest value 0 and the highest value 255.\n    def scale_values(im):\n      scale = 255.0 / (hi - lo)\n      offset = -lo * scale\n      im = tf.to_float(im) * scale + offset\n      im = tf.clip_by_value(im, 0.0, 255.0)\n      return tf.cast(im, tf.uint8)\n\n    result = tf.cond(hi > lo, lambda: scale_values(image), lambda: image)\n    return result\n\n  # Assumes RGB for now.  Scales each channel independently\n  # and then stacks the result.\n  s1 = scale_channel(image[:, :, 0])\n  s2 = scale_channel(image[:, :, 1])\n  s3 = scale_channel(image[:, :, 2])\n  image = tf.stack([s1, s2, s3], 2)\n  return image\n\n\ndef sharpness(image, factor):\n  \"\"\"Implements Sharpness function from PIL using TF ops.\"\"\"\n  orig_image = image\n  image = tf.cast(image, tf.float32)\n  # Make image 4D for conv operation.\n  image = tf.expand_dims(image, 0)\n  # SMOOTH PIL Kernel.\n  kernel = tf.constant(\n      [[1, 1, 1], [1, 5, 1], [1, 1, 1]], dtype=tf.float32,\n      shape=[3, 3, 1, 1]) / 13.\n  # Tile across channel dimension.\n  kernel = tf.tile(kernel, [1, 1, 3, 1])\n  strides = [1, 1, 1, 1]\n  with tf.device('/cpu:0'):\n    # Some augmentation that uses depth-wise conv will cause crashing when\n    # training on GPU. See (b/156242594) for details.\n    degenerate = tf.nn.depthwise_conv2d(\n        image, kernel, strides, padding='VALID', rate=[1, 1])\n  degenerate = tf.clip_by_value(degenerate, 0.0, 255.0)\n  degenerate = tf.squeeze(tf.cast(degenerate, tf.uint8), [0])\n\n  # For the borders of the resulting image, fill in the values of the\n  # original image.\n  mask = tf.ones_like(degenerate)\n  padded_mask = tf.pad(mask, [[1, 1], [1, 1], [0, 0]])\n  padded_degenerate = tf.pad(degenerate, [[1, 1], [1, 1], [0, 0]])\n  result = tf.where(tf.equal(padded_mask, 1), padded_degenerate, orig_image)\n\n  # Blend the final result.\n  return blend(result, orig_image, factor)\n\n\ndef equalize(image):\n  \"\"\"Implements Equalize function from PIL using TF ops.\"\"\"\n  def scale_channel(im, c):\n    \"\"\"Scale the data in the channel to implement equalize.\"\"\"\n    im = tf.cast(im[:, :, c], tf.int32)\n    # Compute the histogram of the image channel.\n    histo = tf.histogram_fixed_width(im, [0, 255], nbins=256)\n\n    # For the purposes of computing the step, filter out the nonzeros.\n    nonzero = tf.where(tf.not_equal(histo, 0))\n    nonzero_histo = tf.reshape(tf.gather(histo, nonzero), [-1])\n    step = (tf.reduce_sum(nonzero_histo) - nonzero_histo[-1]) // 255\n\n    def build_lut(histo, step):\n      # Compute the cumulative sum, shifting by step // 2\n      # and then normalization by step.\n      lut = (tf.cumsum(histo) + (step // 2)) // step\n      # Shift lut, prepending with 0.\n      lut = tf.concat([[0], lut[:-1]], 0)\n      # Clip the counts to be in range.  This is done\n      # in the C code for image.point.\n      return tf.clip_by_value(lut, 0, 255)\n\n    # If step is zero, return the original image.  Otherwise, build\n    # lut from the full histogram and step and then index from it.\n    result = tf.cond(tf.equal(step, 0),\n                     lambda: im,\n                     lambda: tf.gather(build_lut(histo, step), im))\n\n    return tf.cast(result, tf.uint8)\n\n  # Assumes RGB for now.  Scales each channel independently\n  # and then stacks the result.\n  s1 = scale_channel(image, 0)\n  s2 = scale_channel(image, 1)\n  s3 = scale_channel(image, 2)\n  image = tf.stack([s1, s2, s3], 2)\n  return image\n\n\ndef invert(image):\n  \"\"\"Inverts the image pixels.\"\"\"\n  image = tf.convert_to_tensor(image)\n  return 255 - image\n\n\ndef wrap(image):\n  \"\"\"Returns 'image' with an extra channel set to all 1s.\"\"\"\n  shape = tf.shape(image)\n  extended_channel = tf.ones([shape[0], shape[1], 1], image.dtype)\n  extended = tf.concat([image, extended_channel], 2)\n  return extended\n\n\ndef unwrap(image, replace):\n  \"\"\"Unwraps an image produced by wrap.\n\n  Where there is a 0 in the last channel for every spatial position,\n  the rest of the three channels in that spatial dimension are grayed\n  (set to 128).  Operations like translate and shear on a wrapped\n  Tensor will leave 0s in empty locations.  Some transformations look\n  at the intensity of values to do preprocessing, and we want these\n  empty pixels to assume the 'average' value, rather than pure black.\n\n\n  Args:\n    image: A 3D Image Tensor with 4 channels.\n    replace: A one or three value 1D tensor to fill empty pixels.\n\n  Returns:\n    image: A 3D image Tensor with 3 channels.\n  \"\"\"\n  image_shape = tf.shape(image)\n  # Flatten the spatial dimensions.\n  flattened_image = tf.reshape(image, [-1, image_shape[2]])\n\n  # Find all pixels where the last channel is zero.\n  alpha_channel = flattened_image[:, 3]\n\n  replace = tf.concat([replace, tf.ones([1], image.dtype)], 0)\n\n  # Where they are zero, fill them in with 'replace'.\n  flattened_image = tf.where(\n      tf.equal(alpha_channel, 0),\n      tf.ones_like(flattened_image, dtype=image.dtype) * replace,\n      flattened_image)\n\n  image = tf.reshape(flattened_image, image_shape)\n  image = tf.slice(image, [0, 0, 0], [image_shape[0], image_shape[1], 3])\n  return image\n\n\nNAME_TO_FUNC = {\n    'AutoContrast': autocontrast,\n    'Equalize': equalize,\n    'Invert': invert,\n    'Rotate': rotate,\n    'Posterize': posterize,\n    'Solarize': solarize,\n    'SolarizeAdd': solarize_add,\n    'Color': color,\n    'Contrast': contrast,\n    'Brightness': brightness,\n    'Sharpness': sharpness,\n    'ShearX': shear_x,\n    'ShearY': shear_y,\n    'TranslateX': translate_x,\n    'TranslateY': translate_y,\n    'Cutout': cutout,\n}\n\n\ndef _randomly_negate_tensor(tensor):\n  \"\"\"With 50% prob turn the tensor negative.\"\"\"\n  should_flip = tf.cast(tf.floor(tf.random_uniform([]) + 0.5), tf.bool)\n  final_tensor = tf.cond(should_flip, lambda: tensor, lambda: -tensor)\n  return final_tensor\n\n\ndef _rotate_level_to_arg(level):\n  level = (level/_MAX_LEVEL) * 30.\n  level = _randomly_negate_tensor(level)\n  return (level,)\n\n\ndef _shrink_level_to_arg(level):\n  \"\"\"Converts level to ratio by which we shrink the image content.\"\"\"\n  if level == 0:\n    return (1.0,)  # if level is zero, do not shrink the image\n  # Maximum shrinking ratio is 2.9.\n  level = 2. / (_MAX_LEVEL / level) + 0.9\n  return (level,)\n\n\ndef _enhance_level_to_arg(level):\n  return ((level/_MAX_LEVEL) * 1.8 + 0.1,)\n\n\ndef _shear_level_to_arg(level):\n  level = (level/_MAX_LEVEL) * 0.3\n  # Flip level to negative with 50% chance.\n  level = _randomly_negate_tensor(level)\n  return (level,)\n\n\ndef _translate_level_to_arg(level, translate_const):\n  level = (level/_MAX_LEVEL) * float(translate_const)\n  # Flip level to negative with 50% chance.\n  level = _randomly_negate_tensor(level)\n  return (level,)\n\n\ndef level_to_arg(hparams):\n  return {\n      'AutoContrast': lambda level: (),\n      'Equalize': lambda level: (),\n      'Invert': lambda level: (),\n      'Rotate': _rotate_level_to_arg,\n      'Posterize': lambda level: (int((level/_MAX_LEVEL) * 4),),\n      'Solarize': lambda level: (int((level/_MAX_LEVEL) * 256),),\n      'SolarizeAdd': lambda level: (int((level/_MAX_LEVEL) * 110),),\n      'Color': _enhance_level_to_arg,\n      'Contrast': _enhance_level_to_arg,\n      'Brightness': _enhance_level_to_arg,\n      'Sharpness': _enhance_level_to_arg,\n      'ShearX': _shear_level_to_arg,\n      'ShearY': _shear_level_to_arg,\n      'Cutout': lambda level: (int((level/_MAX_LEVEL) * hparams.cutout_const),),\n      # pylint:disable=g-long-lambda\n      'TranslateX': lambda level: _translate_level_to_arg(\n          level, hparams.translate_const),\n      'TranslateY': lambda level: _translate_level_to_arg(\n          level, hparams.translate_const),\n      # pylint:enable=g-long-lambda\n  }\n\n\ndef _parse_policy_info(name, prob, level, replace_value, augmentation_hparams):\n  \"\"\"Return the function that corresponds to `name` and update `level` param.\"\"\"\n  func = NAME_TO_FUNC[name]\n  args = level_to_arg(augmentation_hparams)[name](level)\n\n  # Check to see if prob is passed into function. This is used for operations\n  # where we alter bboxes independently.\n  # pytype:disable=wrong-arg-types\n  if 'prob' in inspect.getfullargspec(func).args:\n    args = tuple([prob] + list(args))\n  # pytype:enable=wrong-arg-types\n\n  # Add in replace arg if it is required for the function that is being called.\n  # pytype:disable=wrong-arg-types\n  if 'replace' in inspect.getfullargspec(func).args:\n    # Make sure replace is the final argument\n    assert 'replace' == inspect.getfullargspec(func).args[-1]\n    args = tuple(list(args) + [replace_value])\n  # pytype:enable=wrong-arg-types\n\n  return (func, prob, args)\n\n\ndef _apply_func_with_prob(func, image, args, prob):\n  \"\"\"Apply `func` to image w/ `args` as input with probability `prob`.\"\"\"\n  assert isinstance(args, tuple)\n\n  # If prob is a function argument, then this randomness is being handled\n  # inside the function, so make sure it is always called.\n  # pytype:disable=wrong-arg-types\n  if 'prob' in inspect.getfullargspec(func).args:\n    prob = 1.0\n  # pytype:enable=wrong-arg-types\n\n  # Apply the function with probability `prob`.\n  should_apply_op = tf.cast(\n      tf.floor(tf.random_uniform([], dtype=tf.float32) + prob), tf.bool)\n  augmented_image = tf.cond(\n      should_apply_op,\n      lambda: func(image, *args),\n      lambda: image)\n  return augmented_image\n\n\ndef select_and_apply_random_policy(policies, image):\n  \"\"\"Select a random policy from `policies` and apply it to `image`.\"\"\"\n  policy_to_select = tf.random_uniform([], maxval=len(policies), dtype=tf.int32)\n  # Note that using tf.case instead of tf.conds would result in significantly\n  # larger graphs and would even break export for some larger policies.\n  for (i, policy) in enumerate(policies):\n    image = tf.cond(\n        tf.equal(i, policy_to_select),\n        lambda selected_policy=policy: selected_policy(image),\n        lambda: image)\n  return image\n\n\ndef build_and_apply_nas_policy(policies, image,\n                               augmentation_hparams):\n  \"\"\"Build a policy from the given policies passed in and apply to image.\n\n  Args:\n    policies: list of lists of tuples in the form `(func, prob, level)`, `func`\n      is a string name of the augmentation function, `prob` is the probability\n      of applying the `func` operation, `level` is the input argument for\n      `func`.\n    image: tf.Tensor that the resulting policy will be applied to.\n    augmentation_hparams: Hparams associated with the NAS learned policy.\n\n  Returns:\n    A version of image that now has data augmentation applied to it based on\n    the `policies` pass into the function.\n  \"\"\"\n  replace_value = [128, 128, 128]\n\n  # func is the string name of the augmentation function, prob is the\n  # probability of applying the operation and level is the parameter associated\n  # with the tf op.\n\n  # tf_policies are functions that take in an image and return an augmented\n  # image.\n  tf_policies = []\n  for policy in policies:\n    tf_policy = []\n    # Link string name to the correct python function and make sure the correct\n    # argument is passed into that function.\n    for policy_info in policy:\n      policy_info = list(policy_info) + [replace_value, augmentation_hparams]\n\n      tf_policy.append(_parse_policy_info(*policy_info))\n    # Now build the tf policy that will apply the augmentation procedue\n    # on image.\n    def make_final_policy(tf_policy_):\n      def final_policy(image_):\n        for func, prob, args in tf_policy_:\n          image_ = _apply_func_with_prob(\n              func, image_, args, prob)\n        return image_\n      return final_policy\n    tf_policies.append(make_final_policy(tf_policy))\n\n  augmented_image = select_and_apply_random_policy(\n      tf_policies, image)\n  return augmented_image\n\n\ndef distort_image_with_autoaugment(image, augmentation_name):\n  \"\"\"Applies the AutoAugment policy to `image`.\n\n  AutoAugment is from the paper: https://arxiv.org/abs/1805.09501.\n\n  Args:\n    image: `Tensor` of shape [height, width, 3] representing an image.\n    augmentation_name: The name of the AutoAugment policy to use. The available\n      options are `v0` and `test`. `v0` is the policy used for\n      all of the results in the paper and was found to achieve the best results\n      on the COCO dataset. `v1`, `v2` and `v3` are additional good policies\n      found on the COCO dataset that have slight variation in what operations\n      were used during the search procedure along with how many operations are\n      applied in parallel to a single image (2 vs 3).\n\n  Returns:\n    A tuple containing the augmented versions of `image`.\n  \"\"\"\n  available_policies = {'v0': policy_v0,\n                        'test': policy_vtest}\n  if augmentation_name not in available_policies:\n    raise ValueError('Invalid augmentation_name: {}'.format(augmentation_name))\n\n  policy = available_policies[augmentation_name]()\n  # Hparams that will be used for AutoAugment.\n  augmentation_hparams = contrib_training.HParams(\n      cutout_const=100, translate_const=250)\n\n  return build_and_apply_nas_policy(policy, image, augmentation_hparams)\n\n\ndef distort_image_with_randaugment(image, num_layers, magnitude):\n  \"\"\"Applies the RandAugment policy to `image`.\n\n  RandAugment is from the paper https://arxiv.org/abs/1909.13719,\n\n  Args:\n    image: `Tensor` of shape [height, width, 3] representing an image.\n    num_layers: Integer, the number of augmentation transformations to apply\n      sequentially to an image. Represented as (N) in the paper. Usually best\n      values will be in the range [1, 3].\n    magnitude: Integer, shared magnitude across all augmentation operations.\n      Represented as (M) in the paper. Usually best values are in the range\n      [5, 30].\n\n  Returns:\n    The augmented version of `image`.\n  \"\"\"\n  replace_value = [128] * 3\n  tf.logging.info('Using RandAug.')\n  augmentation_hparams = contrib_training.HParams(\n      cutout_const=40, translate_const=100)\n  available_ops = [\n      'AutoContrast', 'Equalize', 'Invert', 'Rotate', 'Posterize',\n      'Solarize', 'Color', 'Contrast', 'Brightness', 'Sharpness',\n      'ShearX', 'ShearY', 'TranslateX', 'TranslateY', 'Cutout', 'SolarizeAdd']\n\n  for layer_num in range(num_layers):\n    op_to_select = tf.random_uniform(\n        [], maxval=len(available_ops), dtype=tf.int32)\n    random_magnitude = float(magnitude)\n    with tf.name_scope('randaug_layer_{}'.format(layer_num)):\n      for (i, op_name) in enumerate(available_ops):\n        prob = tf.random_uniform([], minval=0.2, maxval=0.8, dtype=tf.float32)\n        func, _, args = _parse_policy_info(op_name, prob, random_magnitude,\n                                           replace_value, augmentation_hparams)\n        image = tf.cond(\n            tf.equal(i, op_to_select),\n            # pylint:disable=g-long-lambda\n            lambda selected_func=func, selected_args=args: selected_func(\n                image, *selected_args),\n            # pylint:enable=g-long-lambda\n            lambda: image)\n  return image\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/backbone/backbone_factory.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Backbone network factory.\"\"\"\nimport os\nfrom absl import logging\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.backbone import efficientnet_builder\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.backbone import efficientnet_lite_builder\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.backbone import efficientnet_model\n\n\ndef get_model_builder(model_name):\n  \"\"\"Get the model_builder module for a given model name.\"\"\"\n  if model_name.startswith('efficientnet-lite'):\n    return efficientnet_lite_builder\n  elif model_name.startswith('efficientnet-'):\n    return efficientnet_builder\n  else:\n    raise ValueError('Unknown model name {}'.format(model_name))\n\n\ndef get_model(model_name, override_params=None, model_dir=None):\n  \"\"\"A helper function to create and return model.\n\n  Args:\n    model_name: string, the predefined model name.\n    override_params: A dictionary of params for overriding. Fields must exist in\n      efficientnet_model.GlobalParams.\n    model_dir: string, optional model dir for saving configs.\n\n  Returns:\n    created model\n\n  Raises:\n    When model_name specified an undefined model, raises NotImplementedError.\n    When override_params has invalid fields, raises ValueError.\n  \"\"\"\n\n  # For backward compatibility.\n  if override_params and override_params.get('drop_connect_rate', None):\n    override_params['survival_prob'] = 1 - override_params['drop_connect_rate']\n\n  if not override_params:\n    override_params = {}\n\n  if model_name.startswith('efficientnet-lite'):\n    builder = efficientnet_lite_builder\n  elif model_name.startswith('efficientnet-'):\n    builder = efficientnet_builder\n  else:\n    raise ValueError('Unknown model name {}'.format(model_name))\n\n  blocks_args, global_params = builder.get_model_params(model_name,\n                                                        override_params)\n\n  if model_dir:\n    param_file = os.path.join(model_dir, 'model_params.txt')\n    if not tf.io.gfile.exists(param_file):\n      if not tf.io.gfile.exists(model_dir):\n        tf.io.gfile.mkdir(model_dir)\n      with tf.io.gfile.GFile(param_file, 'w') as f:\n        logging.info('writing to %s', param_file)\n        f.write('model_name= %s\\n\\n' % model_name)\n        f.write('global_params= %s\\n\\n' % str(global_params))\n        f.write('blocks_args= %s\\n\\n' % str(blocks_args))\n\n  return efficientnet_model.Model(blocks_args, global_params, model_name)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/backbone/efficientnet_builder.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Model Builder for EfficientNet.\n\nefficientnet-bx (x=0,1,2,3,4,5,6,7) checkpoints are located in:\n  https://storage.googleapis.com/cloud-tpu-checkpoints/efficientnet/ckptsaug/efficientnet-bx.tar.gz\n\"\"\"\nimport functools\nimport os\nimport re\nfrom absl import logging\nimport numpy as np\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.backbone import efficientnet_model\n\n\ndef efficientnet_params(model_name):\n  \"\"\"Get efficientnet params based on model name.\"\"\"\n  params_dict = {\n      # (width_coefficient, depth_coefficient, resolution, dropout_rate)\n      'efficientnet-b0': (1.0, 1.0, 224, 0.2),\n      'efficientnet-b1': (1.0, 1.1, 240, 0.2),\n      'efficientnet-b2': (1.1, 1.2, 260, 0.3),\n      'efficientnet-b3': (1.2, 1.4, 300, 0.3),\n      'efficientnet-b4': (1.4, 1.8, 380, 0.4),\n      'efficientnet-b5': (1.6, 2.2, 456, 0.4),\n      'efficientnet-b6': (1.8, 2.6, 528, 0.5),\n      'efficientnet-b7': (2.0, 3.1, 600, 0.5),\n      'efficientnet-b8': (2.2, 3.6, 672, 0.5),\n      'efficientnet-l2': (4.3, 5.3, 800, 0.5),\n  }\n  return params_dict[model_name]\n\n\nclass BlockDecoder(object):\n  \"\"\"Block Decoder for readability.\"\"\"\n\n  def _decode_block_string(self, block_string):\n    \"\"\"Gets a block through a string notation of arguments.\"\"\"\n    assert isinstance(block_string, str)\n    ops = block_string.split('_')\n    options = {}\n    for op in ops:\n      splits = re.split(r'(\\d.*)', op)\n      if len(splits) >= 2:\n        key, value = splits[:2]\n        options[key] = value\n\n    if 's' not in options or len(options['s']) != 2:\n      raise ValueError('Strides options should be a pair of integers.')\n\n    return efficientnet_model.BlockArgs(\n        kernel_size=int(options['k']),\n        num_repeat=int(options['r']),\n        input_filters=int(options['i']),\n        output_filters=int(options['o']),\n        expand_ratio=int(options['e']),\n        id_skip=('noskip' not in block_string),\n        se_ratio=float(options['se']) if 'se' in options else None,\n        strides=[int(options['s'][0]),\n                 int(options['s'][1])],\n        conv_type=int(options['c']) if 'c' in options else 0,\n        fused_conv=int(options['f']) if 'f' in options else 0,\n        super_pixel=int(options['p']) if 'p' in options else 0,\n        condconv=('cc' in block_string))\n\n  def _encode_block_string(self, block):\n    \"\"\"Encodes a block to a string.\"\"\"\n    args = [\n        'r%d' % block.num_repeat,\n        'k%d' % block.kernel_size,\n        's%d%d' % (block.strides[0], block.strides[1]),\n        'e%s' % block.expand_ratio,\n        'i%d' % block.input_filters,\n        'o%d' % block.output_filters,\n        'c%d' % block.conv_type,\n        'f%d' % block.fused_conv,\n        'p%d' % block.super_pixel,\n    ]\n    if block.se_ratio > 0 and block.se_ratio <= 1:\n      args.append('se%s' % block.se_ratio)\n    if block.id_skip is False:  # pylint: disable=g-bool-id-comparison\n      args.append('noskip')\n    if block.condconv:\n      args.append('cc')\n    return '_'.join(args)\n\n  def decode(self, string_list):\n    \"\"\"Decodes a list of string notations to specify blocks inside the network.\n\n    Args:\n      string_list: a list of strings, each string is a notation of block.\n\n    Returns:\n      A list of namedtuples to represent blocks arguments.\n    \"\"\"\n    assert isinstance(string_list, list)\n    blocks_args = []\n    for block_string in string_list:\n      blocks_args.append(self._decode_block_string(block_string))\n    return blocks_args\n\n  def encode(self, blocks_args):\n    \"\"\"Encodes a list of Blocks to a list of strings.\n\n    Args:\n      blocks_args: A list of namedtuples to represent blocks arguments.\n    Returns:\n      a list of strings, each string is a notation of block.\n    \"\"\"\n    block_strings = []\n    for block in blocks_args:\n      block_strings.append(self._encode_block_string(block))\n    return block_strings\n\n\ndef swish(features, use_native=True, use_hard=False):\n  \"\"\"Computes the Swish activation function.\n\n  We provide three alternatives:\n    - Native tf.nn.swish, use less memory during training than composable swish.\n    - Quantization friendly hard swish.\n    - A composable swish, equivalent to tf.nn.swish, but more general for\n      finetuning and TF-Hub.\n\n  Args:\n    features: A `Tensor` representing preactivation values.\n    use_native: Whether to use the native swish from tf.nn that uses a custom\n      gradient to reduce memory usage, or to use customized swish that uses\n      default TensorFlow gradient computation.\n    use_hard: Whether to use quantization-friendly hard swish.\n\n  Returns:\n    The activation value.\n  \"\"\"\n  if use_native and use_hard:\n    raise ValueError('Cannot specify both use_native and use_hard.')\n\n  if use_native:\n    return tf.nn.swish(features)\n\n  if use_hard:\n    return features * tf.nn.relu6(features + np.float32(3)) * (1. / 6.)\n\n  features = tf.convert_to_tensor(features, name='features')\n  return features * tf.nn.sigmoid(features)\n\n\n_DEFAULT_BLOCKS_ARGS = [\n    'r1_k3_s11_e1_i32_o16_se0.25', 'r2_k3_s22_e6_i16_o24_se0.25',\n    'r2_k5_s22_e6_i24_o40_se0.25', 'r3_k3_s22_e6_i40_o80_se0.25',\n    'r3_k5_s11_e6_i80_o112_se0.25', 'r4_k5_s22_e6_i112_o192_se0.25',\n    'r1_k3_s11_e6_i192_o320_se0.25',\n]\n\n\ndef efficientnet(width_coefficient=None,\n                 depth_coefficient=None,\n                 dropout_rate=0.2,\n                 survival_prob=0.8):\n  \"\"\"Creates a efficientnet model.\"\"\"\n  global_params = efficientnet_model.GlobalParams(\n      blocks_args=_DEFAULT_BLOCKS_ARGS,\n      batch_norm_momentum=0.99,\n      batch_norm_epsilon=1e-3,\n      dropout_rate=dropout_rate,\n      survival_prob=survival_prob,\n      data_format='channels_last',\n      num_classes=1000,\n      width_coefficient=width_coefficient,\n      depth_coefficient=depth_coefficient,\n      depth_divisor=8,\n      min_depth=None,\n      relu_fn=tf.nn.swish,\n      # The default is TPU-specific batch norm.\n      # The alternative is tf.layers.BatchNormalization.\n      batch_norm=utils.TpuBatchNormalization,  # TPU-specific requirement.\n      use_se=True,\n      clip_projection_output=False)\n  return global_params\n\n\ndef get_model_params(model_name, override_params):\n  \"\"\"Get the block args and global params for a given model.\"\"\"\n  if model_name.startswith('efficientnet'):\n    width_coefficient, depth_coefficient, _, dropout_rate = (\n        efficientnet_params(model_name))\n    global_params = efficientnet(\n        width_coefficient, depth_coefficient, dropout_rate)\n  else:\n    raise NotImplementedError('model name is not pre-defined: %s' % model_name)\n\n  if override_params:\n    # ValueError will be raised here if override_params has fields not included\n    # in global_params.\n    global_params = global_params._replace(**override_params)\n\n  decoder = BlockDecoder()\n  blocks_args = decoder.decode(global_params.blocks_args)\n\n  logging.info('global_params= %s', global_params)\n  return blocks_args, global_params\n\n\ndef build_model(images,\n                model_name,\n                training,\n                override_params=None,\n                model_dir=None,\n                fine_tuning=False,\n                features_only=False,\n                pooled_features_only=False):\n  \"\"\"A helper function to create a model and return predicted logits.\n\n  Args:\n    images: input images tensor.\n    model_name: string, the predefined model name.\n    training: boolean, whether the model is constructed for training.\n    override_params: A dictionary of params for overriding. Fields must exist in\n      efficientnet_model.GlobalParams.\n    model_dir: string, optional model dir for saving configs.\n    fine_tuning: boolean, whether the model is used for finetuning.\n    features_only: build the base feature network only (excluding final\n      1x1 conv layer, global pooling, dropout and fc head).\n    pooled_features_only: build the base network for features extraction (after\n      1x1 conv layer and global pooling, but before dropout and fc head).\n\n  Returns:\n    logits: the logits tensor of classes.\n    endpoints: the endpoints for each layer.\n\n  Raises:\n    When model_name specified an undefined model, raises NotImplementedError.\n    When override_params has invalid fields, raises ValueError.\n  \"\"\"\n  assert tf.is_tensor(images)\n  assert not (features_only and pooled_features_only)\n\n  # For backward compatibility.\n  if override_params and override_params.get('drop_connect_rate', None):\n    override_params['survival_prob'] = 1 - override_params['drop_connect_rate']\n\n  if not training or fine_tuning:\n    if not override_params:\n      override_params = {}\n    override_params['batch_norm'] = utils.BatchNormalization\n    if fine_tuning:\n      override_params['relu_fn'] = functools.partial(swish, use_native=False)\n  blocks_args, global_params = get_model_params(model_name, override_params)\n\n  if model_dir:\n    param_file = os.path.join(model_dir, 'model_params.txt')\n    if not tf.io.gfile.exists(param_file):\n      if not tf.io.gfile.exists(model_dir):\n        tf.io.gfile.makedirs(model_dir)\n      with tf.io.gfile.GFile(param_file, 'w') as f:\n        logging.info('writing to %s', param_file)\n        f.write('model_name= %s\\n\\n' % model_name)\n        f.write('global_params= %s\\n\\n' % str(global_params))\n        f.write('blocks_args= %s\\n\\n' % str(blocks_args))\n\n  model = efficientnet_model.Model(blocks_args, global_params, model_name)\n  outputs = model(\n      images,\n      training=training,\n      features_only=features_only,\n      pooled_features_only=pooled_features_only)\n  features, endpoints = outputs[0], outputs[1:]\n  if features_only:\n    features = tf.identity(features, 'features')\n  elif pooled_features_only:\n    features = tf.identity(features, 'pooled_features')\n  else:\n    features = tf.identity(features, 'logits')\n  return features, endpoints\n\n\ndef build_model_base(images, model_name, training, override_params=None):\n  \"\"\"Create a base feature network and return the features before pooling.\n\n  Args:\n    images: input images tensor.\n    model_name: string, the predefined model name.\n    training: boolean, whether the model is constructed for training.\n    override_params: A dictionary of params for overriding. Fields must exist in\n      efficientnet_model.GlobalParams.\n\n  Returns:\n    features: base features before pooling.\n    endpoints: the endpoints for each layer.\n\n  Raises:\n    When model_name specified an undefined model, raises NotImplementedError.\n    When override_params has invalid fields, raises ValueError.\n  \"\"\"\n  assert isinstance(images, tf.Tensor)\n  # For backward compatibility.\n  if override_params and override_params.get('drop_connect_rate', None):\n    override_params['survival_prob'] = 1 - override_params['drop_connect_rate']\n\n  blocks_args, global_params = get_model_params(model_name, override_params)\n\n  model = efficientnet_model.Model(blocks_args, global_params, model_name)\n  outputs = model(images, training=training, features_only=True)\n  return outputs[0], outputs[1:]\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/backbone/efficientnet_lite_builder.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Model Builder for EfficientNet Edge Models.\n\nefficientnet-litex (x=0,1,2,3,4) checkpoints are located in:\n  https://storage.googleapis.com/cloud-tpu-checkpoints/efficientnet/lite/efficientnet-litex.tar.gz\n\"\"\"\nimport os\nfrom absl import logging\nimport tensorflow.compat.v1 as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.backbone import efficientnet_builder\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.backbone import efficientnet_model\n\n# Edge models use inception-style MEAN and STDDEV for better post-quantization.\nMEAN_RGB = [127.0, 127.0, 127.0]\nSTDDEV_RGB = [128.0, 128.0, 128.0]\n\n\ndef efficientnet_lite_params(model_name):\n  \"\"\"Get efficientnet params based on model name.\"\"\"\n  params_dict = {\n      # (width_coefficient, depth_coefficient, resolution, dropout_rate)\n      'efficientnet-lite0': (1.0, 1.0, 224, 0.2),\n      'efficientnet-lite1': (1.0, 1.1, 240, 0.2),\n      'efficientnet-lite2': (1.1, 1.2, 260, 0.3),\n      'efficientnet-lite3': (1.2, 1.4, 280, 0.3),\n      'efficientnet-lite4': (1.4, 1.8, 300, 0.3),\n  }\n  return params_dict[model_name]\n\n\n_DEFAULT_BLOCKS_ARGS = [\n    'r1_k3_s11_e1_i32_o16_se0.25', 'r2_k3_s22_e6_i16_o24_se0.25',\n    'r2_k5_s22_e6_i24_o40_se0.25', 'r3_k3_s22_e6_i40_o80_se0.25',\n    'r3_k5_s11_e6_i80_o112_se0.25', 'r4_k5_s22_e6_i112_o192_se0.25',\n    'r1_k3_s11_e6_i192_o320_se0.25',\n]\n\n\ndef efficientnet_lite(width_coefficient=None,\n                      depth_coefficient=None,\n                      dropout_rate=0.2,\n                      survival_prob=0.8):\n  \"\"\"Creates a efficientnet model.\"\"\"\n  global_params = efficientnet_model.GlobalParams(\n      blocks_args=_DEFAULT_BLOCKS_ARGS,\n      batch_norm_momentum=0.99,\n      batch_norm_epsilon=1e-3,\n      dropout_rate=dropout_rate,\n      survival_prob=survival_prob,\n      data_format='channels_last',\n      num_classes=1000,\n      width_coefficient=width_coefficient,\n      depth_coefficient=depth_coefficient,\n      depth_divisor=8,\n      min_depth=None,\n      relu_fn=tf.nn.relu6,  # Relu6 is for easier quantization.\n      # The default is TPU-specific batch norm.\n      # The alternative is tf.layers.BatchNormalization.\n      batch_norm=utils.TpuBatchNormalization,  # TPU-specific requirement.\n      clip_projection_output=False,\n      fix_head_stem=True,  # Don't scale stem and head.\n      local_pooling=True,  # special cases for tflite issues.\n      use_se=False)  # SE is not well supported on many lite devices.\n  return global_params\n\n\ndef get_model_params(model_name, override_params):\n  \"\"\"Get the block args and global params for a given model.\"\"\"\n  if model_name.startswith('efficientnet-lite'):\n    width_coefficient, depth_coefficient, _, dropout_rate = (\n        efficientnet_lite_params(model_name))\n    global_params = efficientnet_lite(\n        width_coefficient, depth_coefficient, dropout_rate)\n  else:\n    raise NotImplementedError('model name is not pre-defined: %s' % model_name)\n\n  if override_params:\n    # ValueError will be raised here if override_params has fields not included\n    # in global_params.\n    global_params = global_params._replace(**override_params)\n\n  decoder = efficientnet_builder.BlockDecoder()\n  blocks_args = decoder.decode(global_params.blocks_args)\n\n  logging.info('global_params= %s', global_params)\n  return blocks_args, global_params\n\n\ndef build_model(images,\n                model_name,\n                training,\n                override_params=None,\n                model_dir=None,\n                fine_tuning=False,\n                features_only=False,\n                pooled_features_only=False):\n  \"\"\"A helper function to create a model and return predicted logits.\n\n  Args:\n    images: input images tensor.\n    model_name: string, the predefined model name.\n    training: boolean, whether the model is constructed for training.\n    override_params: A dictionary of params for overriding. Fields must exist in\n      efficientnet_model.GlobalParams.\n    model_dir: string, optional model dir for saving configs.\n    fine_tuning: boolean, whether the model is used for finetuning.\n    features_only: build the base feature network only (excluding final\n      1x1 conv layer, global pooling, dropout and fc head).\n    pooled_features_only: build the base network for features extraction (after\n      1x1 conv layer and global pooling, but before dropout and fc head).\n\n  Returns:\n    logits: the logits tensor of classes.\n    endpoints: the endpoints for each layer.\n\n  Raises:\n    When model_name specified an undefined model, raises NotImplementedError.\n    When override_params has invalid fields, raises ValueError.\n  \"\"\"\n  assert isinstance(images, tf.Tensor)\n  assert not (features_only and pooled_features_only)\n\n  # For backward compatibility.\n  if override_params and override_params.get('drop_connect_rate', None):\n    override_params['survival_prob'] = 1 - override_params['drop_connect_rate']\n\n  if not training or fine_tuning:\n    if not override_params:\n      override_params = {}\n    override_params['batch_norm'] = utils.BatchNormalization\n  blocks_args, global_params = get_model_params(model_name, override_params)\n\n  if model_dir:\n    param_file = os.path.join(model_dir, 'model_params.txt')\n    if not tf.gfile.Exists(param_file):\n      if not tf.gfile.Exists(model_dir):\n        tf.gfile.MakeDirs(model_dir)\n      with tf.gfile.GFile(param_file, 'w') as f:\n        logging.info('writing to %s', param_file)\n        f.write('model_name= %s\\n\\n' % model_name)\n        f.write('global_params= %s\\n\\n' % str(global_params))\n        f.write('blocks_args= %s\\n\\n' % str(blocks_args))\n\n  model = efficientnet_model.Model(blocks_args, global_params, model_name)\n  outputs = model(\n      images,\n      training=training,\n      features_only=features_only,\n      pooled_features_only=pooled_features_only)\n  features, endpoints = outputs[0], outputs[1:]\n  if features_only:\n    features = tf.identity(features, 'features')\n  elif pooled_features_only:\n    features = tf.identity(features, 'pooled_features')\n  else:\n    features = tf.identity(features, 'logits')\n  return features, endpoints\n\n\ndef build_model_base(images, model_name, training, override_params=None):\n  \"\"\"Create a base feature network and return the features before pooling.\n\n  Args:\n    images: input images tensor.\n    model_name: string, the predefined model name.\n    training: boolean, whether the model is constructed for training.\n    override_params: A dictionary of params for overriding. Fields must exist in\n      efficientnet_model.GlobalParams.\n\n  Returns:\n    features: base features before pooling.\n    endpoints: the endpoints for each layer.\n\n  Raises:\n    When model_name specified an undefined model, raises NotImplementedError.\n    When override_params has invalid fields, raises ValueError.\n  \"\"\"\n  assert isinstance(images, tf.Tensor)\n  # For backward compatibility.\n  if override_params and override_params.get('drop_connect_rate', None):\n    override_params['survival_prob'] = 1 - override_params['drop_connect_rate']\n\n  blocks_args, global_params = get_model_params(model_name, override_params)\n\n  model = efficientnet_model.Model(blocks_args, global_params, model_name)\n  outputs = model(images, training=training, features_only=True)\n\n  return outputs[0], outputs[1:]\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/backbone/efficientnet_model.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Contains definitions for EfficientNet model.\n\n[1] Mingxing Tan, Quoc V. Le\n  EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks.\n  ICML'19, https://arxiv.org/abs/1905.11946\n\"\"\"\n\nimport collections\nimport itertools\nimport math\n\nfrom absl import logging\nimport numpy as np\nimport six\nfrom six.moves import xrange\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\n\nGlobalParams = collections.namedtuple('GlobalParams', [\n    'batch_norm_momentum', 'batch_norm_epsilon', 'dropout_rate', 'data_format',\n    'num_classes', 'width_coefficient', 'depth_coefficient', 'depth_divisor',\n    'min_depth', 'survival_prob', 'relu_fn', 'batch_norm', 'use_se',\n    'local_pooling', 'condconv_num_experts', 'clip_projection_output',\n    'blocks_args', 'fix_head_stem', 'grad_checkpoint'\n])\nGlobalParams.__new__.__defaults__ = (None,) * len(GlobalParams._fields)\n\nBlockArgs = collections.namedtuple('BlockArgs', [\n    'kernel_size', 'num_repeat', 'input_filters', 'output_filters',\n    'expand_ratio', 'id_skip', 'strides', 'se_ratio', 'conv_type', 'fused_conv',\n    'super_pixel', 'condconv'\n])\n# defaults will be a public argument for namedtuple in Python 3.7\n# https://docs.python.org/3/library/collections.html#collections.namedtuple\nBlockArgs.__new__.__defaults__ = (None,) * len(BlockArgs._fields)\n\n\ndef conv_kernel_initializer(shape, dtype=None, partition_info=None):\n  \"\"\"Initialization for convolutional kernels.\n\n  The main difference with tf.variance_scaling_initializer is that\n  tf.variance_scaling_initializer uses a truncated normal with an uncorrected\n  standard deviation, whereas here we use a normal distribution. Similarly,\n  tf.initializers.variance_scaling uses a truncated normal with\n  a corrected standard deviation.\n\n  Args:\n    shape: shape of variable\n    dtype: dtype of variable\n    partition_info: unused\n\n  Returns:\n    an initialization for the variable\n  \"\"\"\n  del partition_info\n  kernel_height, kernel_width, _, out_filters = shape\n  fan_out = int(kernel_height * kernel_width * out_filters)\n  return tf.random.normal(\n      shape, mean=0.0, stddev=np.sqrt(2.0 / fan_out), dtype=dtype)\n\n\ndef dense_kernel_initializer(shape, dtype=None, partition_info=None):\n  \"\"\"Initialization for dense kernels.\n\n  This initialization is equal to\n    tf.variance_scaling_initializer(scale=1.0/3.0, mode='fan_out',\n                                    distribution='uniform').\n  It is written out explicitly here for clarity.\n\n  Args:\n    shape: shape of variable\n    dtype: dtype of variable\n    partition_info: unused\n\n  Returns:\n    an initialization for the variable\n  \"\"\"\n  del partition_info\n  init_range = 1.0 / np.sqrt(shape[1])\n  return tf.random.uniform(shape, -init_range, init_range, dtype=dtype)\n\n\ndef superpixel_kernel_initializer(shape, dtype='float32', partition_info=None):\n  \"\"\"Initializes superpixel kernels.\n\n  This is inspired by space-to-depth transformation that is mathematically\n  equivalent before and after the transformation. But we do the space-to-depth\n  via a convolution. Moreover, we make the layer trainable instead of direct\n  transform, we can initialization it this way so that the model can learn not\n  to do anything but keep it mathematically equivalent, when improving\n  performance.\n\n\n  Args:\n    shape: shape of variable\n    dtype: dtype of variable\n    partition_info: unused\n\n  Returns:\n    an initialization for the variable\n  \"\"\"\n  del partition_info\n  #  use input depth to make superpixel kernel.\n  depth = shape[-2]\n  filters = np.zeros([2, 2, depth, 4 * depth], dtype=dtype)\n  i = np.arange(2)\n  j = np.arange(2)\n  k = np.arange(depth)\n  mesh = np.array(np.meshgrid(i, j, k)).T.reshape(-1, 3).T\n  filters[mesh[0], mesh[1], mesh[2], 4 * mesh[2] + 2 * mesh[0] + mesh[1]] = 1\n  return filters\n\n\ndef round_filters(filters, global_params, skip=False):\n  \"\"\"Round number of filters based on depth multiplier.\"\"\"\n  multiplier = global_params.width_coefficient\n  divisor = global_params.depth_divisor\n  min_depth = global_params.min_depth\n  if skip or not multiplier:\n    return filters\n\n  filters *= multiplier\n  min_depth = min_depth or divisor\n  new_filters = max(min_depth, int(filters + divisor / 2) // divisor * divisor)\n  # Make sure that round down does not go down by more than 10%.\n  if new_filters < 0.9 * filters:\n    new_filters += divisor\n  return int(new_filters)\n\n\ndef round_repeats(repeats, global_params, skip=False):\n  \"\"\"Round number of filters based on depth multiplier.\"\"\"\n  multiplier = global_params.depth_coefficient\n  if skip or not multiplier:\n    return repeats\n  return int(math.ceil(multiplier * repeats))\n\n\nclass SE(tf.keras.layers.Layer):\n  \"\"\"Squeeze-and-excitation layer.\"\"\"\n\n  def __init__(self, global_params, se_filters, output_filters, name=None):\n    super().__init__(name=name)\n\n    self._local_pooling = global_params.local_pooling\n    self._data_format = global_params.data_format\n    self._relu_fn = global_params.relu_fn or tf.nn.swish\n\n    # Squeeze and Excitation layer.\n    self._se_reduce = tf.keras.layers.Conv2D(\n        se_filters,\n        kernel_size=[1, 1],\n        strides=[1, 1],\n        kernel_initializer=conv_kernel_initializer,\n        padding='same',\n        data_format=self._data_format,\n        use_bias=True,\n        name='conv2d')\n    self._se_expand = tf.keras.layers.Conv2D(\n        output_filters,\n        kernel_size=[1, 1],\n        strides=[1, 1],\n        kernel_initializer=conv_kernel_initializer,\n        padding='same',\n        data_format=self._data_format,\n        use_bias=True,\n        name='conv2d_1')\n\n  def call(self, inputs):\n    h_axis, w_axis = [2, 3] if self._data_format == 'channels_first' else [1, 2]\n    if self._local_pooling:\n      se_tensor = tf.nn.avg_pool(\n          inputs,\n          ksize=[1, inputs.shape[h_axis], inputs.shape[w_axis], 1],\n          strides=[1, 1, 1, 1],\n          padding='VALID')\n    else:\n      se_tensor = tf.reduce_mean(inputs, [h_axis, w_axis], keepdims=True)\n    se_tensor = self._se_expand(self._relu_fn(self._se_reduce(se_tensor)))\n    logging.info('Built SE %s : %s', self.name, se_tensor.shape)\n    return tf.sigmoid(se_tensor) * inputs\n\n\nclass SuperPixel(tf.keras.layers.Layer):\n  \"\"\"Super pixel layer.\"\"\"\n\n  def __init__(self, block_args, global_params, name=None):\n    super().__init__(name=name)\n    self._superpixel = tf.keras.layers.Conv2D(\n        block_args.input_filters,\n        kernel_size=[2, 2],\n        strides=[2, 2],\n        kernel_initializer=conv_kernel_initializer,\n        padding='same',\n        data_format=global_params.data_format,\n        use_bias=False,\n        name='conv2d')\n    self._bnsp = global_params.batch_norm(\n        axis=1 if global_params.data_format == 'channels_first' else -1,\n        momentum=global_params.batch_norm_momentum,\n        epsilon=global_params.batch_norm_epsilon,\n        name='tpu_batch_normalization')\n    self._relu_fn = global_params.relu_fn or tf.nn.swish\n\n  def call(self, inputs, training):\n    return self._relu_fn(self._bnsp(self._superpixel(inputs), training))\n\n\nclass MBConvBlock(tf.keras.layers.Layer):\n  \"\"\"A class of MBConv: Mobile Inverted Residual Bottleneck.\n\n  Attributes:\n    endpoints: dict. A list of internal tensors.\n  \"\"\"\n\n  def __init__(self, block_args, global_params, name=None):\n    \"\"\"Initializes a MBConv block.\n\n    Args:\n      block_args: BlockArgs, arguments to create a Block.\n      global_params: GlobalParams, a set of global parameters.\n      name: layer name.\n    \"\"\"\n    super().__init__(name=name)\n\n    self._block_args = block_args\n    self._global_params = global_params\n    self._local_pooling = global_params.local_pooling\n    self._batch_norm_momentum = global_params.batch_norm_momentum\n    self._batch_norm_epsilon = global_params.batch_norm_epsilon\n    self._batch_norm = global_params.batch_norm\n    self._condconv_num_experts = global_params.condconv_num_experts\n    self._data_format = global_params.data_format\n    self._channel_axis = 1 if self._data_format == 'channels_first' else -1\n\n    self._relu_fn = global_params.relu_fn or tf.nn.swish\n    self._has_se = (\n        global_params.use_se and self._block_args.se_ratio is not None and\n        0 < self._block_args.se_ratio <= 1)\n\n    self._clip_projection_output = global_params.clip_projection_output\n\n    self.endpoints = None\n\n    if self._block_args.condconv:\n      raise ValueError('Condconv is not supported.')\n\n    # Builds the block accordings to arguments.\n    self._build()\n\n  @property\n  def block_args(self):\n    return self._block_args\n\n  def _build(self):\n    \"\"\"Builds block according to the arguments.\"\"\"\n    # pylint: disable=g-long-lambda\n    bid = itertools.count(0)\n    get_bn_name = lambda: 'tpu_batch_normalization' + ('' if not next(\n        bid) else '_' + str(next(bid) // 2))\n    cid = itertools.count(0)\n    get_conv_name = lambda: 'conv2d' + ('' if not next(cid) else '_' + str(\n        next(cid) // 2))\n    # pylint: enable=g-long-lambda\n\n    if self._block_args.super_pixel == 1:\n      self.super_pixel = SuperPixel(\n          self._block_args, self._global_params, name='super_pixel')\n    else:\n      self.super_pixel = None\n\n    filters = self._block_args.input_filters * self._block_args.expand_ratio\n    kernel_size = self._block_args.kernel_size\n\n    if self._block_args.fused_conv:\n      # Fused expansion phase. Called if using fused convolutions.\n      self._fused_conv = tf.keras.layers.Conv2D(\n          filters=filters,\n          kernel_size=[kernel_size, kernel_size],\n          strides=self._block_args.strides,\n          kernel_initializer=conv_kernel_initializer,\n          padding='same',\n          data_format=self._data_format,\n          use_bias=False,\n          name=get_conv_name())\n    else:\n      # Expansion phase. Called if not using fused convolutions and expansion\n      # phase is necessary.\n      if self._block_args.expand_ratio != 1:\n        self._expand_conv = tf.keras.layers.Conv2D(\n            filters=filters,\n            kernel_size=[1, 1],\n            strides=[1, 1],\n            kernel_initializer=conv_kernel_initializer,\n            padding='same',\n            data_format=self._data_format,\n            use_bias=False,\n            name=get_conv_name())\n        self._bn0 = self._batch_norm(\n            axis=self._channel_axis,\n            momentum=self._batch_norm_momentum,\n            epsilon=self._batch_norm_epsilon,\n            name=get_bn_name())\n\n      # Depth-wise convolution phase. Called if not using fused convolutions.\n      self._depthwise_conv = tf.keras.layers.DepthwiseConv2D(\n          kernel_size=[kernel_size, kernel_size],\n          strides=self._block_args.strides,\n          depthwise_initializer=conv_kernel_initializer,\n          padding='same',\n          data_format=self._data_format,\n          use_bias=False,\n          name='depthwise_conv2d')\n\n    self._bn1 = self._batch_norm(\n        axis=self._channel_axis,\n        momentum=self._batch_norm_momentum,\n        epsilon=self._batch_norm_epsilon,\n        name=get_bn_name())\n\n    if self._has_se:\n      num_reduced_filters = max(\n          1, int(self._block_args.input_filters * self._block_args.se_ratio))\n      self._se = SE(\n          self._global_params, num_reduced_filters, filters, name='se')\n    else:\n      self._se = None\n\n    # Output phase.\n    filters = self._block_args.output_filters\n    self._project_conv = tf.keras.layers.Conv2D(\n        filters=filters,\n        kernel_size=[1, 1],\n        strides=[1, 1],\n        kernel_initializer=conv_kernel_initializer,\n        padding='same',\n        data_format=self._data_format,\n        use_bias=False,\n        name=get_conv_name())\n    self._bn2 = self._batch_norm(\n        axis=self._channel_axis,\n        momentum=self._batch_norm_momentum,\n        epsilon=self._batch_norm_epsilon,\n        name=get_bn_name())\n\n  def call(self, inputs, training, survival_prob=None):\n    \"\"\"Implementation of call().\n\n    Args:\n      inputs: the inputs tensor.\n      training: boolean, whether the model is constructed for training.\n      survival_prob: float, between 0 to 1, drop connect rate.\n\n    Returns:\n      A output tensor.\n    \"\"\"\n    @utils.recompute_grad(self._global_params.grad_checkpoint)\n    def _call(inputs):\n      logging.info('Block %s input shape: %s', self.name, inputs.shape)\n      x = inputs\n\n      # creates conv 2x2 kernel\n      if self.super_pixel:\n        x = self.super_pixel(x, training)\n        logging.info('SuperPixel %s: %s', self.name, x.shape)\n\n      if self._block_args.fused_conv:\n        # If use fused mbconv, skip expansion and use regular conv.\n        x = self._relu_fn(self._bn1(self._fused_conv(x), training=training))\n        logging.info('Conv2D shape: %s', x.shape)\n      else:\n        # Otherwise, first apply expansion and then apply depthwise conv.\n        if self._block_args.expand_ratio != 1:\n          x = self._relu_fn(self._bn0(self._expand_conv(x), training=training))\n          logging.info('Expand shape: %s', x.shape)\n\n        x = self._relu_fn(self._bn1(self._depthwise_conv(x), training=training))\n        logging.info('DWConv shape: %s', x.shape)\n\n      if self._se:\n        x = self._se(x)\n\n      self.endpoints = {'expansion_output': x}\n\n      x = self._bn2(self._project_conv(x), training=training)\n      # Add identity so that quantization-aware training can insert quantization\n      # ops correctly.\n      x = tf.identity(x)\n      if self._clip_projection_output:\n        x = tf.clip_by_value(x, -6, 6)\n      if self._block_args.id_skip:\n        if all(\n            s == 1 for s in self._block_args.strides\n        ) and self._block_args.input_filters == self._block_args.output_filters:\n          # Apply only if skip connection presents.\n          if survival_prob:\n            x = utils.drop_connect(x, training, survival_prob)\n          x = tf.add(x, inputs)\n      logging.info('Project shape: %s', x.shape)\n      return x\n\n    return _call(inputs)\n\n\nclass MBConvBlockWithoutDepthwise(MBConvBlock):\n  \"\"\"MBConv-like block without depthwise convolution and squeeze-and-excite.\"\"\"\n\n  def _build(self):\n    \"\"\"Builds block according to the arguments.\"\"\"\n    filters = self._block_args.input_filters * self._block_args.expand_ratio\n    # pylint: disable=g-long-lambda\n    cid = itertools.count(0)\n    get_conv_name = lambda: 'conv2d' + ('' if not next(cid) else '_' + str(\n        next(cid) // 2))\n    # pylint: enable=g-long-lambda\n    kernel_size = self._block_args.kernel_size\n    if self._block_args.expand_ratio != 1:\n      # Expansion phase:\n      self._expand_conv = tf.keras.layers.Conv2D(\n          filters,\n          kernel_size=[kernel_size, kernel_size],\n          strides=[1, 1],\n          kernel_initializer=conv_kernel_initializer,\n          padding='same',\n          use_bias=False,\n          name=get_conv_name())\n      self._bn0 = self._batch_norm(\n          axis=self._channel_axis,\n          momentum=self._batch_norm_momentum,\n          epsilon=self._batch_norm_epsilon)\n\n    # Output phase:\n    filters = self._block_args.output_filters\n    self._project_conv = tf.keras.layers.Conv2D(\n        filters,\n        kernel_size=[1, 1],\n        strides=self._block_args.strides,\n        kernel_initializer=conv_kernel_initializer,\n        padding='same',\n        use_bias=False,\n        name=get_conv_name())\n    self._bn1 = self._batch_norm(\n        axis=self._channel_axis,\n        momentum=self._batch_norm_momentum,\n        epsilon=self._batch_norm_epsilon)\n\n  def call(self, inputs, training, survival_prob=None):\n    \"\"\"Implementation of call().\n\n    Args:\n      inputs: the inputs tensor.\n      training: boolean, whether the model is constructed for training.\n      survival_prob: float, between 0 to 1, drop connect rate.\n\n    Returns:\n      A output tensor.\n    \"\"\"\n\n    @utils.recompute_grad(self._global_params.grad_checkpoint)\n    def _call(inputs):\n      logging.info('Block %s  input shape: %s', self.name, inputs.shape)\n      if self._block_args.expand_ratio != 1:\n        x = self._relu_fn(\n            self._bn0(self._expand_conv(inputs), training=training))\n      else:\n        x = inputs\n      logging.info('Expand shape: %s', x.shape)\n\n      self.endpoints = {'expansion_output': x}\n\n      x = self._bn1(self._project_conv(x), training=training)\n      # Add identity so that quantization-aware training can insert quantization\n      # ops correctly.\n      x = tf.identity(x)\n      if self._clip_projection_output:\n        x = tf.clip_by_value(x, -6, 6)\n\n      if self._block_args.id_skip:\n        if all(\n            s == 1 for s in self._block_args.strides\n        ) and self._block_args.input_filters == self._block_args.output_filters:\n          # Apply only if skip connection presents.\n          if survival_prob:\n            x = utils.drop_connect(x, training, survival_prob)\n          x = tf.add(x, inputs)\n      logging.info('Project shape: %s', x.shape)\n      return x\n\n    return _call(inputs)\n\n\nclass Stem(tf.keras.layers.Layer):\n  \"\"\"Stem layer at the begining of the network.\"\"\"\n\n  def __init__(self, global_params, stem_filters, name=None):\n    super().__init__(name=name)\n    self._conv_stem = tf.keras.layers.Conv2D(\n        filters=round_filters(stem_filters, global_params,\n                              global_params.fix_head_stem),\n        kernel_size=[3, 3],\n        strides=[2, 2],\n        kernel_initializer=conv_kernel_initializer,\n        padding='same',\n        data_format=global_params.data_format,\n        use_bias=False)\n    self._bn = global_params.batch_norm(\n        axis=(1 if global_params.data_format == 'channels_first' else -1),\n        momentum=global_params.batch_norm_momentum,\n        epsilon=global_params.batch_norm_epsilon)\n    self._relu_fn = global_params.relu_fn or tf.nn.swish\n\n  def call(self, inputs, training):\n    return self._relu_fn(self._bn(self._conv_stem(inputs), training=training))\n\n\nclass Head(tf.keras.layers.Layer):\n  \"\"\"Head layer for network outputs.\"\"\"\n\n  def __init__(self, global_params, name=None):\n    super().__init__(name=name)\n\n    self.endpoints = {}\n    self._global_params = global_params\n\n    self._conv_head = tf.keras.layers.Conv2D(\n        filters=round_filters(1280, global_params, global_params.fix_head_stem),\n        kernel_size=[1, 1],\n        strides=[1, 1],\n        kernel_initializer=conv_kernel_initializer,\n        padding='same',\n        data_format=global_params.data_format,\n        use_bias=False,\n        name='conv2d')\n    self._bn = global_params.batch_norm(\n        axis=(1 if global_params.data_format == 'channels_first' else -1),\n        momentum=global_params.batch_norm_momentum,\n        epsilon=global_params.batch_norm_epsilon)\n    self._relu_fn = global_params.relu_fn or tf.nn.swish\n\n    self._avg_pooling = tf.keras.layers.GlobalAveragePooling2D(\n        data_format=global_params.data_format)\n    if global_params.num_classes:\n      self._fc = tf.keras.layers.Dense(\n          global_params.num_classes,\n          kernel_initializer=dense_kernel_initializer)\n    else:\n      self._fc = None\n\n    if global_params.dropout_rate > 0:\n      self._dropout = tf.keras.layers.Dropout(global_params.dropout_rate)\n    else:\n      self._dropout = None\n\n    self.h_axis, self.w_axis = (\n        [2, 3] if global_params.data_format == 'channels_first' else [1, 2])\n\n  def call(self, inputs, training, pooled_features_only):\n    \"\"\"Call the layer.\"\"\"\n    outputs = self._relu_fn(\n        self._bn(self._conv_head(inputs), training=training))\n    self.endpoints['head_1x1'] = outputs\n\n    if self._global_params.local_pooling:\n      shape = outputs.get_shape().as_list()\n      kernel_size = [1, shape[self.h_axis], shape[self.w_axis], 1]\n      outputs = tf.nn.avg_pool(\n          outputs, ksize=kernel_size, strides=[1, 1, 1, 1], padding='VALID')\n      self.endpoints['pooled_features'] = outputs\n      if not pooled_features_only:\n        if self._dropout:\n          outputs = self._dropout(outputs, training=training)\n        self.endpoints['global_pool'] = outputs\n        if self._fc:\n          outputs = tf.squeeze(outputs, [self.h_axis, self.w_axis])\n          outputs = self._fc(outputs)\n        self.endpoints['head'] = outputs\n    else:\n      outputs = self._avg_pooling(outputs)\n      self.endpoints['pooled_features'] = outputs\n      if not pooled_features_only:\n        if self._dropout:\n          outputs = self._dropout(outputs, training=training)\n        self.endpoints['global_pool'] = outputs\n        if self._fc:\n          outputs = self._fc(outputs)\n        self.endpoints['head'] = outputs\n    return outputs\n\n\nclass Model(tf.keras.Model):\n  \"\"\"A class implements tf.keras.Model.\n\n    Reference: https://arxiv.org/abs/1807.11626\n  \"\"\"\n\n  def __init__(self, blocks_args=None, global_params=None, name=None):\n    \"\"\"Initializes an `Model` instance.\n\n    Args:\n      blocks_args: A list of BlockArgs to construct block modules.\n      global_params: GlobalParams, a set of global parameters.\n      name: A string of layer name.\n\n    Raises:\n      ValueError: when blocks_args is not specified as a list.\n    \"\"\"\n    super().__init__(name=name)\n\n    if not isinstance(blocks_args, list):\n      raise ValueError('blocks_args should be a list.')\n    self._global_params = global_params\n    self._blocks_args = blocks_args\n    self._relu_fn = global_params.relu_fn or tf.nn.swish\n    self._batch_norm = global_params.batch_norm\n    self._fix_head_stem = global_params.fix_head_stem\n\n    self.endpoints = None\n\n    self._build()\n\n  def _get_conv_block(self, conv_type):\n    conv_block_map = {0: MBConvBlock, 1: MBConvBlockWithoutDepthwise}\n    return conv_block_map[conv_type]\n\n  def _build(self):\n    \"\"\"Builds a model.\"\"\"\n    self._blocks = []\n\n    # Stem part.\n    self._stem = Stem(self._global_params, self._blocks_args[0].input_filters)\n\n    # Builds blocks.\n    block_id = itertools.count(0)\n    block_name = lambda: 'blocks_%d' % next(block_id)\n    for i, block_args in enumerate(self._blocks_args):\n      assert block_args.num_repeat > 0\n      assert block_args.super_pixel in [0, 1, 2]\n      # Update block input and output filters based on depth multiplier.\n      input_filters = round_filters(block_args.input_filters,\n                                    self._global_params)\n\n      output_filters = round_filters(block_args.output_filters,\n                                     self._global_params)\n      kernel_size = block_args.kernel_size\n      if self._fix_head_stem and (i == 0 or i == len(self._blocks_args) - 1):\n        repeats = block_args.num_repeat\n      else:\n        repeats = round_repeats(block_args.num_repeat, self._global_params)\n      block_args = block_args._replace(\n          input_filters=input_filters,\n          output_filters=output_filters,\n          num_repeat=repeats)\n\n      # The first block needs to take care of stride and filter size increase.\n      conv_block = self._get_conv_block(block_args.conv_type)\n      if not block_args.super_pixel:  #  no super_pixel at all\n        self._blocks.append(\n            conv_block(block_args, self._global_params, name=block_name()))\n      else:\n        # if superpixel, adjust filters, kernels, and strides.\n        depth_factor = int(4 / block_args.strides[0] / block_args.strides[1])\n        block_args = block_args._replace(\n            input_filters=block_args.input_filters * depth_factor,\n            output_filters=block_args.output_filters * depth_factor,\n            kernel_size=((block_args.kernel_size + 1) //\n                         2 if depth_factor > 1 else block_args.kernel_size))\n        # if the first block has stride-2 and super_pixel trandformation\n        if (block_args.strides[0] == 2 and block_args.strides[1] == 2):\n          block_args = block_args._replace(strides=[1, 1])\n          self._blocks.append(\n              conv_block(block_args, self._global_params, name=block_name()))\n          block_args = block_args._replace(  # sp stops at stride-2\n              super_pixel=0,\n              input_filters=input_filters,\n              output_filters=output_filters,\n              kernel_size=kernel_size)\n        elif block_args.super_pixel == 1:\n          self._blocks.append(\n              conv_block(block_args, self._global_params, name=block_name()))\n          block_args = block_args._replace(super_pixel=2)\n        else:\n          self._blocks.append(\n              conv_block(block_args, self._global_params, name=block_name()))\n      if block_args.num_repeat > 1:  # rest of blocks with the same block_arg\n        # pylint: disable=protected-access\n        block_args = block_args._replace(\n            input_filters=block_args.output_filters, strides=[1, 1])\n        # pylint: enable=protected-access\n      for _ in xrange(block_args.num_repeat - 1):\n        self._blocks.append(\n            conv_block(block_args, self._global_params, name=block_name()))\n\n    # Head part.\n    self._head = Head(self._global_params)\n\n  def call(self,  # pytype: disable=signature-mismatch  # overriding-parameter-count-checks\n           inputs,\n           training,\n           features_only=None,\n           pooled_features_only=False):\n    \"\"\"Implementation of call().\n\n    Args:\n      inputs: input tensors.\n      training: boolean, whether the model is constructed for training.\n      features_only: build the base feature network only.\n      pooled_features_only: build the base network for features extraction\n        (after 1x1 conv layer and global pooling, but before dropout and fc\n        head).\n\n    Returns:\n      output tensors.\n    \"\"\"\n    outputs = None\n    # Keep the original inputs in endpoints for U-Net style FPN.\n    self.endpoints = {'inputs': inputs}\n    reduction_idx = 0\n\n    # Calls Stem layers\n    outputs = self._stem(inputs, training)\n    logging.info('Built stem %s : %s', self._stem.name, outputs.shape)\n    self.endpoints['stem'] = outputs\n\n    # Calls blocks.\n    for idx, block in enumerate(self._blocks):\n      is_reduction = False  # reduction flag for blocks after the stem layer\n      # If the first block has super-pixel (space-to-depth) layer, then stem is\n      # the first reduction point.\n      if (block.block_args.super_pixel == 1 and idx == 0):\n        reduction_idx += 1\n        self.endpoints['reduction_%s' % reduction_idx] = outputs\n\n      elif ((idx == len(self._blocks) - 1) or\n            self._blocks[idx + 1].block_args.strides[0] > 1):\n        is_reduction = True\n        reduction_idx += 1\n\n      survival_prob = self._global_params.survival_prob\n      if survival_prob:\n        drop_rate = 1.0 - survival_prob\n        survival_prob = 1.0 - drop_rate * float(idx) / len(self._blocks)\n        logging.info('block_%s survival_prob: %s', idx, survival_prob)\n      outputs = block(outputs, training=training, survival_prob=survival_prob)\n      self.endpoints['block_%s' % idx] = outputs\n      if is_reduction:\n        self.endpoints['reduction_%s' % reduction_idx] = outputs\n      if block.endpoints:\n        for k, v in six.iteritems(block.endpoints):\n          self.endpoints['block_%s/%s' % (idx, k)] = v\n          if is_reduction:\n            self.endpoints['reduction_%s/%s' % (reduction_idx, k)] = v\n    self.endpoints['features'] = outputs\n\n    if not features_only:\n      # Calls final layers and returns logits.\n      outputs = self._head(outputs, training, pooled_features_only)\n      self.endpoints.update(self._head.endpoints)\n\n    return [outputs] + list(\n        filter(lambda endpoint: endpoint is not None, [\n            self.endpoints.get('reduction_1'),\n            self.endpoints.get('reduction_2'),\n            self.endpoints.get('reduction_3'),\n            self.endpoints.get('reduction_4'),\n            self.endpoints.get('reduction_5'),\n        ]))\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/backbone/preprocessing.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"ImageNet preprocessing.\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nfrom absl import logging\n\nimport tensorflow.compat.v1 as tf\n\n\nIMAGE_SIZE = 224\nCROP_PADDING = 32\n\n\ndef distorted_bounding_box_crop(image_bytes,\n                                bbox,\n                                min_object_covered=0.1,\n                                aspect_ratio_range=(0.75, 1.33),\n                                area_range=(0.05, 1.0),\n                                max_attempts=100,\n                                scope=None):\n  \"\"\"Generates cropped_image using one of the bboxes randomly distorted.\n\n  See `tf.image.sample_distorted_bounding_box` for more documentation.\n\n  Args:\n    image_bytes: `Tensor` of binary image data.\n    bbox: `Tensor` of bounding boxes arranged `[1, num_boxes, coords]`\n        where each coordinate is [0, 1) and the coordinates are arranged\n        as `[ymin, xmin, ymax, xmax]`. If num_boxes is 0 then use the whole\n        image.\n    min_object_covered: An optional `float`. Defaults to `0.1`. The cropped\n        area of the image must contain at least this fraction of any bounding\n        box supplied.\n    aspect_ratio_range: An optional list of `float`s. The cropped area of the\n        image must have an aspect ratio = width / height within this range.\n    area_range: An optional list of `float`s. The cropped area of the image\n        must contain a fraction of the supplied image within in this range.\n    max_attempts: An optional `int`. Number of attempts at generating a cropped\n        region of the image of the specified constraints. After `max_attempts`\n        failures, return the entire image.\n    scope: Optional `str` for name scope.\n  Returns:\n    cropped image `Tensor`\n  \"\"\"\n  with tf.name_scope(scope, 'distorted_bounding_box_crop', [image_bytes, bbox]):\n    shape = tf.image.extract_jpeg_shape(image_bytes)\n    sample_distorted_bounding_box = tf.image.sample_distorted_bounding_box(\n        shape,\n        bounding_boxes=bbox,\n        min_object_covered=min_object_covered,\n        aspect_ratio_range=aspect_ratio_range,\n        area_range=area_range,\n        max_attempts=max_attempts,\n        use_image_if_no_bounding_boxes=True)\n    bbox_begin, bbox_size, _ = sample_distorted_bounding_box\n\n    # Crop the image to the specified bounding box.\n    offset_y, offset_x, _ = tf.unstack(bbox_begin)\n    target_height, target_width, _ = tf.unstack(bbox_size)\n    crop_window = tf.stack([offset_y, offset_x, target_height, target_width])\n    image = tf.image.decode_and_crop_jpeg(image_bytes, crop_window, channels=3)\n\n    return image\n\n\ndef _at_least_x_are_equal(a, b, x):\n  \"\"\"At least `x` of `a` and `b` `Tensors` are equal.\"\"\"\n  match = tf.equal(a, b)\n  match = tf.cast(match, tf.int32)\n  return tf.greater_equal(tf.reduce_sum(match), x)\n\n\ndef _resize_image(image, image_size, method=None):\n  if method is not None:\n    tf.logging.info('Use customized resize method {}'.format(method))\n    return tf.image.resize([image], [image_size, image_size], method)[0]\n  tf.logging.info('Use default resize_bicubic.')\n  return tf.image.resize_bicubic([image], [image_size, image_size])[0]\n\n\ndef _decode_and_random_crop(image_bytes, image_size, resize_method=None):\n  \"\"\"Make a random crop of image_size.\"\"\"\n  bbox = tf.constant([0.0, 0.0, 1.0, 1.0], dtype=tf.float32, shape=[1, 1, 4])\n  image = distorted_bounding_box_crop(\n      image_bytes,\n      bbox,\n      min_object_covered=0.1,\n      aspect_ratio_range=(3. / 4, 4. / 3.),\n      area_range=(0.08, 1.0),\n      max_attempts=10,\n      scope=None)\n  original_shape = tf.image.extract_jpeg_shape(image_bytes)\n  bad = _at_least_x_are_equal(original_shape, tf.shape(image), 3)\n\n  image = tf.cond(\n      bad,\n      lambda: _decode_and_center_crop(image_bytes, image_size),\n      lambda: _resize_image(image, image_size, resize_method))\n\n  return image\n\n\ndef _decode_and_center_crop(image_bytes, image_size, resize_method=None):\n  \"\"\"Crops to center of image with padding then scales image_size.\"\"\"\n  shape = tf.image.extract_jpeg_shape(image_bytes)\n  image_height = shape[0]\n  image_width = shape[1]\n\n  padded_center_crop_size = tf.cast(\n      ((image_size / (image_size + CROP_PADDING)) *\n       tf.cast(tf.minimum(image_height, image_width), tf.float32)),\n      tf.int32)\n\n  offset_height = ((image_height - padded_center_crop_size) + 1) // 2\n  offset_width = ((image_width - padded_center_crop_size) + 1) // 2\n  crop_window = tf.stack([offset_height, offset_width,\n                          padded_center_crop_size, padded_center_crop_size])\n  image = tf.image.decode_and_crop_jpeg(image_bytes, crop_window, channels=3)\n  image = _resize_image(image, image_size, resize_method)\n  return image\n\n\ndef _flip(image):\n  \"\"\"Random horizontal image flip.\"\"\"\n  image = tf.image.random_flip_left_right(image)\n  return image\n\n\ndef preprocess_for_train(image_bytes,\n                         use_bfloat16,\n                         image_size=IMAGE_SIZE,\n                         augment_name=None,\n                         randaug_num_layers=None,\n                         randaug_magnitude=None,\n                         resize_method=None):\n  \"\"\"Preprocesses the given image for evaluation.\n\n  Args:\n    image_bytes: `Tensor` representing an image binary of arbitrary size.\n    use_bfloat16: `bool` for whether to use bfloat16.\n    image_size: image size.\n    augment_name: `string` that is the name of the augmentation method\n      to apply to the image. `autoaugment` if AutoAugment is to be used or\n      `randaugment` if RandAugment is to be used. If the value is `None` no\n      augmentation method will be applied applied. See autoaugment.py for more\n      details.\n    randaug_num_layers: 'int', if RandAug is used, what should the number of\n      layers be. See autoaugment.py for detailed description.\n    randaug_magnitude: 'int', if RandAug is used, what should the magnitude\n      be. See autoaugment.py for detailed description.\n    resize_method: resize method. If none, use bicubic.\n\n  Returns:\n    A preprocessed image `Tensor`.\n  \"\"\"\n  image = _decode_and_random_crop(image_bytes, image_size, resize_method)\n  image = _flip(image)\n  image = tf.reshape(image, [image_size, image_size, 3])\n\n  if augment_name:\n    try:\n      from tensorflow_examples.lite.model_maker.third_party.efficientdet.backbone import autoaugment  # pylint: disable=g-import-not-at-top\n    except ImportError as e:\n      logging.exception('Autoaugment is not supported in TF 2.x.')\n      raise e\n\n    logging.info('Apply AutoAugment policy %s', augment_name)\n    input_image_type = image.dtype\n    image = tf.clip_by_value(image, 0.0, 255.0)\n    image = tf.cast(image, dtype=tf.uint8)\n\n    if augment_name == 'autoaugment':\n      logging.info('Apply AutoAugment policy %s', augment_name)\n      image = autoaugment.distort_image_with_autoaugment(image, 'v0')\n    elif augment_name == 'randaugment':\n      image = autoaugment.distort_image_with_randaugment(\n          image, randaug_num_layers, randaug_magnitude)\n    else:\n      raise ValueError('Invalid value for augment_name: %s' % (augment_name))\n\n    image = tf.cast(image, dtype=input_image_type)\n\n  image = tf.image.convert_image_dtype(\n      image, dtype=tf.bfloat16 if use_bfloat16 else tf.float32)\n\n  return image\n\n\ndef preprocess_for_eval(image_bytes,\n                        use_bfloat16,\n                        image_size=IMAGE_SIZE,\n                        resize_method=None):\n  \"\"\"Preprocesses the given image for evaluation.\n\n  Args:\n    image_bytes: `Tensor` representing an image binary of arbitrary size.\n    use_bfloat16: `bool` for whether to use bfloat16.\n    image_size: image size.\n    resize_method: if None, use bicubic.\n\n  Returns:\n    A preprocessed image `Tensor`.\n  \"\"\"\n  image = _decode_and_center_crop(image_bytes, image_size, resize_method)\n  image = tf.reshape(image, [image_size, image_size, 3])\n  image = tf.image.convert_image_dtype(\n      image, dtype=tf.bfloat16 if use_bfloat16 else tf.float32)\n  return image\n\n\ndef preprocess_image(image_bytes,\n                     is_training=False,\n                     use_bfloat16=False,\n                     image_size=IMAGE_SIZE,\n                     augment_name=None,\n                     randaug_num_layers=None,\n                     randaug_magnitude=None,\n                     resize_method=None):\n  \"\"\"Preprocesses the given image.\n\n  Args:\n    image_bytes: `Tensor` representing an image binary of arbitrary size.\n    is_training: `bool` for whether the preprocessing is for training.\n    use_bfloat16: `bool` for whether to use bfloat16.\n    image_size: image size.\n    augment_name: `string` that is the name of the augmentation method\n      to apply to the image. `autoaugment` if AutoAugment is to be used or\n      `randaugment` if RandAugment is to be used. If the value is `None` no\n      augmentation method will be applied applied. See autoaugment.py for more\n      details.\n    randaug_num_layers: 'int', if RandAug is used, what should the number of\n      layers be. See autoaugment.py for detailed description.\n    randaug_magnitude: 'int', if RandAug is used, what should the magnitude\n      be. See autoaugment.py for detailed description.\n    resize_method: 'string' or None. Use resize_bicubic in default.\n\n  Returns:\n    A preprocessed image `Tensor` with value range of [0, 255].\n  \"\"\"\n  if is_training:\n    return preprocess_for_train(\n        image_bytes, use_bfloat16, image_size, augment_name,\n        randaug_num_layers, randaug_magnitude, resize_method)\n  else:\n    return preprocess_for_eval(image_bytes, use_bfloat16, image_size,\n                               resize_method)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/backbone/train_backbone.py",
    "content": "# Copyright 2021 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"A simple script to allow pretraining of the efficientnet backbone.\"\"\"\n\nimport os\nimport re\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\nimport tensorflow as tf\nimport tensorflow_datasets as tfds\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.backbone import efficientnet_builder\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.backbone import efficientnet_model\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.backbone import preprocessing\n\nflags.DEFINE_string(\n    'tpu',\n    default=None,\n    help='The Cloud TPU to use for training. This should be either the name '\n    'used when creating the Cloud TPU, or a grpc://ip.address.of.tpu:8470 '\n    'url.')\nflags.DEFINE_string(\n    'gcp_project',\n    default=None,\n    help='Project name for the Cloud TPU-enabled project. If not specified, '\n    'we will attempt to automatically detect the GCE project from metadata.')\nflags.DEFINE_string(\n    'tpu_zone',\n    default=None,\n    help='GCE zone where the Cloud TPU is located in. If not specified, we '\n    'will attempt to automatically detect the GCE project from metadata.')\n\nflags.DEFINE_enum('strategy', '', ['tpu', 'gpus', ''],\n                  'Training: gpus for multi-gpu, if None, use TF default.')\nflags.DEFINE_string(\n    'model_dir', None, 'The directory where the model weights and '\n    'training/evaluation summaries are stored.')\nflags.DEFINE_string('model_name', 'efficientnet-b0', 'Model name.')\nflags.DEFINE_integer('batch_size', 64, 'training batch size')\nflags.DEFINE_integer('num_epochs', 10, 'Number of epochs to train.')\nflags.DEFINE_float('learning_rate', 1e-4, 'The learning rate to use.')\nflags.DEFINE_float(\n    'label_smoothing', 0.1,\n    'Label smoothing parameter used in the softmax_cross_entropy')\nflags.DEFINE_float('weight_decay', 1e-5,\n                   'Weight decay coefficiant for l2 regularization.')\nflags.DEFINE_string(\n    'augment_name', None, '`string` that is the name of the augmentation method'\n    'to apply to the image. `autoaugment` if AutoAugment is to be used or'\n    '`randaugment` if RandAugment is to be used. If the value is `None` no'\n    'augmentation method will be applied applied. See autoaugment.py for  '\n    'more details.')\n\nflags.DEFINE_integer(\n    'randaug_num_layers', 2,\n    'If RandAug is used, what should the number of layers be.'\n    'See autoaugment.py for detailed description.')\n\nflags.DEFINE_integer(\n    'randaug_magnitude', 10,\n    'If RandAug is used, what should the magnitude be. '\n    'See autoaugment.py for detailed description.')\n\nFLAGS = flags.FLAGS\n\n\ndef create_dataset(dataset: tf.data.Dataset, num_classes: int,\n                   is_training: bool) -> tf.data.Dataset:\n  \"\"\"Produces a full, augmented dataset from the inptu dataset.\"\"\"\n  _, _, resolution, _ = efficientnet_builder.efficientnet_params(\n      FLAGS.model_name)\n\n  def process_data(image, label):\n    image = preprocessing.preprocess_image(\n        image,\n        is_training=is_training,\n        use_bfloat16=FLAGS.strategy == 'tpus',\n        image_size=resolution,\n        augment_name=FLAGS.augment_name,\n        randaug_num_layers=FLAGS.randaug_num_layers,\n        randaug_magnitude=FLAGS.randaug_magnitude,\n        resize_method=None)\n\n    label = tf.one_hot(label, num_classes)\n    return image, label\n\n  dataset = dataset.map(\n      process_data, num_parallel_calls=tf.data.experimental.AUTOTUNE)\n  dataset = dataset.batch(FLAGS.batch_size, drop_remainder=True)\n  dataset = dataset.prefetch(tf.data.experimental.AUTOTUNE)\n\n  return dataset\n\n\nclass TrainableModel(efficientnet_model.Model):\n  \"\"\"Wraps efficientnet to make a keras trainable model.\n\n  Handles efficientnet's multiple outputs and adds weight decay.\n  \"\"\"\n\n  def __init__(self,\n               blocks_args=None,\n               global_params=None,\n               name=None,\n               weight_decay=0.0):\n    super().__init__(\n        blocks_args=blocks_args, global_params=global_params, name=name)\n\n    self.weight_decay = weight_decay\n\n  def _reg_l2_loss(self, weight_decay, regex=r'.*(kernel|weight):0$'):\n    \"\"\"Return regularization l2 loss loss.\"\"\"\n    var_match = re.compile(regex)\n    return weight_decay * tf.add_n([\n        tf.nn.l2_loss(v)\n        for v in self.trainable_variables\n        if var_match.match(v.name)\n    ])\n\n  def train_step(self, data):\n    images, labels = data\n\n    with tf.GradientTape() as tape:\n      pred = self(images, training=True)[0]\n      loss = self.compiled_loss(\n          labels,\n          pred,\n          regularization_losses=[self._reg_l2_loss(self.weight_decay)])\n\n    self.optimizer.minimize(loss, self.trainable_variables, tape=tape)\n    self.compiled_metrics.update_state(labels, pred)\n    return {m.name: m.result() for m in self.metrics}\n\n  def test_step(self, data):\n    images, labels = data\n    pred = self(images, training=False)[0]\n\n    self.compiled_loss(\n        labels,\n        pred,\n        regularization_losses=[self._reg_l2_loss(self.weight_decay)])\n\n    self.compiled_metrics.update_state(labels, pred)\n    return {m.name: m.result() for m in self.metrics}\n\n\ndef main(_) -> None:\n  if FLAGS.strategy == 'tpu':\n    tpu_cluster_resolver = tf.distribute.cluster_resolver.TPUClusterResolver(\n        FLAGS.tpu, zone=FLAGS.tpu_zone, project=FLAGS.gcp_project)\n    tf.config.experimental_connect_to_cluster(tpu_cluster_resolver)\n    tf.tpu.experimental.initialize_tpu_system(tpu_cluster_resolver)\n    ds_strategy = tf.distribute.TPUStrategy(tpu_cluster_resolver)\n    logging.info('All devices: %s', tf.config.list_logical_devices('TPU'))\n  elif FLAGS.strategy == 'gpus':\n    ds_strategy = tf.distribute.MirroredStrategy()\n    logging.info('All devices: %s', tf.config.list_physical_devices('GPU'))\n  else:\n    if tf.config.list_physical_devices('GPU'):\n      ds_strategy = tf.distribute.OneDeviceStrategy('device:GPU:0')\n    else:\n      ds_strategy = tf.distribute.OneDeviceStrategy('device:CPU:0')\n\n  with ds_strategy.scope():\n    (ds_train, ds_val), ds_info = tfds.load(\n        'imagenet2012',\n        split=['train', 'validation'],\n        shuffle_files=True,\n        as_supervised=True,\n        with_info=True,\n        decoders={\n            'image': tfds.decode.SkipDecoding(),\n        },\n    )\n\n    num_classes = ds_info.features['label'].num_classes\n\n    ds_train = create_dataset(ds_train, num_classes, is_training=True)\n\n    ds_val = create_dataset(ds_val, num_classes, is_training=False)\n\n    blocks_args, global_params = efficientnet_builder.get_model_params(\n        FLAGS.model_name,\n        override_params={'num_classes': ds_info.features['label'].num_classes})\n    model = TrainableModel(blocks_args, global_params, FLAGS.model_name,\n                           FLAGS.weight_decay)\n\n    steps_per_epoch = ds_info.splits['train'].num_examples // FLAGS.batch_size\n    total_steps = steps_per_epoch * FLAGS.num_epochs\n    cosign_decay = tf.keras.experimental.CosineDecay(FLAGS.learning_rate,\n                                                     total_steps)\n    model.compile(\n        optimizer=tf.keras.optimizers.Adam(cosign_decay),\n        loss=tf.keras.losses.CategoricalCrossentropy(\n            label_smoothing=FLAGS.label_smoothing, from_logits=True),\n        metrics=[tf.keras.metrics.CategoricalAccuracy()],\n    )\n\n    ckpt_callback = tf.keras.callbacks.ModelCheckpoint(\n        os.path.join(FLAGS.model_dir, 'ckpt-{epoch:d}'),\n        verbose=1,\n        save_weights_only=True)\n    tb_callback = tf.keras.callbacks.TensorBoard(\n        log_dir=FLAGS.model_dir, update_freq=100)\n    rstr_callback = tf.keras.callbacks.experimental.BackupAndRestore(\n        backup_dir=FLAGS.model_dir)\n\n    model.fit(\n        ds_train,\n        epochs=FLAGS.num_epochs,\n        validation_data=ds_val,\n        callbacks=[ckpt_callback, tb_callback, rstr_callback],\n        # don't log spam if running on tpus\n        verbose=2 if FLAGS.strategy == 'tpu' else 1,\n    )\n\n\nif __name__ == '__main__':\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/coco_metric.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"COCO-style evaluation metrics.\n\nImplements the interface of COCO API and metric_fn in tf.TPUEstimator.\nCOCO API: github.com/cocodataset/cocoapi/\n\"\"\"\nimport json\nimport os\nimport sys\nfrom absl import logging\nimport numpy as np\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import label_util\n\ntry:\n# pylint: disable=g-import-not-at-top\n  from pycocotools.coco import COCO\n  from pycocotools.cocoeval import COCOeval\n# pylint: enable=g-import-not-at-top\nexcept ImportError:\n  COCO = None\n  COCOeval = None\n\n\ndef block_print(log_level):\n  \"\"\"Disables print function when current logging level > log_level.\"\"\"\n  if tf.get_logger().getEffectiveLevel() > log_level:\n    sys.stdout = open(os.devnull, 'w')\n\n\ndef enable_print(original_stdout):\n  \"\"\"Enables print function.\"\"\"\n  sys.stdout = original_stdout\n\n\nclass EvaluationMetric():\n  \"\"\"COCO evaluation metric class.\n\n  This class cannot inherit from tf.keras.metrics.Metric due to numpy.\n  \"\"\"\n\n  def __init__(self, filename=None, testdev_dir=None, label_map=None):\n    \"\"\"Constructs COCO evaluation class.\n\n    The class provides the interface to metrics_fn in TPUEstimator. The\n    _update_op() takes detections from each image and push them to\n    self.detections. The _evaluate() loads a JSON file in COCO annotation format\n    as the groundtruth and runs COCO evaluation.\n\n    Args:\n      filename: Ground truth JSON file name. If filename is None, use\n        groundtruth data passed from the dataloader for evaluation. filename is\n        ignored if testdev_dir is not None.\n      testdev_dir: folder name for testdev data. If None, run eval without\n        groundtruth, and filename will be ignored.\n      label_map: a dict from id to class name. Used for per-class AP.\n    \"\"\"\n    self.label_map = label_map\n    self.filename = filename\n    self.testdev_dir = testdev_dir\n    self.metric_names = ['AP', 'AP50', 'AP75', 'APs', 'APm', 'APl', 'ARmax1',\n                         'ARmax10', 'ARmax100', 'ARs', 'ARm', 'ARl']\n    self.reset_states()\n\n  def reset_states(self):\n    \"\"\"Reset COCO API object.\"\"\"\n    self.detections = []\n    self.dataset = {\n        'images': [],\n        'annotations': [],\n        'categories': []\n    }\n    self.image_id = 1\n    self.annotation_id = 1\n    self.category_ids = []\n    self.metric_values = None\n\n  def evaluate(self, log_level=tf.compat.v1.logging.INFO):\n    \"\"\"Evaluates with detections from all images with COCO API.\n\n    Args:\n      log_level: Logging lavel to print logs.\n    Returns:\n      coco_metric: float numpy array with shape [12] representing the\n        coco-style evaluation metrics.\n    Raises:\n      ImportError: if the pip package `pycocotools` is not installed.\n    \"\"\"\n    if COCO is None or COCOeval is None:\n      message = ('You must install pycocotools (`pip install pycocotools`) '\n                 '(see github repo at https://github.com/cocodataset/cocoapi) '\n                 'for efficientdet/coco_metric to work.')\n      raise ImportError(message)\n\n    original_stdout = sys.stdout\n    block_print(log_level)\n    if self.filename:\n      coco_gt = COCO(self.filename)\n    else:\n      coco_gt = COCO()\n      coco_gt.dataset = self.dataset\n      coco_gt.createIndex()\n    enable_print(original_stdout)\n\n    if self.testdev_dir:\n      # Run on test-dev dataset.\n      box_result_list = []\n      for det in self.detections:\n        box_result_list.append({\n            'image_id': int(det[0]),\n            'category_id': int(det[6]),\n            'bbox': np.around(\n                det[1:5].astype(np.float64), decimals=2).tolist(),\n            'score': float(np.around(det[5], decimals=3)),\n        })\n      json.encoder.FLOAT_REPR = lambda o: format(o, '.3f')\n      # Must be in the formst of 'detections_test-dev2017_xxx_results'.\n      fname = 'detections_test-dev2017_test_results'\n      output_path = os.path.join(self.testdev_dir, fname + '.json')\n      logging.info('Writing output json file to: %s', output_path)\n      with tf.io.gfile.GFile(output_path, 'w') as fid:\n        json.dump(box_result_list, fid)\n      return np.array([-1.], dtype=np.float32)\n    else:\n      # Run on validation dataset.\n      block_print(log_level)\n      detections = np.array(self.detections)\n      image_ids = list(set(detections[:, 0]))\n      coco_dt = coco_gt.loadRes(detections)\n      coco_eval = COCOeval(coco_gt, coco_dt, iouType='bbox')\n      coco_eval.params.imgIds = image_ids\n      coco_eval.evaluate()\n      coco_eval.accumulate()\n      coco_eval.summarize()\n      enable_print(original_stdout)\n      coco_metrics = coco_eval.stats\n\n      if self.label_map:\n        # Get per_class AP, see pycocotools/cocoeval.py:334\n        # TxRxKxAxM: iouThrs x recThrs x catIds x areaRng x maxDets\n        # Use areaRng_id=0 ('all') and maxDets_id=-1 (200) in default\n        precision = coco_eval.eval['precision'][:, :, :, 0, -1]\n        # Ideally, label_map should match the eval set, but it is possible that\n        # some classes has no data in the eval set.\n        ap_perclass = [0] * max(precision.shape[-1], len(self.label_map))\n        for c in range(precision.shape[-1]):  # iterate over all classes\n          precision_c = precision[:, :, c]\n          # Only consider values if > -1.\n          precision_c = precision_c[precision_c > -1]\n          ap_c = np.mean(precision_c) if precision_c.size else -1.\n          ap_perclass[c] = ap_c\n        coco_metrics = np.concatenate((coco_metrics, ap_perclass))\n\n      # Return the concat normal and per-class AP.\n      return np.array(coco_metrics, dtype=np.float32)\n\n  def result(self, log_level=tf.compat.v1.logging.INFO):\n    \"\"\"Return the metric values (and compute it if needed).\"\"\"\n    if self.metric_values is None:\n      self.metric_values = self.evaluate(log_level)\n    return self.metric_values\n\n  def update_state(self, groundtruth_data, detections):\n    \"\"\"Update detection results and groundtruth data.\n\n    Append detection results to self.detections to aggregate results from\n    all validation set. The groundtruth_data is parsed and added into a\n    dictionary with the same format as COCO dataset, which can be used for\n    evaluation.\n\n    Args:\n      groundtruth_data: Groundtruth annotations in a tensor with each row\n        representing [y1, x1, y2, x2, is_crowd, area, class].\n      detections: Detection results in a tensor with each row representing\n        [image_id, x, y, width, height, score, class].\n    \"\"\"\n    for i, det in enumerate(detections):\n      # Filter out detections with predicted class label = -1.\n      indices = np.where(det[:, -1] > -1)[0]\n      det = det[indices]\n      if det.shape[0] == 0:\n        continue\n      # Append groundtruth annotations to create COCO dataset object.\n      # Add images.\n      image_id = det[0, 0]\n      if image_id == -1:\n        image_id = self.image_id\n      det[:, 0] = image_id\n      self.detections.extend(det)\n\n      if not self.filename and not self.testdev_dir:\n        # process groudtruth data only if filename is empty and no test_dev.\n        self.dataset['images'].append({\n            'id': int(image_id),\n        })\n\n        # Add annotations.\n        indices = np.where(groundtruth_data[i, :, -1] > -1)[0]\n        for data in groundtruth_data[i, indices]:\n          box = data[0:4]\n          is_crowd = data[4]\n          area = (box[3] - box[1]) * (box[2] - box[0])\n          category_id = data[6]\n          if category_id < 0:\n            break\n          self.dataset['annotations'].append({\n              'id': int(self.annotation_id),\n              'image_id': int(image_id),\n              'category_id': int(category_id),\n              'bbox': [box[1], box[0], box[3] - box[1], box[2] - box[0]],\n              'area': area,\n              'iscrowd': int(is_crowd)\n          })\n          self.annotation_id += 1\n          self.category_ids.append(category_id)\n\n      self.image_id += 1\n\n    if not self.filename:\n      self.category_ids = list(set(self.category_ids))\n      self.dataset['categories'] = [\n          {'id': int(category_id)} for category_id in self.category_ids\n      ]\n\n  def estimator_metric_fn(self, detections, groundtruth_data):\n    \"\"\"Constructs the metric function for tf.TPUEstimator.\n\n    For each metric, we return the evaluation op and an update op; the update op\n    is shared across all metrics and simply appends the set of detections to the\n    `self.detections` list. The metric op is invoked after all examples have\n    been seen and computes the aggregate COCO metrics. Please find details API\n    in: https://www.tensorflow.org/api_docs/python/tf/contrib/learn/MetricSpec\n\n    Args:\n      detections: Detection results in a tensor with each row representing\n        [image_id, x, y, width, height, score, class]\n      groundtruth_data: Groundtruth annotations in a tensor with each row\n        representing [y1, x1, y2, x2, is_crowd, area, class].\n    Returns:\n      metrics_dict: A dictionary mapping from evaluation name to a tuple of\n        operations (`metric_op`, `update_op`). `update_op` appends the\n        detections for the metric to the `self.detections` list.\n    \"\"\"\n    with tf.name_scope('coco_metric'):\n      if self.testdev_dir:\n        update_op = tf.numpy_function(self.update_state,\n                                      [groundtruth_data, detections], [])\n        metrics = tf.numpy_function(self.result, [], tf.float32)\n        metrics_dict = {'AP': (metrics, update_op)}\n        return metrics_dict\n      else:\n        update_op = tf.numpy_function(self.update_state,\n                                      [groundtruth_data, detections], [])\n        metrics = tf.numpy_function(self.result, [], tf.float32)\n        metrics_dict = {}\n        for i, name in enumerate(self.metric_names):\n          metrics_dict[name] = (metrics[i], update_op)\n\n        if self.label_map:\n          # process per-class AP.\n          label_map = label_util.get_label_map(self.label_map)\n          for i, cid in enumerate(sorted(label_map.keys())):\n            name = 'AP_/%s' % label_map[cid]\n            metrics_dict[name] = (metrics[i + len(self.metric_names)],\n                                  update_op)\n        return metrics_dict\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/dataloader.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Data loader and processing.\"\"\"\nfrom absl import logging\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import anchors\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.object_detection import preprocessor\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.object_detection import tf_example_decoder\n\n\nclass InputProcessor:\n  \"\"\"Base class of Input processor.\"\"\"\n\n  def __init__(self, image, output_size):\n    \"\"\"Initializes a new `InputProcessor`.\n\n    Args:\n      image: The input image before processing.\n      output_size: The output image size after calling resize_and_crop_image\n        function.\n    \"\"\"\n    self._image = image\n    if isinstance(output_size, int):\n      self._output_size = (output_size, output_size)\n    else:\n      self._output_size = output_size\n    # Parameters to control rescaling and shifting during preprocessing.\n    # Image scale defines scale from original image to scaled image.\n    self._image_scale = tf.constant(1.0)\n    # The integer height and width of scaled image.\n    self._scaled_height = tf.shape(image)[0]\n    self._scaled_width = tf.shape(image)[1]\n    # The x and y translation offset to crop scaled image to the output size.\n    self._crop_offset_y = tf.constant(0)\n    self._crop_offset_x = tf.constant(0)\n\n  @property\n  def image(self):\n    return self._image\n\n  @image.setter\n  def image(self, image):\n    self._image = image\n\n  def normalize_image(self, mean_rgb, stddev_rgb):\n    \"\"\"Normalize the image to zero mean and unit variance.\"\"\"\n    # The image normalization is identical to Cloud TPU ResNet.\n    self._image = tf.cast(self._image, dtype=tf.float32)\n    self._image -= tf.constant(mean_rgb, shape=(1, 1, 3), dtype=tf.float32)\n    self._image /= tf.constant(stddev_rgb, shape=(1, 1, 3), dtype=tf.float32)\n    return self._image\n\n  def set_training_random_scale_factors(self,\n                                        scale_min,\n                                        scale_max,\n                                        target_size=None):\n    \"\"\"Set the parameters for multiscale training.\n\n    Notably, if train and eval use different sizes, then target_size should be\n    set as eval size to avoid the discrency between train and eval.\n\n    Args:\n      scale_min: minimal scale factor.\n      scale_max: maximum scale factor.\n      target_size: targeted size, usually same as eval. If None, use train size.\n    \"\"\"\n    if not target_size:\n      target_size = self._output_size\n    target_size = utils.parse_image_size(target_size)\n    logging.info('target_size = %s, output_size = %s', target_size,\n                 self._output_size)\n\n    # Select a random scale factor.\n    random_scale_factor = tf.random.uniform([], scale_min, scale_max)\n    scaled_y = tf.cast(random_scale_factor * target_size[0], tf.int32)\n    scaled_x = tf.cast(random_scale_factor * target_size[1], tf.int32)\n\n    # Recompute the accurate scale_factor using rounded scaled image size.\n    height = tf.cast(tf.shape(self._image)[0], tf.float32)\n    width = tf.cast(tf.shape(self._image)[1], tf.float32)\n    image_scale_y = tf.cast(scaled_y, tf.float32) / height\n    image_scale_x = tf.cast(scaled_x, tf.float32) / width\n    image_scale = tf.minimum(image_scale_x, image_scale_y)\n\n    # Select non-zero random offset (x, y) if scaled image is larger than\n    # self._output_size.\n    scaled_height = tf.cast(height * image_scale, tf.int32)\n    scaled_width = tf.cast(width * image_scale, tf.int32)\n    offset_y = tf.cast(scaled_height - self._output_size[0], tf.float32)\n    offset_x = tf.cast(scaled_width - self._output_size[1], tf.float32)\n    offset_y = tf.maximum(0.0, offset_y) * tf.random.uniform([], 0, 1)\n    offset_x = tf.maximum(0.0, offset_x) * tf.random.uniform([], 0, 1)\n    offset_y = tf.cast(offset_y, tf.int32)\n    offset_x = tf.cast(offset_x, tf.int32)\n    self._image_scale = image_scale\n    self._scaled_height = scaled_height\n    self._scaled_width = scaled_width\n    self._crop_offset_x = offset_x\n    self._crop_offset_y = offset_y\n\n  def set_scale_factors_to_output_size(self):\n    \"\"\"Set the parameters to resize input image to self._output_size.\"\"\"\n    # Compute the scale_factor using rounded scaled image size.\n    height = tf.cast(tf.shape(self._image)[0], tf.float32)\n    width = tf.cast(tf.shape(self._image)[1], tf.float32)\n    image_scale_y = tf.cast(self._output_size[0], tf.float32) / height\n    image_scale_x = tf.cast(self._output_size[1], tf.float32) / width\n    image_scale = tf.minimum(image_scale_x, image_scale_y)\n    scaled_height = tf.cast(height * image_scale, tf.int32)\n    scaled_width = tf.cast(width * image_scale, tf.int32)\n    self._image_scale = image_scale\n    self._scaled_height = scaled_height\n    self._scaled_width = scaled_width\n\n  def resize_and_crop_image(self, method=tf.image.ResizeMethod.BILINEAR):\n    \"\"\"Resize input image and crop it to the self._output dimension.\"\"\"\n    dtype = self._image.dtype\n    scaled_image = tf.image.resize(\n        self._image, [self._scaled_height, self._scaled_width], method=method)\n    scaled_image = scaled_image[self._crop_offset_y:self._crop_offset_y +\n                                self._output_size[0],\n                                self._crop_offset_x:self._crop_offset_x +\n                                self._output_size[1], :]\n    output_image = tf.image.pad_to_bounding_box(scaled_image, 0, 0,\n                                                self._output_size[0],\n                                                self._output_size[1])\n    self._image = tf.cast(output_image, dtype)\n    return self._image\n\n\nclass DetectionInputProcessor(InputProcessor):\n  \"\"\"Input processor for object detection.\"\"\"\n\n  def __init__(self, image, output_size, boxes=None, classes=None):\n    InputProcessor.__init__(self, image, output_size)\n    self._boxes = boxes\n    self._classes = classes\n\n  def random_horizontal_flip(self):\n    \"\"\"Randomly flip input image and bounding boxes.\"\"\"\n    self._image, self._boxes = preprocessor.random_horizontal_flip(\n        self._image, boxes=self._boxes)\n\n  def clip_boxes(self, boxes):\n    \"\"\"Clip boxes to fit in an image.\"\"\"\n    ymin, xmin, ymax, xmax = tf.unstack(boxes, axis=1)\n    ymin = tf.clip_by_value(ymin, 0, self._output_size[0] - 1)\n    xmin = tf.clip_by_value(xmin, 0, self._output_size[1] - 1)\n    ymax = tf.clip_by_value(ymax, 0, self._output_size[0] - 1)\n    xmax = tf.clip_by_value(xmax, 0, self._output_size[1] - 1)\n    boxes = tf.stack([ymin, xmin, ymax, xmax], axis=1)\n    return boxes\n\n  def resize_and_crop_boxes(self):\n    \"\"\"Resize boxes and crop it to the self._output dimension.\"\"\"\n    boxlist = preprocessor.box_list.BoxList(self._boxes)\n    # boxlist is in range of [0, 1], so here we pass the scale_height/width\n    # instead of just scale.\n    boxes = preprocessor.box_list_scale(boxlist, self._scaled_height,\n                                        self._scaled_width).get()\n    # Adjust box coordinates based on the offset.\n    box_offset = tf.stack([\n        self._crop_offset_y,\n        self._crop_offset_x,\n        self._crop_offset_y,\n        self._crop_offset_x,\n    ])\n    boxes -= tf.cast(tf.reshape(box_offset, [1, 4]), tf.float32)\n    # Clip the boxes.\n    boxes = self.clip_boxes(boxes)\n    # Filter out ground truth boxes that are illegal.\n    indices = tf.where(\n        tf.not_equal((boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1]),\n                     0))\n    boxes = tf.gather_nd(boxes, indices)\n    classes = tf.gather_nd(self._classes, indices)\n    return boxes, classes\n\n  @property\n  def image_scale(self):\n    # Return image scale from original image to scaled image.\n    return self._image_scale\n\n  @property\n  def image_scale_to_original(self):\n    # Return image scale from scaled image to original image.\n    return 1.0 / self._image_scale\n\n  @property\n  def offset_x(self):\n    return self._crop_offset_x\n\n  @property\n  def offset_y(self):\n    return self._crop_offset_y\n\n\ndef pad_to_fixed_size(data, pad_value, output_shape):\n  \"\"\"Pad data to a fixed length at the first dimension.\n\n  Args:\n    data: Tensor to be padded to output_shape.\n    pad_value: A constant value assigned to the paddings.\n    output_shape: The output shape of a 2D tensor.\n  Returns:\n    The Padded tensor with output_shape [max_instances_per_image, dimension].\n  \"\"\"\n  max_instances_per_image = output_shape[0]\n  dimension = output_shape[1]\n  data = tf.reshape(data, [-1, dimension])\n  num_instances = tf.shape(data)[0]\n  msg = 'ERROR: please increase config.max_instances_per_image'\n  with tf.control_dependencies(\n      [tf.assert_less(num_instances, max_instances_per_image, message=msg)]):\n    pad_length = max_instances_per_image - num_instances\n  paddings = pad_value * tf.ones([pad_length, dimension])\n  padded_data = tf.concat([data, paddings], axis=0)\n  padded_data = tf.reshape(padded_data, output_shape)\n  return padded_data\n\n\nclass InputReader:\n  \"\"\"Input reader for dataset.\"\"\"\n\n  def __init__(self,\n               file_pattern,\n               is_training,\n               use_fake_data=False,\n               max_instances_per_image=None,\n               debug=False):\n    self._file_pattern = file_pattern\n    self._is_training = is_training\n    self._use_fake_data = use_fake_data\n    # COCO has 100 limit, but users may set different values for custom dataset.\n    self._max_instances_per_image = max_instances_per_image or 100\n    self._debug = debug\n\n  @tf.autograph.experimental.do_not_convert\n  def dataset_parser(self, value, example_decoder, anchor_labeler, params):\n    \"\"\"Parse data to a fixed dimension input image and learning targets.\n\n    Args:\n      value: a single serialized tf.Example string.\n      example_decoder: TF example decoder.\n      anchor_labeler: anchor box labeler.\n      params: a dict of extra parameters.\n\n    Returns:\n      image: Image tensor that is preprocessed to have normalized value and\n        fixed dimension [image_height, image_width, 3]\n      cls_targets_dict: ordered dictionary with keys\n        [min_level, min_level+1, ..., max_level]. The values are tensor with\n        shape [height_l, width_l, num_anchors]. The height_l and width_l\n        represent the dimension of class logits at l-th level.\n      box_targets_dict: ordered dictionary with keys\n        [min_level, min_level+1, ..., max_level]. The values are tensor with\n        shape [height_l, width_l, num_anchors * 4]. The height_l and\n        width_l represent the dimension of bounding box regression output at\n        l-th level.\n      num_positives: Number of positive anchors in the image.\n      source_id: Source image id. Default value -1 if the source id is empty\n        in the groundtruth annotation.\n      image_scale: Scale of the processed image to the original image.\n      boxes: Groundtruth bounding box annotations. The box is represented in\n        [y1, x1, y2, x2] format. The tensor is padded with -1 to the fixed\n        dimension [self._max_instances_per_image, 4].\n      is_crowds: Groundtruth annotations to indicate if an annotation\n        represents a group of instances by value {0, 1}. The tensor is\n        padded with 0 to the fixed dimension [self._max_instances_per_image].\n      areas: Groundtruth areas annotations. The tensor is padded with -1\n        to the fixed dimension [self._max_instances_per_image].\n      classes: Groundtruth classes annotations. The tensor is padded with -1\n        to the fixed dimension [self._max_instances_per_image].\n    \"\"\"\n    with tf.name_scope('parser'):\n      data = example_decoder.decode(value)\n      source_id = data['source_id']\n      image = data['image']\n      boxes = data['groundtruth_boxes']\n      classes = data['groundtruth_classes']\n      classes = tf.reshape(tf.cast(classes, dtype=tf.float32), [-1, 1])\n      areas = data['groundtruth_area']\n      is_crowds = data['groundtruth_is_crowd']\n      image_masks = data.get('groundtruth_instance_masks', [])\n      classes = tf.reshape(tf.cast(classes, dtype=tf.float32), [-1, 1])\n\n      if self._is_training:\n        # Training time preprocessing.\n        if params['skip_crowd_during_training']:\n          indices = tf.where(tf.logical_not(data['groundtruth_is_crowd']))\n          classes = tf.gather_nd(classes, indices)\n          boxes = tf.gather_nd(boxes, indices)\n\n        if params.get('grid_mask', None):\n          from tensorflow_examples.lite.model_maker.third_party.efficientdet.aug import gridmask  # pylint: disable=g-import-not-at-top\n          image, boxes = gridmask.gridmask(image, boxes)\n\n        if params.get('autoaugment_policy', None):\n          from tensorflow_examples.lite.model_maker.third_party.efficientdet.aug import autoaugment  # pylint: disable=g-import-not-at-top\n          if params['autoaugment_policy'] == 'randaug':\n            image, boxes = autoaugment.distort_image_with_randaugment(\n                image, boxes, num_layers=1, magnitude=15)\n          else:\n            image, boxes = autoaugment.distort_image_with_autoaugment(\n                image, boxes, params['autoaugment_policy'])\n\n      input_processor = DetectionInputProcessor(image, params['image_size'],\n                                                boxes, classes)\n      input_processor.normalize_image(params['mean_rgb'], params['stddev_rgb'])\n      if self._is_training:\n        if params['input_rand_hflip']:\n          input_processor.random_horizontal_flip()\n\n        input_processor.set_training_random_scale_factors(\n            params['jitter_min'], params['jitter_max'],\n            params.get('target_size', None))\n      else:\n        input_processor.set_scale_factors_to_output_size()\n      image = input_processor.resize_and_crop_image()\n      boxes, classes = input_processor.resize_and_crop_boxes()\n\n      # Assign anchors.\n      (cls_targets, box_targets,\n       num_positives) = anchor_labeler.label_anchors(boxes, classes)\n\n      source_id = tf.where(\n          tf.equal(source_id, tf.constant('')), '-1', source_id)\n      source_id = tf.strings.to_number(source_id)\n\n      # Pad groundtruth data for evaluation.\n      image_scale = input_processor.image_scale_to_original\n      boxes *= image_scale\n      is_crowds = tf.cast(is_crowds, dtype=tf.float32)\n      boxes = pad_to_fixed_size(boxes, -1, [self._max_instances_per_image, 4])\n      is_crowds = pad_to_fixed_size(is_crowds, 0,\n                                    [self._max_instances_per_image, 1])\n      areas = pad_to_fixed_size(areas, -1, [self._max_instances_per_image, 1])\n      classes = pad_to_fixed_size(classes, -1,\n                                  [self._max_instances_per_image, 1])\n      if params['scale_range']:\n        image = image * 2.0 / 255 - 1.0\n      if params['mixed_precision']:\n        dtype = tf.keras.mixed_precision.global_policy().compute_dtype\n        image = tf.cast(image, dtype=dtype)\n        box_targets = tf.nest.map_structure(\n            lambda box_target: tf.cast(box_target, dtype=dtype), box_targets)\n      return (image, cls_targets, box_targets, num_positives, source_id,\n              image_scale, boxes, is_crowds, areas, classes, image_masks)\n\n  @tf.autograph.experimental.do_not_convert\n  def process_example(self, params, batch_size, images, cls_targets,\n                      box_targets, num_positives, source_ids, image_scales,\n                      boxes, is_crowds, areas, classes, image_masks):\n    \"\"\"Processes one batch of data.\"\"\"\n    labels = {}\n    # Count num_positives in a batch.\n    num_positives_batch = tf.reduce_mean(num_positives)\n    labels['mean_num_positives'] = tf.reshape(\n        tf.tile(tf.expand_dims(num_positives_batch, 0), [\n            batch_size,\n        ]), [batch_size, 1])\n\n    if params['data_format'] == 'channels_first':\n      images = tf.transpose(images, [0, 3, 1, 2])\n\n    for level in range(params['min_level'], params['max_level'] + 1):\n      labels['cls_targets_%d' % level] = cls_targets[level]\n      labels['box_targets_%d' % level] = box_targets[level]\n      if params['data_format'] == 'channels_first':\n        labels['cls_targets_%d' % level] = tf.transpose(\n            labels['cls_targets_%d' % level], [0, 3, 1, 2])\n        labels['box_targets_%d' % level] = tf.transpose(\n            labels['box_targets_%d' % level], [0, 3, 1, 2])\n    # Concatenate groundtruth annotations to a tensor.\n    groundtruth_data = tf.concat([boxes, is_crowds, areas, classes], axis=2)\n    labels['source_ids'] = source_ids\n    labels['groundtruth_data'] = groundtruth_data\n    labels['image_scales'] = image_scales\n    labels['image_masks'] = image_masks\n    return images, labels\n\n  @property\n  def dataset_options(self):\n    options = tf.data.Options()\n    options.experimental_deterministic = self._debug or not self._is_training\n    options.experimental_optimization.map_parallelization = True\n    options.experimental_optimization.parallel_batch = True\n    return options\n\n  def __call__(self, params, input_context=None, batch_size=None):\n    input_anchors = anchors.Anchors(params['min_level'], params['max_level'],\n                                    params['num_scales'],\n                                    params['aspect_ratios'],\n                                    params['anchor_scale'],\n                                    params['image_size'])\n    anchor_labeler = anchors.AnchorLabeler(input_anchors, params['num_classes'])\n    example_decoder = tf_example_decoder.TfExampleDecoder(\n        include_mask='segmentation' in params['heads'],\n        regenerate_source_id=params['regenerate_source_id']\n    )\n\n    batch_size = batch_size or params['batch_size']\n    seed = params['tf_random_seed'] if self._debug else None\n    dataset = tf.data.Dataset.list_files(\n        self._file_pattern, shuffle=self._is_training, seed=seed)\n    if input_context:\n      dataset = dataset.shard(input_context.num_input_pipelines,\n                              input_context.input_pipeline_id)\n    # Prefetch data from files.\n    def _prefetch_dataset(filename):\n      if params.get('dataset_type', None) == 'sstable':\n        pass\n      else:\n        dataset = tf.data.TFRecordDataset(filename).prefetch(1)\n      return dataset\n\n    dataset = dataset.interleave(\n        _prefetch_dataset, num_parallel_calls=tf.data.AUTOTUNE)\n    dataset = dataset.with_options(self.dataset_options)\n    if self._is_training:\n      dataset = dataset.shuffle(64, seed=seed)\n\n    # Parse the fetched records to input tensors for model function.\n    # pylint: disable=g-long-lambda\n    if params.get('dataset_type', None) == 'sstable':\n      map_fn = lambda key, value: self.dataset_parser(value, example_decoder,\n                                                      anchor_labeler, params)\n    else:\n      map_fn = lambda value: self.dataset_parser(value, example_decoder,\n                                                 anchor_labeler, params)\n    # pylint: enable=g-long-lambda\n    dataset = dataset.map(\n        map_fn, num_parallel_calls=tf.data.AUTOTUNE)\n    dataset = dataset.prefetch(batch_size)\n    dataset = dataset.batch(batch_size, drop_remainder=params['drop_remainder'])\n    dataset = dataset.map(\n        lambda *args: self.process_example(params, batch_size, *args))\n    dataset = dataset.prefetch(tf.data.AUTOTUNE)\n    if self._is_training:\n      dataset = dataset.repeat()\n    if self._use_fake_data:\n      # Turn this dataset into a semi-fake dataset which always loop at the\n      # first batch. This reduces variance in performance and is useful in\n      # testing.\n      dataset = dataset.take(1).cache().repeat()\n    return dataset\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/dataset/README.md",
    "content": "This folder provides tools for converting raw coco/pascal data to tfrecord.\n\n### 1. Convert COCO validation set to tfrecord:\n\n    # Download coco data.\n    !wget http://images.cocodataset.org/zips/val2017.zip\n    !wget http://images.cocodataset.org/annotations/annotations_trainval2017.zip\n    !unzip val2017.zip\n    !unzip annotations_trainval2017.zip\n\n    # convert coco data to tfrecord.\n    !mkdir tfrecord\n    !PYTHONPATH=\".:$PYTHONPATH\"  python dataset/create_coco_tfrecord.py \\\n      --image_dir=val2017 \\\n      --caption_annotations_file=annotations/captions_val2017.json \\\n      --output_file_prefix=tfrecord/val \\\n      --num_shards=32\n\n### 2. Convert Pascal VOC 2012 to tfrecord:\n\n    # Download and convert pascal data.\n    !wget http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar\n    !tar xf VOCtrainval_11-May-2012.tar\n    !mkdir tfrecord\n    !PYTHONPATH=\".:$PYTHONPATH\"  python dataset/create_pascal_tfrecord.py  \\\n        --data_dir=VOCdevkit --year=VOC2012  --output_path=tfrecord/pascal\n\nAttention:  soure_id (or image_id) needs to be an integer due to the official COCO library requreiments. \n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/dataset/__init__.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n# This library is mostly based on tensorflow object detection API\n# https://github.com/tensorflow/models/blob/master/research/object_detection/dataset_tools/create_coco_tf_record.py\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/dataset/create_coco_tfrecord.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\nr\"\"\"Convert raw COCO 2017 dataset to TFRecord.\n\nExample usage:\n    python create_coco_tf_record.py --logtostderr \\\n      --image_dir=\"${TRAIN_IMAGE_DIR}\" \\\n      --image_info_file=\"${TRAIN_IMAGE_INFO_FILE}\" \\\n      --object_annotations_file=\"${TRAIN_ANNOTATIONS_FILE}\" \\\n      --caption_annotations_file=\"${CAPTION_ANNOTATIONS_FILE}\" \\\n      --output_file_prefix=\"${OUTPUT_DIR/FILE_PREFIX}\" \\\n      --num_shards=100\n\"\"\"\n\nimport collections\nimport hashlib\nimport io\nimport json\nimport multiprocessing\nimport os\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\nimport numpy as np\nimport PIL.Image\n\nfrom pycocotools import mask\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.dataset import label_map_util\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.dataset import tfrecord_util\n\nflags.DEFINE_boolean(\n    'include_masks', False, 'Whether to include instance segmentations masks '\n    '(PNG encoded) in the result. default: False.')\nflags.DEFINE_string('image_dir', '', 'Directory containing images.')\nflags.DEFINE_string(\n    'image_info_file', '', 'File containing image information. '\n    'Tf Examples in the output files correspond to the image '\n    'info entries in this file. If this file is not provided '\n    'object_annotations_file is used if present. Otherwise, '\n    'caption_annotations_file is used to get image info.')\nflags.DEFINE_string(\n    'object_annotations_file', '', 'File containing object '\n    'annotations - boxes and instance masks.')\nflags.DEFINE_string('caption_annotations_file', '', 'File containing image '\n                    'captions.')\nflags.DEFINE_string('output_file_prefix', '/tmp/train', 'Path to output file')\nflags.DEFINE_integer('num_shards', 32, 'Number of shards for output file.')\nflags.DEFINE_integer('num_threads', None, 'Number of threads to run.')\nFLAGS = flags.FLAGS\n\n\ndef create_tf_example(image,\n                      image_dir,\n                      bbox_annotations=None,\n                      category_index=None,\n                      caption_annotations=None,\n                      include_masks=False):\n  \"\"\"Converts image and annotations to a tf.Example proto.\n\n  Args:\n    image: dict with keys: [u'license', u'file_name', u'coco_url', u'height',\n      u'width', u'date_captured', u'flickr_url', u'id']\n    image_dir: directory containing the image files.\n    bbox_annotations:\n      list of dicts with keys: [u'segmentation', u'area', u'iscrowd',\n        u'image_id', u'bbox', u'category_id', u'id'] Notice that bounding box\n        coordinates in the official COCO dataset are given as [x, y, width,\n        height] tuples using absolute coordinates where x, y represent the\n        top-left (0-indexed) corner.  This function converts to the format\n        expected by the Tensorflow Object Detection API (which is which is\n        [ymin, xmin, ymax, xmax] with coordinates normalized relative to image\n        size).\n    category_index: a dict containing COCO category information keyed by the\n      'id' field of each category.  See the label_map_util.create_category_index\n      function.\n    caption_annotations:\n      list of dict with keys: [u'id', u'image_id', u'str'].\n    include_masks: Whether to include instance segmentations masks\n      (PNG encoded) in the result. default: False.\n\n  Returns:\n    example: The converted tf.Example\n    num_annotations_skipped: Number of (invalid) annotations that were ignored.\n\n  Raises:\n    ValueError: if the image pointed to by data['filename'] is not a valid JPEG\n  \"\"\"\n  image_height = image['height']\n  image_width = image['width']\n  filename = image['file_name']\n  image_id = image['id']\n\n  full_path = os.path.join(image_dir, filename)\n  with tf.io.gfile.GFile(full_path, 'rb') as fid:\n    encoded_jpg = fid.read()\n  encoded_jpg_io = io.BytesIO(encoded_jpg)\n  image = PIL.Image.open(encoded_jpg_io)\n  key = hashlib.sha256(encoded_jpg).hexdigest()\n  feature_dict = {\n      'image/height':\n          tfrecord_util.int64_feature(image_height),\n      'image/width':\n          tfrecord_util.int64_feature(image_width),\n      'image/filename':\n          tfrecord_util.bytes_feature(filename.encode('utf8')),\n      'image/source_id':\n          tfrecord_util.bytes_feature(str(image_id).encode('utf8')),\n      'image/key/sha256':\n          tfrecord_util.bytes_feature(key.encode('utf8')),\n      'image/encoded':\n          tfrecord_util.bytes_feature(encoded_jpg),\n      'image/format':\n          tfrecord_util.bytes_feature('jpeg'.encode('utf8')),\n  }\n\n  num_annotations_skipped = 0\n  if bbox_annotations:\n    xmin = []\n    xmax = []\n    ymin = []\n    ymax = []\n    is_crowd = []\n    category_names = []\n    category_ids = []\n    area = []\n    encoded_mask_png = []\n    for object_annotations in bbox_annotations:\n      (x, y, width, height) = tuple(object_annotations['bbox'])\n      if width <= 0 or height <= 0:\n        num_annotations_skipped += 1\n        continue\n      if x + width > image_width or y + height > image_height:\n        num_annotations_skipped += 1\n        continue\n      xmin.append(float(x) / image_width)\n      xmax.append(float(x + width) / image_width)\n      ymin.append(float(y) / image_height)\n      ymax.append(float(y + height) / image_height)\n      is_crowd.append(object_annotations['iscrowd'])\n      category_id = int(object_annotations['category_id'])\n      category_ids.append(category_id)\n      category_names.append(category_index[category_id]['name'].encode('utf8'))\n      area.append(object_annotations['area'])\n\n      if include_masks:\n        run_len_encoding = mask.frPyObjects(object_annotations['segmentation'],\n                                            image_height, image_width)\n        binary_mask = mask.decode(run_len_encoding)\n        if not object_annotations['iscrowd']:\n          binary_mask = np.amax(binary_mask, axis=2)\n        pil_image = PIL.Image.fromarray(binary_mask)\n        output_io = io.BytesIO()\n        pil_image.save(output_io, format='PNG')\n        encoded_mask_png.append(output_io.getvalue())\n    feature_dict.update({\n        'image/object/bbox/xmin':\n            tfrecord_util.float_list_feature(xmin),\n        'image/object/bbox/xmax':\n            tfrecord_util.float_list_feature(xmax),\n        'image/object/bbox/ymin':\n            tfrecord_util.float_list_feature(ymin),\n        'image/object/bbox/ymax':\n            tfrecord_util.float_list_feature(ymax),\n        'image/object/class/text':\n            tfrecord_util.bytes_list_feature(category_names),\n        'image/object/class/label':\n            tfrecord_util.int64_list_feature(category_ids),\n        'image/object/is_crowd':\n            tfrecord_util.int64_list_feature(is_crowd),\n        'image/object/area':\n            tfrecord_util.float_list_feature(area),\n    })\n    if include_masks:\n      feature_dict['image/object/mask'] = (\n          tfrecord_util.bytes_list_feature(encoded_mask_png))\n  if caption_annotations:\n    captions = []\n    for caption_annotation in caption_annotations:\n      captions.append(caption_annotation['caption'].encode('utf8'))\n    feature_dict.update(\n        {'image/caption': tfrecord_util.bytes_list_feature(captions)})\n\n  example = tf.train.Example(features=tf.train.Features(feature=feature_dict))\n  return key, example, num_annotations_skipped\n\n\ndef _pool_create_tf_example(args):\n  return create_tf_example(*args)\n\n\ndef _load_object_annotations(object_annotations_file):\n  \"\"\"Loads object annotation JSON file.\"\"\"\n  with tf.io.gfile.GFile(object_annotations_file, 'r') as fid:\n    obj_annotations = json.load(fid)\n\n  images = obj_annotations['images']\n  category_index = label_map_util.create_category_index(\n      obj_annotations['categories'])\n\n  img_to_obj_annotation = collections.defaultdict(list)\n  logging.info('Building bounding box index.')\n  for annotation in obj_annotations['annotations']:\n    image_id = annotation['image_id']\n    img_to_obj_annotation[image_id].append(annotation)\n\n  missing_annotation_count = 0\n  for image in images:\n    image_id = image['id']\n    if image_id not in img_to_obj_annotation:\n      missing_annotation_count += 1\n\n  logging.info('%d images are missing bboxes.', missing_annotation_count)\n\n  return img_to_obj_annotation, category_index\n\n\ndef _load_caption_annotations(caption_annotations_file):\n  \"\"\"Loads caption annotation JSON file.\"\"\"\n  with tf.io.gfile.GFile(caption_annotations_file, 'r') as fid:\n    caption_annotations = json.load(fid)\n\n  img_to_caption_annotation = collections.defaultdict(list)\n  logging.info('Building caption index.')\n  for annotation in caption_annotations['annotations']:\n    image_id = annotation['image_id']\n    img_to_caption_annotation[image_id].append(annotation)\n\n  missing_annotation_count = 0\n  images = caption_annotations['images']\n  for image in images:\n    image_id = image['id']\n    if image_id not in img_to_caption_annotation:\n      missing_annotation_count += 1\n\n  logging.info('%d images are missing captions.', missing_annotation_count)\n\n  return img_to_caption_annotation\n\n\ndef _load_images_info(image_info_file):\n  with tf.io.gfile.GFile(image_info_file, 'r') as fid:\n    info_dict = json.load(fid)\n  return info_dict['images']\n\n\ndef _create_tf_record_from_coco_annotations(image_info_file,\n                                            image_dir,\n                                            output_path,\n                                            num_shards,\n                                            object_annotations_file=None,\n                                            caption_annotations_file=None,\n                                            include_masks=False):\n  \"\"\"Loads COCO annotation json files and converts to tf.Record format.\n\n  Args:\n    image_info_file: JSON file containing image info. The number of tf.Examples\n      in the output tf Record files is exactly equal to the number of image info\n      entries in this file. This can be any of train/val/test annotation json\n      files Eg. 'image_info_test-dev2017.json',\n      'instance_annotations_train2017.json',\n      'caption_annotations_train2017.json', etc.\n    image_dir: Directory containing the image files.\n    output_path: Path to output tf.Record file.\n    num_shards: Number of output files to create.\n    object_annotations_file: JSON file containing bounding box annotations.\n    caption_annotations_file: JSON file containing caption annotations.\n    include_masks: Whether to include instance segmentations masks\n      (PNG encoded) in the result. default: False.\n  \"\"\"\n\n  logging.info('writing to output path: %s', output_path)\n  writers = [\n      tf.io.TFRecordWriter(output_path + '-%05d-of-%05d.tfrecord' %\n                           (i, num_shards)) for i in range(num_shards)\n  ]\n  images = _load_images_info(image_info_file)\n\n  img_to_obj_annotation = None\n  img_to_caption_annotation = None\n  category_index = None\n  if object_annotations_file:\n    img_to_obj_annotation, category_index = (\n        _load_object_annotations(object_annotations_file))\n  if caption_annotations_file:\n    img_to_caption_annotation = (\n        _load_caption_annotations(caption_annotations_file))\n\n  def _get_object_annotation(image_id):\n    if img_to_obj_annotation:\n      return img_to_obj_annotation[image_id]\n    else:\n      return None\n\n  def _get_caption_annotation(image_id):\n    if img_to_caption_annotation:\n      return img_to_caption_annotation[image_id]\n    else:\n      return None\n\n  pool = multiprocessing.Pool(FLAGS.num_threads)\n  total_num_annotations_skipped = 0\n  for idx, (_, tf_example, num_annotations_skipped) in enumerate(\n      pool.imap(\n          _pool_create_tf_example,\n          [(image, image_dir, _get_object_annotation(image['id']),\n            category_index, _get_caption_annotation(image['id']), include_masks)\n           for image in images])):\n    if idx % 100 == 0:\n      logging.info('On image %d of %d', idx, len(images))\n\n    total_num_annotations_skipped += num_annotations_skipped\n    writers[idx % num_shards].write(tf_example.SerializeToString())\n\n  pool.close()\n  pool.join()\n\n  for writer in writers:\n    writer.close()\n\n  logging.info('Finished writing, skipped %d annotations.',\n               total_num_annotations_skipped)\n\n\ndef main(_):\n  assert FLAGS.image_dir, '`image_dir` missing.'\n  assert (FLAGS.image_info_file or FLAGS.object_annotations_file or\n          FLAGS.caption_annotations_file), ('All annotation files are '\n                                            'missing.')\n  if FLAGS.image_info_file:\n    image_info_file = FLAGS.image_info_file\n  elif FLAGS.object_annotations_file:\n    image_info_file = FLAGS.object_annotations_file\n  else:\n    image_info_file = FLAGS.caption_annotations_file\n\n  directory = os.path.dirname(FLAGS.output_file_prefix)\n  if not tf.io.gfile.isdir(directory):\n    tf.io.gfile.mkdir(directory)\n\n  _create_tf_record_from_coco_annotations(image_info_file, FLAGS.image_dir,\n                                          FLAGS.output_file_prefix,\n                                          FLAGS.num_shards,\n                                          FLAGS.object_annotations_file,\n                                          FLAGS.caption_annotations_file,\n                                          FLAGS.include_masks)\n\n\nif __name__ == '__main__':\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/dataset/create_pascal_tfrecord.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\nr\"\"\"Convert PASCAL dataset to TFRecord.\n\nExample usage:\n    python create_pascal_tfrecord.py  --data_dir=/tmp/VOCdevkit  \\\n        --year=VOC2012  --output_path=/tmp/pascal\n\"\"\"\nimport hashlib\nimport io\nimport json\nimport os\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\n\nfrom lxml import etree\nimport PIL.Image\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.dataset import tfrecord_util\n\nFLAGS = flags.FLAGS\n\nSETS = ['train', 'val', 'trainval', 'test']\nYEARS = ['VOC2007', 'VOC2012', 'merged']\n\npascal_label_map_dict = {\n    'background': 0,\n    'aeroplane': 1,\n    'bicycle': 2,\n    'bird': 3,\n    'boat': 4,\n    'bottle': 5,\n    'bus': 6,\n    'car': 7,\n    'cat': 8,\n    'chair': 9,\n    'cow': 10,\n    'diningtable': 11,\n    'dog': 12,\n    'horse': 13,\n    'motorbike': 14,\n    'person': 15,\n    'pottedplant': 16,\n    'sheep': 17,\n    'sofa': 18,\n    'train': 19,\n    'tvmonitor': 20,\n}\n\n\ndef define_flags():\n  \"\"\"Define the flags.\"\"\"\n  flags.DEFINE_string('data_dir', '',\n                      'Root directory to raw PASCAL VOC dataset.')\n  flags.DEFINE_string('set', 'train', 'Convert training set, validation set or '\n                      'merged set.')\n  flags.DEFINE_string('annotations_dir', 'Annotations',\n                      '(Relative) path to annotations directory.')\n  flags.DEFINE_string('year', 'VOC2007', 'Desired challenge year.')\n  flags.DEFINE_string('output_path', '', 'Path to output TFRecord and json.')\n  flags.DEFINE_string('label_map_json_path', None,\n                      'Path to label map json file with a dictionary.')\n  flags.DEFINE_boolean('ignore_difficult_instances', False, 'Whether to ignore '\n                       'difficult instances')\n  flags.DEFINE_integer('num_shards', 100, 'Number of shards for output file.')\n  flags.DEFINE_integer('num_images', None, 'Max number of imags to process.')\n\n\nclass UniqueId(object):\n  \"\"\"Class to get the unique {image/ann}_id each time calling the functions.\"\"\"\n\n  def __init__(self):\n    self.image_id = 0\n    self.ann_id = 0\n\n  def get_image_id(self):\n    self.image_id += 1\n    return self.image_id\n\n  def get_ann_id(self):\n    self.ann_id += 1\n    return self.ann_id\n\n\ndef dict_to_tf_example(data,\n                       images_dir,\n                       label_map_dict,\n                       unique_id,\n                       ignore_difficult_instances=False,\n                       ann_json_dict=None):\n  \"\"\"Convert XML derived dict to tf.Example proto.\n\n  Notice that this function normalizes the bounding box coordinates provided\n  by the raw data.\n\n  Args:\n    data: dict holding PASCAL XML fields for a single image (obtained by running\n      tfrecord_util.recursive_parse_xml_to_dict)\n    images_dir: Path to the directory holding raw images.\n    label_map_dict: A map from string label names to integers ids.\n    unique_id: UniqueId object to get the unique {image/ann}_id for the image\n      and the annotations.\n    ignore_difficult_instances: Whether to skip difficult instances in the\n      dataset  (default: False).\n    ann_json_dict: annotation json dictionary.\n\n  Returns:\n    example: The converted tf.Example.\n\n  Raises:\n    ValueError: if the image pointed to by data['filename'] is not a valid JPEG\n  \"\"\"\n  full_path = os.path.join(images_dir, data['filename'])\n  with tf.io.gfile.GFile(full_path, 'rb') as fid:\n    encoded_jpg = fid.read()\n  encoded_jpg_io = io.BytesIO(encoded_jpg)\n  image = PIL.Image.open(encoded_jpg_io)\n  if image.format != 'JPEG':\n    raise ValueError('Image format not JPEG')\n  key = hashlib.sha256(encoded_jpg).hexdigest()\n\n  image_id = unique_id.get_image_id()\n\n  width = int(data['size']['width'])\n  height = int(data['size']['height'])\n  if ann_json_dict:\n    image = {\n        'file_name': data['filename'],\n        'height': height,\n        'width': width,\n        'id': image_id,\n    }\n    ann_json_dict['images'].append(image)\n\n  xmin = []\n  ymin = []\n  xmax = []\n  ymax = []\n  area = []\n  classes = []\n  classes_text = []\n  truncated = []\n  poses = []\n  difficult_obj = []\n  if 'object' in data:\n    for obj in data['object']:\n      if obj['difficult'] == 'Unspecified':\n        difficult = False\n      else:\n        difficult = bool(int(obj['difficult']))\n      if ignore_difficult_instances and difficult:\n        continue\n\n      difficult_obj.append(int(difficult))\n\n      xmin.append(float(obj['bndbox']['xmin']) / width)\n      ymin.append(float(obj['bndbox']['ymin']) / height)\n      xmax.append(float(obj['bndbox']['xmax']) / width)\n      ymax.append(float(obj['bndbox']['ymax']) / height)\n      area.append((xmax[-1] - xmin[-1]) * (ymax[-1] - ymin[-1]))\n      classes_text.append(obj['name'].encode('utf8'))\n      classes.append(label_map_dict[obj['name']])\n      if obj['truncated'] == 'Unspecified':\n        truncated.append(0)\n      else:\n        truncated.append(int(obj['truncated']))\n      poses.append(obj['pose'].encode('utf8'))\n\n      if ann_json_dict:\n        abs_xmin = int(obj['bndbox']['xmin'])\n        abs_ymin = int(obj['bndbox']['ymin'])\n        abs_xmax = int(obj['bndbox']['xmax'])\n        abs_ymax = int(obj['bndbox']['ymax'])\n        abs_width = abs_xmax - abs_xmin\n        abs_height = abs_ymax - abs_ymin\n        ann = {\n            'area': abs_width * abs_height,\n            'iscrowd': 0,\n            'image_id': image_id,\n            'bbox': [abs_xmin, abs_ymin, abs_width, abs_height],\n            'category_id': label_map_dict[obj['name']],\n            'id': unique_id.get_ann_id(),\n            'ignore': 0,\n            'segmentation': [],\n        }\n        ann_json_dict['annotations'].append(ann)\n\n  example = tf.train.Example(\n      features=tf.train.Features(\n          feature={\n              'image/height':\n                  tfrecord_util.int64_feature(height),\n              'image/width':\n                  tfrecord_util.int64_feature(width),\n              'image/filename':\n                  tfrecord_util.bytes_feature(data['filename'].encode('utf8')),\n              'image/source_id':\n                  tfrecord_util.bytes_feature(str(image_id).encode('utf8')),\n              'image/key/sha256':\n                  tfrecord_util.bytes_feature(key.encode('utf8')),\n              'image/encoded':\n                  tfrecord_util.bytes_feature(encoded_jpg),\n              'image/format':\n                  tfrecord_util.bytes_feature('jpeg'.encode('utf8')),\n              'image/object/bbox/xmin':\n                  tfrecord_util.float_list_feature(xmin),\n              'image/object/bbox/xmax':\n                  tfrecord_util.float_list_feature(xmax),\n              'image/object/bbox/ymin':\n                  tfrecord_util.float_list_feature(ymin),\n              'image/object/bbox/ymax':\n                  tfrecord_util.float_list_feature(ymax),\n              'image/object/area':\n                  tfrecord_util.float_list_feature(area),\n              'image/object/class/text':\n                  tfrecord_util.bytes_list_feature(classes_text),\n              'image/object/class/label':\n                  tfrecord_util.int64_list_feature(classes),\n              'image/object/difficult':\n                  tfrecord_util.int64_list_feature(difficult_obj),\n              'image/object/truncated':\n                  tfrecord_util.int64_list_feature(truncated),\n              'image/object/view':\n                  tfrecord_util.bytes_list_feature(poses),\n          }))\n  return example\n\n\ndef main(_):\n  if FLAGS.set not in SETS:\n    raise ValueError('set must be in : {}'.format(SETS))\n  if FLAGS.year not in YEARS:\n    raise ValueError('year must be in : {}'.format(YEARS))\n  if not FLAGS.output_path:\n    raise ValueError('output_path cannot be empty.')\n\n  data_dir = FLAGS.data_dir\n  years = ['VOC2007', 'VOC2012']\n  if FLAGS.year != 'merged':\n    years = [FLAGS.year]\n\n  output_dir = os.path.dirname(FLAGS.output_path)\n  if not tf.io.gfile.exists(output_dir):\n    tf.io.gfile.makedirs(output_dir)\n  logging.info('Writing to output directory: %s', output_dir)\n\n  writers = [\n      tf.io.TFRecordWriter(FLAGS.output_path + '-%05d-of-%05d.tfrecord' %\n                           (i, FLAGS.num_shards))\n      for i in range(FLAGS.num_shards)\n  ]\n\n  if FLAGS.label_map_json_path:\n    with tf.io.gfile.GFile(FLAGS.label_map_json_path, 'rb') as f:\n      label_map_dict = json.load(f)\n  else:\n    label_map_dict = pascal_label_map_dict\n\n  ann_json_dict = {\n      'images': [],\n      'type': 'instances',\n      'annotations': [],\n      'categories': []\n  }\n\n  unique_id = UniqueId()\n  for year in years:\n    example_class = list(label_map_dict.keys())[1]\n    examples_path = os.path.join(data_dir, year, 'ImageSets', 'Main',\n                                 example_class + '_' + FLAGS.set + '.txt')\n    examples_list = tfrecord_util.read_examples_list(examples_path)\n    annotations_dir = os.path.join(data_dir, year, FLAGS.annotations_dir)\n\n    for class_name, class_id in label_map_dict.items():\n      cls = {'supercategory': 'none', 'id': class_id, 'name': class_name}\n      ann_json_dict['categories'].append(cls)\n\n    logging.info('Reading from PASCAL %s dataset.', year)\n    for idx, example in enumerate(examples_list):\n      if FLAGS.num_images and idx >= FLAGS.num_images:\n        break\n      if idx % 100 == 0:\n        logging.info('On image %d of %d', idx, len(examples_list))\n      path = os.path.join(annotations_dir, example + '.xml')\n      with tf.io.gfile.GFile(path, 'r') as fid:\n        xml_str = fid.read()\n      xml = etree.fromstring(xml_str)\n      data = tfrecord_util.recursive_parse_xml_to_dict(xml)['annotation']\n\n      img_dir = os.path.join(FLAGS.data_dir, data['folder'], 'JPEGImages')\n\n      tf_example = dict_to_tf_example(\n          data,\n          img_dir,\n          label_map_dict,\n          unique_id,\n          FLAGS.ignore_difficult_instances,\n          ann_json_dict=ann_json_dict)\n      writers[idx % FLAGS.num_shards].write(tf_example.SerializeToString())\n\n  for writer in writers:\n    writer.close()\n\n  json_file_path = os.path.join(\n      os.path.dirname(FLAGS.output_path),\n      'json_' + os.path.basename(FLAGS.output_path) + '.json')\n  with tf.io.gfile.GFile(json_file_path, 'w') as f:\n    json.dump(ann_json_dict, f)\n\n\nif __name__ == '__main__':\n  define_flags()\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/dataset/inspect_tfrecords.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Inspect tfrecord dataset.\"\"\"\nimport os\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\nimport numpy as np\nfrom PIL import Image\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import dataloader\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import hparams_config\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.visualize import vis_utils\n\nflags.DEFINE_string('save_samples_dir', 'tfrecord_samples',\n                    'Location of samples to save')\nflags.DEFINE_string('model_name', 'efficientdet-d0',\n                    'model name for config and image_size')\nflags.DEFINE_string(\n    'hparams', '', 'Comma separated k=v pairs of hyperparameters or a module'\n    ' containing attributes to use as hyperparameters.')\nflags.DEFINE_integer('samples', 10,\n                     'Number of random samples for visualization.')\nflags.DEFINE_string('file_pattern', None,\n                    'Glob for data files (e.g., COCO train - minival set)')\nflags.DEFINE_bool('eval', False, 'flag for file pattern mode i.e eval')\nFLAGS = flags.FLAGS\n\n\nclass RecordInspect:\n  \"\"\"Inspection Class.\"\"\"\n\n  def __init__(self, config):\n    \"\"\"Initializes RecordInspect with passed config.\n\n    Args:\n        config: config file to initialize input_fn.\n    \"\"\"\n    self.input_fn = dataloader.InputReader(\n        FLAGS.file_pattern,\n        is_training=not FLAGS.eval,\n        use_fake_data=False,\n        max_instances_per_image=config.max_instances_per_image)\n\n    self.params = dict(\n        config.as_dict(), batch_size=FLAGS.samples, model_name=FLAGS.model_name)\n    logging.info(self.params)\n    self.cls_to_label = config.label_map\n    os.makedirs(FLAGS.save_samples_dir, exist_ok=True)\n\n  def visualize(self):\n    \"\"\"save tfrecords images with bounding boxes.\"\"\"\n    vis_ds = self.input_fn(params=self.params)\n    data = next(iter(vis_ds))  # iterable.\n    images = data[0]\n    gt_data = data[1]['groundtruth_data']\n\n    # scales\n    scale_to_org = data[1]['image_scales']\n    scales = 1.0 / scale_to_org\n    offset = tf.constant([0.485, 0.456, 0.406])\n    offset = tf.reshape(offset, (1, 1, -1))\n    scale_image = tf.constant([0.229, 0.224, 0.225])\n    scale_image = tf.reshape(scale_image, (1, 1, -1))\n\n    logging.info('Visualizing TfRecords %s', FLAGS.file_pattern)\n    for i, zip_data in enumerate(zip(gt_data, images, scales)):\n      gt, image, scale = zip_data\n      boxes = gt[:, :4]\n      boxes = boxes[np.any(boxes > 0, axis=1)].numpy()\n      if boxes.shape[0] > 0:\n        classes = gt[:boxes.shape[0], -1].numpy()\n        try:\n          category_index = {idx: {'id': idx, 'name': self.cls_to_label[idx]}\n                            for idx in np.asarray(classes, dtype=int)}\n        except Exception:  # pylint: disable=broad-except\n          category_index = {}\n\n        # unnormalize image.\n        image *= scale_image\n        image += offset\n\n        # 0-255. range\n        image = np.asarray(image.numpy() * 255., dtype=np.uint8)\n\n        # scale to image_size\n        boxes *= scale.numpy()\n\n        image = vis_utils.visualize_boxes_and_labels_on_image_array(\n            image,\n            boxes=boxes,\n            classes=classes.astype(int),\n            scores=np.ones(boxes.shape[0]),\n            category_index=category_index,\n            line_thickness=2,\n            skip_scores=True)\n        image = Image.fromarray(image)\n        image.save(os.path.join(FLAGS.save_samples_dir, f'sample{i}.jpg'))\n\n\ndef main(_):\n  # Parse and override hparams\n  config = hparams_config.get_detection_config(FLAGS.model_name)\n  config.override(FLAGS.hparams)\n\n  # Parse image size in case it is in string format.\n  config.image_size = utils.parse_image_size(config.image_size)\n  try:\n    recordinspect = RecordInspect(config)\n    recordinspect.visualize()\n  except Exception as e:  # pylint: disable=broad-except\n    logging.error(e)\n  else:\n    logging.info('Done, please find samples at %s', FLAGS.save_samples_dir)\n\n\nif __name__ == '__main__':\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/dataset/label_map_util.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Label map utility functions.\"\"\"\nfrom absl import logging\nfrom six.moves import range\n\n\ndef _validate_label_map(label_map):\n  \"\"\"Checks if a label map is valid.\n\n  Args:\n    label_map: StringIntLabelMap to validate.\n\n  Raises:\n    ValueError: if label map is invalid.\n  \"\"\"\n  for item in label_map.item:\n    if item.id < 0:\n      raise ValueError('Label map ids should be >= 0.')\n    if (item.id == 0 and item.name != 'background' and\n        item.display_name != 'background'):\n      raise ValueError('Label map id 0 is reserved for the background label')\n\n\ndef create_category_index(categories):\n  \"\"\"Creates dictionary of COCO compatible categories keyed by category id.\n\n  Args:\n    categories: a list of dicts, each of which has the following keys:\n      'id': (required) an integer id uniquely identifying this category.\n      'name': (required) string representing category name\n        e.g., 'cat', 'dog', 'pizza'.\n\n  Returns:\n    category_index: a dict containing the same entries as categories, but keyed\n      by the 'id' field of each category.\n  \"\"\"\n  category_index = {}\n  for cat in categories:\n    category_index[cat['id']] = cat\n  return category_index\n\n\ndef get_max_label_map_index(label_map):\n  \"\"\"Get maximum index in label map.\n\n  Args:\n    label_map: a StringIntLabelMapProto\n\n  Returns:\n    an integer\n  \"\"\"\n  return max([item.id for item in label_map.item])\n\n\ndef convert_label_map_to_categories(label_map,\n                                    max_num_classes,\n                                    use_display_name=True):\n  \"\"\"Given label map proto returns categories list compatible with eval.\n\n  This function converts label map proto and returns a list of dicts, each of\n  which  has the following keys:\n    'id': (required) an integer id uniquely identifying this category.\n    'name': (required) string representing category name\n      e.g., 'cat', 'dog', 'pizza'.\n    'keypoints': (optional) a dictionary of keypoint string 'label' to integer\n      'id'.\n  We only allow class into the list if its id-label_id_offset is\n  between 0 (inclusive) and max_num_classes (exclusive).\n  If there are several items mapping to the same id in the label map,\n  we will only keep the first one in the categories list.\n\n  Args:\n    label_map: a StringIntLabelMapProto or None.  If None, a default categories\n      list is created with max_num_classes categories.\n    max_num_classes: maximum number of (consecutive) label indices to include.\n    use_display_name: (boolean) choose whether to load 'display_name' field as\n      category name.  If False or if the display_name field does not exist, uses\n      'name' field as category names instead.\n\n  Returns:\n    categories: a list of dictionaries representing all possible categories.\n  \"\"\"\n  categories = []\n  list_of_ids_already_added = []\n  if not label_map:\n    label_id_offset = 1\n    for class_id in range(max_num_classes):\n      categories.append({\n          'id': class_id + label_id_offset,\n          'name': 'category_{}'.format(class_id + label_id_offset)\n      })\n    return categories\n  for item in label_map.item:\n    if not 0 < item.id <= max_num_classes:\n      logging.info(\n          'Ignore item %d since it falls outside of requested '\n          'label range.', item.id)\n      continue\n    if use_display_name and item.HasField('display_name'):\n      name = item.display_name\n    else:\n      name = item.name\n    if item.id not in list_of_ids_already_added:\n      list_of_ids_already_added.append(item.id)\n      category = {'id': item.id, 'name': name}\n      if item.keypoints:\n        keypoints = {}\n        list_of_keypoint_ids = []\n        for kv in item.keypoints:\n          if kv.id in list_of_keypoint_ids:\n            raise ValueError('Duplicate keypoint ids are not allowed. '\n                             'Found {} more than once'.format(kv.id))\n          keypoints[kv.label] = kv.id\n          list_of_keypoint_ids.append(kv.id)\n        category['keypoints'] = keypoints\n      categories.append(category)\n  return categories\n\n\ndef create_class_agnostic_category_index():\n  \"\"\"Creates a category index with a single `object` class.\"\"\"\n  return {1: {'id': 1, 'name': 'object'}}\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/dataset/tfrecord_util.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\nr\"\"\"TFRecord related utilities.\"\"\"\nfrom six.moves import range\nimport tensorflow as tf\n\n\ndef int64_feature(value):\n  return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))\n\n\ndef int64_list_feature(value):\n  return tf.train.Feature(int64_list=tf.train.Int64List(value=value))\n\n\ndef bytes_feature(value):\n  return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))\n\n\ndef bytes_list_feature(value):\n  return tf.train.Feature(bytes_list=tf.train.BytesList(value=value))\n\n\ndef float_list_feature(value):\n  return tf.train.Feature(float_list=tf.train.FloatList(value=value))\n\n\ndef read_examples_list(path):\n  \"\"\"Read list of training or validation examples.\n\n  The file is assumed to contain a single example per line where the first\n  token in the line is an identifier that allows us to find the image and\n  annotation xml for that example.\n\n  For example, the line:\n  xyz 3\n  would allow us to find files xyz.jpg and xyz.xml (the 3 would be ignored).\n\n  Args:\n    path: absolute path to examples list file.\n\n  Returns:\n    list of example identifiers (strings).\n  \"\"\"\n  with tf.io.gfile.GFile(path) as fid:\n    lines = fid.readlines()\n  return [line.strip().split(' ')[0] for line in lines]\n\n\ndef recursive_parse_xml_to_dict(xml):\n  \"\"\"Recursively parses XML contents to python dict.\n\n  We assume that `object` tags are the only ones that can appear\n  multiple times at the same level of a tree.\n\n  Args:\n    xml: xml tree obtained by parsing XML file contents using lxml.etree\n\n  Returns:\n    Python dictionary holding XML contents.\n  \"\"\"\n  if not len(xml):  # pylint: disable=g-explicit-length-test\n    return {xml.tag: xml.text if xml.text else ''}\n  result = {}\n  for child in xml:\n    child_result = recursive_parse_xml_to_dict(child)\n    if child.tag != 'object':\n      result[child.tag] = child_result[child.tag]\n    else:\n      if child.tag not in result:\n        result[child.tag] = []\n      result[child.tag].append(child_result[child.tag])\n  return {xml.tag: result}\n\n\ndef open_sharded_output_tfrecords(exit_stack, base_path, num_shards):\n  \"\"\"Opens all TFRecord shards for writing and adds them to an exit stack.\n\n  Args:\n    exit_stack: A context2.ExitStack used to automatically closed the TFRecords\n      opened in this function.\n    base_path: The base path for all shards\n    num_shards: The number of shards\n\n  Returns:\n    The list of opened TFRecords. Position k in the list corresponds to shard k.\n  \"\"\"\n  tf_record_output_filenames = [\n      '{}-{:05d}-of-{:05d}'.format(base_path, idx, num_shards)\n      for idx in range(num_shards)\n  ]\n\n  tfrecords = [\n      exit_stack.enter_context(tf.io.TFRecordWriter(file_name))\n      for file_name in tf_record_output_filenames\n  ]\n\n  return tfrecords\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/det_advprop_tutorial.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"V8-yl-s-WKMG\"\n      },\n      \"source\": [\n        \"# Det-AdvProp Tutorial: eval \\n\",\n        \"\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"\\u003ctable align=\\\"left\\\"\\u003e\\u003ctd\\u003e\\n\",\n        \"  \\u003ca target=\\\"_blank\\\"  href=\\\"https://github.com/google/automl/blob/master/efficientdet/Det-AdvProp-tutorial.ipynb\\\"\\u003e\\n\",\n        \"    \\u003cimg src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" /\\u003eView source on github\\n\",\n        \"  \\u003c/a\\u003e\\n\",\n        \"\\u003c/td\\u003e\\u003ctd\\u003e\\n\",\n        \"  \\u003ca target=\\\"_blank\\\"  href=\\\"https://colab.sandbox.google.com/github/google/automl/blob/master/efficientdet/det-advprop-tutorial.ipynb\\\"\\u003e\\n\",\n        \"    \\u003cimg width=32px src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" /\\u003eRun in Google Colab\\u003c/a\\u003e\\n\",\n        \"\\u003c/td\\u003e\\u003c/table\\u003e\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"muwOCNHaq85j\"\n      },\n      \"source\": [\n        \"# 0. Install and view graph.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"dggLVarNxxvC\"\n      },\n      \"source\": [\n        \"## 0.1 Install package and download source code/image.\\n\",\n        \"\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"hGL97-GXjSUw\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"%%capture\\n\",\n        \"#@title\\n\",\n        \"import os\\n\",\n        \"import sys\\n\",\n        \"import tensorflow.compat.v1 as tf\\n\",\n        \"\\n\",\n        \"# Download source code.\\n\",\n        \"if \\\"efficientdet\\\" not in os.getcwd():\\n\",\n        \"  !git clone --depth 1 https://github.com/google/automl\\n\",\n        \"  os.chdir('automl/efficientdet')\\n\",\n        \"  sys.path.append('.')\\n\",\n        \"  !pip install -r requirements.txt\\n\",\n        \"  !pip install -U 'git+https://github.com/cocodataset/cocoapi.git#subdirectory=PythonAPI'\\n\",\n        \"else:\\n\",\n        \"  !git pull\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\"\n        },\n        \"id\": \"Tow-ic7H3d7i\",\n        \"outputId\": \"0f9abcf0-9b39-4bf6-9cf5-f98d6d21ff26\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"--2021-05-10 20:07:35--  https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/advprop/efficientdet-d1.tar.gz\\n\",\n            \"Resolving storage.googleapis.com (storage.googleapis.com)... 74.125.71.128, 64.233.184.128, 74.125.133.128, ...\\n\",\n            \"Connecting to storage.googleapis.com (storage.googleapis.com)|74.125.71.128|:443... connected.\\n\",\n            \"HTTP request sent, awaiting response... 200 OK\\n\",\n            \"Length: 149546361 (143M) [application/octet-stream]\\n\",\n            \"Saving to: ‘efficientdet-d1.tar.gz’\\n\",\n            \"\\n\",\n            \"efficientdet-d1.tar 100%[===================\\u003e] 142.62M  39.4MB/s    in 3.6s    \\n\",\n            \"\\n\",\n            \"2021-05-10 20:07:40 (39.4 MB/s) - ‘efficientdet-d1.tar.gz’ saved [149546361/149546361]\\n\",\n            \"\\n\",\n            \"Use model in /content/automl/efficientdet/efficientdet-d1\\n\",\n            \"--2021-05-10 20:07:42--  https://user-images.githubusercontent.com/11736571/77320690-099af300-6d37-11ea-9d86-24f14dc2d540.png\\n\",\n            \"Resolving user-images.githubusercontent.com (user-images.githubusercontent.com)... 185.199.109.133, 185.199.111.133, 185.199.108.133, ...\\n\",\n            \"Connecting to user-images.githubusercontent.com (user-images.githubusercontent.com)|185.199.109.133|:443... connected.\\n\",\n            \"HTTP request sent, awaiting response... 200 OK\\n\",\n            \"Length: 4080549 (3.9M) [image/png]\\n\",\n            \"Saving to: ‘img.png’\\n\",\n            \"\\n\",\n            \"img.png             100%[===================\\u003e]   3.89M  --.-KB/s    in 0.1s    \\n\",\n            \"\\n\",\n            \"2021-05-10 20:07:42 (38.4 MB/s) - ‘img.png’ saved [4080549/4080549]\\n\",\n            \"\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"MODEL = 'efficientdet-d1'  #@param\\n\",\n        \"\\n\",\n        \"def download(m):\\n\",\n        \"  if m not in os.listdir():\\n\",\n        \"    !wget https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/advprop/{m}.tar.gz\\n\",\n        \"    !tar zxf {m}.tar.gz\\n\",\n        \"  ckpt_path = os.path.join(os.getcwd(), m)\\n\",\n        \"  return ckpt_path\\n\",\n        \"\\n\",\n        \"# Download checkpoint.\\n\",\n        \"ckpt_path = download(MODEL)\\n\",\n        \"print('Use model in {}'.format(ckpt_path))\\n\",\n        \"\\n\",\n        \"# Prepare image and visualization settings.\\n\",\n        \"image_url =  'https://user-images.githubusercontent.com/11736571/77320690-099af300-6d37-11ea-9d86-24f14dc2d540.png'#@param\\n\",\n        \"image_name = 'img.png' #@param\\n\",\n        \"!wget {image_url} -O img.png\\n\",\n        \"import os\\n\",\n        \"img_path = os.path.join(os.getcwd(), 'img.png')\\n\",\n        \"\\n\",\n        \"min_score_thresh = 0.35  #@param\\n\",\n        \"max_boxes_to_draw = 200  #@param\\n\",\n        \"line_thickness =   2#@param\\n\",\n        \"\\n\",\n        \"import PIL\\n\",\n        \"# Get the largest of height/width and round to 128.\\n\",\n        \"image_size = max(PIL.Image.open(img_path).size)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"GvdjcYpUVuQ5\"\n      },\n      \"source\": [\n        \"## 0.2 View graph in TensorBoard\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"U2oz3r1LUDzr\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!python model_inspect.py --model_name={MODEL} --logdir=logs \\u0026\\u003e /dev/null\\n\",\n        \"%load_ext tensorboard\\n\",\n        \"%tensorboard --logdir logs\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"RW26DwfirQQN\"\n      },\n      \"source\": [\n        \"# 1. COCO evaluation\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"cfn_tRFOWKMO\"\n      },\n      \"source\": [\n        \"## 1.1 COCO evaluation on validation set.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\"\n        },\n        \"id\": \"2s6E8IsVN0pB\",\n        \"outputId\": \"4e711771-81ca-48c3-c178-ca4228473e6d\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"--2021-05-10 19:43:48--  http://images.cocodataset.org/zips/val2017.zip\\n\",\n            \"Resolving images.cocodataset.org (images.cocodataset.org)... 52.216.27.28\\n\",\n            \"Connecting to images.cocodataset.org (images.cocodataset.org)|52.216.27.28|:80... connected.\\n\",\n            \"HTTP request sent, awaiting response... 200 OK\\n\",\n            \"Length: 815585330 (778M) [application/zip]\\n\",\n            \"Saving to: ‘val2017.zip’\\n\",\n            \"\\n\",\n            \"val2017.zip         100%[===================\\u003e] 777.80M  36.0MB/s    in 22s     \\n\",\n            \"\\n\",\n            \"2021-05-10 19:44:10 (35.1 MB/s) - ‘val2017.zip’ saved [815585330/815585330]\\n\",\n            \"\\n\",\n            \"--2021-05-10 19:44:11--  http://images.cocodataset.org/annotations/annotations_trainval2017.zip\\n\",\n            \"Resolving images.cocodataset.org (images.cocodataset.org)... 52.217.92.164\\n\",\n            \"Connecting to images.cocodataset.org (images.cocodataset.org)|52.217.92.164|:80... connected.\\n\",\n            \"HTTP request sent, awaiting response... 200 OK\\n\",\n            \"Length: 252907541 (241M) [application/zip]\\n\",\n            \"Saving to: ‘annotations_trainval2017.zip’\\n\",\n            \"\\n\",\n            \"annotations_trainva 100%[===================\\u003e] 241.19M  37.0MB/s    in 7.2s    \\n\",\n            \"\\n\",\n            \"2021-05-10 19:44:18 (33.6 MB/s) - ‘annotations_trainval2017.zip’ saved [252907541/252907541]\\n\",\n            \"\\n\",\n            \"Archive:  annotations_trainval2017.zip\\n\",\n            \"  inflating: annotations/instances_train2017.json  \\n\",\n            \"  inflating: annotations/instances_val2017.json  \\n\",\n            \"  inflating: annotations/captions_train2017.json  \\n\",\n            \"  inflating: annotations/captions_val2017.json  \\n\",\n            \"  inflating: annotations/person_keypoints_train2017.json  \\n\",\n            \"  inflating: annotations/person_keypoints_val2017.json  \\n\",\n            \"2021-05-10 19:44:33.210219: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudart.so.11.0\\n\",\n            \"I0510 19:44:35.059431 140632818317184 create_coco_tfrecord.py:285] writing to output path: tfrecord/val\\n\",\n            \"I0510 19:44:35.159756 140632818317184 create_coco_tfrecord.py:237] Building caption index.\\n\",\n            \"I0510 19:44:35.166594 140632818317184 create_coco_tfrecord.py:249] 0 images are missing captions.\\n\",\n            \"I0510 19:44:36.985283 140632818317184 create_coco_tfrecord.py:323] On image 0 of 5000\\n\",\n            \"I0510 19:44:37.145407 140632818317184 create_coco_tfrecord.py:323] On image 100 of 5000\\n\",\n            \"I0510 19:44:37.295032 140632818317184 create_coco_tfrecord.py:323] On image 200 of 5000\\n\",\n            \"I0510 19:44:37.435169 140632818317184 create_coco_tfrecord.py:323] On image 300 of 5000\\n\",\n            \"I0510 19:44:37.580727 140632818317184 create_coco_tfrecord.py:323] On image 400 of 5000\\n\",\n            \"I0510 19:44:37.715295 140632818317184 create_coco_tfrecord.py:323] On image 500 of 5000\\n\",\n            \"I0510 19:44:37.862454 140632818317184 create_coco_tfrecord.py:323] On image 600 of 5000\\n\",\n            \"I0510 19:44:37.993027 140632818317184 create_coco_tfrecord.py:323] On image 700 of 5000\\n\",\n            \"I0510 19:44:38.239493 140632818317184 create_coco_tfrecord.py:323] On image 800 of 5000\\n\",\n            \"I0510 19:44:38.386704 140632818317184 create_coco_tfrecord.py:323] On image 900 of 5000\\n\",\n            \"I0510 19:44:38.555314 140632818317184 create_coco_tfrecord.py:323] On image 1000 of 5000\\n\",\n            \"I0510 19:44:38.714288 140632818317184 create_coco_tfrecord.py:323] On image 1100 of 5000\\n\",\n            \"I0510 19:44:38.871498 140632818317184 create_coco_tfrecord.py:323] On image 1200 of 5000\\n\",\n            \"I0510 19:44:39.004296 140632818317184 create_coco_tfrecord.py:323] On image 1300 of 5000\\n\",\n            \"I0510 19:44:39.177606 140632818317184 create_coco_tfrecord.py:323] On image 1400 of 5000\\n\",\n            \"I0510 19:44:39.347730 140632818317184 create_coco_tfrecord.py:323] On image 1500 of 5000\\n\",\n            \"I0510 19:44:39.513748 140632818317184 create_coco_tfrecord.py:323] On image 1600 of 5000\\n\",\n            \"I0510 19:44:39.655339 140632818317184 create_coco_tfrecord.py:323] On image 1700 of 5000\\n\",\n            \"I0510 19:44:39.812914 140632818317184 create_coco_tfrecord.py:323] On image 1800 of 5000\\n\",\n            \"I0510 19:44:39.939900 140632818317184 create_coco_tfrecord.py:323] On image 1900 of 5000\\n\",\n            \"I0510 19:44:40.087029 140632818317184 create_coco_tfrecord.py:323] On image 2000 of 5000\\n\",\n            \"I0510 19:44:40.226217 140632818317184 create_coco_tfrecord.py:323] On image 2100 of 5000\\n\",\n            \"I0510 19:44:40.365591 140632818317184 create_coco_tfrecord.py:323] On image 2200 of 5000\\n\",\n            \"I0510 19:44:40.499703 140632818317184 create_coco_tfrecord.py:323] On image 2300 of 5000\\n\",\n            \"I0510 19:44:40.638760 140632818317184 create_coco_tfrecord.py:323] On image 2400 of 5000\\n\",\n            \"I0510 19:44:41.050563 140632818317184 create_coco_tfrecord.py:323] On image 2500 of 5000\\n\",\n            \"I0510 19:44:41.431067 140632818317184 create_coco_tfrecord.py:323] On image 2600 of 5000\\n\",\n            \"I0510 19:44:41.786906 140632818317184 create_coco_tfrecord.py:323] On image 2700 of 5000\\n\",\n            \"I0510 19:44:42.212045 140632818317184 create_coco_tfrecord.py:323] On image 2800 of 5000\\n\",\n            \"I0510 19:44:42.739339 140632818317184 create_coco_tfrecord.py:323] On image 2900 of 5000\\n\",\n            \"I0510 19:44:43.172071 140632818317184 create_coco_tfrecord.py:323] On image 3000 of 5000\\n\",\n            \"I0510 19:44:43.651128 140632818317184 create_coco_tfrecord.py:323] On image 3100 of 5000\\n\",\n            \"I0510 19:44:44.109982 140632818317184 create_coco_tfrecord.py:323] On image 3200 of 5000\\n\",\n            \"I0510 19:44:44.504407 140632818317184 create_coco_tfrecord.py:323] On image 3300 of 5000\\n\",\n            \"I0510 19:44:44.679261 140632818317184 create_coco_tfrecord.py:323] On image 3400 of 5000\\n\",\n            \"I0510 19:44:44.843348 140632818317184 create_coco_tfrecord.py:323] On image 3500 of 5000\\n\",\n            \"I0510 19:44:45.013960 140632818317184 create_coco_tfrecord.py:323] On image 3600 of 5000\\n\",\n            \"I0510 19:44:45.209258 140632818317184 create_coco_tfrecord.py:323] On image 3700 of 5000\\n\",\n            \"I0510 19:44:45.453207 140632818317184 create_coco_tfrecord.py:323] On image 3800 of 5000\\n\",\n            \"I0510 19:44:45.758646 140632818317184 create_coco_tfrecord.py:323] On image 3900 of 5000\\n\",\n            \"I0510 19:44:50.063420 140632818317184 create_coco_tfrecord.py:323] On image 4000 of 5000\\n\",\n            \"I0510 19:44:50.098973 140632818317184 create_coco_tfrecord.py:323] On image 4100 of 5000\\n\",\n            \"I0510 19:44:50.144104 140632818317184 create_coco_tfrecord.py:323] On image 4200 of 5000\\n\",\n            \"I0510 19:44:50.187048 140632818317184 create_coco_tfrecord.py:323] On image 4300 of 5000\\n\",\n            \"I0510 19:44:50.278038 140632818317184 create_coco_tfrecord.py:323] On image 4400 of 5000\\n\",\n            \"I0510 19:44:50.474480 140632818317184 create_coco_tfrecord.py:323] On image 4500 of 5000\\n\",\n            \"I0510 19:44:50.887932 140632818317184 create_coco_tfrecord.py:323] On image 4600 of 5000\\n\",\n            \"I0510 19:44:51.437325 140632818317184 create_coco_tfrecord.py:323] On image 4700 of 5000\\n\",\n            \"I0510 19:44:51.890431 140632818317184 create_coco_tfrecord.py:323] On image 4800 of 5000\\n\",\n            \"I0510 19:44:52.091532 140632818317184 create_coco_tfrecord.py:323] On image 4900 of 5000\\n\",\n            \"I0510 19:44:52.366421 140632818317184 create_coco_tfrecord.py:335] Finished writing, skipped 0 annotations.\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"if 'val2017' not in os.listdir():\\n\",\n        \"  !wget http://images.cocodataset.org/zips/val2017.zip\\n\",\n        \"  !wget http://images.cocodataset.org/annotations/annotations_trainval2017.zip\\n\",\n        \"  !unzip -q val2017.zip\\n\",\n        \"  !unzip annotations_trainval2017.zip\\n\",\n        \"\\n\",\n        \"  !mkdir tfrecord\\n\",\n        \"  !PYTHONPATH=\\\".:$PYTHONPATH\\\"  python dataset/create_coco_tfrecord.py \\\\\\n\",\n        \"      --image_dir=val2017 \\\\\\n\",\n        \"      --caption_annotations_file=annotations/captions_val2017.json \\\\\\n\",\n        \"      --output_file_prefix=tfrecord/val \\\\\\n\",\n        \"      --num_shards=32\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\"\n        },\n        \"id\": \"eLHZUY3jQpZr\",\n        \"outputId\": \"88f9aff2-708c-45cf-f7d4-c78b21576843\"\n      },\n      \"outputs\": [\n        {\n          \"name\": \"stdout\",\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"2021-05-10 20:07:49.603102: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudart.so.11.0\\n\",\n            \"I0510 20:07:52.228707 139834041194368 main.py:264] {'name': 'efficientdet-d1', 'act_type': 'swish', 'image_size': (640, 640), 'target_size': None, 'input_rand_hflip': True, 'jitter_min': 0.1, 'jitter_max': 2.0, 'autoaugment_policy': None, 'grid_mask': False, 'sample_image': None, 'map_freq': 5, 'num_classes': 90, 'seg_num_classes': 3, 'heads': ['object_detection'], 'skip_crowd_during_training': True, 'label_map': None, 'max_instances_per_image': 100, 'regenerate_source_id': False, 'min_level': 3, 'max_level': 7, 'num_scales': 3, 'aspect_ratios': [1.0, 2.0, 0.5], 'anchor_scale': 4.0, 'is_training_bn': True, 'momentum': 0.9, 'optimizer': 'sgd', 'learning_rate': 0.08, 'lr_warmup_init': 0.008, 'lr_warmup_epoch': 1.0, 'first_lr_drop_epoch': 200.0, 'second_lr_drop_epoch': 250.0, 'poly_lr_power': 0.9, 'clip_gradients_norm': 10.0, 'num_epochs': 300, 'data_format': 'channels_last', 'mean_rgb': 0.0, 'stddev_rgb': 1.0, 'scale_range': True, 'label_smoothing': 0.0, 'alpha': 0.25, 'gamma': 1.5, 'delta': 0.1, 'box_loss_weight': 50.0, 'iou_loss_type': None, 'iou_loss_weight': 1.0, 'weight_decay': 4e-05, 'strategy': None, 'mixed_precision': False, 'loss_scale': None, 'model_optimizations': {}, 'box_class_repeats': 3, 'fpn_cell_repeats': 4, 'fpn_num_filters': 88, 'separable_conv': True, 'apply_bn_for_resampling': True, 'conv_after_downsample': False, 'conv_bn_act_pattern': False, 'drop_remainder': True, 'nms_configs': {'method': 'gaussian', 'iou_thresh': None, 'score_thresh': 0.0, 'sigma': None, 'pyfunc': False, 'max_nms_inputs': 0, 'max_output_size': 100}, 'tflite_max_detections': 100, 'fpn_name': None, 'fpn_weight_method': None, 'fpn_config': None, 'survival_prob': None, 'img_summary_steps': None, 'lr_decay_method': 'cosine', 'moving_average_decay': 0.9998, 'ckpt_var_scope': None, 'skip_mismatch': True, 'backbone_name': 'efficientnet-b1', 'backbone_config': None, 'var_freeze_expr': None, 'use_keras_model': True, 'dataset_type': None, 'positives_momentum': None, 'grad_checkpoint': False, 'verbose': 1, 'save_freq': 'epoch', 'model_name': 'efficientdet-d1', 'iterations_per_loop': 100, 'model_dir': '/content/automl/efficientdet/efficientdet-d1', 'num_shards': 8, 'num_examples_per_epoch': 120000, 'backbone_ckpt': '', 'ckpt': None, 'val_json_file': 'annotations/instances_val2017.json', 'testdev_dir': None, 'profile': False, 'mode': 'eval'}\\n\",\n            \"INFO:tensorflow:Using config: {'_model_dir': '/content/automl/efficientdet/efficientdet-d1', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': 100, '_save_checkpoints_secs': None, '_session_config': allow_soft_placement: true\\n\",\n            \", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': 100, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_checkpoint_save_graph_def': True, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1}\\n\",\n            \"I0510 20:07:52.287737 139834041194368 estimator.py:191] Using config: {'_model_dir': '/content/automl/efficientdet/efficientdet-d1', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': 100, '_save_checkpoints_secs': None, '_session_config': allow_soft_placement: true\\n\",\n            \", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': 100, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_checkpoint_save_graph_def': True, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1}\\n\",\n            \"INFO:tensorflow:Using config: {'_model_dir': '/content/automl/efficientdet/efficientdet-d1', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': 100, '_save_checkpoints_secs': None, '_session_config': allow_soft_placement: true\\n\",\n            \", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': 100, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_checkpoint_save_graph_def': True, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1}\\n\",\n            \"I0510 20:07:52.288912 139834041194368 estimator.py:191] Using config: {'_model_dir': '/content/automl/efficientdet/efficientdet-d1', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': 100, '_save_checkpoints_secs': None, '_session_config': allow_soft_placement: true\\n\",\n            \", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': 100, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_checkpoint_save_graph_def': True, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1}\\n\",\n            \"INFO:tensorflow:Waiting for new checkpoint at /content/automl/efficientdet/efficientdet-d1\\n\",\n            \"I0510 20:07:52.289403 139834041194368 checkpoint_utils.py:139] Waiting for new checkpoint at /content/automl/efficientdet/efficientdet-d1\\n\",\n            \"INFO:tensorflow:Found new checkpoint at /content/automl/efficientdet/efficientdet-d1/model\\n\",\n            \"I0510 20:07:52.292483 139834041194368 checkpoint_utils.py:148] Found new checkpoint at /content/automl/efficientdet/efficientdet-d1/model\\n\",\n            \"I0510 20:07:52.292706 139834041194368 main.py:344] Starting to evaluate.\\n\",\n            \"2021-05-10 20:07:52.489416: I tensorflow/compiler/jit/xla_cpu_device.cc:41] Not creating XLA devices, tf_xla_enable_xla_devices not set\\n\",\n            \"2021-05-10 20:07:52.490577: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcuda.so.1\\n\",\n            \"2021-05-10 20:07:52.508703: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:941] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2021-05-10 20:07:52.509616: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1720] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla P100-PCIE-16GB computeCapability: 6.0\\n\",\n            \"coreClock: 1.3285GHz coreCount: 56 deviceMemorySize: 15.90GiB deviceMemoryBandwidth: 681.88GiB/s\\n\",\n            \"2021-05-10 20:07:52.509660: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudart.so.11.0\\n\",\n            \"2021-05-10 20:07:52.512220: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcublas.so.11\\n\",\n            \"2021-05-10 20:07:52.512295: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcublasLt.so.11\\n\",\n            \"2021-05-10 20:07:52.514123: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2021-05-10 20:07:52.514558: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2021-05-10 20:07:52.516516: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2021-05-10 20:07:52.517094: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcusparse.so.11\\n\",\n            \"2021-05-10 20:07:52.517346: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudnn.so.8\\n\",\n            \"2021-05-10 20:07:52.517450: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:941] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2021-05-10 20:07:52.518379: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:941] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2021-05-10 20:07:52.519223: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1862] Adding visible gpu devices: 0\\n\",\n            \"INFO:tensorflow:Calling model_fn.\\n\",\n            \"I0510 20:07:53.088464 139834041194368 estimator.py:1162] Calling model_fn.\\n\",\n            \"I0510 20:07:53.093623 139834041194368 efficientnet_builder.py:215] global_params= GlobalParams(batch_norm_momentum=0.99, batch_norm_epsilon=0.001, dropout_rate=0.2, data_format='channels_last', num_classes=1000, width_coefficient=1.0, depth_coefficient=1.1, depth_divisor=8, min_depth=None, survival_prob=0.8, relu_fn=functools.partial(\\u003cfunction activation_fn at 0x7f2d4fb79cb0\\u003e, act_type='swish'), batch_norm=\\u003cclass 'utils.BatchNormalization'\\u003e, use_se=True, local_pooling=None, condconv_num_experts=None, clip_projection_output=False, blocks_args=['r1_k3_s11_e1_i32_o16_se0.25', 'r2_k3_s22_e6_i16_o24_se0.25', 'r2_k5_s22_e6_i24_o40_se0.25', 'r3_k3_s22_e6_i40_o80_se0.25', 'r3_k5_s11_e6_i80_o112_se0.25', 'r4_k5_s22_e6_i112_o192_se0.25', 'r1_k3_s11_e6_i192_o320_se0.25'], fix_head_stem=None, grad_checkpoint=False)\\n\",\n            \"I0510 20:07:53.595246 139834041194368 efficientdet_keras.py:760] fnode 0 : {'feat_level': 6, 'inputs_offsets': [3, 4]}\\n\",\n            \"I0510 20:07:53.596441 139834041194368 efficientdet_keras.py:760] fnode 1 : {'feat_level': 5, 'inputs_offsets': [2, 5]}\\n\",\n            \"I0510 20:07:53.597646 139834041194368 efficientdet_keras.py:760] fnode 2 : {'feat_level': 4, 'inputs_offsets': [1, 6]}\\n\",\n            \"I0510 20:07:53.598873 139834041194368 efficientdet_keras.py:760] fnode 3 : {'feat_level': 3, 'inputs_offsets': [0, 7]}\\n\",\n            \"I0510 20:07:53.600070 139834041194368 efficientdet_keras.py:760] fnode 4 : {'feat_level': 4, 'inputs_offsets': [1, 7, 8]}\\n\",\n            \"I0510 20:07:53.601099 139834041194368 efficientdet_keras.py:760] fnode 5 : {'feat_level': 5, 'inputs_offsets': [2, 6, 9]}\\n\",\n            \"I0510 20:07:53.602200 139834041194368 efficientdet_keras.py:760] fnode 6 : {'feat_level': 6, 'inputs_offsets': [3, 5, 10]}\\n\",\n            \"I0510 20:07:53.603379 139834041194368 efficientdet_keras.py:760] fnode 7 : {'feat_level': 7, 'inputs_offsets': [4, 11]}\\n\",\n            \"I0510 20:07:53.606173 139834041194368 efficientdet_keras.py:760] fnode 0 : {'feat_level': 6, 'inputs_offsets': [3, 4]}\\n\",\n            \"I0510 20:07:53.607471 139834041194368 efficientdet_keras.py:760] fnode 1 : {'feat_level': 5, 'inputs_offsets': [2, 5]}\\n\",\n            \"I0510 20:07:53.608979 139834041194368 efficientdet_keras.py:760] fnode 2 : {'feat_level': 4, 'inputs_offsets': [1, 6]}\\n\",\n            \"I0510 20:07:53.610496 139834041194368 efficientdet_keras.py:760] fnode 3 : {'feat_level': 3, 'inputs_offsets': [0, 7]}\\n\",\n            \"I0510 20:07:53.611800 139834041194368 efficientdet_keras.py:760] fnode 4 : {'feat_level': 4, 'inputs_offsets': [1, 7, 8]}\\n\",\n            \"I0510 20:07:53.613338 139834041194368 efficientdet_keras.py:760] fnode 5 : {'feat_level': 5, 'inputs_offsets': [2, 6, 9]}\\n\",\n            \"I0510 20:07:53.615097 139834041194368 efficientdet_keras.py:760] fnode 6 : {'feat_level': 6, 'inputs_offsets': [3, 5, 10]}\\n\",\n            \"I0510 20:07:53.617069 139834041194368 efficientdet_keras.py:760] fnode 7 : {'feat_level': 7, 'inputs_offsets': [4, 11]}\\n\",\n            \"I0510 20:07:53.619574 139834041194368 efficientdet_keras.py:760] fnode 0 : {'feat_level': 6, 'inputs_offsets': [3, 4]}\\n\",\n            \"I0510 20:07:53.620916 139834041194368 efficientdet_keras.py:760] fnode 1 : {'feat_level': 5, 'inputs_offsets': [2, 5]}\\n\",\n            \"I0510 20:07:53.622064 139834041194368 efficientdet_keras.py:760] fnode 2 : {'feat_level': 4, 'inputs_offsets': [1, 6]}\\n\",\n            \"I0510 20:07:53.623369 139834041194368 efficientdet_keras.py:760] fnode 3 : {'feat_level': 3, 'inputs_offsets': [0, 7]}\\n\",\n            \"I0510 20:07:53.624463 139834041194368 efficientdet_keras.py:760] fnode 4 : {'feat_level': 4, 'inputs_offsets': [1, 7, 8]}\\n\",\n            \"I0510 20:07:53.625886 139834041194368 efficientdet_keras.py:760] fnode 5 : {'feat_level': 5, 'inputs_offsets': [2, 6, 9]}\\n\",\n            \"I0510 20:07:53.627297 139834041194368 efficientdet_keras.py:760] fnode 6 : {'feat_level': 6, 'inputs_offsets': [3, 5, 10]}\\n\",\n            \"I0510 20:07:53.628497 139834041194368 efficientdet_keras.py:760] fnode 7 : {'feat_level': 7, 'inputs_offsets': [4, 11]}\\n\",\n            \"I0510 20:07:53.630418 139834041194368 efficientdet_keras.py:760] fnode 0 : {'feat_level': 6, 'inputs_offsets': [3, 4]}\\n\",\n            \"I0510 20:07:53.631624 139834041194368 efficientdet_keras.py:760] fnode 1 : {'feat_level': 5, 'inputs_offsets': [2, 5]}\\n\",\n            \"I0510 20:07:53.632795 139834041194368 efficientdet_keras.py:760] fnode 2 : {'feat_level': 4, 'inputs_offsets': [1, 6]}\\n\",\n            \"I0510 20:07:53.633984 139834041194368 efficientdet_keras.py:760] fnode 3 : {'feat_level': 3, 'inputs_offsets': [0, 7]}\\n\",\n            \"I0510 20:07:53.635079 139834041194368 efficientdet_keras.py:760] fnode 4 : {'feat_level': 4, 'inputs_offsets': [1, 7, 8]}\\n\",\n            \"I0510 20:07:53.636193 139834041194368 efficientdet_keras.py:760] fnode 5 : {'feat_level': 5, 'inputs_offsets': [2, 6, 9]}\\n\",\n            \"I0510 20:07:53.637408 139834041194368 efficientdet_keras.py:760] fnode 6 : {'feat_level': 6, 'inputs_offsets': [3, 5, 10]}\\n\",\n            \"I0510 20:07:53.638536 139834041194368 efficientdet_keras.py:760] fnode 7 : {'feat_level': 7, 'inputs_offsets': [4, 11]}\\n\",\n            \"I0510 20:07:53.750490 139834041194368 efficientnet_model.py:735] Built stem stem : (1, 320, 320, 32)\\n\",\n            \"I0510 20:07:53.750730 139834041194368 efficientnet_model.py:756] block_0 survival_prob: 1.0\\n\",\n            \"I0510 20:07:53.751127 139834041194368 efficientnet_model.py:374] Block blocks_0 input shape: (1, 320, 320, 32)\\n\",\n            \"I0510 20:07:53.780265 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 320, 320, 32)\\n\",\n            \"I0510 20:07:53.809039 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 32)\\n\",\n            \"I0510 20:07:53.836266 139834041194368 efficientnet_model.py:414] Project shape: (1, 320, 320, 16)\\n\",\n            \"I0510 20:07:53.836619 139834041194368 efficientnet_model.py:756] block_1 survival_prob: 0.991304347826087\\n\",\n            \"I0510 20:07:53.837053 139834041194368 efficientnet_model.py:374] Block blocks_1 input shape: (1, 320, 320, 16)\\n\",\n            \"I0510 20:07:53.871055 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 320, 320, 16)\\n\",\n            \"I0510 20:07:53.900743 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 16)\\n\",\n            \"I0510 20:07:53.930487 139834041194368 efficientnet_model.py:414] Project shape: (1, 320, 320, 16)\\n\",\n            \"I0510 20:07:53.930959 139834041194368 efficientnet_model.py:756] block_2 survival_prob: 0.9826086956521739\\n\",\n            \"I0510 20:07:53.931439 139834041194368 efficientnet_model.py:374] Block blocks_2 input shape: (1, 320, 320, 16)\\n\",\n            \"I0510 20:07:53.961296 139834041194368 efficientnet_model.py:390] Expand shape: (1, 320, 320, 96)\\n\",\n            \"I0510 20:07:53.993832 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 160, 160, 96)\\n\",\n            \"I0510 20:07:54.025872 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 96)\\n\",\n            \"I0510 20:07:54.059447 139834041194368 efficientnet_model.py:414] Project shape: (1, 160, 160, 24)\\n\",\n            \"I0510 20:07:54.059780 139834041194368 efficientnet_model.py:756] block_3 survival_prob: 0.9739130434782609\\n\",\n            \"I0510 20:07:54.060328 139834041194368 efficientnet_model.py:374] Block blocks_3 input shape: (1, 160, 160, 24)\\n\",\n            \"I0510 20:07:54.088966 139834041194368 efficientnet_model.py:390] Expand shape: (1, 160, 160, 144)\\n\",\n            \"I0510 20:07:54.121675 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 160, 160, 144)\\n\",\n            \"I0510 20:07:54.150545 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 144)\\n\",\n            \"I0510 20:07:54.182002 139834041194368 efficientnet_model.py:414] Project shape: (1, 160, 160, 24)\\n\",\n            \"I0510 20:07:54.182313 139834041194368 efficientnet_model.py:756] block_4 survival_prob: 0.9652173913043478\\n\",\n            \"I0510 20:07:54.182894 139834041194368 efficientnet_model.py:374] Block blocks_4 input shape: (1, 160, 160, 24)\\n\",\n            \"I0510 20:07:54.211359 139834041194368 efficientnet_model.py:390] Expand shape: (1, 160, 160, 144)\\n\",\n            \"I0510 20:07:54.241073 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 160, 160, 144)\\n\",\n            \"I0510 20:07:54.271023 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 144)\\n\",\n            \"I0510 20:07:54.299235 139834041194368 efficientnet_model.py:414] Project shape: (1, 160, 160, 24)\\n\",\n            \"I0510 20:07:54.299630 139834041194368 efficientnet_model.py:756] block_5 survival_prob: 0.9565217391304348\\n\",\n            \"I0510 20:07:54.300070 139834041194368 efficientnet_model.py:374] Block blocks_5 input shape: (1, 160, 160, 24)\\n\",\n            \"I0510 20:07:54.328530 139834041194368 efficientnet_model.py:390] Expand shape: (1, 160, 160, 144)\\n\",\n            \"I0510 20:07:54.358716 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 80, 80, 144)\\n\",\n            \"I0510 20:07:54.389357 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 144)\\n\",\n            \"I0510 20:07:54.418314 139834041194368 efficientnet_model.py:414] Project shape: (1, 80, 80, 40)\\n\",\n            \"I0510 20:07:54.418715 139834041194368 efficientnet_model.py:756] block_6 survival_prob: 0.9478260869565217\\n\",\n            \"I0510 20:07:54.419190 139834041194368 efficientnet_model.py:374] Block blocks_6 input shape: (1, 80, 80, 40)\\n\",\n            \"I0510 20:07:54.451548 139834041194368 efficientnet_model.py:390] Expand shape: (1, 80, 80, 240)\\n\",\n            \"I0510 20:07:54.486734 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 80, 80, 240)\\n\",\n            \"I0510 20:07:54.516430 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 240)\\n\",\n            \"I0510 20:07:54.543974 139834041194368 efficientnet_model.py:414] Project shape: (1, 80, 80, 40)\\n\",\n            \"I0510 20:07:54.544296 139834041194368 efficientnet_model.py:756] block_7 survival_prob: 0.9391304347826087\\n\",\n            \"I0510 20:07:54.544718 139834041194368 efficientnet_model.py:374] Block blocks_7 input shape: (1, 80, 80, 40)\\n\",\n            \"I0510 20:07:54.573758 139834041194368 efficientnet_model.py:390] Expand shape: (1, 80, 80, 240)\\n\",\n            \"I0510 20:07:54.607336 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 80, 80, 240)\\n\",\n            \"I0510 20:07:54.636443 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 240)\\n\",\n            \"I0510 20:07:54.663759 139834041194368 efficientnet_model.py:414] Project shape: (1, 80, 80, 40)\\n\",\n            \"I0510 20:07:54.664189 139834041194368 efficientnet_model.py:756] block_8 survival_prob: 0.9304347826086956\\n\",\n            \"I0510 20:07:54.664561 139834041194368 efficientnet_model.py:374] Block blocks_8 input shape: (1, 80, 80, 40)\\n\",\n            \"I0510 20:07:54.696891 139834041194368 efficientnet_model.py:390] Expand shape: (1, 80, 80, 240)\\n\",\n            \"I0510 20:07:54.726265 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 40, 40, 240)\\n\",\n            \"I0510 20:07:54.755805 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 240)\\n\",\n            \"I0510 20:07:54.786793 139834041194368 efficientnet_model.py:414] Project shape: (1, 40, 40, 80)\\n\",\n            \"I0510 20:07:54.787131 139834041194368 efficientnet_model.py:756] block_9 survival_prob: 0.9217391304347826\\n\",\n            \"I0510 20:07:54.787655 139834041194368 efficientnet_model.py:374] Block blocks_9 input shape: (1, 40, 40, 80)\\n\",\n            \"I0510 20:07:54.815595 139834041194368 efficientnet_model.py:390] Expand shape: (1, 40, 40, 480)\\n\",\n            \"I0510 20:07:54.844414 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 40, 40, 480)\\n\",\n            \"I0510 20:07:54.874174 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 480)\\n\",\n            \"I0510 20:07:54.902137 139834041194368 efficientnet_model.py:414] Project shape: (1, 40, 40, 80)\\n\",\n            \"I0510 20:07:54.902514 139834041194368 efficientnet_model.py:756] block_10 survival_prob: 0.9130434782608696\\n\",\n            \"I0510 20:07:54.902967 139834041194368 efficientnet_model.py:374] Block blocks_10 input shape: (1, 40, 40, 80)\\n\",\n            \"I0510 20:07:54.931709 139834041194368 efficientnet_model.py:390] Expand shape: (1, 40, 40, 480)\\n\",\n            \"I0510 20:07:54.962241 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 40, 40, 480)\\n\",\n            \"I0510 20:07:54.993660 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 480)\\n\",\n            \"I0510 20:07:55.021482 139834041194368 efficientnet_model.py:414] Project shape: (1, 40, 40, 80)\\n\",\n            \"I0510 20:07:55.021827 139834041194368 efficientnet_model.py:756] block_11 survival_prob: 0.9043478260869565\\n\",\n            \"I0510 20:07:55.022259 139834041194368 efficientnet_model.py:374] Block blocks_11 input shape: (1, 40, 40, 80)\\n\",\n            \"I0510 20:07:55.057896 139834041194368 efficientnet_model.py:390] Expand shape: (1, 40, 40, 480)\\n\",\n            \"I0510 20:07:55.091666 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 40, 40, 480)\\n\",\n            \"I0510 20:07:55.121704 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 480)\\n\",\n            \"I0510 20:07:55.149158 139834041194368 efficientnet_model.py:414] Project shape: (1, 40, 40, 80)\\n\",\n            \"I0510 20:07:55.149526 139834041194368 efficientnet_model.py:756] block_12 survival_prob: 0.8956521739130435\\n\",\n            \"I0510 20:07:55.150015 139834041194368 efficientnet_model.py:374] Block blocks_12 input shape: (1, 40, 40, 80)\\n\",\n            \"I0510 20:07:55.178953 139834041194368 efficientnet_model.py:390] Expand shape: (1, 40, 40, 480)\\n\",\n            \"I0510 20:07:55.209172 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 40, 40, 480)\\n\",\n            \"I0510 20:07:55.238858 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 480)\\n\",\n            \"I0510 20:07:55.267069 139834041194368 efficientnet_model.py:414] Project shape: (1, 40, 40, 112)\\n\",\n            \"I0510 20:07:55.267507 139834041194368 efficientnet_model.py:756] block_13 survival_prob: 0.8869565217391304\\n\",\n            \"I0510 20:07:55.268048 139834041194368 efficientnet_model.py:374] Block blocks_13 input shape: (1, 40, 40, 112)\\n\",\n            \"I0510 20:07:55.296053 139834041194368 efficientnet_model.py:390] Expand shape: (1, 40, 40, 672)\\n\",\n            \"I0510 20:07:55.331140 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 40, 40, 672)\\n\",\n            \"I0510 20:07:55.360097 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 672)\\n\",\n            \"I0510 20:07:55.391819 139834041194368 efficientnet_model.py:414] Project shape: (1, 40, 40, 112)\\n\",\n            \"I0510 20:07:55.392213 139834041194368 efficientnet_model.py:756] block_14 survival_prob: 0.8782608695652174\\n\",\n            \"I0510 20:07:55.392651 139834041194368 efficientnet_model.py:374] Block blocks_14 input shape: (1, 40, 40, 112)\\n\",\n            \"I0510 20:07:55.422633 139834041194368 efficientnet_model.py:390] Expand shape: (1, 40, 40, 672)\\n\",\n            \"I0510 20:07:55.452295 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 40, 40, 672)\\n\",\n            \"I0510 20:07:55.482393 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 672)\\n\",\n            \"I0510 20:07:55.512572 139834041194368 efficientnet_model.py:414] Project shape: (1, 40, 40, 112)\\n\",\n            \"I0510 20:07:55.512948 139834041194368 efficientnet_model.py:756] block_15 survival_prob: 0.8695652173913044\\n\",\n            \"I0510 20:07:55.513380 139834041194368 efficientnet_model.py:374] Block blocks_15 input shape: (1, 40, 40, 112)\\n\",\n            \"I0510 20:07:55.542927 139834041194368 efficientnet_model.py:390] Expand shape: (1, 40, 40, 672)\\n\",\n            \"I0510 20:07:55.574158 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 40, 40, 672)\\n\",\n            \"I0510 20:07:55.605713 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 672)\\n\",\n            \"I0510 20:07:55.633415 139834041194368 efficientnet_model.py:414] Project shape: (1, 40, 40, 112)\\n\",\n            \"I0510 20:07:55.633908 139834041194368 efficientnet_model.py:756] block_16 survival_prob: 0.8608695652173913\\n\",\n            \"I0510 20:07:55.634328 139834041194368 efficientnet_model.py:374] Block blocks_16 input shape: (1, 40, 40, 112)\\n\",\n            \"I0510 20:07:55.663101 139834041194368 efficientnet_model.py:390] Expand shape: (1, 40, 40, 672)\\n\",\n            \"I0510 20:07:55.696253 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 20, 20, 672)\\n\",\n            \"I0510 20:07:55.728366 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 672)\\n\",\n            \"I0510 20:07:55.756615 139834041194368 efficientnet_model.py:414] Project shape: (1, 20, 20, 192)\\n\",\n            \"I0510 20:07:55.756961 139834041194368 efficientnet_model.py:756] block_17 survival_prob: 0.8521739130434782\\n\",\n            \"I0510 20:07:55.757386 139834041194368 efficientnet_model.py:374] Block blocks_17 input shape: (1, 20, 20, 192)\\n\",\n            \"I0510 20:07:55.792434 139834041194368 efficientnet_model.py:390] Expand shape: (1, 20, 20, 1152)\\n\",\n            \"I0510 20:07:55.829316 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 20, 20, 1152)\\n\",\n            \"I0510 20:07:55.860946 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 1152)\\n\",\n            \"I0510 20:07:55.888971 139834041194368 efficientnet_model.py:414] Project shape: (1, 20, 20, 192)\\n\",\n            \"I0510 20:07:55.889437 139834041194368 efficientnet_model.py:756] block_18 survival_prob: 0.8434782608695652\\n\",\n            \"I0510 20:07:55.889867 139834041194368 efficientnet_model.py:374] Block blocks_18 input shape: (1, 20, 20, 192)\\n\",\n            \"I0510 20:07:55.924473 139834041194368 efficientnet_model.py:390] Expand shape: (1, 20, 20, 1152)\\n\",\n            \"I0510 20:07:55.968310 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 20, 20, 1152)\\n\",\n            \"I0510 20:07:56.009804 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 1152)\\n\",\n            \"I0510 20:07:56.041101 139834041194368 efficientnet_model.py:414] Project shape: (1, 20, 20, 192)\\n\",\n            \"I0510 20:07:56.041589 139834041194368 efficientnet_model.py:756] block_19 survival_prob: 0.8347826086956522\\n\",\n            \"I0510 20:07:56.042091 139834041194368 efficientnet_model.py:374] Block blocks_19 input shape: (1, 20, 20, 192)\\n\",\n            \"I0510 20:07:56.093295 139834041194368 efficientnet_model.py:390] Expand shape: (1, 20, 20, 1152)\\n\",\n            \"I0510 20:07:56.136825 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 20, 20, 1152)\\n\",\n            \"I0510 20:07:56.168875 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 1152)\\n\",\n            \"I0510 20:07:56.195683 139834041194368 efficientnet_model.py:414] Project shape: (1, 20, 20, 192)\\n\",\n            \"I0510 20:07:56.196047 139834041194368 efficientnet_model.py:756] block_20 survival_prob: 0.8260869565217391\\n\",\n            \"I0510 20:07:56.196481 139834041194368 efficientnet_model.py:374] Block blocks_20 input shape: (1, 20, 20, 192)\\n\",\n            \"I0510 20:07:56.234993 139834041194368 efficientnet_model.py:390] Expand shape: (1, 20, 20, 1152)\\n\",\n            \"I0510 20:07:56.272101 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 20, 20, 1152)\\n\",\n            \"I0510 20:07:56.306418 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 1152)\\n\",\n            \"I0510 20:07:56.335123 139834041194368 efficientnet_model.py:414] Project shape: (1, 20, 20, 192)\\n\",\n            \"I0510 20:07:56.335595 139834041194368 efficientnet_model.py:756] block_21 survival_prob: 0.8173913043478261\\n\",\n            \"I0510 20:07:56.336132 139834041194368 efficientnet_model.py:374] Block blocks_21 input shape: (1, 20, 20, 192)\\n\",\n            \"I0510 20:07:56.374043 139834041194368 efficientnet_model.py:390] Expand shape: (1, 20, 20, 1152)\\n\",\n            \"I0510 20:07:56.412933 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 20, 20, 1152)\\n\",\n            \"I0510 20:07:56.446010 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 1152)\\n\",\n            \"I0510 20:07:56.475004 139834041194368 efficientnet_model.py:414] Project shape: (1, 20, 20, 320)\\n\",\n            \"I0510 20:07:56.475410 139834041194368 efficientnet_model.py:756] block_22 survival_prob: 0.808695652173913\\n\",\n            \"I0510 20:07:56.476164 139834041194368 efficientnet_model.py:374] Block blocks_22 input shape: (1, 20, 20, 320)\\n\",\n            \"I0510 20:07:56.514158 139834041194368 efficientnet_model.py:390] Expand shape: (1, 20, 20, 1920)\\n\",\n            \"I0510 20:07:56.550471 139834041194368 efficientnet_model.py:393] DWConv shape: (1, 20, 20, 1920)\\n\",\n            \"I0510 20:07:56.586025 139834041194368 efficientnet_model.py:195] Built SE se : (1, 1, 1, 1920)\\n\",\n            \"I0510 20:07:56.615339 139834041194368 efficientnet_model.py:414] Project shape: (1, 20, 20, 320)\\n\",\n            \"I0510 20:08:00.628787 139834041194368 det_model_fn.py:81] LR schedule method: cosine\\n\",\n            \"I0510 20:08:01.077699 139834041194368 postprocess.py:90] use max_nms_inputs for pre-nms topk.\\n\",\n            \"I0510 20:08:02.011834 139834041194368 det_model_fn.py:476] Eval val with groudtruths annotations/instances_val2017.json.\\n\",\n            \"I0510 20:08:02.068171 139834041194368 det_model_fn.py:553] Load EMA vars with ema_decay=0.999800\\n\",\n            \"INFO:tensorflow:Done calling model_fn.\\n\",\n            \"I0510 20:08:03.211773 139834041194368 estimator.py:1164] Done calling model_fn.\\n\",\n            \"INFO:tensorflow:Starting evaluation at 2021-05-10T20:08:03Z\\n\",\n            \"I0510 20:08:03.234877 139834041194368 evaluation.py:255] Starting evaluation at 2021-05-10T20:08:03Z\\n\",\n            \"INFO:tensorflow:Graph was finalized.\\n\",\n            \"I0510 20:08:04.095394 139834041194368 monitored_session.py:246] Graph was finalized.\\n\",\n            \"2021-05-10 20:08:04.096106: I tensorflow/compiler/jit/xla_gpu_device.cc:99] Not creating XLA devices, tf_xla_enable_xla_devices not set\\n\",\n            \"2021-05-10 20:08:04.096396: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:941] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2021-05-10 20:08:04.097427: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1720] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla P100-PCIE-16GB computeCapability: 6.0\\n\",\n            \"coreClock: 1.3285GHz coreCount: 56 deviceMemorySize: 15.90GiB deviceMemoryBandwidth: 681.88GiB/s\\n\",\n            \"2021-05-10 20:08:04.097507: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudart.so.11.0\\n\",\n            \"2021-05-10 20:08:04.097573: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcublas.so.11\\n\",\n            \"2021-05-10 20:08:04.097644: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcublasLt.so.11\\n\",\n            \"2021-05-10 20:08:04.097691: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2021-05-10 20:08:04.097739: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2021-05-10 20:08:04.097786: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2021-05-10 20:08:04.097891: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcusparse.so.11\\n\",\n            \"2021-05-10 20:08:04.097938: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudnn.so.8\\n\",\n            \"2021-05-10 20:08:04.098070: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:941] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2021-05-10 20:08:04.099073: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:941] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2021-05-10 20:08:04.099983: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1862] Adding visible gpu devices: 0\\n\",\n            \"2021-05-10 20:08:04.100061: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudart.so.11.0\\n\",\n            \"2021-05-10 20:08:04.802699: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1261] Device interconnect StreamExecutor with strength 1 edge matrix:\\n\",\n            \"2021-05-10 20:08:04.802772: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1267]      0 \\n\",\n            \"2021-05-10 20:08:04.802798: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1280] 0:   N \\n\",\n            \"2021-05-10 20:08:04.803063: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:941] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2021-05-10 20:08:04.804184: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:941] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2021-05-10 20:08:04.805216: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:941] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2021-05-10 20:08:04.806026: W tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:39] Overriding allow_growth setting because the TF_FORCE_GPU_ALLOW_GROWTH environment variable is set. Original config value was 0.\\n\",\n            \"2021-05-10 20:08:04.806089: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1406] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 14975 MB memory) -\\u003e physical GPU (device: 0, name: Tesla P100-PCIE-16GB, pci bus id: 0000:00:04.0, compute capability: 6.0)\\n\",\n            \"INFO:tensorflow:Restoring parameters from /content/automl/efficientdet/efficientdet-d1/model\\n\",\n            \"I0510 20:08:04.806891 139834041194368 saver.py:1292] Restoring parameters from /content/automl/efficientdet/efficientdet-d1/model\\n\",\n            \"2021-05-10 20:08:04.944665: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:196] None of the MLIR optimization passes are enabled (registered 0 passes)\\n\",\n            \"2021-05-10 20:08:05.123288: I tensorflow/core/platform/profile_utils/cpu_utils.cc:112] CPU Frequency: 2299995000 Hz\\n\",\n            \"INFO:tensorflow:Running local_init_op.\\n\",\n            \"I0510 20:08:06.682386 139834041194368 session_manager.py:505] Running local_init_op.\\n\",\n            \"INFO:tensorflow:Done running local_init_op.\\n\",\n            \"I0510 20:08:06.769455 139834041194368 session_manager.py:508] Done running local_init_op.\\n\",\n            \"2021-05-10 20:08:07.729877: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:116] None of the MLIR optimization passes are enabled (registered 2)\\n\",\n            \"2021-05-10 20:08:10.942540: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudnn.so.8\\n\",\n            \"2021-05-10 20:08:12.145170: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcublas.so.11\\n\",\n            \"2021-05-10 20:08:12.421326: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcublasLt.so.11\\n\",\n            \"INFO:tensorflow:Evaluation [500/5000]\\n\",\n            \"I0510 20:09:43.242785 139834041194368 evaluation.py:167] Evaluation [500/5000]\\n\",\n            \"INFO:tensorflow:Evaluation [1000/5000]\\n\",\n            \"I0510 20:11:13.138355 139834041194368 evaluation.py:167] Evaluation [1000/5000]\\n\",\n            \"INFO:tensorflow:Evaluation [1500/5000]\\n\",\n            \"I0510 20:12:42.090077 139834041194368 evaluation.py:167] Evaluation [1500/5000]\\n\",\n            \"INFO:tensorflow:Evaluation [2000/5000]\\n\",\n            \"I0510 20:14:10.714957 139834041194368 evaluation.py:167] Evaluation [2000/5000]\\n\",\n            \"INFO:tensorflow:Evaluation [2500/5000]\\n\",\n            \"I0510 20:15:39.312114 139834041194368 evaluation.py:167] Evaluation [2500/5000]\\n\",\n            \"INFO:tensorflow:Evaluation [3000/5000]\\n\",\n            \"I0510 20:17:08.327113 139834041194368 evaluation.py:167] Evaluation [3000/5000]\\n\",\n            \"INFO:tensorflow:Evaluation [3500/5000]\\n\",\n            \"I0510 20:18:37.902203 139834041194368 evaluation.py:167] Evaluation [3500/5000]\\n\",\n            \"INFO:tensorflow:Evaluation [4000/5000]\\n\",\n            \"I0510 20:20:06.601424 139834041194368 evaluation.py:167] Evaluation [4000/5000]\\n\",\n            \"INFO:tensorflow:Evaluation [4500/5000]\\n\",\n            \"I0510 20:21:35.209655 139834041194368 evaluation.py:167] Evaluation [4500/5000]\\n\",\n            \"INFO:tensorflow:Evaluation [5000/5000]\\n\",\n            \"I0510 20:23:04.230208 139834041194368 evaluation.py:167] Evaluation [5000/5000]\\n\",\n            \"loading annotations into memory...\\n\",\n            \"Done (t=0.76s)\\n\",\n            \"creating index...\\n\",\n            \"index created!\\n\",\n            \"Loading and preparing results...\\n\",\n            \"Converting ndarray to lists...\\n\",\n            \"(500000, 7)\\n\",\n            \"0/500000\\n\",\n            \"DONE (t=4.48s)\\n\",\n            \"creating index...\\n\",\n            \"index created!\\n\",\n            \"Running per image evaluation...\\n\",\n            \"Evaluate annotation type *bbox*\\n\",\n            \"DONE (t=90.24s).\\n\",\n            \"Accumulating evaluation results...\\n\",\n            \"DONE (t=14.09s).\\n\",\n            \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.408\\n\",\n            \" Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=100 ] = 0.599\\n\",\n            \" Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=100 ] = 0.440\\n\",\n            \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.214\\n\",\n            \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.463\\n\",\n            \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.591\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=  1 ] = 0.328\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets= 10 ] = 0.520\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.551\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.326\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.624\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.748\\n\",\n            \"INFO:tensorflow:Inference Time : 1013.67302s\\n\",\n            \"I0510 20:24:56.908151 139834041194368 evaluation.py:273] Inference Time : 1013.67302s\\n\",\n            \"INFO:tensorflow:Finished evaluation at 2021-05-10-20:24:56\\n\",\n            \"I0510 20:24:56.908450 139834041194368 evaluation.py:276] Finished evaluation at 2021-05-10-20:24:56\\n\",\n            \"INFO:tensorflow:Saving dict for global step 0: AP = 0.40841353, AP50 = 0.5991094, AP75 = 0.4402199, APl = 0.59102863, APm = 0.46323815, APs = 0.21414877, ARl = 0.74805754, ARm = 0.62390226, ARmax1 = 0.32788682, ARmax10 = 0.52038383, ARmax100 = 0.55101603, ARs = 0.32568377, box_loss = 0.0, cls_loss = 39.20923, global_step = 0, loss = 39.2865\\n\",\n            \"I0510 20:24:56.908696 139834041194368 estimator.py:2066] Saving dict for global step 0: AP = 0.40841353, AP50 = 0.5991094, AP75 = 0.4402199, APl = 0.59102863, APm = 0.46323815, APs = 0.21414877, ARl = 0.74805754, ARm = 0.62390226, ARmax1 = 0.32788682, ARmax10 = 0.52038383, ARmax100 = 0.55101603, ARs = 0.32568377, box_loss = 0.0, cls_loss = 39.20923, global_step = 0, loss = 39.2865\\n\",\n            \"INFO:tensorflow:Saving 'checkpoint_path' summary for global step 0: /content/automl/efficientdet/efficientdet-d1/model\\n\",\n            \"I0510 20:24:58.433520 139834041194368 estimator.py:2127] Saving 'checkpoint_path' summary for global step 0: /content/automl/efficientdet/efficientdet-d1/model\\n\",\n            \"I0510 20:24:58.434658 139834041194368 main.py:351] /content/automl/efficientdet/efficientdet-d1/model has no global step info: stop!\\n\"\n          ]\n        }\n      ],\n      \"source\": [\n        \"# Evalute on validation set (takes about 10 mins for efficientdet-d0)\\n\",\n        \"!python main.py --mode=eval  \\\\\\n\",\n        \"    --model_name={MODEL}  --model_dir={ckpt_path}  \\\\\\n\",\n        \"    --val_file_pattern=tfrecord/val*  \\\\\\n\",\n        \"    --val_json_file=annotations/instances_val2017.json  \\\\\\n\",\n        \"    --hparams=\\\"mean_rgb=0.0,stddev_rgb=1.0,scale_range=True\\\"\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"Det-AdvProp-tutorial.ipynb\",\n      \"provenance\": []\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/det_model_fn.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Model function definition, including both architecture and loss.\"\"\"\nimport functools\nimport re\nfrom absl import logging\nimport numpy as np\nimport tensorflow.compat.v1 as tf\nfrom tensorflow.compat.v1 import estimator as tf_estimator\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import coco_metric\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import efficientdet_arch\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import hparams_config\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import nms_np\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import anchors\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import efficientdet_keras\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import postprocess\n\n_DEFAULT_BATCH_SIZE = 64\n\n\ndef update_learning_rate_schedule_parameters(params):\n  \"\"\"Updates params that are related to the learning rate schedule.\"\"\"\n  # params['batch_size'] is per-shard within model_fn if strategy=tpu.\n  batch_size = (\n      params['batch_size'] * params['num_shards']\n      if params['strategy'] in ['tpu', 'gpus'] else params['batch_size'])\n  # Learning rate is proportional to the batch size\n  params['adjusted_learning_rate'] = (\n      params['learning_rate'] * batch_size / _DEFAULT_BATCH_SIZE)\n\n  if 'lr_warmup_init' in params:\n    params['adjusted_lr_warmup_init'] = (\n        params['lr_warmup_init'] * batch_size / _DEFAULT_BATCH_SIZE)\n\n  steps_per_epoch = params['num_examples_per_epoch'] / batch_size\n  params['lr_warmup_step'] = int(params['lr_warmup_epoch'] * steps_per_epoch)\n  params['first_lr_drop_step'] = int(params['first_lr_drop_epoch'] *\n                                     steps_per_epoch)\n  params['second_lr_drop_step'] = int(params['second_lr_drop_epoch'] *\n                                      steps_per_epoch)\n  params['total_steps'] = int(params['num_epochs'] * steps_per_epoch)\n  params['steps_per_epoch'] = steps_per_epoch\n\n\ndef stepwise_lr_schedule(adjusted_learning_rate, adjusted_lr_warmup_init,\n                         lr_warmup_step, first_lr_drop_step,\n                         second_lr_drop_step, global_step):\n  \"\"\"Handles linear scaling rule, gradual warmup, and LR decay.\"\"\"\n  # adjusted_lr_warmup_init is the starting learning rate; LR is linearly\n  # scaled up to the full learning rate after `lr_warmup_step` before decaying.\n  logging.info('LR schedule method: stepwise')\n  linear_warmup = (\n      adjusted_lr_warmup_init +\n      (tf.cast(global_step, dtype=tf.float32) / lr_warmup_step *\n       (adjusted_learning_rate - adjusted_lr_warmup_init)))\n  learning_rate = tf.where(global_step < lr_warmup_step, linear_warmup,\n                           adjusted_learning_rate)\n  lr_schedule = [[1.0, lr_warmup_step], [0.1, first_lr_drop_step],\n                 [0.01, second_lr_drop_step]]\n  for mult, start_global_step in lr_schedule:\n    learning_rate = tf.where(global_step < start_global_step, learning_rate,\n                             adjusted_learning_rate * mult)\n  return learning_rate\n\n\ndef cosine_lr_schedule(adjusted_lr, adjusted_lr_warmup_init, lr_warmup_step,\n                       total_steps, step):\n  \"\"\"Cosine learning rate scahedule.\"\"\"\n  logging.info('LR schedule method: cosine')\n  linear_warmup = (\n      adjusted_lr_warmup_init +\n      (tf.cast(step, dtype=tf.float32) / lr_warmup_step *\n       (adjusted_lr - adjusted_lr_warmup_init)))\n  decay_steps = tf.cast(total_steps - lr_warmup_step, tf.float32)\n  cosine_lr = 0.5 * adjusted_lr * (\n      1 + tf.cos(np.pi * tf.cast(step, tf.float32) / decay_steps))\n  return tf.where(step < lr_warmup_step, linear_warmup, cosine_lr)\n\n\ndef polynomial_lr_schedule(adjusted_lr, adjusted_lr_warmup_init, lr_warmup_step,\n                           power, total_steps, step):\n  logging.info('LR schedule method: polynomial')\n  linear_warmup = (\n      adjusted_lr_warmup_init +\n      (tf.cast(step, dtype=tf.float32) / lr_warmup_step *\n       (adjusted_lr - adjusted_lr_warmup_init)))\n  polynomial_lr = adjusted_lr * tf.pow(\n      1 - (tf.cast(step, tf.float32) / total_steps), power)\n  return tf.where(step < lr_warmup_step, linear_warmup, polynomial_lr)\n\n\ndef learning_rate_schedule(params, global_step):\n  \"\"\"Learning rate schedule based on global step.\"\"\"\n  lr_decay_method = params['lr_decay_method']\n  if lr_decay_method == 'stepwise':\n    return stepwise_lr_schedule(params['adjusted_learning_rate'],\n                                params['adjusted_lr_warmup_init'],\n                                params['lr_warmup_step'],\n                                params['first_lr_drop_step'],\n                                params['second_lr_drop_step'], global_step)\n\n  if lr_decay_method == 'cosine':\n    return cosine_lr_schedule(params['adjusted_learning_rate'],\n                              params['adjusted_lr_warmup_init'],\n                              params['lr_warmup_step'], params['total_steps'],\n                              global_step)\n\n  if lr_decay_method == 'polynomial':\n    return polynomial_lr_schedule(params['adjusted_learning_rate'],\n                                  params['adjusted_lr_warmup_init'],\n                                  params['lr_warmup_step'],\n                                  params['poly_lr_power'],\n                                  params['total_steps'], global_step)\n\n  if lr_decay_method == 'constant':\n    return params['adjusted_learning_rate']\n\n  raise ValueError('unknown lr_decay_method: {}'.format(lr_decay_method))\n\n\ndef focal_loss(y_pred, y_true, alpha, gamma, normalizer, label_smoothing=0.0):\n  \"\"\"Compute the focal loss between `logits` and the golden `target` values.\n\n  Focal loss = -(1-pt)^gamma * log(pt)\n  where pt is the probability of being classified to the true class.\n\n  Args:\n    y_pred: A float tensor of size [batch, height_in, width_in,\n      num_predictions].\n    y_true: A float tensor of size [batch, height_in, width_in,\n      num_predictions].\n    alpha: A float scalar multiplying alpha to the loss from positive examples\n      and (1-alpha) to the loss from negative examples.\n    gamma: A float scalar modulating loss from hard and easy examples.\n    normalizer: Divide loss by this value.\n    label_smoothing: Float in [0, 1]. If > `0` then smooth the labels.\n\n  Returns:\n    loss: A float32 scalar representing normalized total loss.\n  \"\"\"\n  with tf.name_scope('focal_loss'):\n    normalizer = tf.cast(normalizer, dtype=y_pred.dtype)\n\n    # compute focal loss multipliers before label smoothing, such that it will\n    # not blow up the loss.\n    pred_prob = tf.sigmoid(y_pred)\n    p_t = (y_true * pred_prob) + ((1 - y_true) * (1 - pred_prob))\n    alpha_factor = y_true * alpha + (1 - y_true) * (1 - alpha)\n    modulating_factor = (1.0 - p_t)**gamma\n\n    # apply label smoothing for cross_entropy for each entry.\n    if label_smoothing:\n      y_true = y_true * (1.0 - label_smoothing) + 0.5 * label_smoothing\n    ce = tf.nn.sigmoid_cross_entropy_with_logits(labels=y_true, logits=y_pred)\n\n    # compute the final loss and return\n    return (1 / normalizer) * alpha_factor * modulating_factor * ce\n\n\ndef _box_loss(box_outputs, box_targets, num_positives, delta=0.1):\n  \"\"\"Computes box regression loss.\"\"\"\n  # delta is typically around the mean value of regression target.\n  # for instances, the regression targets of 512x512 input with 6 anchors on\n  # P3-P7 pyramid is about [0.1, 0.1, 0.2, 0.2].\n  normalizer = num_positives * 4.0\n  mask = tf.not_equal(box_targets, 0.0)\n  box_loss = tf.losses.huber_loss(\n      box_targets,\n      box_outputs,\n      weights=mask,\n      delta=delta,\n      reduction=tf.losses.Reduction.SUM)\n  box_loss /= normalizer\n  return box_loss\n\n\ndef detection_loss(cls_outputs, box_outputs, labels, params):\n  \"\"\"Computes total detection loss.\n\n  Computes total detection loss including box and class loss from all levels.\n  Args:\n    cls_outputs: an OrderDict with keys representing levels and values\n      representing logits in [batch_size, height, width, num_anchors].\n    box_outputs: an OrderDict with keys representing levels and values\n      representing box regression targets in [batch_size, height, width,\n      num_anchors * 4].\n    labels: the dictionary that returned from dataloader that includes\n      groundtruth targets.\n    params: the dictionary including training parameters specified in\n      default_haprams function in this file.\n\n  Returns:\n    total_loss: an integer tensor representing total loss reducing from\n      class and box losses from all levels.\n    cls_loss: an integer tensor representing total class loss.\n    box_loss: an integer tensor representing total box regression loss.\n  \"\"\"\n  # Sum all positives in a batch for normalization and avoid zero\n  # num_positives_sum, which would lead to inf loss during training\n  num_positives_sum = tf.reduce_sum(labels['mean_num_positives']) + 1.0\n  positives_momentum = params.get('positives_momentum', None) or 0\n  if positives_momentum > 0:\n    # normalize the num_positive_examples for training stability.\n    moving_normalizer_var = tf.Variable(\n        0.0,\n        name='moving_normalizer',\n        dtype=tf.float32,\n        synchronization=tf.VariableSynchronization.ON_READ,\n        trainable=False,\n        aggregation=tf.VariableAggregation.MEAN)\n    num_positives_sum = tf.keras.backend.moving_average_update(\n        moving_normalizer_var,\n        num_positives_sum,\n        momentum=params['positives_momentum'])\n  elif positives_momentum < 0:\n    num_positives_sum = utils.cross_replica_mean(num_positives_sum)\n\n  levels = cls_outputs.keys()\n  cls_losses = []\n  box_losses = []\n  for level in levels:\n    # Onehot encoding for classification labels.\n    cls_targets_at_level = tf.one_hot(\n        labels['cls_targets_%d' % level],\n        params['num_classes'],\n        dtype=cls_outputs[level].dtype)\n\n    if params['data_format'] == 'channels_first':\n      bs, _, width, height, _ = cls_targets_at_level.get_shape().as_list()\n      cls_targets_at_level = tf.reshape(cls_targets_at_level,\n                                        [bs, -1, width, height])\n    else:\n      bs, width, height, _, _ = cls_targets_at_level.get_shape().as_list()\n      cls_targets_at_level = tf.reshape(cls_targets_at_level,\n                                        [bs, width, height, -1])\n    box_targets_at_level = labels['box_targets_%d' % level]\n\n    cls_loss = focal_loss(\n        cls_outputs[level],\n        cls_targets_at_level,\n        params['alpha'],\n        params['gamma'],\n        normalizer=num_positives_sum,\n        label_smoothing=params['label_smoothing'])\n\n    if params['data_format'] == 'channels_first':\n      cls_loss = tf.reshape(cls_loss,\n                            [bs, -1, width, height, params['num_classes']])\n    else:\n      cls_loss = tf.reshape(cls_loss,\n                            [bs, width, height, -1, params['num_classes']])\n\n    cls_loss *= tf.cast(\n        tf.expand_dims(tf.not_equal(labels['cls_targets_%d' % level], -2), -1),\n        cls_loss.dtype)\n    cls_loss_sum = tf.reduce_sum(cls_loss)\n    cls_losses.append(tf.cast(cls_loss_sum, tf.float32))\n\n    if params['box_loss_weight']:\n      box_losses.append(\n          _box_loss(\n              box_outputs[level],\n              box_targets_at_level,\n              num_positives_sum,\n              delta=params['delta']))\n\n  # Sum per level losses to total loss.\n  cls_loss = tf.add_n(cls_losses)\n  box_loss = tf.add_n(box_losses) if box_losses else tf.constant(0.)\n\n  total_loss = (\n      cls_loss + params['box_loss_weight'] * box_loss)\n\n  return total_loss, cls_loss, box_loss\n\n\ndef reg_l2_loss(weight_decay, regex=r'.*(kernel|weight):0$'):\n  \"\"\"Return regularization l2 loss loss.\"\"\"\n  var_match = re.compile(regex)\n  return weight_decay * tf.add_n([\n      tf.nn.l2_loss(v)\n      for v in tf.trainable_variables()\n      if var_match.match(v.name)\n  ])\n\n\n@tf.autograph.experimental.do_not_convert\ndef _model_fn(features, labels, mode, params, model, variable_filter_fn=None):\n  \"\"\"Model definition entry.\n\n  Args:\n    features: the input image tensor with shape [batch_size, height, width, 3].\n      The height and width are fixed and equal.\n    labels: the input labels in a dictionary. The labels include class targets\n      and box targets which are dense label maps. The labels are generated from\n      get_input_fn function in data/dataloader.py\n    mode: the mode of TPUEstimator including TRAIN and EVAL.\n    params: the dictionary defines hyperparameters of model. The default\n      settings are in default_hparams function in this file.\n    model: the model outputs class logits and box regression outputs.\n    variable_filter_fn: the filter function that takes trainable_variables and\n      returns the variable list after applying the filter rule.\n\n  Returns:\n    tpu_spec: the TPUEstimatorSpec to run training, evaluation, or prediction.\n\n  Raises:\n    RuntimeError: if both ckpt and backbone_ckpt are set.\n  \"\"\"\n  is_tpu = params['strategy'] == 'tpu'\n  if params['img_summary_steps']:\n    utils.image('input_image', features, is_tpu)\n  training_hooks = []\n  params['is_training_bn'] = (mode == tf_estimator.ModeKeys.TRAIN)\n\n  if params['use_keras_model']:\n\n    def model_fn(inputs):\n      model = efficientdet_keras.EfficientDetNet(\n          config=hparams_config.Config(params))\n      cls_out_list, box_out_list = model(inputs, params['is_training_bn'])\n      cls_outputs, box_outputs = {}, {}\n      for i in range(params['min_level'], params['max_level'] + 1):\n        cls_outputs[i] = cls_out_list[i - params['min_level']]\n        box_outputs[i] = box_out_list[i - params['min_level']]\n      return cls_outputs, box_outputs\n  else:\n    model_fn = functools.partial(model, config=hparams_config.Config(params))\n\n  precision = utils.get_precision(params['strategy'], params['mixed_precision'])\n  cls_outputs, box_outputs = utils.build_model_with_precision(\n      precision, model_fn, features)\n\n  levels = cls_outputs.keys()\n  for level in levels:\n    cls_outputs[level] = tf.cast(cls_outputs[level], tf.float32)\n    box_outputs[level] = tf.cast(box_outputs[level], tf.float32)\n\n  # Set up training loss and learning rate.\n  update_learning_rate_schedule_parameters(params)\n  global_step = tf.train.get_or_create_global_step()\n  learning_rate = learning_rate_schedule(params, global_step)\n\n  # cls_loss and box_loss are for logging. only total_loss is optimized.\n  det_loss, cls_loss, box_loss = detection_loss(\n      cls_outputs, box_outputs, labels, params)\n  reg_l2loss = reg_l2_loss(params['weight_decay'])\n  total_loss = det_loss + reg_l2loss\n\n  if mode == tf_estimator.ModeKeys.TRAIN:\n    utils.scalar('lrn_rate', learning_rate, is_tpu)\n    utils.scalar('trainloss/cls_loss', cls_loss, is_tpu)\n    utils.scalar('trainloss/box_loss', box_loss, is_tpu)\n    utils.scalar('trainloss/det_loss', det_loss, is_tpu)\n    utils.scalar('trainloss/reg_l2_loss', reg_l2loss, is_tpu)\n    utils.scalar('trainloss/loss', total_loss, is_tpu)\n    train_epochs = tf.cast(global_step, tf.float32) / params['steps_per_epoch']\n    utils.scalar('train_epochs', train_epochs, is_tpu)\n\n  moving_average_decay = params['moving_average_decay']\n  if moving_average_decay:\n    ema = tf.train.ExponentialMovingAverage(\n        decay=moving_average_decay, num_updates=global_step)\n    ema_vars = utils.get_ema_vars()\n\n  if mode == tf_estimator.ModeKeys.TRAIN:\n    if params['optimizer'].lower() == 'sgd':\n      optimizer = tf.train.MomentumOptimizer(\n          learning_rate, momentum=params['momentum'])\n    elif params['optimizer'].lower() == 'adam':\n      optimizer = tf.train.AdamOptimizer(learning_rate)\n    else:\n      raise ValueError('optimizers should be adam or sgd')\n\n    if is_tpu:\n      optimizer = tf.tpu.CrossShardOptimizer(optimizer)\n\n    # Batch norm requires update_ops to be added as a train_op dependency.\n    update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)\n    var_list = tf.trainable_variables()\n    if variable_filter_fn:\n      var_list = variable_filter_fn(var_list)\n\n    if params.get('clip_gradients_norm', None):\n      logging.info('clip gradients norm by %f', params['clip_gradients_norm'])\n      grads_and_vars = optimizer.compute_gradients(total_loss, var_list)\n      with tf.name_scope('clip'):\n        grads = [gv[0] for gv in grads_and_vars]\n        tvars = [gv[1] for gv in grads_and_vars]\n        # First clip each variable's norm, then clip global norm.\n        clip_norm = abs(params['clip_gradients_norm'])\n        clipped_grads = [\n            tf.clip_by_norm(g, clip_norm) if g is not None else None\n            for g in grads\n        ]\n        clipped_grads, _ = tf.clip_by_global_norm(clipped_grads, clip_norm)\n        utils.scalar('gradient_norm', tf.linalg.global_norm(clipped_grads),\n                     is_tpu)\n        grads_and_vars = list(zip(clipped_grads, tvars))\n\n      with tf.control_dependencies(update_ops):\n        train_op = optimizer.apply_gradients(grads_and_vars, global_step)\n    else:\n      with tf.control_dependencies(update_ops):\n        train_op = optimizer.minimize(\n            total_loss, global_step, var_list=var_list)\n\n    if moving_average_decay:\n      with tf.control_dependencies([train_op]):\n        train_op = ema.apply(ema_vars)\n\n  else:\n    train_op = None\n\n  eval_metrics = None\n  if mode == tf_estimator.ModeKeys.EVAL:\n\n    def metric_fn(**kwargs):\n      \"\"\"Returns a dictionary that has the evaluation metrics.\"\"\"\n      if params['nms_configs'].get('pyfunc', True):\n        detections_bs = []\n        nms_configs = params['nms_configs']\n        for index in range(kwargs['boxes'].shape[0]):\n          detections = tf.numpy_function(\n              functools.partial(nms_np.per_class_nms, nms_configs=nms_configs),\n              [\n                  kwargs['boxes'][index],\n                  kwargs['scores'][index],\n                  kwargs['classes'][index],\n                  tf.slice(kwargs['image_ids'], [index], [1]),\n                  tf.slice(kwargs['image_scales'], [index], [1]),\n                  params['num_classes'],\n                  nms_configs['max_output_size'],\n              ], tf.float32)\n          detections_bs.append(detections)\n        detections_bs = postprocess.transform_detections(\n            tf.stack(detections_bs))\n      else:\n        # These two branches should be equivalent, but currently they are not.\n        # TODO(tanmingxing): enable the non_pyfun path after bug fix.\n        nms_boxes, nms_scores, nms_classes, _ = postprocess.per_class_nms(\n            params, kwargs['boxes'], kwargs['scores'], kwargs['classes'],\n            kwargs['image_scales'])\n        img_ids = tf.cast(\n            tf.expand_dims(kwargs['image_ids'], -1), nms_scores.dtype)\n        detections_bs = [\n            img_ids * tf.ones_like(nms_scores),\n            nms_boxes[:, :, 1],\n            nms_boxes[:, :, 0],\n            nms_boxes[:, :, 3] - nms_boxes[:, :, 1],\n            nms_boxes[:, :, 2] - nms_boxes[:, :, 0],\n            nms_scores,\n            nms_classes,\n        ]\n        detections_bs = tf.stack(detections_bs, axis=-1, name='detnections')\n\n      if params.get('testdev_dir', None):\n        logging.info('Eval testdev_dir %s', params['testdev_dir'])\n        eval_metric = coco_metric.EvaluationMetric(\n            testdev_dir=params['testdev_dir'])\n        coco_metrics = eval_metric.estimator_metric_fn(detections_bs,\n                                                       tf.zeros([1]))\n      else:\n        logging.info('Eval val with groudtruths %s.', params['val_json_file'])\n        eval_metric = coco_metric.EvaluationMetric(\n            filename=params['val_json_file'], label_map=params['label_map'])\n        coco_metrics = eval_metric.estimator_metric_fn(\n            detections_bs, kwargs['groundtruth_data'])\n\n      # Add metrics to output.\n      cls_loss = tf.metrics.mean(kwargs['cls_loss_repeat'])\n      box_loss = tf.metrics.mean(kwargs['box_loss_repeat'])\n      output_metrics = {\n          'cls_loss': cls_loss,\n          'box_loss': box_loss,\n      }\n      output_metrics.update(coco_metrics)\n      return output_metrics\n\n    cls_loss_repeat = tf.reshape(\n        tf.tile(tf.expand_dims(cls_loss, 0), [\n            params['batch_size'],\n        ]), [params['batch_size'], 1])\n    box_loss_repeat = tf.reshape(\n        tf.tile(tf.expand_dims(box_loss, 0), [\n            params['batch_size'],\n        ]), [params['batch_size'], 1])\n\n    cls_outputs = postprocess.to_list(cls_outputs)\n    box_outputs = postprocess.to_list(box_outputs)\n    params['nms_configs']['max_nms_inputs'] = anchors.MAX_DETECTION_POINTS\n    boxes, scores, classes = postprocess.pre_nms(params, cls_outputs,\n                                                 box_outputs)\n    metric_fn_inputs = {\n        'cls_loss_repeat': cls_loss_repeat,\n        'box_loss_repeat': box_loss_repeat,\n        'image_ids': labels['source_ids'],\n        'groundtruth_data': labels['groundtruth_data'],\n        'image_scales': labels['image_scales'],\n        'boxes': boxes,\n        'scores': scores,\n        'classes': classes,\n    }\n    eval_metrics = (metric_fn, metric_fn_inputs)\n\n  checkpoint = params.get('ckpt') or params.get('backbone_ckpt')\n\n  if checkpoint and mode == tf_estimator.ModeKeys.TRAIN:\n    # Initialize the model from an EfficientDet or backbone checkpoint.\n    if params.get('ckpt') and params.get('backbone_ckpt'):\n      raise RuntimeError(\n          '--backbone_ckpt and --checkpoint are mutually exclusive')\n\n    if params.get('backbone_ckpt'):\n      var_scope = params['backbone_name'] + '/'\n      if params['ckpt_var_scope'] is None:\n        # Use backbone name as default checkpoint scope.\n        ckpt_scope = params['backbone_name'] + '/'\n      else:\n        ckpt_scope = params['ckpt_var_scope'] + '/'\n    else:\n      # Load every var in the given checkpoint\n      var_scope = ckpt_scope = '/'\n\n    def scaffold_fn():\n      \"\"\"Loads pretrained model through scaffold function.\"\"\"\n      logging.info('restore variables from %s', checkpoint)\n\n      var_map = utils.get_ckpt_var_map(\n          ckpt_path=checkpoint,\n          ckpt_scope=ckpt_scope,\n          var_scope=var_scope,\n          skip_mismatch=params['skip_mismatch'])\n\n      tf.train.init_from_checkpoint(checkpoint, var_map)\n      return tf.train.Scaffold()\n  elif mode == tf_estimator.ModeKeys.EVAL and moving_average_decay:\n\n    def scaffold_fn():\n      \"\"\"Load moving average variables for eval.\"\"\"\n      logging.info('Load EMA vars with ema_decay=%f', moving_average_decay)\n      restore_vars_dict = ema.variables_to_restore(ema_vars)\n      saver = tf.train.Saver(restore_vars_dict)\n      return tf.train.Scaffold(saver=saver)\n  else:\n    scaffold_fn = None\n\n  if is_tpu:\n    return tf_estimator.tpu.TPUEstimatorSpec(\n        mode=mode,\n        loss=total_loss,\n        train_op=train_op,\n        eval_metrics=eval_metrics,\n        host_call=utils.get_tpu_host_call(global_step, params),\n        scaffold_fn=scaffold_fn,\n        training_hooks=training_hooks)\n  else:\n    # Profile every 1K steps.\n    if params.get('profile', False):\n      profile_hook = tf_estimator.ProfilerHook(\n          save_steps=1000, output_dir=params['model_dir'], show_memory=True)\n      training_hooks.append(profile_hook)\n\n      # Report memory allocation if OOM; it will slow down the running.\n      class OomReportingHook(tf_estimator.SessionRunHook):\n\n        def before_run(self, run_context):\n          return tf_estimator.SessionRunArgs(\n              fetches=[],\n              options=tf.RunOptions(report_tensor_allocations_upon_oom=True))\n\n      training_hooks.append(OomReportingHook())\n\n    logging_hook = tf_estimator.LoggingTensorHook(\n        {\n            'step': global_step,\n            'det_loss': det_loss,\n            'cls_loss': cls_loss,\n            'box_loss': box_loss,\n        },\n        every_n_iter=params.get('iterations_per_loop', 100),\n    )\n    training_hooks.append(logging_hook)\n\n    eval_metric_ops = (\n        eval_metrics[0](**eval_metrics[1]) if eval_metrics else None)\n    return tf_estimator.EstimatorSpec(\n        mode=mode,\n        loss=total_loss,\n        train_op=train_op,\n        eval_metric_ops=eval_metric_ops,\n        scaffold=scaffold_fn() if scaffold_fn else None,\n        training_hooks=training_hooks)\n\n\ndef efficientdet_model_fn(features, labels, mode, params):\n  \"\"\"EfficientDet model.\"\"\"\n  variable_filter_fn = functools.partial(\n      efficientdet_arch.freeze_vars, pattern=params['var_freeze_expr'])\n  return _model_fn(\n      features,\n      labels,\n      mode,\n      params,\n      model=efficientdet_arch.efficientdet,\n      variable_filter_fn=variable_filter_fn)\n\n\ndef get_model_arch(model_name='efficientdet-d0'):\n  \"\"\"Get model architecture for a given model name.\"\"\"\n  if 'efficientdet' in model_name:\n    return efficientdet_arch.efficientdet\n\n  raise ValueError('Invalide model name {}'.format(model_name))\n\n\ndef get_model_fn(model_name='efficientdet-d0'):\n  \"\"\"Get model fn for a given model name.\"\"\"\n  if 'efficientdet' in model_name:\n    return efficientdet_model_fn\n\n  raise ValueError('Invalide model name {}'.format(model_name))\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/efficientdet_arch.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"EfficientDet model definition.\n\n[1] Mingxing Tan, Ruoming Pang, Quoc Le.\n    EfficientDet: Scalable and Efficient Object Detection.\n    CVPR 2020, https://arxiv.org/abs/1911.09070\n\"\"\"\nimport functools\nimport re\n\nfrom absl import logging\nimport numpy as np\nimport tensorflow.compat.v1 as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import hparams_config\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.backbone import backbone_factory\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.backbone import efficientnet_builder\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import fpn_configs\n\n\n################################################################################\ndef freeze_vars(variables, pattern):\n  \"\"\"Removes backbone+fpn variables from the input.\n\n  Args:\n    variables: all the variables in training\n    pattern: a reg experession such as \".*(efficientnet|fpn_cells).*\".\n\n  Returns:\n    var_list: a list containing variables for training\n  \"\"\"\n  if pattern:\n    filtered_vars = [v for v in variables if not re.match(pattern, v.name)]\n    if len(filtered_vars) == len(variables):\n      logging.warning('%s didnt match with any variable. Please use compatible '\n                      'pattern. i.e \"(efficientnet)\"', pattern)\n    return filtered_vars\n  return variables\n\n\ndef resample_feature_map(feat,\n                         name,\n                         target_height,\n                         target_width,\n                         target_num_channels,\n                         apply_bn=False,\n                         is_training=None,\n                         conv_after_downsample=False,\n                         strategy=None,\n                         data_format='channels_last',\n                         batch_norm_trainable=True):\n  \"\"\"Resample input feature map to have target number of channels and size.\"\"\"\n  if data_format == 'channels_first':\n    _, num_channels, height, width = feat.get_shape().as_list()\n  else:\n    _, height, width, num_channels = feat.get_shape().as_list()\n\n  if height is None or width is None or num_channels is None:\n    raise ValueError(\n        'shape[1] or shape[2] or shape[3] of feat is None (shape:{}).'.format(\n            feat.shape))\n  if apply_bn and is_training is None:\n    raise ValueError('If BN is applied, need to provide is_training')\n\n  def _maybe_apply_1x1(feat):\n    \"\"\"Apply 1x1 conv to change layer width if necessary.\"\"\"\n    if num_channels != target_num_channels:\n      feat = tf.layers.conv2d(\n          feat,\n          filters=target_num_channels,\n          kernel_size=(1, 1),\n          padding='same',\n          data_format=data_format)\n      if apply_bn:\n        feat = utils.batch_norm_act(\n            feat,\n            is_training_bn=is_training,\n            act_type=None,\n            data_format=data_format,\n            strategy=strategy,\n            batch_norm_trainable=batch_norm_trainable,\n            name='bn')\n    return feat\n\n  with tf.variable_scope('resample_{}'.format(name)):\n    # If conv_after_downsample is True, when downsampling, apply 1x1 after\n    # downsampling for efficiency.\n    if height > target_height and width > target_width:\n      if not conv_after_downsample:\n        feat = _maybe_apply_1x1(feat)\n      height_stride_size = int((height - 1) // target_height + 1)\n      width_stride_size = int((width - 1) // target_width + 1)\n\n      # Use max pooling in default.\n      feat = tf.layers.max_pooling2d(\n          inputs=feat,\n          pool_size=[height_stride_size + 1, width_stride_size + 1],\n          strides=[height_stride_size, width_stride_size],\n          padding='SAME',\n          data_format=data_format)\n\n      if conv_after_downsample:\n        feat = _maybe_apply_1x1(feat)\n    elif height <= target_height and width <= target_width:\n      feat = _maybe_apply_1x1(feat)\n      if height < target_height or width < target_width:\n        if data_format == 'channels_first':\n          feat = tf.transpose(feat, [0, 2, 3, 1])\n        feat = tf.cast(\n            tf.image.resize_nearest_neighbor(\n                tf.cast(feat, tf.float32), [target_height, target_width]),\n            dtype=feat.dtype)\n        if data_format == 'channels_first':\n          feat = tf.transpose(feat, [0, 3, 1, 2])\n    else:\n      raise ValueError(\n          'Incompatible target feature map size: target_height: {},'\n          'target_width: {}'.format(target_height, target_width))\n\n  return feat\n\n\n###############################################################################\ndef class_net(images,\n              level,\n              num_classes,\n              num_anchors,\n              num_filters,\n              is_training,\n              act_type,\n              separable_conv=True,\n              repeats=4,\n              survival_prob=None,\n              strategy=None,\n              data_format='channels_last'):\n  \"\"\"Class prediction network.\"\"\"\n  if separable_conv:\n    conv_op = functools.partial(\n        tf.layers.separable_conv2d, depth_multiplier=1,\n        data_format=data_format,\n        pointwise_initializer=tf.initializers.variance_scaling(),\n        depthwise_initializer=tf.initializers.variance_scaling())\n  else:\n    conv_op = functools.partial(\n        tf.layers.conv2d,\n        data_format=data_format,\n        kernel_initializer=tf.random_normal_initializer(stddev=0.01))\n\n  for i in range(repeats):\n    orig_images = images\n    images = conv_op(\n        images,\n        num_filters,\n        kernel_size=3,\n        bias_initializer=tf.zeros_initializer(),\n        activation=None,\n        padding='same',\n        name='class-%d' % i)\n    images = utils.batch_norm_act(\n        images,\n        is_training,\n        act_type=act_type,\n        init_zero=False,\n        strategy=strategy,\n        data_format=data_format,\n        name='class-%d-bn-%d' % (i, level))\n\n    if i > 0 and survival_prob:\n      images = utils.drop_connect(images, is_training, survival_prob)\n      images = images + orig_images\n\n  classes = conv_op(\n      images,\n      num_classes * num_anchors,\n      kernel_size=3,\n      bias_initializer=tf.constant_initializer(-np.log((1 - 0.01) / 0.01)),\n      padding='same',\n      name='class-predict')\n  return classes\n\n\ndef box_net(images,\n            level,\n            num_anchors,\n            num_filters,\n            is_training,\n            act_type,\n            repeats=4,\n            separable_conv=True,\n            survival_prob=None,\n            strategy=None,\n            data_format='channels_last'):\n  \"\"\"Box regression network.\"\"\"\n  if separable_conv:\n    conv_op = functools.partial(\n        tf.layers.separable_conv2d, depth_multiplier=1,\n        data_format=data_format,\n        pointwise_initializer=tf.initializers.variance_scaling(),\n        depthwise_initializer=tf.initializers.variance_scaling())\n  else:\n    conv_op = functools.partial(\n        tf.layers.conv2d,\n        data_format=data_format,\n        kernel_initializer=tf.random_normal_initializer(stddev=0.01))\n\n  for i in range(repeats):\n    orig_images = images\n    images = conv_op(\n        images,\n        num_filters,\n        kernel_size=3,\n        activation=None,\n        bias_initializer=tf.zeros_initializer(),\n        padding='same',\n        name='box-%d' % i)\n    images = utils.batch_norm_act(\n        images,\n        is_training,\n        act_type=act_type,\n        init_zero=False,\n        strategy=strategy,\n        data_format=data_format,\n        name='box-%d-bn-%d' % (i, level))\n\n    if i > 0 and survival_prob:\n      images = utils.drop_connect(images, is_training, survival_prob)\n      images = images + orig_images\n\n  boxes = conv_op(\n      images,\n      4 * num_anchors,\n      kernel_size=3,\n      bias_initializer=tf.zeros_initializer(),\n      padding='same',\n      name='box-predict')\n\n  return boxes\n\n\ndef build_class_and_box_outputs(feats, config):\n  \"\"\"Builds box net and class net.\n\n  Args:\n   feats: input tensor.\n   config: a dict-like config, including all parameters.\n\n  Returns:\n   A tuple (class_outputs, box_outputs) for class/box predictions.\n  \"\"\"\n\n  class_outputs = {}\n  box_outputs = {}\n  num_anchors = len(config.aspect_ratios) * config.num_scales\n  cls_fsize = config.fpn_num_filters\n  with tf.variable_scope('class_net', reuse=tf.AUTO_REUSE):\n    for level in range(config.min_level,\n                       config.max_level + 1):\n      class_outputs[level] = class_net(\n          images=feats[level],\n          level=level,\n          num_classes=config.num_classes,\n          num_anchors=num_anchors,\n          num_filters=cls_fsize,\n          is_training=config.is_training_bn,\n          act_type=config.act_type,\n          repeats=config.box_class_repeats,\n          separable_conv=config.separable_conv,\n          survival_prob=config.survival_prob,\n          strategy=config.strategy,\n          data_format=config.data_format\n          )\n\n  box_fsize = config.fpn_num_filters\n  with tf.variable_scope('box_net', reuse=tf.AUTO_REUSE):\n    for level in range(config.min_level,\n                       config.max_level + 1):\n      box_outputs[level] = box_net(\n          images=feats[level],\n          level=level,\n          num_anchors=num_anchors,\n          num_filters=box_fsize,\n          is_training=config.is_training_bn,\n          act_type=config.act_type,\n          repeats=config.box_class_repeats,\n          separable_conv=config.separable_conv,\n          survival_prob=config.survival_prob,\n          strategy=config.strategy,\n          data_format=config.data_format)\n\n  return class_outputs, box_outputs\n\n\ndef build_backbone(features, config):\n  \"\"\"Builds backbone model.\n\n  Args:\n   features: input tensor.\n   config: config for backbone, such as is_training_bn and backbone name.\n\n  Returns:\n    A dict from levels to the feature maps from the output of the backbone model\n    with strides of 8, 16 and 32.\n\n  Raises:\n    ValueError: if backbone_name is not supported.\n  \"\"\"\n  backbone_name = config.backbone_name\n  is_training_bn = config.is_training_bn\n  if 'efficientnet' in backbone_name:\n    override_params = {\n        'batch_norm':\n            utils.batch_norm_class(is_training_bn, config.strategy),\n        'relu_fn':\n            functools.partial(utils.activation_fn, act_type=config.act_type),\n    }\n    if 'b0' in backbone_name:\n      override_params['survival_prob'] = 0.0\n    backbone_config = config.backbone_config\n    if backbone_config is not None:\n      override_params['blocks_args'] = (\n          efficientnet_builder.BlockDecoder().encode(\n              backbone_config.blocks))\n    override_params['data_format'] = config.data_format\n    model_builder = backbone_factory.get_model_builder(backbone_name)\n    _, endpoints = model_builder.build_model_base(\n        features,\n        backbone_name,\n        training=is_training_bn,\n        override_params=override_params)\n    u1 = endpoints[0]\n    u2 = endpoints[1]\n    u3 = endpoints[2]\n    u4 = endpoints[3]\n    u5 = endpoints[4]\n  else:\n    raise ValueError(\n        'backbone model {} is not supported.'.format(backbone_name))\n  return {0: features, 1: u1, 2: u2, 3: u3, 4: u4, 5: u5}\n\n\ndef build_feature_network(features, config):\n  \"\"\"Build FPN input features.\n\n  Args:\n   features: input tensor.\n   config: a dict-like config, including all parameters.\n\n  Returns:\n    A dict from levels to the feature maps processed after feature network.\n  \"\"\"\n  feat_sizes = utils.get_feat_sizes(config.image_size, config.max_level)\n  feats = []\n  if config.min_level not in features.keys():\n    raise ValueError('features.keys ({}) should include min_level ({})'.format(\n        features.keys(), config.min_level))\n\n  # Build additional input features that are not from backbone.\n  for level in range(config.min_level, config.max_level + 1):\n    if level in features.keys():\n      feats.append(features[level])\n    else:\n      h_id, w_id = (2, 3) if config.data_format == 'channels_first' else (1, 2)\n      # Adds a coarser level by downsampling the last feature map.\n      feats.append(\n          resample_feature_map(\n              feats[-1],\n              name='p%d' % level,\n              target_height=(feats[-1].shape[h_id] - 1) // 2 + 1,\n              target_width=(feats[-1].shape[w_id] - 1) // 2 + 1,\n              target_num_channels=config.fpn_num_filters,\n              apply_bn=config.apply_bn_for_resampling,\n              is_training=config.is_training_bn,\n              conv_after_downsample=config.conv_after_downsample,\n              strategy=config.strategy,\n              data_format=config.data_format,\n              batch_norm_trainable=config.batch_norm_trainable\n          ))\n\n  utils.verify_feats_size(\n      feats,\n      feat_sizes=feat_sizes,\n      min_level=config.min_level,\n      max_level=config.max_level,\n      data_format=config.data_format)\n\n  with tf.variable_scope('fpn_cells'):\n    for rep in range(config.fpn_cell_repeats):\n      with tf.variable_scope('cell_{}'.format(rep)):\n        logging.info('building cell %d', rep)\n        new_feats = build_bifpn_layer(feats, feat_sizes, config)\n\n        feats = [\n            new_feats[level]\n            for level in range(\n                config.min_level, config.max_level + 1)\n        ]\n\n        utils.verify_feats_size(\n            feats,\n            feat_sizes=feat_sizes,\n            min_level=config.min_level,\n            max_level=config.max_level,\n            data_format=config.data_format)\n\n  return new_feats\n\n\ndef fuse_features(nodes, weight_method):\n  \"\"\"Fuse features from different resolutions and return a weighted sum.\n\n  Args:\n    nodes: a list of tensorflow features at different levels\n    weight_method: feature fusion method. One of:\n      - \"attn\" - Softmax weighted fusion\n      - \"fastattn\" - Fast normalzied feature fusion\n      - \"sum\" - a sum of inputs\n\n  Returns:\n    A tensor denoting the fused feature.\n  \"\"\"\n  dtype = nodes[0].dtype\n\n  if weight_method == 'attn':\n    edge_weights = [tf.cast(tf.Variable(1.0, name='WSM'), dtype=dtype)\n                    for _ in nodes]\n    normalized_weights = tf.nn.softmax(tf.stack(edge_weights))\n    nodes = tf.stack(nodes, axis=-1)\n    new_node = tf.reduce_sum(nodes * normalized_weights, -1)\n  elif weight_method == 'fastattn':\n    edge_weights = []\n    for i, _ in enumerate(nodes):\n      edge_weights.append(\n          tf.nn.relu(\n              tf.get_variable(\n                  'WSM' if i == 0 else f'WSM_{i}',\n                  shape=[],\n                  initializer=tf.ones_initializer(),\n                  dtype=dtype)))\n\n    weights_sum = tf.add_n(edge_weights)\n    nodes = [nodes[i] * edge_weights[i] / (weights_sum + 0.0001)\n             for i in range(len(nodes))]\n    new_node = tf.add_n(nodes)\n  elif weight_method == 'channel_attn':\n    num_filters = int(nodes[0].shape[-1])\n    edge_weights = [\n        tf.cast(\n            tf.Variable(lambda: tf.ones([num_filters]), name='WSM'),\n            dtype=dtype) for _ in nodes\n    ]\n    normalized_weights = tf.nn.softmax(tf.stack(edge_weights, -1), axis=-1)\n    nodes = tf.stack(nodes, axis=-1)\n    new_node = tf.reduce_sum(nodes * normalized_weights, -1)\n  elif weight_method == 'channel_fastattn':\n    num_filters = int(nodes[0].shape[-1])\n    edge_weights = [\n        tf.nn.relu(tf.cast(\n            tf.Variable(lambda: tf.ones([num_filters]), name='WSM'),\n            dtype=dtype)) for _ in nodes\n    ]\n    weights_sum = tf.add_n(edge_weights)\n    nodes = [nodes[i] * edge_weights[i] / (weights_sum + 0.0001)\n             for i in range(len(nodes))]\n    new_node = tf.add_n(nodes)\n  elif weight_method == 'sum':\n    new_node = tf.add_n(nodes)\n  else:\n    raise ValueError(\n        'unknown weight_method {}'.format(weight_method))\n\n  return new_node\n\n\ndef build_bifpn_layer(feats, feat_sizes, config):\n  \"\"\"Builds a feature pyramid given previous feature pyramid and config.\"\"\"\n  p = config  # use p to denote the network config.\n  if p.fpn_config:\n    fpn_config = p.fpn_config\n  else:\n    fpn_config = fpn_configs.get_fpn_config(p.fpn_name, p.min_level,\n                                            p.max_level, p.fpn_weight_method)\n\n  num_output_connections = [0 for _ in feats]\n  for i, fnode in enumerate(fpn_config.nodes):\n    with tf.variable_scope('fnode{}'.format(i)):\n      logging.info('fnode %d : %s', i, fnode)\n      new_node_height = feat_sizes[fnode['feat_level']]['height']\n      new_node_width = feat_sizes[fnode['feat_level']]['width']\n      nodes = []\n      for idx, input_offset in enumerate(fnode['inputs_offsets']):\n        input_node = feats[input_offset]\n        num_output_connections[input_offset] += 1\n        input_node = resample_feature_map(\n            input_node, '{}_{}_{}'.format(idx, input_offset, len(feats)),\n            new_node_height, new_node_width, p.fpn_num_filters,\n            p.apply_bn_for_resampling, p.is_training_bn,\n            p.conv_after_downsample,\n            strategy=p.strategy,\n            data_format=config.data_format,\n            batch_norm_trainable=p.batch_norm_trainable)\n        nodes.append(input_node)\n\n      new_node = fuse_features(nodes, fpn_config.weight_method)\n\n      with tf.variable_scope('op_after_combine{}'.format(len(feats))):\n        if not p.conv_bn_act_pattern:\n          new_node = utils.activation_fn(new_node, p.act_type)\n\n        if p.separable_conv:\n          conv_op = functools.partial(\n              tf.layers.separable_conv2d, depth_multiplier=1)\n        else:\n          conv_op = tf.layers.conv2d\n\n        new_node = conv_op(\n            new_node,\n            filters=p.fpn_num_filters,\n            kernel_size=(3, 3),\n            padding='same',\n            use_bias=not p.conv_bn_act_pattern,\n            data_format=config.data_format,\n            name='conv')\n\n        new_node = utils.batch_norm_act(\n            new_node,\n            is_training_bn=p.is_training_bn,\n            act_type=None if not p.conv_bn_act_pattern else p.act_type,\n            data_format=config.data_format,\n            strategy=p.strategy,\n            batch_norm_trainable=p.batch_norm_trainable,\n            name='bn')\n\n      feats.append(new_node)\n      num_output_connections.append(0)\n\n  output_feats = {}\n  for l in range(p.min_level, p.max_level + 1):\n    for i, fnode in enumerate(reversed(fpn_config.nodes)):\n      if fnode['feat_level'] == l:\n        output_feats[l] = feats[-1 - i]\n        break\n  return output_feats\n\n\ndef efficientdet(features, model_name=None, config=None, **kwargs):\n  \"\"\"Build EfficientDet model.\"\"\"\n  if not config and not model_name:\n    raise ValueError('please specify either model name or config')\n\n  if not config:\n    config = hparams_config.get_efficientdet_config(model_name)\n  elif isinstance(config, dict):\n    config = hparams_config.Config(config)  # wrap dict in Config object\n\n  if kwargs:\n    config.override(kwargs)\n\n  logging.info(config)\n\n  # build backbone features.\n  features = build_backbone(features, config)\n  logging.info('backbone params/flops = {:.6f}M, {:.9f}B'.format(\n      *utils.num_params_flops()))\n\n  # build feature network.\n  fpn_feats = build_feature_network(features, config)\n  logging.info('backbone+fpn params/flops = {:.6f}M, {:.9f}B'.format(\n      *utils.num_params_flops()))\n\n  # build class and box predictions.\n  class_outputs, box_outputs = build_class_and_box_outputs(fpn_feats, config)\n  logging.info('backbone+fpn+box params/flops = {:.6f}M, {:.9f}B'.format(\n      *utils.num_params_flops()))\n\n  return class_outputs, box_outputs\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/hparams_config.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Hparams for model architecture and trainer.\"\"\"\nimport ast\nfrom collections import abc\nimport copy\nfrom typing import Any, Dict, Text\nimport six\nimport tensorflow as tf\nimport yaml\n\n\ndef eval_str_fn(val):\n  if val in {'true', 'false'}:\n    return val == 'true'\n  try:\n    return ast.literal_eval(val)\n  except (ValueError, SyntaxError):\n    return val\n\n\n# pylint: disable=protected-access\nclass Config(object):\n  \"\"\"A config utility class.\"\"\"\n\n  def __init__(self, config_dict=None):\n    self.update(config_dict)\n\n  def __setattr__(self, k, v):\n    self.__dict__[k] = Config(v) if isinstance(v, dict) else copy.deepcopy(v)\n\n  def __getattr__(self, k):\n    return self.__dict__[k]\n\n  def __getitem__(self, k):\n    return self.__dict__[k]\n\n  def __repr__(self):\n    return repr(self.as_dict())\n\n  def __deepcopy__(self, memodict):\n    return type(self)(self.as_dict())\n\n  def __str__(self):\n    try:\n      return yaml.dump(self.as_dict(), indent=4)\n    except TypeError:\n      return str(self.as_dict())\n\n  def _update(self, config_dict, allow_new_keys=True):\n    \"\"\"Recursively update internal members.\"\"\"\n    if not config_dict:\n      return\n\n    for k, v in six.iteritems(config_dict):\n      if k not in self.__dict__:\n        if allow_new_keys:\n          self.__setattr__(k, v)\n        else:\n          raise KeyError('Key `{}` does not exist for overriding. '.format(k))\n      else:\n        if isinstance(self.__dict__[k], Config) and isinstance(v, dict):\n          self.__dict__[k]._update(v, allow_new_keys)\n        elif isinstance(self.__dict__[k], Config) and isinstance(v, Config):\n          self.__dict__[k]._update(v.as_dict(), allow_new_keys)\n        else:\n          self.__setattr__(k, v)\n\n  def get(self, k, default_value=None):\n    return self.__dict__.get(k, default_value)\n\n  def update(self, config_dict):\n    \"\"\"Update members while allowing new keys.\"\"\"\n    self._update(config_dict, allow_new_keys=True)\n\n  def keys(self):\n    return self.__dict__.keys()\n\n  def override(self, config_dict_or_str, allow_new_keys=False):\n    \"\"\"Update members while disallowing new keys.\"\"\"\n    if isinstance(config_dict_or_str, str):\n      if not config_dict_or_str:\n        return\n      elif '=' in config_dict_or_str:\n        config_dict = self.parse_from_str(config_dict_or_str)\n      elif config_dict_or_str.endswith('.yaml'):\n        config_dict = self.parse_from_yaml(config_dict_or_str)\n      else:\n        raise ValueError(\n            'Invalid string {}, must end with .yaml or contains \"=\".'.format(\n                config_dict_or_str))\n    elif isinstance(config_dict_or_str, dict):\n      config_dict = config_dict_or_str\n    else:\n      raise ValueError('Unknown value type: {}'.format(config_dict_or_str))\n\n    self._update(config_dict, allow_new_keys)\n\n  def parse_from_yaml(self, yaml_file_path: Text) -> Dict[Any, Any]:\n    \"\"\"Parses a yaml file and returns a dictionary.\"\"\"\n    with tf.io.gfile.GFile(yaml_file_path, 'r') as f:\n      config_dict = yaml.load(f, Loader=yaml.FullLoader)\n      return config_dict\n\n  def save_to_yaml(self, yaml_file_path):\n    \"\"\"Write a dictionary into a yaml file.\"\"\"\n    with tf.io.gfile.GFile(yaml_file_path, 'w') as f:\n      yaml.dump(self.as_dict(), f, default_flow_style=False)\n\n  def parse_from_str(self, config_str: Text) -> Dict[Any, Any]:\n    \"\"\"Parse a string like 'x.y=1,x.z=2' to nested dict {x: {y: 1, z: 2}}.\"\"\"\n    if not config_str:\n      return {}\n    config_dict = {}\n    try:\n      for kv_pair in config_str.split(','):\n        if not kv_pair:  # skip empty string\n          continue\n        key_str, value_str = kv_pair.split('=')\n        key_str = key_str.strip()\n\n        def add_kv_recursive(k, v):\n          \"\"\"Recursively parse x.y.z=tt to {x: {y: {z: tt}}}.\"\"\"\n          if '.' not in k:\n            if '*' in v:\n              # we reserve * to split arrays.\n              return {k: [eval_str_fn(vv) for vv in v.split('*')]}\n            return {k: eval_str_fn(v)}\n          pos = k.index('.')\n          return {k[:pos]: add_kv_recursive(k[pos + 1:], v)}\n\n        def merge_dict_recursive(target, src):\n          \"\"\"Recursively merge two nested dictionary.\"\"\"\n          for k in src.keys():\n            if ((k in target and isinstance(target[k], dict) and\n                 isinstance(src[k], abc.Mapping))):\n              merge_dict_recursive(target[k], src[k])\n            else:\n              target[k] = src[k]\n\n        merge_dict_recursive(config_dict, add_kv_recursive(key_str, value_str))\n      return config_dict\n    except ValueError:\n      raise ValueError('Invalid config_str: {}'.format(config_str))\n\n  def as_dict(self):\n    \"\"\"Returns a dict representation.\"\"\"\n    config_dict = {}\n    for k, v in six.iteritems(self.__dict__):\n      if isinstance(v, Config):\n        config_dict[k] = v.as_dict()\n      else:\n        config_dict[k] = copy.deepcopy(v)\n    return config_dict\n    # pylint: enable=protected-access\n\n\ndef default_detection_configs():\n  \"\"\"Returns a default detection configs.\"\"\"\n  h = Config()\n\n  # model name.\n  h.name = 'efficientdet-d1'\n\n  # activation type: see activation_fn in utils.py.\n  h.act_type = 'swish'\n\n  # input preprocessing parameters\n  h.image_size = 640  # An integer or a string WxH such as 640x320.\n  h.target_size = None\n  h.input_rand_hflip = True\n  h.jitter_min = 0.1\n  h.jitter_max = 2.0\n  h.autoaugment_policy = None\n  h.grid_mask = False\n  h.sample_image = None\n  h.map_freq = 5  # AP eval frequency in epochs.\n\n  # dataset specific parameters\n  # TODO(tanmingxing): update this to be 91 for COCO, and 21 for pascal.\n  h.num_classes = 90  # 1+ actual classes, 0 is reserved for background.\n  h.seg_num_classes = 3  # segmentation classes\n  h.heads = ['object_detection']  # 'object_detection', 'segmentation'\n\n  h.skip_crowd_during_training = True\n  h.label_map = None  # a dict or a string of 'coco', 'voc', 'waymo'.\n  h.max_instances_per_image = 100  # Default to 100 for COCO.\n  h.regenerate_source_id = False\n\n  # model architecture\n  h.min_level = 3\n  h.max_level = 7\n  h.num_scales = 3\n  # ratio w/h: 2.0 means w=1.4, h=0.7. Can be computed with k-mean per dataset.\n  h.aspect_ratios = [1.0, 2.0, 0.5]  # [[0.7, 1.4], [1.0, 1.0], [1.4, 0.7]]\n  h.anchor_scale = 4.0\n  # is batchnorm training mode\n  h.is_training_bn = True\n  # optimization\n  h.momentum = 0.9\n  h.optimizer = 'sgd'  # can be 'adam' or 'sgd'.\n  h.learning_rate = 0.08  # 0.008 for adam.\n  h.lr_warmup_init = 0.008  # 0.0008 for adam.\n  h.lr_warmup_epoch = 1.0\n  h.first_lr_drop_epoch = 200.0\n  h.second_lr_drop_epoch = 250.0\n  h.poly_lr_power = 0.9\n  h.clip_gradients_norm = 10.0\n  h.num_epochs = 300\n  h.data_format = 'channels_last'\n  # The default image normalization is identical to Cloud TPU ResNet.\n  h.mean_rgb = [0.485 * 255, 0.456 * 255, 0.406 * 255]\n  h.stddev_rgb = [0.229 * 255, 0.224 * 255, 0.225 * 255]\n  h.scale_range = False\n\n  # classification loss\n  h.label_smoothing = 0.0  # 0.1 is a good default\n  # Behold the focal loss parameters\n  h.alpha = 0.25\n  h.gamma = 1.5\n\n  # localization loss\n  h.delta = 0.1  # regularization parameter of huber loss.\n  # total loss = box_loss * box_loss_weight + iou_loss * iou_loss_weight\n  h.box_loss_weight = 50.0\n  h.iou_loss_type = None\n  h.iou_loss_weight = 1.0\n\n  # regularization l2 loss.\n  h.weight_decay = 4e-5\n  h.strategy = None  # 'tpu', 'gpus', None\n  h.mixed_precision = False  # If False, use float32.\n  h.loss_scale = None  # set to 2**16 enables dynamic loss scale\n  h.model_optimizations = {}  # 'prune':{}\n\n  # For detection.\n  h.box_class_repeats = 3\n  h.fpn_cell_repeats = 3\n  h.fpn_num_filters = 88\n  h.separable_conv = True\n  h.apply_bn_for_resampling = True\n  h.conv_after_downsample = False\n  h.conv_bn_act_pattern = False\n  h.drop_remainder = True  # drop remainder for the final batch eval.\n\n  # For post-processing nms, must be a dict.\n  h.nms_configs = {\n      'method': 'gaussian',\n      'iou_thresh': None,  # use the default value based on method.\n      'score_thresh': 0.,\n      'sigma': None,\n      'pyfunc': False,\n      'max_nms_inputs': 0,\n      'max_output_size': 100,\n  }\n  h.tflite_max_detections = 100\n\n  # version.\n  h.fpn_name = None\n  h.fpn_weight_method = None\n  h.fpn_config = None\n  h.batch_norm_trainable = True\n\n  # No stochastic depth in default.\n  h.survival_prob = None\n  h.img_summary_steps = None\n\n  h.lr_decay_method = 'cosine'\n  h.moving_average_decay = 0.9998\n  h.ckpt_var_scope = None  # ckpt variable scope.\n  # If true, skip loading pretrained weights if shape mismatches.\n  h.skip_mismatch = True\n\n  h.backbone_name = 'efficientnet-b1'\n  h.backbone_config = None\n  h.var_freeze_expr = None\n\n  # A temporary flag to switch between legacy and keras models.\n  h.use_keras_model = True\n  h.dataset_type = None\n  h.positives_momentum = None\n  h.grad_checkpoint = False\n\n  # Parameters for the Checkpoint Callback.\n  h.verbose = 1\n  h.save_freq = 'epoch'\n\n  return h\n\n\nefficientdet_model_param_dict = {\n    'efficientdet-d0':\n        dict(\n            name='efficientdet-d0',\n            backbone_name='efficientnet-b0',\n            image_size=512,\n            fpn_num_filters=64,\n            fpn_cell_repeats=3,\n            box_class_repeats=3,\n        ),\n    'efficientdet-d1':\n        dict(\n            name='efficientdet-d1',\n            backbone_name='efficientnet-b1',\n            image_size=640,\n            fpn_num_filters=88,\n            fpn_cell_repeats=4,\n            box_class_repeats=3,\n        ),\n    'efficientdet-d2':\n        dict(\n            name='efficientdet-d2',\n            backbone_name='efficientnet-b2',\n            image_size=768,\n            fpn_num_filters=112,\n            fpn_cell_repeats=5,\n            box_class_repeats=3,\n        ),\n    'efficientdet-d3':\n        dict(\n            name='efficientdet-d3',\n            backbone_name='efficientnet-b3',\n            image_size=896,\n            fpn_num_filters=160,\n            fpn_cell_repeats=6,\n            box_class_repeats=4,\n        ),\n    'efficientdet-d4':\n        dict(\n            name='efficientdet-d4',\n            backbone_name='efficientnet-b4',\n            image_size=1024,\n            fpn_num_filters=224,\n            fpn_cell_repeats=7,\n            box_class_repeats=4,\n        ),\n    'efficientdet-d5':\n        dict(\n            name='efficientdet-d5',\n            backbone_name='efficientnet-b5',\n            image_size=1280,\n            fpn_num_filters=288,\n            fpn_cell_repeats=7,\n            box_class_repeats=4,\n        ),\n    'efficientdet-d6':\n        dict(\n            name='efficientdet-d6',\n            backbone_name='efficientnet-b6',\n            image_size=1280,\n            fpn_num_filters=384,\n            fpn_cell_repeats=8,\n            box_class_repeats=5,\n            fpn_weight_method='sum',  # Use unweighted sum for stability.\n        ),\n    'efficientdet-d7':\n        dict(\n            name='efficientdet-d7',\n            backbone_name='efficientnet-b6',\n            image_size=1536,\n            fpn_num_filters=384,\n            fpn_cell_repeats=8,\n            box_class_repeats=5,\n            anchor_scale=5.0,\n            fpn_weight_method='sum',  # Use unweighted sum for stability.\n        ),\n    'efficientdet-d7x':\n        dict(\n            name='efficientdet-d7x',\n            backbone_name='efficientnet-b7',\n            image_size=1536,\n            fpn_num_filters=384,\n            fpn_cell_repeats=8,\n            box_class_repeats=5,\n            anchor_scale=4.0,\n            max_level=8,\n            fpn_weight_method='sum',  # Use unweighted sum for stability.\n        ),\n}\n\n\nlite_common_param = dict(\n    mean_rgb=127.0,\n    stddev_rgb=128.0,\n    act_type='relu6',\n    fpn_weight_method='sum',\n)\n\nefficientdet_lite_param_dict = {\n    # lite models are in progress and subject to changes.\n    # mean_rgb and stddev_rgb are consistent with EfficientNet-Lite models in\n    # https://github.com/tensorflow/tpu/blob/master/models/official/efficientnet/lite/efficientnet_lite_builder.py#L28\n    'efficientdet-lite0':\n        dict(\n            name='efficientdet-lite0',\n            backbone_name='efficientnet-lite0',\n            image_size=320,\n            fpn_num_filters=64,\n            fpn_cell_repeats=3,\n            box_class_repeats=3,\n            anchor_scale=3.0,\n            **lite_common_param,\n        ),\n    'efficientdet-lite1':\n        dict(\n            name='efficientdet-lite1',\n            backbone_name='efficientnet-lite1',\n            image_size=384,\n            fpn_num_filters=88,\n            fpn_cell_repeats=4,\n            box_class_repeats=3,\n            anchor_scale=3.0,\n            **lite_common_param,\n        ),\n    'efficientdet-lite2':\n        dict(\n            name='efficientdet-lite2',\n            backbone_name='efficientnet-lite2',\n            image_size=448,\n            fpn_num_filters=112,\n            fpn_cell_repeats=5,\n            box_class_repeats=3,\n            anchor_scale=3.0,\n            **lite_common_param,\n        ),\n    'efficientdet-lite3':\n        dict(\n            name='efficientdet-lite3',\n            backbone_name='efficientnet-lite3',\n            image_size=512,\n            fpn_num_filters=160,\n            fpn_cell_repeats=6,\n            box_class_repeats=4,\n            **lite_common_param,\n        ),\n    'efficientdet-lite3x':\n        dict(\n            name='efficientdet-lite3x',\n            backbone_name='efficientnet-lite3',\n            image_size=640,\n            fpn_num_filters=200,\n            fpn_cell_repeats=6,\n            box_class_repeats=4,\n            anchor_scale=3.0,\n            **lite_common_param,\n        ),\n    'efficientdet-lite4':\n        dict(\n            name='efficientdet-lite4',\n            backbone_name='efficientnet-lite4',\n            image_size=640,\n            fpn_num_filters=224,\n            fpn_cell_repeats=7,\n            box_class_repeats=4,\n            **lite_common_param,\n        ),\n}\n\n\ndef get_efficientdet_config(model_name='efficientdet-d1'):\n  \"\"\"Get the default config for EfficientDet based on model name.\"\"\"\n  h = default_detection_configs()\n  if model_name in efficientdet_model_param_dict:\n    h.override(efficientdet_model_param_dict[model_name])\n  elif model_name in efficientdet_lite_param_dict:\n    h.override(efficientdet_lite_param_dict[model_name])\n  else:\n    raise ValueError('Unknown model name: {}'.format(model_name))\n\n  return h\n\n\ndef get_detection_config(model_name):\n  if model_name.startswith('efficientdet'):\n    return get_efficientdet_config(model_name)\n  else:\n    raise ValueError('model name must start with efficientdet.')\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/inference.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\nr\"\"\"Inference related utilities.\"\"\"\nimport copy\nimport functools\nimport os\nimport time\nfrom typing import Text, Dict, Any, List, Tuple, Union\nfrom absl import logging\nimport numpy as np\nfrom PIL import Image\nimport tensorflow.compat.v1 as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import dataloader\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import det_model_fn\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import hparams_config\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import efficientdet_keras\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import label_util\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import postprocess\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.visualize import vis_utils\nfrom tensorflow.python.client import timeline  # pylint: disable=g-direct-tensorflow-import\n\n\ndef image_preprocess(image, image_size, mean_rgb, stddev_rgb):\n  \"\"\"Preprocess image for inference.\n\n  Args:\n    image: input image, can be a tensor or a numpy arary.\n    image_size: single integer of image size for square image or tuple of two\n      integers, in the format of (image_height, image_width).\n    mean_rgb: Mean value of RGB, can be a list of float or a float value.\n    stddev_rgb: Standard deviation of RGB, can be a list of float or a float\n      value.\n\n  Returns:\n    (image, scale): a tuple of processed image and its scale.\n  \"\"\"\n  input_processor = dataloader.DetectionInputProcessor(image, image_size)\n  input_processor.normalize_image(mean_rgb, stddev_rgb)\n  input_processor.set_scale_factors_to_output_size()\n  image = input_processor.resize_and_crop_image()\n  image_scale = input_processor.image_scale_to_original\n  return image, image_scale\n\n\n@tf.autograph.to_graph\ndef batch_image_files_decode(image_files):\n  raw_images = tf.TensorArray(tf.uint8, size=0, dynamic_size=True)\n  for i in tf.range(tf.shape(image_files)[0]):\n    image = tf.io.decode_image(image_files[i])\n    image.set_shape([None, None, None])\n    raw_images = raw_images.write(i, image)\n  return raw_images.stack()\n\n\ndef batch_image_preprocess(raw_images,\n                           image_size: Union[int, Tuple[int, int]],\n                           mean_rgb,\n                           stddev_rgb,\n                           batch_size: int = None):\n  \"\"\"Preprocess batched images for inference.\n\n  Args:\n    raw_images: a list of images, each image can be a tensor or a numpy arary.\n    image_size: single integer of image size for square image or tuple of two\n      integers, in the format of (image_height, image_width).\n    mean_rgb: Mean value of RGB, can be a list of float or a float value.\n    stddev_rgb: Standard deviation of RGB, can be a list of float or a float\n      value.\n    batch_size: if None, use map_fn to deal with dynamic batch size.\n\n  Returns:\n    (image, scale): a tuple of processed images and scales.\n  \"\"\"\n  if not batch_size:\n    # map_fn is a little bit slower due to some extra overhead.\n    # map_fn -> vectorized_map (fully parallelizes the batch).\n    map_fn = functools.partial(\n        image_preprocess,\n        image_size=image_size,\n        mean_rgb=mean_rgb,\n        stddev_rgb=stddev_rgb)\n    images, scales = tf.vectorized_map(map_fn, raw_images)\n    images = tf.stop_gradient(tf.cast(images, tf.float32))\n    scales = tf.stop_gradient(tf.cast(scales, tf.float32))\n    return (images, scales)\n\n  # If batch size is known, use a simple loop.\n  scales, images = [], []\n  for i in range(batch_size):\n    image, scale = image_preprocess(raw_images[i], image_size, mean_rgb,\n                                    stddev_rgb)\n    scales.append(scale)\n    images.append(image)\n  images = tf.stack(images)\n  scales = tf.stack(scales)\n  return (images, scales)\n\n\ndef build_inputs(\n    image_path_pattern: Text,\n    image_size: Union[int, Tuple[int, int]],\n    mean_rgb,\n    stddev_rgb,\n):\n  \"\"\"Read and preprocess input images.\n\n  Args:\n    image_path_pattern: a path to indicate a single or multiple files.\n    image_size: single integer of image size for square image or tuple of two\n      integers, in the format of (image_height, image_width).\n    mean_rgb: Mean value of RGB, can be a list of float or a float value.\n    stddev_rgb: Standard deviation of RGB, can be a list of float or a float\n      value.\n\n  Returns:\n    (raw_images, images, scales): raw images, processed images, and scales.\n\n  Raises:\n    ValueError if image_path_pattern doesn't match any file.\n  \"\"\"\n  raw_images, images, scales = [], [], []\n  for f in tf.io.gfile.glob(image_path_pattern):\n    image = Image.open(f)\n    raw_images.append(image)\n    image, scale = image_preprocess(image, image_size, mean_rgb, stddev_rgb)\n    images.append(image)\n    scales.append(scale)\n  if not images:\n    raise ValueError(\n        'Cannot find any images for pattern {}'.format(image_path_pattern))\n  return raw_images, tf.stack(images), tf.stack(scales)\n\n\ndef build_model(model_name: Text, inputs: tf.Tensor, **kwargs):\n  \"\"\"Build model for a given model name.\n\n  Args:\n    model_name: the name of the model.\n    inputs: an image tensor or a numpy array.\n    **kwargs: extra parameters for model builder.\n\n  Returns:\n    (cls_outputs, box_outputs): the outputs for class and box predictions.\n    Each is a dictionary with key as feature level and value as predictions.\n  \"\"\"\n  mixed_precision = kwargs.get('mixed_precision', None)\n  precision = utils.get_precision(kwargs.get('strategy', None), mixed_precision)\n\n  if kwargs.get('use_keras_model', None):\n\n    def model_arch(feats, model_name=None, **kwargs):\n      \"\"\"Construct a model arch for keras models.\"\"\"\n      config = hparams_config.get_efficientdet_config(model_name)\n      config.override(kwargs)\n      model = efficientdet_keras.EfficientDetNet(config=config)\n      cls_out_list, box_out_list = model(feats, training=False)\n      # convert the list of model outputs to a dictionary with key=level.\n      assert len(cls_out_list) == config.max_level - config.min_level + 1\n      assert len(box_out_list) == config.max_level - config.min_level + 1\n      cls_outputs, box_outputs = {}, {}\n      for i in range(config.min_level, config.max_level + 1):\n        cls_outputs[i] = cls_out_list[i - config.min_level]\n        box_outputs[i] = box_out_list[i - config.min_level]\n      return cls_outputs, box_outputs\n\n  else:\n    model_arch = det_model_fn.get_model_arch(model_name)\n\n  cls_outputs, box_outputs = utils.build_model_with_precision(\n      precision, model_arch, inputs, model_name, **kwargs)\n\n  if mixed_precision:\n    # Post-processing has multiple places with hard-coded float32.\n    # TODO(tanmingxing): Remove them once post-process can adpat to dtypes.\n    cls_outputs = {k: tf.cast(v, tf.float32) for k, v in cls_outputs.items()}\n    box_outputs = {k: tf.cast(v, tf.float32) for k, v in box_outputs.items()}\n\n  return cls_outputs, box_outputs\n\n\ndef restore_ckpt(sess, ckpt_path, ema_decay=0.9998, export_ckpt=None):\n  \"\"\"Restore variables from a given checkpoint.\n\n  Args:\n    sess: a tf session for restoring or exporting models.\n    ckpt_path: the path of the checkpoint. Can be a file path or a folder path.\n    ema_decay: ema decay rate. If None or zero or negative value, disable ema.\n    export_ckpt: whether to export the restored model.\n  \"\"\"\n  sess.run(tf.global_variables_initializer())\n  if tf.io.gfile.isdir(ckpt_path):\n    ckpt_path = tf.train.latest_checkpoint(ckpt_path)\n  if ema_decay > 0:\n    ema = tf.train.ExponentialMovingAverage(decay=0.0)\n    ema_vars = utils.get_ema_vars()\n    var_dict = ema.variables_to_restore(ema_vars)\n    ema_assign_op = ema.apply(ema_vars)\n  else:\n    var_dict = utils.get_ema_vars()\n    ema_assign_op = None\n\n  tf.train.get_or_create_global_step()\n  sess.run(tf.global_variables_initializer())\n  saver = tf.train.Saver(var_dict, max_to_keep=1)\n  if ckpt_path == '_':\n    logging.info('Running test: do not load any ckpt.')\n    return\n\n  # Restore all variables from ckpt.\n  saver.restore(sess, ckpt_path)\n\n  if export_ckpt:\n    print('export model to {}'.format(export_ckpt))\n    if ema_assign_op is not None:\n      sess.run(ema_assign_op)\n    saver = tf.train.Saver(max_to_keep=1, save_relative_paths=True)\n    saver.save(sess, export_ckpt)\n\n\ndef det_post_process(params: Dict[Any, Any], cls_outputs: Dict[int, tf.Tensor],\n                     box_outputs: Dict[int, tf.Tensor], scales: List[float]):\n  \"\"\"Post preprocessing the box/class predictions.\n\n  Args:\n    params: a parameter dictionary that includes `min_level`, `max_level`,\n      `batch_size`, and `num_classes`.\n    cls_outputs: an OrderDict with keys representing levels and values\n      representing logits in [batch_size, height, width, num_anchors].\n    box_outputs: an OrderDict with keys representing levels and values\n      representing box regression targets in [batch_size, height, width,\n      num_anchors * 4].\n    scales: a list of float values indicating image scale.\n\n  Returns:\n    detections_batch: a batch of detection results. Each detection is a tensor\n      with each row as [image_id, ymin, xmin, ymax, xmax, score, class].\n  \"\"\"\n  if params.get('combined_nms', None):\n    # Use combined version for dynamic batch size.\n    nms_boxes, nms_scores, nms_classes, _ = postprocess.postprocess_combined(\n        params, cls_outputs, box_outputs, scales)\n  else:\n    nms_boxes, nms_scores, nms_classes, _ = postprocess.postprocess_global(\n        params, cls_outputs, box_outputs, scales)\n\n  batch_size = tf.shape(cls_outputs[params['min_level']])[0]\n  img_ids = tf.expand_dims(\n      tf.cast(tf.range(0, batch_size), nms_scores.dtype), -1)\n  detections = [\n      img_ids * tf.ones_like(nms_scores),\n      nms_boxes[:, :, 0],\n      nms_boxes[:, :, 1],\n      nms_boxes[:, :, 2],\n      nms_boxes[:, :, 3],\n      nms_scores,\n      nms_classes,\n  ]\n  return tf.stack(detections, axis=-1, name='detections')\n\n\ndef visualize_image(image,\n                    boxes,\n                    classes,\n                    scores,\n                    label_map=None,\n                    min_score_thresh=0.01,\n                    max_boxes_to_draw=1000,\n                    line_thickness=2,\n                    **kwargs):\n  \"\"\"Visualizes a given image.\n\n  Args:\n    image: a image with shape [H, W, C].\n    boxes: a box prediction with shape [N, 4] ordered [ymin, xmin, ymax, xmax].\n    classes: a class prediction with shape [N].\n    scores: A list of float value with shape [N].\n    label_map: a dictionary from class id to name.\n    min_score_thresh: minimal score for showing. If claass probability is below\n      this threshold, then the object will not show up.\n    max_boxes_to_draw: maximum bounding box to draw.\n    line_thickness: how thick is the bounding box line.\n    **kwargs: extra parameters.\n\n  Returns:\n    output_image: an output image with annotated boxes and classes.\n  \"\"\"\n  label_map = label_util.get_label_map(label_map or 'coco')\n  category_index = {k: {'id': k, 'name': label_map[k]} for k in label_map}\n  img = np.array(image)\n  vis_utils.visualize_boxes_and_labels_on_image_array(\n      img,\n      boxes,\n      classes,\n      scores,\n      category_index,\n      min_score_thresh=min_score_thresh,\n      max_boxes_to_draw=max_boxes_to_draw,\n      line_thickness=line_thickness,\n      **kwargs)\n  return img\n\n\ndef visualize_image_prediction(image,\n                               prediction,\n                               label_map=None,\n                               **kwargs):\n  \"\"\"Viusalize detections on a given image.\n\n  Args:\n    image: Image content in shape of [height, width, 3].\n    prediction: a list of vector, with each vector has the format of [image_id,\n      ymin, xmin, ymax, xmax, score, class].\n    label_map: a map from label id to name.\n    **kwargs: extra parameters for vistualization, such as min_score_thresh,\n      max_boxes_to_draw, and line_thickness.\n\n  Returns:\n    a list of annotated images.\n  \"\"\"\n  boxes = prediction[:, 1:5]\n  classes = prediction[:, 6].astype(int)\n  scores = prediction[:, 5]\n\n  return visualize_image(image, boxes, classes, scores, label_map, **kwargs)\n\n\nclass ServingDriver(object):\n  \"\"\"A driver for serving single or batch images.\n\n  This driver supports serving with image files or arrays, with configurable\n  batch size.\n\n  Example 1. Serving streaming image contents:\n\n    driver = inference.ServingDriver(\n      'efficientdet-d0', '/tmp/efficientdet-d0', batch_size=1)\n    driver.build()\n    for m in image_iterator():\n      predictions = driver.serve_files([m])\n      driver.visualize(m, predictions[0])\n      # m is the new image with annotated boxes.\n\n  Example 2. Serving batch image contents:\n\n    imgs = []\n    for f in ['/tmp/1.jpg', '/tmp/2.jpg']:\n      imgs.append(np.array(Image.open(f)))\n\n    driver = inference.ServingDriver(\n      'efficientdet-d0', '/tmp/efficientdet-d0', batch_size=len(imgs))\n    driver.build()\n    predictions = driver.serve_images(imgs)\n    for i in range(len(imgs)):\n      driver.visualize(imgs[i], predictions[i])\n\n  Example 3: another way is to use SavedModel:\n\n    # step1: export a model.\n    driver = inference.ServingDriver('efficientdet-d0', '/tmp/efficientdet-d0')\n    driver.build()\n    driver.export('/tmp/saved_model_path')\n\n    # step2: Serve a model.\n    with tf.Session() as sess:\n      tf.saved_model.load(sess, ['serve'], self.saved_model_dir)\n      raw_images = []\n      for f in tf.io.gfile.glob('/tmp/images/*.jpg'):\n        raw_images.append(np.array(PIL.Image.open(f)))\n      detections = sess.run('detections:0', {'image_arrays:0': raw_images})\n      driver = inference.ServingDriver(\n        'efficientdet-d0', '/tmp/efficientdet-d0')\n      driver.visualize(raw_images[0], detections[0])\n      PIL.Image.fromarray(raw_images[0]).save(output_image_path)\n  \"\"\"\n\n  def __init__(self,\n               model_name: Text,\n               ckpt_path: Text,\n               batch_size: int = 1,\n               use_xla: bool = False,\n               min_score_thresh: float = None,\n               max_boxes_to_draw: float = None,\n               line_thickness: int = None,\n               model_params: Dict[Text, Any] = None):\n    \"\"\"Initialize the inference driver.\n\n    Args:\n      model_name: target model name, such as efficientdet-d0.\n      ckpt_path: checkpoint path, such as /tmp/efficientdet-d0/.\n      batch_size: batch size for inference.\n      use_xla: Whether run with xla optimization.\n      min_score_thresh: minimal score threshold for filtering predictions.\n      max_boxes_to_draw: the maximum number of boxes per image.\n      line_thickness: the line thickness for drawing boxes.\n      model_params: model parameters for overriding the config.\n    \"\"\"\n    self.model_name = model_name\n    self.ckpt_path = ckpt_path\n    self.batch_size = batch_size\n\n    self.params = hparams_config.get_detection_config(model_name).as_dict()\n\n    if model_params:\n      self.params.update(model_params)\n    self.params.update(dict(is_training_bn=False))\n    self.label_map = self.params.get('label_map', None)\n\n    self.signitures = None\n    self.sess = None\n    self.use_xla = use_xla\n\n    self.min_score_thresh = min_score_thresh\n    self.max_boxes_to_draw = max_boxes_to_draw\n    self.line_thickness = line_thickness\n\n  def __del__(self):\n    if self.sess:\n      self.sess.close()\n\n  def _build_session(self):\n    sess_config = tf.ConfigProto()\n    if self.use_xla:\n      sess_config.graph_options.optimizer_options.global_jit_level = (\n          tf.OptimizerOptions.ON_2)\n    return tf.Session(config=sess_config)\n\n  def build(self, params_override=None):\n    \"\"\"Build model and restore checkpoints.\"\"\"\n    params = copy.deepcopy(self.params)\n    if params_override:\n      params.update(params_override)\n\n    if not self.sess:\n      self.sess = self._build_session()\n    with self.sess.graph.as_default():\n      image_files = tf.placeholder(tf.string, name='image_files', shape=[None])\n      raw_images = batch_image_files_decode(image_files)\n      raw_images = tf.identity(raw_images, name='image_arrays')\n      images, scales = batch_image_preprocess(raw_images, params['image_size'],\n                                              params['mean_rgb'],\n                                              params['stddev_rgb'],\n                                              self.batch_size)\n      if params['data_format'] == 'channels_first':\n        images = tf.transpose(images, [0, 3, 1, 2])\n      class_outputs, box_outputs = build_model(self.model_name, images,\n                                               **params)\n      params.update(dict(batch_size=self.batch_size))\n      detections = det_post_process(params, class_outputs, box_outputs, scales)\n\n      restore_ckpt(\n          self.sess,\n          self.ckpt_path,\n          ema_decay=self.params['moving_average_decay'],\n          export_ckpt=None)\n\n    self.signitures = {\n        'image_files': image_files,\n        'image_arrays': raw_images,\n        'prediction': detections,\n    }\n    return self.signitures\n\n  def visualize(self, image, prediction, **kwargs):\n    \"\"\"Visualize prediction on image.\"\"\"\n    return visualize_image_prediction(\n        image,\n        prediction,\n        label_map=self.label_map,\n        **kwargs)\n\n  def serve_files(self, image_files: List[Text]):\n    \"\"\"Serve a list of input image files.\n\n    Args:\n      image_files: a list of image files with shape [1] and type string.\n\n    Returns:\n      A list of detections.\n    \"\"\"\n    if not self.sess:\n      self.build()\n    predictions = self.sess.run(\n        self.signitures['prediction'],\n        feed_dict={self.signitures['image_files']: image_files})\n    return predictions\n\n  def benchmark(self, image_arrays, trace_filename=None):\n    \"\"\"Benchmark inference latency/throughput.\n\n    Args:\n      image_arrays: a list of images in numpy array format.\n      trace_filename: If None, specify the filename for saving trace.\n    \"\"\"\n    if not self.sess:\n      self.build()\n\n    # init session\n    self.sess.run(\n        self.signitures['prediction'],\n        feed_dict={self.signitures['image_arrays']: image_arrays})\n\n    start = time.perf_counter()\n    for _ in range(10):\n      self.sess.run(\n          self.signitures['prediction'],\n          feed_dict={self.signitures['image_arrays']: image_arrays})\n    end = time.perf_counter()\n    inference_time = (end - start) / 10\n\n    print('Per batch inference time: ', inference_time)\n    print('FPS: ', self.batch_size / inference_time)\n\n    if trace_filename:\n      run_options = tf.RunOptions()\n      run_options.trace_level = tf.RunOptions.FULL_TRACE\n      run_metadata = tf.RunMetadata()\n      self.sess.run(\n          self.signitures['prediction'],\n          feed_dict={self.signitures['image_arrays']: image_arrays},\n          options=run_options,\n          run_metadata=run_metadata)\n      with tf.io.gfile.GFile(trace_filename, 'w') as trace_file:\n        trace = timeline.Timeline(step_stats=run_metadata.step_stats)\n        trace_file.write(trace.generate_chrome_trace_format(show_memory=True))\n\n  def serve_images(self, image_arrays):\n    \"\"\"Serve a list of image arrays.\n\n    Args:\n      image_arrays: A list of image content with each image has shape [height,\n        width, 3] and uint8 type.\n\n    Returns:\n      A list of detections.\n    \"\"\"\n    if not self.sess:\n      self.build()\n    predictions = self.sess.run(\n        self.signitures['prediction'],\n        feed_dict={self.signitures['image_arrays']: image_arrays})\n    return predictions\n\n  def load(self, saved_model_dir_or_frozen_graph: Text):\n    \"\"\"Load the model using saved model or a frozen graph.\"\"\"\n    if not self.sess:\n      self.sess = self._build_session()\n    self.signitures = {\n        'image_files': 'image_files:0',\n        'image_arrays': 'image_arrays:0',\n        'prediction': 'detections:0',\n    }\n\n    # Load saved model if it is a folder.\n    if tf.io.gfile.isdir(saved_model_dir_or_frozen_graph):\n      return tf.saved_model.load(self.sess, ['serve'],\n                                 saved_model_dir_or_frozen_graph)\n\n    # Load a frozen graph.\n    graph_def = tf.GraphDef()\n    with tf.gfile.GFile(saved_model_dir_or_frozen_graph, 'rb') as f:\n      graph_def.ParseFromString(f.read())\n    return tf.import_graph_def(graph_def, name='')\n\n  def freeze(self):\n    \"\"\"Freeze the graph.\"\"\"\n    output_names = [self.signitures['prediction'].op.name]\n    graphdef = tf.graph_util.convert_variables_to_constants(\n        self.sess, self.sess.graph_def, output_names)\n    return graphdef\n\n  def export(self,\n             output_dir: Text,\n             tflite_path: Text = None,\n             tensorrt: Text = None):\n    \"\"\"Export a saved model, frozen graph, and potential tflite/tensorrt model.\n\n    Args:\n      output_dir: the output folder for saved model.\n      tflite_path: the path for saved tflite file.\n      tensorrt: If not None, must be {'FP32', 'FP16', 'INT8'}.\n    \"\"\"\n    signitures = self.signitures\n    signature_def_map = {\n        'serving_default':\n            tf.saved_model.predict_signature_def(\n                {signitures['image_arrays'].name: signitures['image_arrays']},\n                {signitures['prediction'].name: signitures['prediction']}),\n    }\n    b = tf.saved_model.Builder(output_dir)\n    b.add_meta_graph_and_variables(\n        self.sess,\n        tags=['serve'],\n        signature_def_map=signature_def_map,\n        assets_collection=tf.get_collection(tf.GraphKeys.ASSET_FILEPATHS),\n        clear_devices=True)\n    b.save()\n    logging.info('Model saved at %s', output_dir)\n\n    # also save freeze pb file.\n    graphdef = self.freeze()\n    pb_path = os.path.join(output_dir, self.model_name + '_frozen.pb')\n    tf.io.gfile.GFile(pb_path, 'wb').write(graphdef.SerializeToString())\n    logging.info('Frozen graph saved at %s', pb_path)\n\n    if tflite_path:\n      height, width = utils.parse_image_size(self.params['image_size'])\n      input_name = signitures['image_arrays'].op.name\n      input_shapes = {input_name: [None, height, width, 3]}\n      converter = tf.lite.TFLiteConverter.from_saved_model(\n          output_dir,\n          input_arrays=[input_name],\n          input_shapes=input_shapes,\n          output_arrays=[signitures['prediction'].op.name])\n      converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS]\n      tflite_model = converter.convert()\n\n      tf.io.gfile.GFile(tflite_path, 'wb').write(tflite_model)\n      logging.info('TFLite is saved at %s', tflite_path)\n\n    if tensorrt:\n      from tensorflow.python.compiler.tensorrt import trt  # pylint: disable=g-direct-tensorflow-import,g-import-not-at-top\n      sess_config = tf.ConfigProto(gpu_options=tf.GPUOptions(allow_growth=True))\n      trt_path = os.path.join(output_dir, 'tensorrt_' + tensorrt.lower())\n      trt.create_inference_graph(\n          None,\n          None,\n          precision_mode=tensorrt,\n          input_saved_model_dir=output_dir,\n          output_saved_model_dir=trt_path,\n          session_config=sess_config)\n      logging.info('TensorRT model is saved at %s', trt_path)\n\n\nclass InferenceDriver(object):\n  \"\"\"A driver for doing batch inference.\n\n  Example usage:\n\n   driver = inference.InferenceDriver('efficientdet-d0', '/tmp/efficientdet-d0')\n   driver.inference('/tmp/*.jpg', '/tmp/outputdir')\n\n  \"\"\"\n\n  def __init__(self,\n               model_name: Text,\n               ckpt_path: Text,\n               model_params: Dict[Text, Any] = None):\n    \"\"\"Initialize the inference driver.\n\n    Args:\n      model_name: target model name, such as efficientdet-d0.\n      ckpt_path: checkpoint path, such as /tmp/efficientdet-d0/.\n      model_params: model parameters for overriding the config.\n    \"\"\"\n    self.model_name = model_name\n    self.ckpt_path = ckpt_path\n    self.params = hparams_config.get_detection_config(model_name).as_dict()\n    if model_params:\n      self.params.update(model_params)\n    self.params.update(dict(is_training_bn=False))\n    self.label_map = self.params.get('label_map', None)\n\n  def inference(self, image_path_pattern: Text, output_dir: Text, **kwargs):\n    \"\"\"Read and preprocess input images.\n\n    Args:\n      image_path_pattern: Image file pattern such as /tmp/img*.jpg\n      output_dir: the directory for output images. Output images will be named\n        as 0.jpg, 1.jpg, ....\n      **kwargs: extra parameters for for vistualization, such as\n        min_score_thresh, max_boxes_to_draw, and line_thickness.\n\n    Returns:\n      Annotated image.\n    \"\"\"\n    params = copy.deepcopy(self.params)\n    with tf.Session() as sess:\n      # Buid inputs and preprocessing.\n      raw_images, images, scales = build_inputs(image_path_pattern,\n                                                params['image_size'],\n                                                params['mean_rgb'],\n                                                params['stddev_rgb'])\n      if params['data_format'] == 'channels_first':\n        images = tf.transpose(images, [0, 3, 1, 2])\n      # Build model.\n      class_outputs, box_outputs = build_model(self.model_name, images,\n                                               **self.params)\n      restore_ckpt(\n          sess,\n          self.ckpt_path,\n          ema_decay=self.params['moving_average_decay'],\n          export_ckpt=None)\n      # Build postprocessing.\n      detections_batch = det_post_process(params, class_outputs, box_outputs,\n                                          scales)\n      predictions = sess.run(detections_batch)\n      # Visualize results.\n      for i, prediction in enumerate(predictions):\n        img = visualize_image_prediction(\n            raw_images[i],\n            prediction,\n            label_map=self.label_map,\n            **kwargs)\n        output_image_path = os.path.join(output_dir, str(i) + '.jpg')\n        Image.fromarray(img).save(output_image_path)\n        print('writing file to %s' % output_image_path)\n\n      return predictions\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/install_deps.sh",
    "content": "#!/usr/bin/env bash\n# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\nfor line in $(cat efficientdet/requirements.txt)\ndo\n  pip install $line\ndone\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/iou_utils.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"IoU utils for box regression with iou losses.\n\nDistance-IoU Loss: Faster and Better Learning for Bounding Box Regression.\nhttps://arxiv.org/pdf/1911.08287.pdf\n\"\"\"\nimport math\nfrom typing import Union, Text\nimport numpy as np\nimport tensorflow as tf\nFloatType = Union[tf.Tensor, float, np.float32, np.float64]\n\n\ndef _get_v(b1_height: FloatType, b1_width: FloatType, b2_height: FloatType,\n           b2_width: FloatType) -> tf.Tensor:\n  \"\"\"Get the consistency measurement of aspect ratio for ciou.\"\"\"\n\n  @tf.custom_gradient\n  def _get_grad_v(height, width):\n    \"\"\"backpropogate gradient.\"\"\"\n    arctan = tf.atan(tf.math.divide_no_nan(b1_width, b1_height)) - tf.atan(\n        tf.math.divide_no_nan(width, height))\n    v = 4 * ((arctan / math.pi)**2)\n\n    def _grad_v(dv):\n      \"\"\"Grad for eager mode.\"\"\"\n      gdw = dv * 8 * arctan * height / (math.pi**2)\n      gdh = -dv * 8 * arctan * width / (math.pi**2)\n      return [gdh, gdw]\n\n    def _grad_v_graph(dv, variables):\n      \"\"\"Grad for graph mode.\"\"\"\n      gdw = dv * 8 * arctan * height / (math.pi**2)\n      gdh = -dv * 8 * arctan * width / (math.pi**2)\n      return [gdh, gdw], tf.gradients(v, variables, grad_ys=dv)\n\n    if tf.compat.v1.executing_eagerly_outside_functions():\n      return v, _grad_v\n    return v, _grad_v_graph\n\n  return _get_grad_v(b2_height, b2_width)\n\n\ndef _iou_per_anchor(pred_boxes: FloatType,\n                    target_boxes: FloatType,\n                    iou_type: Text = 'iou') -> tf.Tensor:\n  \"\"\"Computing the IoU for a single anchor.\n\n  Args:\n    pred_boxes: predicted boxes, with coordinate [y_min, x_min, y_max, x_max].\n    target_boxes: target boxes, with coordinate [y_min, x_min, y_max, x_max].\n    iou_type: one of ['iou', 'ciou', 'diou', 'giou'].\n\n  Returns:\n    IoU loss float `Tensor`.\n  \"\"\"\n  # t_ denotes target boxes and p_ denotes predicted boxes.\n  t_ymin, t_xmin, t_ymax, t_xmax = target_boxes\n  p_ymin, p_xmin, p_ymax, p_xmax = pred_boxes\n\n  zero = tf.convert_to_tensor(0.0, t_ymin.dtype)\n  p_width = tf.maximum(zero, p_xmax - p_xmin)\n  p_height = tf.maximum(zero, p_ymax - p_ymin)\n  t_width = tf.maximum(zero, t_xmax - t_xmin)\n  t_height = tf.maximum(zero, t_ymax - t_ymin)\n  p_area = p_width * p_height\n  t_area = t_width * t_height\n\n  intersect_ymin = tf.maximum(p_ymin, t_ymin)\n  intersect_xmin = tf.maximum(p_xmin, t_xmin)\n  intersect_ymax = tf.minimum(p_ymax, t_ymax)\n  intersect_xmax = tf.minimum(p_xmax, t_xmax)\n  intersect_width = tf.maximum(zero, intersect_xmax - intersect_xmin)\n  intersect_height = tf.maximum(zero, intersect_ymax - intersect_ymin)\n  intersect_area = intersect_width * intersect_height\n\n  union_area = p_area + t_area - intersect_area\n  iou_v = tf.math.divide_no_nan(intersect_area, union_area)\n  if iou_type == 'iou':\n    return iou_v  # iou is the simplest form.\n\n  enclose_ymin = tf.minimum(p_ymin, t_ymin)\n  enclose_xmin = tf.minimum(p_xmin, t_xmin)\n  enclose_ymax = tf.maximum(p_ymax, t_ymax)\n  enclose_xmax = tf.maximum(p_xmax, t_xmax)\n\n  assert iou_type in ('giou', 'diou', 'ciou')\n  if iou_type == 'giou':  # giou is the generalized iou.\n    enclose_width = tf.maximum(zero, enclose_xmax - enclose_xmin)\n    enclose_height = tf.maximum(zero, enclose_ymax - enclose_ymin)\n    enclose_area = enclose_width * enclose_height\n    giou_v = iou_v - tf.math.divide_no_nan(\n        (enclose_area - union_area), enclose_area)\n    return giou_v\n\n  assert iou_type in ('diou', 'ciou')\n  p_center = tf.stack([(p_ymin + p_ymax) / 2, (p_xmin + p_xmax) / 2], axis=-1)\n  t_center = tf.stack([(t_ymin + t_ymax) / 2, (t_xmin + t_xmax) / 2], axis=-1)\n  # Compute squared norms directly, avoiding a sqrt call and a singularity\n  # at zero in the gradient.\n  euclidean_squared = tf.math.reduce_sum(\n      tf.math.squared_difference(t_center, p_center), axis=-1\n  )\n  diag_length_squared = tf.math.reduce_sum(\n      tf.math.square(\n          tf.stack(\n              [enclose_ymax - enclose_ymin, enclose_xmax - enclose_xmin],\n              axis=-1,\n          )\n      ),\n      axis=-1,\n  )\n  diou_v = iou_v - tf.math.divide_no_nan(euclidean_squared, diag_length_squared)\n  if iou_type == 'diou':  # diou is the distance iou.\n    return diou_v\n\n  assert iou_type == 'ciou'\n  v = _get_v(p_height, p_width, t_height, t_width)\n  alpha = tf.math.divide_no_nan(v, ((1 - iou_v) + v))\n  return diou_v - alpha * v  # the last one is ciou.\n\n\ndef iou_loss(pred_boxes: FloatType,\n             target_boxes: FloatType,\n             iou_type: Text = 'iou') -> tf.Tensor:\n  \"\"\"A unified interface for computing various IoU losses.\n\n  Let B and B_gt denotes the pred_box and B_gt is the target box (ground truth):\n\n    IoU = |B & B_gt| / |B | B_gt|\n\n    GIoU = IoU - |C - B U B_gt| / C, where C is the smallest box covering B and\n    B_gt.\n\n    DIoU = IoU - E(B, B_gt)^2 / c^2, E is the Euclidean distance of the center\n    points of B and B_gt, and c is the diagonal length of the smallest box\n    covering the two boxes\n\n    CIoU = IoU - DIoU - a * v, where a is a positive trade-off parameter, and\n    v measures the consistency of aspect ratio:\n      v = (arctan(w_gt / h_gt) - arctan(w / h)) * 4 / pi^2\n    where (w_gt, h_gt) and (w, h) are the width and height of the target and\n    predicted box respectively.\n\n  The returned loss is computed as 1 - one of {IoU, GIoU, DIoU, CIoU}.\n\n  Args:\n    pred_boxes: predicted boxes, with coordinate [y_min, x_min, y_max, x_max]*.\n      It can be multiple anchors, with each anchor box has four coordinates.\n    target_boxes: target boxes, with coordinate [y_min, x_min, y_max, x_max]*.\n      It can be multiple anchors, with each anchor box has four coordinates.\n    iou_type: one of ['iou', 'ciou', 'diou', 'giou'].\n\n  Returns:\n    IoU loss float `Tensor`.\n  \"\"\"\n  if iou_type not in ('iou', 'ciou', 'diou', 'giou'):\n    raise ValueError(\n        'Unknown loss_type {}, not iou/ciou/diou/giou'.format(iou_type))\n\n  pred_boxes = tf.convert_to_tensor(pred_boxes)\n  target_boxes = tf.cast(target_boxes, pred_boxes.dtype)\n\n  # t_ denotes target boxes and p_ denotes predicted boxes: (y, x, y_max, x_max)\n  pred_boxes_list = tf.unstack(pred_boxes, None, axis=-1)\n  target_boxes_list = tf.unstack(target_boxes, None, axis=-1)\n  assert len(pred_boxes_list) == len(target_boxes_list)\n  assert len(pred_boxes_list) % 4 == 0\n\n  iou_loss_list = []\n  for i in range(0, len(pred_boxes_list), 4):\n    pred_boxes = pred_boxes_list[i:i + 4]\n    target_boxes = target_boxes_list[i:i + 4]\n\n    # Compute mask.\n    t_ymin, t_xmin, t_ymax, t_xmax = target_boxes\n    mask = tf.math.logical_and(t_ymax > t_ymin, t_xmax > t_xmin)\n    mask = tf.cast(mask, t_ymin.dtype)\n    # Loss should be mask * (1 - iou) = mask - masked_iou.\n    pred_boxes = [b * mask for b in pred_boxes]\n    target_boxes = [b * mask for b in target_boxes]\n    iou_loss_list.append(\n        mask *\n        (1 - tf.squeeze(_iou_per_anchor(pred_boxes, target_boxes, iou_type))))  # pytype: disable=wrong-arg-types  # numpy-scalars\n  if len(iou_loss_list) == 1:\n    return iou_loss_list[0]\n  return tf.reduce_sum(tf.stack(iou_loss_list), 0)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/keras/README.md",
    "content": "# EfficientDet\n\n[1] Mingxing Tan, Ruoming Pang, Quoc V. Le. EfficientDet: Scalable and Efficient Object Detection. CVPR 2020.\nArxiv link: https://arxiv.org/abs/1911.09070\n\n**Quick start tutorial: [tutorial.ipynb](tutorial.ipynb)**\n\n**Quick install dependencies: ```pip install -r requirements.txt```**\n\n## 1. About EfficientDet Models\n\nEfficientDets are a family of object detection models, which achieve state-of-the-art 55.1mAP on COCO test-dev, yet being 4x - 9x smaller and using 13x - 42x fewer FLOPs than previous detectors. Our models also run 2x - 4x faster on GPU, and 5x - 11x faster on CPU than other detectors.\n\n\nEfficientDets are developed based on the advanced backbone, a new BiFPN, and a new scaling technique:\n\n<p align=\"center\">\n<img src=\"../g3doc/network.png\" width=\"800\" />\n</p>\n\n  * **Backbone**: we employ [EfficientNets](https://arxiv.org/abs/1905.11946) as our backbone networks.\n  * **BiFPN**: we propose BiFPN, a bi-directional feature network enhanced with fast normalization, which enables easy and fast feature fusion.\n  * **Scaling**: we use a single compound scaling factor to govern the depth, width, and resolution for all backbone, feature & prediction networks.\n\nOur model family starts from EfficientDet-D0, which has comparable accuracy as [YOLOv3](https://arxiv.org/abs/1804.02767). Then we scale up this baseline model using our compound scaling method to obtain a list of detection models EfficientDet-D1 to D6, with different trade-offs between accuracy and model complexity.\n\n\n<table border=\"0\">\n<tr>\n    <td>\n    <img src=\"../g3doc/flops.png\" width=\"100%\" />\n    </td>\n    <td>\n    <img src=\"../g3doc/params.png\", width=\"100%\" />\n    </td>\n</tr>\n</table>\n\n** For simplicity, we compare the whole detectors here. For more comparison on FPN/NAS-FPN/BiFPN, please see Table 4 of our [paper](https://arxiv.org/abs/1911.09070).\n\n\n\n## 2. Pretrained EfficientDet Checkpoints\n\nWe have provided a list of EfficientDet checkpoints and results as follows:\n\n|       Model    | AP<sup>test</sup>    |  AP<sub>50</sub> | AP<sub>75</sub> |AP<sub>S</sub>   |  AP<sub>M</sub>    |  AP<sub>L</sub>   |  AP<sup>val</sup> | | #params | #FLOPs |\n|----------     |------ |------ |------ | -------- | ------| ------| ------ |------ |------ |  :------: |\n|     EfficientDet-D0 ([ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d0.tar.gz), [val](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/val/d0_coco_val.txt), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/testdev/d0_coco_test-dev2017.txt))    | 34.6 | 53.0 | 37.1 | 12.4 | 39.0 | 52.7 | 34.3 |  | 3.9M | 2.54B  |\n|     EfficientDet-D1 ([ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d1.tar.gz), [val](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/val/d1_coco_val.txt), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/testdev/d1_coco_test-dev2017.txt))    | 40.5 | 59.1 | 43.7 | 18.3 | 45.0 | 57.5 | 40.2 | | 6.6M | 6.10B |\n|     EfficientDet-D2 ([ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d2.tar.gz), [val](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/val/d2_coco_val.txt), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/testdev/d2_coco_test-dev2017.txt))    | 43.9 | 62.7 | 47.6 | 22.9 | 48.1 | 59.5 | 43.1 | | 8.1M | 11.0B |\n|     EfficientDet-D3 ([ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d3.tar.gz), [val](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/val/d3_coco_val.txt), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/testdev/d3_coco_test-dev2017.txt))    | 47.2 | 65.9 | 51.2 | 27.2 | 51.0 | 62.1 | 46.8 | | 12.0M | 24.9B |\n|     EfficientDet-D4 ([ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d4.tar.gz), [val](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/val/d4_coco_val.txt), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/testdev/d4_coco_test-dev2017.txt))    | 49.7 | 68.4 | 53.9 | 30.7 | 53.2 | 63.2 | 49.3 |  | 20.7M | 55.2B |\n|     EfficientDet-D5 ([ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d5.tar.gz), [val](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/val/d5_coco_val.txt), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/testdev/d5_coco_test-dev2017.txt))    | 51.5 | 70.5 | 56.1 | 33.9 | 54.7 | 64.1 | 51.2 |  | 33.7M | 130B |\n|     EfficientDet-D6 ([ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d6.tar.gz), [val](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/val/d6_coco_val.txt), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/testdev/d6_coco_test-dev2017.txt))    | 52.6 | 71.5 | 57.2 | 34.9 | 56.0 | 65.4 | 52.1 | | 51.9M  |  226B  |\n|     EfficientDet-D7 ([ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d7.tar.gz), [val](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/val/d7_coco_val.txt), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/testdev/d7_coco_test-dev2017.txt))    | 53.7 | 72.4 | 58.4 | 35.8 | 57.0 | 66.3 | 53.4 | | 51.9M  |  325B  |\n|     EfficientDet-D7x ([ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/efficientdet-d7x.tar.gz), [val](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/val/d7x_coco_val.txt), [test-dev](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco2/testdev/d7x_coco_test-dev2017.txt))    | 55.1 | 74.3 | 59.9 | 37.2 | 57.9 | 68.0 | 54.4 | | 77.0M  |  410B  |\n\n<sup><em>val</em> denotes validation results, <em>test-dev</em> denotes test-dev2017 results. AP<sup>val</sup> is for validation accuracy, all other AP results in the table are for COCO test-dev2017. All accuracy numbers are for single-model single-scale without ensemble or test-time augmentation.  EfficientDet-D0 to D6 are trained for 300 epochs and D7/D7x are trained for 600 epochs.</sup>\n\nIn addition, the following table includes a list of models trained with fixed 640x640 image sizes (see appendix of [this paper](https://arxiv.org/abs/1911.09070)):\n\n\n|       Model    |   mAP | Latency |\n| ------ | ------ | ------  |\n| D2(640) [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco640/efficientdet-d2-640.tar.gz) |  41.7 | 14.8ms |\n| D3(640) [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco640/efficientdet-d3-640.tar.gz) |  44.0 | 18.7ms |\n| D4(640) [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco640/efficientdet-d4-640.tar.gz) |  45.7 | 21.7ms |\n| D5(640) [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco640/efficientdet-d5-640.tar.gz) |  46.6 | 26.6ms |\n| D6(640) [ckpt](https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco640/efficientdet-d6-640.tar.gz) |  47.9 | 33.8ms |\n\n\n\n## 3. Export SavedModel, frozen graph, tensort models, or tflite.\n\nRun the following command line to export models:\n\n    !rm  -rf savedmodeldir\n    !python inspector.py --mode=saved_model --model_name=efficientdet-d0 \\\n      --model_dir=efficientdet-d0 --saved_model_dir=savedmodeldir \\\n      --tensorrt=FP32  --tflite=FP32 \\\n      --hparams=voc_config.yaml\n\nThen you will get:\n\n - saved model under `savedmodeldir/`\n - frozen graph with name `savedmodeldir/efficientdet-d0_frozen.pb`\n - TensorRT saved model under `savedmodeldir/tensorrt_fp32/`\n - tflite file with name `savedmodeldir/fp32.tflite`\n\nNotably,\n --model_dir=xx/archive is the folder for exporting the best model.\n\n\n## 4. Benchmark model latency.\n\n\nThere are two types of latency: network latency and end-to-end latency.\n\n(1) To measure the network latency (from the fist conv to the last class/box\nprediction output), use the following command:\n\n    !python inspector.py --mode=benchmark --only_network --model_name=efficientdet-d0\n\nadd --hparams=\"mixed_precision=True\" if running on V100.\n\nOn single Tesla V100 without TensorRT, our D0 network (no pre/post-processing)\nhas 134 FPS (frame per second) for batch size 1, and 238 FPS for batch size 8.\n\n(2) To measure the end-to-end latency (from the input image to the final rendered\nnew image, including: image preprocessing, network, postprocessing and NMS),\nuse the following command:\n\n    !python inspector.py --mode=benchmark --model_name=efficientdet-d0 \\\n      --model_dir=efficientdet-d0 --hparams=mixed_precision=true\n\nOn single Tesla V100 without using TensorRT, our end-to-end\nlatency and throughput are:\n\n\n|       Model    |   mAP | batch1 latency |  batch1 throughput |  batch8 throughput |\n| ------ | ------ | ------  | ------ | ------ |\n| EfficientDet-D0 |  34.6 | 10.2ms | 97 fps | 209 fps |\n| EfficientDet-D1 |  40.5 | 13.5ms | 74 fps | 140 fps |\n| EfficientDet-D2 |  43.0 | 17.7ms | 57 fps | 97 fps  |\n| EfficientDet-D3 |  47.5 | 28.0ms | 36 fps | 58 fps  |\n| EfficientDet-D4 |  49.7 | 42.8ms | 23 fps | 35 fps  |\n| EfficientDet-D5 |  51.5 | 72.5ms | 14 fps | 18 fps  |\n| EfficientDet-D6 |  52.6 | 92.8ms | 11 fps | - fps  |\n| EfficientDet-D7 |  53.7 | 122ms  | 8.2 fps | - fps  |\n| EfficientDet-D7x |  55.1 | 153ms  | 6.5 fps | - fps  |\n\n** FPS means frames per second (or images/second).\n\n## 5. Inference for images.\n\n    # Step1: download model and testing image.\n    !export MODEL=efficientdet-d0\n    !export CKPT_PATH=efficientdet-d0\n    !wget https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco/${MODEL}.tar.gz\n    !wget https://user-images.githubusercontent.com/11736571/77320690-099af300-6d37-11ea-9d86-24f14dc2d540.png -O img.png\n    !tar xf ${MODEL}.tar.gz\n\n    # Step2: inference image.\n    !python inspector.py --mode=infer \\\n      --model_name=efficientdet-d0 --saved_model_dir=/tmp/saved_model \\\n      --hparams=\"image_size=1920x1280\" \\\n      --input_image=img.png --output_image_dir=/tmp/\n\n\nIf you want to do inference using frozen graph, you can run\n\n    # Step 1 is the same as before.\n    # Step 2: do inference with frozen graph.\n    !python inspector.py --mode=infer \\\n      --model_name=efficientdet-d0  \\\n      --saved_model_dir=/tmp/saved_model/efficientdet-d0_frozen.pb  \\\n      --input_image=img.png --output_image_dir=/tmp/\n\nIf you want to do inference using tflite, you can run\n\n    # Step 1 is the same as before.\n    # Step 2: do inference with frozen graph.\n    !python inspector.py --mode=infer \\\n      --model_name=efficientdet-d0  \\\n      --saved_model_dir=/tmp/saved_model/fp32.tflite  \\\n      --input_image=img.png --output_image_dir=/tmp/\n\nLastly, if you only have one image and just want to run a quick test, you can also run the following command (it is slow because it needs to construct the graph from scratch):\n\n    # Run inference for a single image.\n    !python inspector.py --mode=infer \\\n      --model_name=efficientdet-d0 --model_dir=$CKPT_PATH \\\n      --hparams=\"image_size=1920x1280\" \\\n      --input_image=img.png --output_image_dir=/tmp/\n    # you can visualize the output /tmp/0.jpg\n\nHere is an example of EfficientDet-D0 visualization: more on [tutorial](tutorial.ipynb)\n\n<p align=\"center\">\n<img src=\"../g3doc/street.jpg\" width=\"800\" />\n</p>\n\n## 6. Inference for videos.\n\nYou can run inference for a video and show the results online:\n\n    # step 1: download the example video.\n    !wget https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/data/video480p.mov -O input.mov\n\n    # step 2: export saved model.\n    !python inspector.py --mode=video \\\n      --model_name=efficientdet-d0 --model_dir=efficientdet-d0 \\\n      --hparams=voc_config.yaml --input_video=input.mov\n\n    # alternative step 2: inference video and save the result.\n    !python inspector.py --mode=video --hparams=voc_config.yaml \\\n      --model_name=efficientdet-d0   \\\n      --input_video=input.mov  \\\n      --output_video=output.mov\n\n## 7. Eval on COCO 2017 val or test-dev.\n\n    // Download coco data.\n    !wget http://images.cocodataset.org/zips/val2017.zip\n    !wget http://images.cocodataset.org/annotations/annotations_trainval2017.zip\n    !unzip val2017.zip\n    !unzip annotations_trainval2017.zip\n\n    // convert coco data to tfrecord.\n    !mkdir tfrecord\n    !PYTHONPATH=\".:$PYTHONPATH\"  python dataset/create_coco_tfrecord.py \\\n        --image_dir=val2017 \\\n        --caption_annotations_file=annotations/captions_val2017.json \\\n        --output_file_prefix=tfrecord/val \\\n        --num_shards=32\n\n    // Run eval.\n    !python eval.py  \\\n        --model_name=${MODEL}  --model_dir=${CKPT_PATH}  \\\n        --val_file_pattern=tfrecord/val/pascal*.tfrecord  \\\n        --val_json_file=annotations/instances_val2017.json\n\n## 8. Finetune on PASCAL VOC 2012 with detector COCO ckpt.\n\nDownload data and checkpoints.\n\n    # Download and convert pascal data.\n    !wget http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar\n    !tar xf VOCtrainval_11-May-2012.tar\n    !mkdir tfrecord\n    !PYTHONPATH=\".:$PYTHONPATH\"  python dataset/create_pascal_tfrecord.py  \\\n        --data_dir=VOCdevkit --year=VOC2012  --output_path=tfrecord/pascal\n\n    # Download backbone checkopints.\n    !wget https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco/efficientdet-d0.tar.gz\n    !tar xf efficientdet-d0.tar.gz\n\nCreate a config file for the PASCAL VOC dataset called voc_config.yaml and put this in it.\n\n      num_classes: 21\n      var_freeze_expr: '(efficientnet|fpn_cells|resample_p6)'\n      label_map: {1: aeroplane, 2: bicycle, 3: bird, 4: boat, 5: bottle, 6: bus, 7: car, 8: cat, 9: chair, 10: cow, 11: diningtable, 12: dog, 13: horse, 14: motorbike, 15: person, 16: pottedplant, 17: sheep, 18: sofa, 19: train, 20: tvmonitor}\n\nFinetune needs to use --pretrained_ckpt.\n\n    !python train.py\n        --train_file_pattern=tfrecord/pascal*.tfrecord \\\n        --val_file_pattern=tfrecord/pascal*.tfrecord \\\n        --val_file_pattern=tfrecord/*.json \\\n        --model_name=efficientdet-d0 \\\n        --model_dir=/tmp/efficientdet-d0-finetune  \\\n        --pretrained_ckpt=efficientdet-d0  \\\n        --batch_size=64 \\\n        --eval_samples=1024 \\\n        --num_examples_per_epoch=5717 --num_epochs=50  \\\n        --hparams=voc_config.yaml\n\nIf you want to continue to train the model, simply re-run the above command because the `num_epochs` is a maximum number of epochs. For example, to reproduce the result of efficientdet-d0, set `--num_epochs=300` then run the command multiple times until the training is finished.\n\n## 9. Train on multi GPUs.\n\nJust add ```--strategy=gpus```\n\n## 10. Training EfficientDets on TPUs.\n\nTo train this model on Cloud TPU, you will need:\n\n   * A GCE VM instance with an associated Cloud TPU resource.\n   * A GCS bucket to store your training checkpoints (the \"model directory\").\n   * Install latest TensorFlow for both GCE VM and Cloud.\n\nThen train the model:\n\n    !export PYTHONPATH=\"$PYTHONPATH:/path/to/models\"\n    !python train.py --tpu=TPU_NAME --train_file_pattern=DATA_DIR/*.tfrecord --model_dir=MODEL_DIR --strategy=tpu\n\n    # TPU_NAME is the name of the TPU node, the same name that appears when you run gcloud compute tpus list, or ctpu ls.\n    # MODEL_DIR is a GCS location (a URL starting with gs:// where both the GCE VM and the associated Cloud TPU have write access.\n    # DATA_DIR is a GCS location to which both the GCE VM and associated Cloud TPU have read access.\n\n\nFor more instructions about training on TPUs, please refer to the following tutorials:\n\n  * EfficientNet tutorial: https://cloud.google.com/tpu/docs/tutorials/efficientnet\n\n## 11. Reducing Memory Usage when Training EfficientDets on GPU.\n\nEfficientDets use a lot of GPU memory for a few reasons:\n\n* Large input resolution: because resolution is one of the scaling dimension, our resolution tends to be higher, which significantly increase activations (although no parameter increase).\n* Large internal activations for backbone: our backbone uses a relatively large expansion ratio (6), causing the large expanded activations.\n* Deep BiFPN: our BiFPN has multiple top-down and bottom-up paths, which leads to a lot of intermediate memory usage during training.\n\nTo train this model on GPU with low memory there is an experimental option grad_checkpoint.\n\nCheck these links for a high-level idea of what gradient checkpointing is doing:\n1. https://medium.com/tensorflow/fitting-larger-networks-into-memory-583e3c758ff9\n\n**grad_checkpoint: True**\n\nIf set to True, keras model uses ```tf.recompute_grad``` to achieve gradient checkpoints.\n\nTesting shows that:\n* It allows to train a d7x network with batch size of 2 on a 11Gb (1080Ti) GPU\n\n## 12. Visualize TF-Records.\n\nYou can visualize tf-records with following commands:\n\nTo visualize training tfrecords with input dataloader use.\n```\npython dataset/inspect_tfrecords.py --file_pattern dataset/sample.record\\ \n--model_name \"efficientdet-d0\" --samples 10\\ \n--save_samples_dir train_samples/  -hparams=\"label_map={1:'label1'}, autoaugmentation_policy=v3\"\n\n```\n\nTo visualize evaluation tfrecords use.\n```\npython dataset/inspect_tfrecords.py --file_pattern dataset/sample.record\\ \n--model_name \"efficientdet-d0\" --samples 10\\ \n--save_samples_dir train_samples/  -eval\\\n-hparams=\"label_map={1:'label1'}\"\n\n```\n* samples: random samples to visualize.\n* model_name: model name will be used to get image_size.\n* save_samples_dir: save dir.\n* eval: flag for eval data.\n\n## 13. Export to ONNX\n(1)  Install tf2onnx\n```\npip install tf2onnx\n```\n(2) Set nms configs (onnx doesn't support soft nms, use normal nms instead):\n```\nnms_configs:\n  method: 'hard'\n  iou_thresh: 0.5  # use the default value based on method.\n  score_thresh: 0.\n  sigma: 0.0\n  pyfunc: False\n  max_nms_inputs: 0\n  max_output_size: 100\n```\n(3) Export to onnx from saved_model\n```\npython -m tf2onnx.convert --saved-model=<saved model directory> --output=<onnx filename> --opset=11\n```\n\n## 14. Debug\nJust add ```--debug``` after command, then you could use pdb debug the model with eager execution and deterministic operations.\n\nNOTE: this is not an official Google product.\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/keras/__init__.py",
    "content": ""
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/keras/anchors.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Anchor definition.\"\"\"\nimport collections\nimport numpy as np\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.object_detection import argmax_matcher\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.object_detection import box_list\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.object_detection import faster_rcnn_box_coder\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.object_detection import region_similarity_calculator\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.object_detection import target_assigner\n\nMAX_DETECTION_POINTS = 5000\n\n\ndef decode_box_outputs(pred_boxes, anchor_boxes):\n  \"\"\"Transforms relative regression coordinates to absolute positions.\n\n  Network predictions are normalized and relative to a given anchor; this\n  reverses the transformation and outputs absolute coordinates for the input\n  image.\n\n  Args:\n    pred_boxes: predicted box regression targets.\n    anchor_boxes: anchors on all feature levels.\n  Returns:\n    outputs: bounding boxes.\n  \"\"\"\n  anchor_boxes = tf.cast(anchor_boxes, pred_boxes.dtype)\n  ycenter_a = (anchor_boxes[..., 0] + anchor_boxes[..., 2]) / 2\n  xcenter_a = (anchor_boxes[..., 1] + anchor_boxes[..., 3]) / 2\n  ha = anchor_boxes[..., 2] - anchor_boxes[..., 0]\n  wa = anchor_boxes[..., 3] - anchor_boxes[..., 1]\n  ty, tx, th, tw = tf.unstack(pred_boxes, num=4, axis=-1)\n\n  w = tf.math.exp(tw) * wa\n  h = tf.math.exp(th) * ha\n  ycenter = ty * ha + ycenter_a\n  xcenter = tx * wa + xcenter_a\n  ymin = ycenter - h / 2.\n  xmin = xcenter - w / 2.\n  ymax = ycenter + h / 2.\n  xmax = xcenter + w / 2.\n  return tf.stack([ymin, xmin, ymax, xmax], axis=-1)\n\n\ndef decode_anchors_to_centersize(pred_boxes, anchor_boxes):\n  \"\"\"Transforms anchor boxes' encoding from box-corner to center-size.\n\n  Box-corner encoding is of form: {ymin, ymax, xmin, xmax}\n  Center-size encoding is of form: {y_center, x_center, height, width}\n  This is used for TFLite's custom NMS post-processing.\n\n  Args:\n    pred_boxes: predicted box regression targets.\n    anchor_boxes: anchors on all feature levels.\n\n  Returns:\n    outputs: anchor_boxes in center-size encoding.\n  \"\"\"\n  anchor_boxes = tf.cast(anchor_boxes, pred_boxes.dtype)\n  ycenter_a = (anchor_boxes[..., 0] + anchor_boxes[..., 2]) / 2\n  xcenter_a = (anchor_boxes[..., 1] + anchor_boxes[..., 3]) / 2\n  ha = anchor_boxes[..., 2] - anchor_boxes[..., 0]\n  wa = anchor_boxes[..., 3] - anchor_boxes[..., 1]\n  return tf.stack([ycenter_a, xcenter_a, ha, wa], axis=-1)\n\n\nclass Anchors():\n  \"\"\"Multi-scale anchors class.\"\"\"\n\n  def __init__(self, min_level, max_level, num_scales, aspect_ratios,\n               anchor_scale, image_size):\n    \"\"\"Constructs multiscale anchors.\n\n    Args:\n      min_level: integer number of minimum level of the output feature pyramid.\n      max_level: integer number of maximum level of the output feature pyramid.\n      num_scales: integer number representing intermediate scales added\n        on each level. For instances, num_scales=2 adds two additional\n        anchor scales [2^0, 2^0.5] on each level.\n      aspect_ratios: list of representing the aspect ratio anchors added\n        on each level. For instances, aspect_ratios = [1.0, 2.0, 0..5]\n        adds three anchors on each level.\n      anchor_scale: float number representing the scale of size of the base\n        anchor to the feature stride 2^level. Or a list, one value per layer.\n      image_size: integer number or tuple of integer number of input image size.\n    \"\"\"\n    self.min_level = min_level\n    self.max_level = max_level\n    self.num_scales = num_scales\n    self.aspect_ratios = aspect_ratios\n    if isinstance(anchor_scale, (list, tuple)):\n      assert len(anchor_scale) == max_level - min_level + 1\n      self.anchor_scales = anchor_scale\n    else:\n      self.anchor_scales = [anchor_scale] * (max_level - min_level + 1)\n    self.image_size = utils.parse_image_size(image_size)\n    self.feat_sizes = utils.get_feat_sizes(image_size, max_level)\n    self.config = self._generate_configs()\n    self.boxes = self._generate_boxes()\n\n  def _generate_configs(self):\n    \"\"\"Generate configurations of anchor boxes.\"\"\"\n    anchor_configs = {}\n    feat_sizes = self.feat_sizes\n    for level in range(self.min_level, self.max_level + 1):\n      anchor_configs[level] = []\n      for scale_octave in range(self.num_scales):\n        for aspect in self.aspect_ratios:\n          anchor_configs[level].append(\n              ((feat_sizes[0]['height'] / float(feat_sizes[level]['height']),\n                feat_sizes[0]['width'] / float(feat_sizes[level]['width'])),\n               scale_octave / float(self.num_scales), aspect,\n               self.anchor_scales[level - self.min_level]))\n    return anchor_configs\n\n  def _generate_boxes(self):\n    \"\"\"Generates multiscale anchor boxes.\"\"\"\n    boxes_all = []\n    for _, configs in self.config.items():\n      boxes_level = []\n      for config in configs:\n        stride, octave_scale, aspect, anchor_scale = config\n        base_anchor_size_x = anchor_scale * stride[1] * 2**octave_scale\n        base_anchor_size_y = anchor_scale * stride[0] * 2**octave_scale\n        if isinstance(aspect, list):\n          aspect_x, aspect_y = aspect\n        else:\n          aspect_x = np.sqrt(aspect)\n          aspect_y = 1.0 / aspect_x\n        anchor_size_x_2 = base_anchor_size_x * aspect_x / 2.0\n        anchor_size_y_2 = base_anchor_size_y * aspect_y / 2.0\n\n        x = np.arange(stride[1] / 2, self.image_size[1], stride[1])\n        y = np.arange(stride[0] / 2, self.image_size[0], stride[0])\n        xv, yv = np.meshgrid(x, y)\n        xv = xv.reshape(-1)\n        yv = yv.reshape(-1)\n\n        boxes = np.vstack((yv - anchor_size_y_2, xv - anchor_size_x_2,\n                           yv + anchor_size_y_2, xv + anchor_size_x_2))\n        boxes = np.swapaxes(boxes, 0, 1)\n        boxes_level.append(np.expand_dims(boxes, axis=1))\n      # concat anchors on the same level to the reshape NxAx4\n      boxes_level = np.concatenate(boxes_level, axis=1)\n      boxes_all.append(boxes_level.reshape([-1, 4]))\n\n    anchor_boxes = np.vstack(boxes_all)\n    anchor_boxes = tf.convert_to_tensor(anchor_boxes, dtype=tf.float32)\n    return anchor_boxes\n\n  def get_anchors_per_location(self):\n    return self.num_scales * len(self.aspect_ratios)\n\n\nclass AnchorLabeler(object):\n  \"\"\"Labeler for multiscale anchor boxes.\"\"\"\n\n  def __init__(self, anchors, num_classes, match_threshold=0.5):\n    \"\"\"Constructs anchor labeler to assign labels to anchors.\n\n    Args:\n      anchors: an instance of class Anchors.\n      num_classes: integer number representing number of classes in the dataset.\n      match_threshold: float number between 0 and 1 representing the threshold\n        to assign positive labels for anchors.\n    \"\"\"\n    similarity_calc = region_similarity_calculator.IouSimilarity()\n    matcher = argmax_matcher.ArgMaxMatcher(\n        match_threshold,\n        unmatched_threshold=match_threshold,\n        negatives_lower_than_unmatched=True,\n        force_match_for_each_row=True)\n    box_coder = faster_rcnn_box_coder.FasterRcnnBoxCoder()\n\n    self._target_assigner = target_assigner.TargetAssigner(\n        similarity_calc, matcher, box_coder)\n    self._anchors = anchors\n    self._match_threshold = match_threshold\n    self._num_classes = num_classes\n\n  def _unpack_labels(self, labels):\n    \"\"\"Unpacks an array of labels into multiscales labels.\"\"\"\n    labels_unpacked = collections.OrderedDict()\n    anchors = self._anchors\n    count = 0\n    for level in range(anchors.min_level, anchors.max_level + 1):\n      feat_size = anchors.feat_sizes[level]\n      steps = feat_size['height'] * feat_size[\n          'width'] * anchors.get_anchors_per_location()\n      indices = tf.range(count, count + steps)\n      count += steps\n      labels_unpacked[level] = tf.reshape(\n          tf.gather(labels, indices),\n          [feat_size['height'], feat_size['width'], -1])\n    return labels_unpacked\n\n  def label_anchors(self, gt_boxes, gt_labels):\n    \"\"\"Labels anchors with ground truth inputs.\n\n    Args:\n      gt_boxes: A float tensor with shape [N, 4] representing groundtruth boxes.\n        For each row, it stores [y0, x0, y1, x1] for four corners of a box.\n      gt_labels: A integer tensor with shape [N, 1] representing groundtruth\n        classes.\n    Returns:\n      cls_targets_dict: ordered dictionary with keys\n        [min_level, min_level+1, ..., max_level]. The values are tensor with\n        shape [height_l, width_l, num_anchors]. The height_l and width_l\n        represent the dimension of class logits at l-th level.\n      box_targets_dict: ordered dictionary with keys\n        [min_level, min_level+1, ..., max_level]. The values are tensor with\n        shape [height_l, width_l, num_anchors * 4]. The height_l and\n        width_l represent the dimension of bounding box regression output at\n        l-th level.\n      num_positives: scalar tensor storing number of positives in an image.\n    \"\"\"\n    gt_box_list = box_list.BoxList(gt_boxes)\n    anchor_box_list = box_list.BoxList(self._anchors.boxes)\n\n    # cls_weights, box_weights are not used\n    cls_targets, _, box_targets, _, matches = self._target_assigner.assign(\n        anchor_box_list, gt_box_list, gt_labels)\n\n    # class labels start from 1 and the background class = -1\n    cls_targets -= 1\n    cls_targets = tf.cast(cls_targets, tf.int32)\n\n    # Unpack labels.\n    cls_targets_dict = self._unpack_labels(cls_targets)\n    box_targets_dict = self._unpack_labels(box_targets)\n    num_positives = tf.reduce_sum(\n        tf.cast(tf.not_equal(matches.match_results, -1), tf.float32))\n\n    return cls_targets_dict, box_targets_dict, num_positives\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/keras/efficientdet_keras.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Keras implementation of efficientdet.\"\"\"\nimport functools\nfrom absl import logging\nimport numpy as np\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import dataloader\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import hparams_config\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.backbone import backbone_factory\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.backbone import efficientnet_builder\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import fpn_configs\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import postprocess\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import tfmot\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import util_keras\n\n\ndef add_n(nodes):\n  \"\"\"A customized add_n to add up a list of tensors.\"\"\"\n  # tf.add_n is not supported by EdgeTPU, while tf.reduce_sum is not supported\n  # by GPU and runs slow on EdgeTPU because of the 5-dimension op.\n  with tf.name_scope('add_n'):\n    new_node = nodes[0]\n    for n in nodes[1:]:\n      new_node = new_node + n\n    return new_node\n\n\nclass FNode(tf.keras.layers.Layer):\n  \"\"\"A Keras Layer implementing BiFPN Node.\"\"\"\n\n  def __init__(self,\n               feat_level,\n               inputs_offsets,\n               fpn_num_filters,\n               apply_bn_for_resampling,\n               is_training_bn,\n               conv_after_downsample,\n               conv_bn_act_pattern,\n               separable_conv,\n               act_type,\n               strategy,\n               weight_method,\n               data_format,\n               model_optimizations,\n               name='fnode'):\n    super().__init__(name=name)\n    self.feat_level = feat_level\n    self.inputs_offsets = inputs_offsets\n    self.fpn_num_filters = fpn_num_filters\n    self.apply_bn_for_resampling = apply_bn_for_resampling\n    self.separable_conv = separable_conv\n    self.act_type = act_type\n    self.is_training_bn = is_training_bn\n    self.conv_after_downsample = conv_after_downsample\n    self.strategy = strategy\n    self.data_format = data_format\n    self.weight_method = weight_method\n    self.conv_bn_act_pattern = conv_bn_act_pattern\n    self.resample_layers = []\n    self.vars = []\n    self.model_optimizations = model_optimizations\n\n  def fuse_features(self, nodes):\n    \"\"\"Fuse features from different resolutions and return a weighted sum.\n\n    Args:\n      nodes: a list of tensorflow features at different levels\n\n    Returns:\n      A tensor denoting the fused feature.\n    \"\"\"\n    dtype = nodes[0].dtype\n\n    if self.weight_method == 'attn':\n      edge_weights = [tf.cast(var, dtype=dtype) for var in self.vars]\n      normalized_weights = tf.nn.softmax(tf.stack(edge_weights))\n      nodes = tf.stack(nodes, axis=-1)\n      new_node = tf.reduce_sum(nodes * normalized_weights, -1)\n    elif self.weight_method == 'fastattn':\n      edge_weights = [\n          tf.nn.relu(tf.cast(var, dtype=dtype)) for var in self.vars\n      ]\n      weights_sum = add_n(edge_weights)\n      nodes = [\n          nodes[i] * edge_weights[i] / (weights_sum + 0.0001)\n          for i in range(len(nodes))\n      ]\n      new_node = add_n(nodes)\n    elif self.weight_method == 'channel_attn':\n      edge_weights = [tf.cast(var, dtype=dtype) for var in self.vars]\n      normalized_weights = tf.nn.softmax(tf.stack(edge_weights, -1), axis=-1)\n      nodes = tf.stack(nodes, axis=-1)\n      new_node = tf.reduce_sum(nodes * normalized_weights, -1)\n    elif self.weight_method == 'channel_fastattn':\n      edge_weights = [\n          tf.nn.relu(tf.cast(var, dtype=dtype)) for var in self.vars\n      ]\n      weights_sum = add_n(edge_weights)\n      nodes = [\n          nodes[i] * edge_weights[i] / (weights_sum + 0.0001)\n          for i in range(len(nodes))\n      ]\n      new_node = add_n(nodes)\n    elif self.weight_method == 'sum':\n      new_node = add_n(nodes)\n    else:\n      raise ValueError('unknown weight_method %s' % self.weight_method)\n\n    return new_node\n\n  def _add_wsm(self, initializer, shape=None):\n    for i, _ in enumerate(self.inputs_offsets):\n      name = 'WSM' + ('' if i == 0 else '_' + str(i))\n      self.vars.append(\n          self.add_weight(initializer=initializer, name=name, shape=shape))\n\n  def build(self, feats_shape):\n    for i, input_offset in enumerate(self.inputs_offsets):\n      name = 'resample_{}_{}_{}'.format(i, input_offset, len(feats_shape))\n      self.resample_layers.append(\n          ResampleFeatureMap(\n              self.feat_level,\n              self.fpn_num_filters,\n              self.apply_bn_for_resampling,\n              self.is_training_bn,\n              self.conv_after_downsample,\n              strategy=self.strategy,\n              data_format=self.data_format,\n              model_optimizations=self.model_optimizations,\n              name=name))\n    if self.weight_method == 'attn':\n      self._add_wsm('ones')\n    elif self.weight_method == 'fastattn':\n      self._add_wsm('ones')\n    elif self.weight_method == 'channel_attn':\n      num_filters = int(self.fpn_num_filters)\n      self._add_wsm(tf.ones, num_filters)\n    elif self.weight_method == 'channel_fastattn':\n      num_filters = int(self.fpn_num_filters)\n      self._add_wsm(tf.ones, num_filters)\n    self.op_after_combine = OpAfterCombine(\n        self.is_training_bn,\n        self.conv_bn_act_pattern,\n        self.separable_conv,\n        self.fpn_num_filters,\n        self.act_type,\n        self.data_format,\n        self.strategy,\n        self.model_optimizations,\n        name='op_after_combine{}'.format(len(feats_shape)))\n    self.built = True\n    super().build(feats_shape)\n\n  def call(self, feats, training):\n    nodes = []\n    for i, input_offset in enumerate(self.inputs_offsets):\n      input_node = feats[input_offset]\n      input_node = self.resample_layers[i](input_node, training, feats)\n      nodes.append(input_node)\n    new_node = self.fuse_features(nodes)\n    new_node = self.op_after_combine(new_node)\n    return feats + [new_node]\n\n\nclass OpAfterCombine(tf.keras.layers.Layer):\n  \"\"\"Operation after combining input features during feature fusiong.\"\"\"\n\n  def __init__(self,\n               is_training_bn,\n               conv_bn_act_pattern,\n               separable_conv,\n               fpn_num_filters,\n               act_type,\n               data_format,\n               strategy,\n               model_optimizations,\n               name='op_after_combine'):\n    super().__init__(name=name)\n    self.conv_bn_act_pattern = conv_bn_act_pattern\n    self.separable_conv = separable_conv\n    self.fpn_num_filters = fpn_num_filters\n    self.act_type = act_type\n    self.data_format = data_format\n    self.strategy = strategy\n    self.is_training_bn = is_training_bn\n    if self.separable_conv:\n      conv2d_layer = functools.partial(\n          tf.keras.layers.SeparableConv2D, depth_multiplier=1)\n    else:\n      conv2d_layer = tf.keras.layers.Conv2D\n\n    self.conv_op = conv2d_layer(\n        filters=fpn_num_filters,\n        kernel_size=(3, 3),\n        padding='same',\n        use_bias=not self.conv_bn_act_pattern,\n        data_format=self.data_format,\n        name='conv')\n    if model_optimizations:\n      for method in model_optimizations.keys():\n        self.conv_op = (\n            tfmot.get_method(method)(self.conv_op))\n    self.bn = util_keras.build_batch_norm(\n        is_training_bn=self.is_training_bn,\n        data_format=self.data_format,\n        strategy=self.strategy,\n        name='bn')\n\n  def call(self, new_node, training):\n    if not self.conv_bn_act_pattern:\n      new_node = utils.activation_fn(new_node, self.act_type)\n    new_node = self.conv_op(new_node)\n    new_node = self.bn(new_node, training=training)\n    if self.conv_bn_act_pattern:\n      new_node = utils.activation_fn(new_node, self.act_type)\n    return new_node\n\n\nclass ResampleFeatureMap(tf.keras.layers.Layer):\n  \"\"\"Resample feature map for downsampling or upsampling.\"\"\"\n\n  def __init__(self,\n               feat_level,\n               target_num_channels,\n               apply_bn=False,\n               is_training_bn=None,\n               conv_after_downsample=False,\n               strategy=None,\n               data_format=None,\n               pooling_type=None,\n               upsampling_type=None,\n               model_optimizations=None,\n               name='resample_p0'):\n    super().__init__(name=name)\n    self.apply_bn = apply_bn\n    self.is_training_bn = is_training_bn\n    self.data_format = data_format\n    self.target_num_channels = target_num_channels\n    self.feat_level = feat_level\n    self.strategy = strategy\n    self.conv_after_downsample = conv_after_downsample\n    self.pooling_type = pooling_type or 'max'\n    self.upsampling_type = upsampling_type or 'nearest'\n\n    self.conv2d = tf.keras.layers.Conv2D(\n        self.target_num_channels, (1, 1),\n        padding='same',\n        data_format=self.data_format,\n        name='conv2d')\n    if model_optimizations:\n      for method in model_optimizations.keys():\n        self.conv2d = tfmot.get_method(method)(self.conv2d)\n    self.bn = util_keras.build_batch_norm(\n        is_training_bn=self.is_training_bn,\n        data_format=self.data_format,\n        strategy=self.strategy,\n        name='bn')\n\n  def _pool2d(self, inputs, height, width, target_height, target_width):\n    \"\"\"Pool the inputs to target height and width.\"\"\"\n    height_stride_size = int((height - 1) // target_height + 1)\n    width_stride_size = int((width - 1) // target_width + 1)\n    if self.pooling_type == 'max':\n      return tf.keras.layers.MaxPooling2D(\n          pool_size=[height_stride_size + 1, width_stride_size + 1],\n          strides=[height_stride_size, width_stride_size],\n          padding='SAME',\n          data_format=self.data_format)(inputs)\n    if self.pooling_type == 'avg':\n      return tf.keras.layers.AveragePooling2D(\n          pool_size=[height_stride_size + 1, width_stride_size + 1],\n          strides=[height_stride_size, width_stride_size],\n          padding='SAME',\n          data_format=self.data_format)(inputs)\n    raise ValueError('Unsupported pooling type {}.'.format(self.pooling_type))\n\n  def _upsample2d(self, inputs, target_height, target_width, training=False):\n    return tf.cast(\n        tf.compat.v1.image.resize_nearest_neighbor(\n            tf.cast(inputs, tf.float32), [target_height, target_width]),\n        inputs.dtype)\n\n  def _maybe_apply_1x1(self, feat, training, num_channels):\n    \"\"\"Apply 1x1 conv to change layer width if necessary.\"\"\"\n    if num_channels != self.target_num_channels:\n      feat = self.conv2d(feat)\n      if self.apply_bn:\n        feat = self.bn(feat, training=training)\n    return feat\n\n  def call(self, feat, training, all_feats):\n    hwc_idx = (2, 3, 1) if self.data_format == 'channels_first' else (1, 2, 3)\n    height, width, num_channels = [feat.shape.as_list()[i] for i in hwc_idx]\n    if all_feats:\n      target_feat_shape = all_feats[self.feat_level].shape.as_list()\n      target_height, target_width, _ = [target_feat_shape[i] for i in hwc_idx]\n    else:\n      # Default to downsampling if all_feats is empty.\n      target_height, target_width = (height + 1) // 2, (width + 1) // 2\n\n    # If conv_after_downsample is True, when downsampling, apply 1x1 after\n    # downsampling for efficiency.\n    if height > target_height and width > target_width:\n      if not self.conv_after_downsample:\n        feat = self._maybe_apply_1x1(feat, training, num_channels)\n      feat = self._pool2d(feat, height, width, target_height, target_width)\n      if self.conv_after_downsample:\n        feat = self._maybe_apply_1x1(feat, training, num_channels)\n    elif height <= target_height and width <= target_width:\n      feat = self._maybe_apply_1x1(feat, training, num_channels)\n      if height < target_height or width < target_width:\n        feat = self._upsample2d(feat, target_height, target_width, training)\n    else:\n      raise ValueError(\n          'Incompatible Resampling : feat shape {}x{} target_shape: {}x{}'\n          .format(height, width, target_height, target_width))\n\n    return feat\n\n\nclass ClassNet(tf.keras.layers.Layer):\n  \"\"\"Object class prediction network.\"\"\"\n\n  def __init__(self,\n               num_classes=90,\n               num_anchors=9,\n               num_filters=32,\n               min_level=3,\n               max_level=7,\n               is_training_bn=False,\n               act_type='swish',\n               repeats=4,\n               separable_conv=True,\n               survival_prob=None,\n               strategy=None,\n               data_format='channels_last',\n               grad_checkpoint=False,\n               name='class_net',\n               feature_only=False,\n               **kwargs):\n    \"\"\"Initialize the ClassNet.\n\n    Args:\n      num_classes: number of classes.\n      num_anchors: number of anchors.\n      num_filters: number of filters for \"intermediate\" layers.\n      min_level: minimum level for features.\n      max_level: maximum level for features.\n      is_training_bn: True if we train the BatchNorm.\n      act_type: String of the activation used.\n      repeats: number of intermediate layers.\n      separable_conv: True to use separable_conv instead of conv2D.\n      survival_prob: if a value is set then drop connect will be used.\n      strategy: string to specify training strategy for TPU/GPU/CPU.\n      data_format: string of 'channel_first' or 'channels_last'.\n      grad_checkpoint: bool, If true, apply grad checkpoint for saving memory.\n      name: the name of this layerl.\n      feature_only: build the base feature network only (excluding final class\n        head).\n      **kwargs: other parameters.\n    \"\"\"\n\n    super().__init__(name=name, **kwargs)\n    self.num_classes = num_classes\n    self.num_anchors = num_anchors\n    self.num_filters = num_filters\n    self.min_level = min_level\n    self.max_level = max_level\n    self.repeats = repeats\n    self.separable_conv = separable_conv\n    self.is_training_bn = is_training_bn\n    self.survival_prob = survival_prob\n    self.act_type = act_type\n    self.strategy = strategy\n    self.data_format = data_format\n    self.conv_ops = []\n    self.bns = []\n    self.grad_checkpoint = grad_checkpoint\n    self.feature_only = feature_only\n\n    conv2d_layer = self.conv2d_layer(separable_conv, data_format)\n    for i in range(self.repeats):\n      # If using SeparableConv2D\n      self.conv_ops.append(\n          conv2d_layer(\n              self.num_filters,\n              kernel_size=3,\n              bias_initializer=tf.zeros_initializer(),\n              activation=None,\n              padding='same',\n              name='class-%d' % i))\n\n      bn_per_level = []\n      for level in range(self.min_level, self.max_level + 1):\n        bn_per_level.append(\n            util_keras.build_batch_norm(\n                is_training_bn=self.is_training_bn,\n                strategy=self.strategy,\n                data_format=self.data_format,\n                name='class-%d-bn-%d' % (i, level),\n            ))\n      self.bns.append(bn_per_level)\n\n    self.classes = self.classes_layer(\n        conv2d_layer, num_classes, num_anchors, name='class-predict')\n\n  @tf.autograph.experimental.do_not_convert\n  def _conv_bn_act(self, image, i, level_id, training):\n    conv_op = self.conv_ops[i]\n    bn = self.bns[i][level_id]\n    act_type = self.act_type\n\n    @utils.recompute_grad(self.grad_checkpoint)\n    def _call(image):\n      original_image = image\n      image = conv_op(image)\n      image = bn(image, training=training)\n      if self.act_type:\n        image = utils.activation_fn(image, act_type)\n      if i > 0 and self.survival_prob:\n        image = utils.drop_connect(image, training, self.survival_prob)\n        image = image + original_image\n      return image\n\n    return _call(image)\n\n  def call(self, inputs, training, **kwargs):\n    \"\"\"Call ClassNet.\"\"\"\n    class_outputs = []\n    for level_id in range(0, self.max_level - self.min_level + 1):\n      image = inputs[level_id]\n      for i in range(self.repeats):\n        image = self._conv_bn_act(image, i, level_id, training)\n      if self.feature_only:\n        class_outputs.append(image)\n      else:\n        class_outputs.append(self.classes(image))\n\n    return class_outputs\n\n  @classmethod\n  def conv2d_layer(cls, separable_conv, data_format):\n    \"\"\"Gets the conv2d layer in ClassNet class.\"\"\"\n    if separable_conv:\n      conv2d_layer = functools.partial(\n          tf.keras.layers.SeparableConv2D,\n          depth_multiplier=1,\n          data_format=data_format,\n          pointwise_initializer=tf.initializers.variance_scaling(),\n          depthwise_initializer=tf.initializers.variance_scaling())\n    else:\n      conv2d_layer = functools.partial(\n          tf.keras.layers.Conv2D,\n          data_format=data_format,\n          kernel_initializer=tf.random_normal_initializer(stddev=0.01))\n    return conv2d_layer\n\n  @classmethod\n  def classes_layer(cls, conv2d_layer, num_classes, num_anchors, name):\n    \"\"\"Gets the classes layer in ClassNet class.\"\"\"\n    return conv2d_layer(\n        num_classes * num_anchors,\n        kernel_size=3,\n        bias_initializer=tf.constant_initializer(-np.log((1 - 0.01) / 0.01)),\n        padding='same',\n        name=name)\n\n\nclass BoxNet(tf.keras.layers.Layer):\n  \"\"\"Box regression network.\"\"\"\n\n  def __init__(self,\n               num_anchors=9,\n               num_filters=32,\n               min_level=3,\n               max_level=7,\n               is_training_bn=False,\n               act_type='swish',\n               repeats=4,\n               separable_conv=True,\n               survival_prob=None,\n               strategy=None,\n               data_format='channels_last',\n               grad_checkpoint=False,\n               name='box_net',\n               feature_only=False,\n               **kwargs):\n    \"\"\"Initialize BoxNet.\n\n    Args:\n      num_anchors: number of  anchors used.\n      num_filters: number of filters for \"intermediate\" layers.\n      min_level: minimum level for features.\n      max_level: maximum level for features.\n      is_training_bn: True if we train the BatchNorm.\n      act_type: String of the activation used.\n      repeats: number of \"intermediate\" layers.\n      separable_conv: True to use separable_conv instead of conv2D.\n      survival_prob: if a value is set then drop connect will be used.\n      strategy: string to specify training strategy for TPU/GPU/CPU.\n      data_format: string of 'channel_first' or 'channels_last'.\n      grad_checkpoint: bool, If true, apply grad checkpoint for saving memory.\n      name: Name of the layer.\n      feature_only: build the base feature network only (excluding box class\n        head).\n      **kwargs: other parameters.\n    \"\"\"\n\n    super().__init__(name=name, **kwargs)\n\n    self.num_anchors = num_anchors\n    self.num_filters = num_filters\n    self.min_level = min_level\n    self.max_level = max_level\n    self.repeats = repeats\n    self.separable_conv = separable_conv\n    self.is_training_bn = is_training_bn\n    self.survival_prob = survival_prob\n    self.act_type = act_type\n    self.strategy = strategy\n    self.data_format = data_format\n    self.grad_checkpoint = grad_checkpoint\n    self.feature_only = feature_only\n\n    self.conv_ops = []\n    self.bns = []\n\n    for i in range(self.repeats):\n      # If using SeparableConv2D\n      if self.separable_conv:\n        self.conv_ops.append(\n            tf.keras.layers.SeparableConv2D(\n                filters=self.num_filters,\n                depth_multiplier=1,\n                pointwise_initializer=tf.initializers.variance_scaling(),\n                depthwise_initializer=tf.initializers.variance_scaling(),\n                data_format=self.data_format,\n                kernel_size=3,\n                activation=None,\n                bias_initializer=tf.zeros_initializer(),\n                padding='same',\n                name='box-%d' % i))\n      # If using Conv2d\n      else:\n        self.conv_ops.append(\n            tf.keras.layers.Conv2D(\n                filters=self.num_filters,\n                kernel_initializer=tf.random_normal_initializer(stddev=0.01),\n                data_format=self.data_format,\n                kernel_size=3,\n                activation=None,\n                bias_initializer=tf.zeros_initializer(),\n                padding='same',\n                name='box-%d' % i))\n\n      bn_per_level = []\n      for level in range(self.min_level, self.max_level + 1):\n        bn_per_level.append(\n            util_keras.build_batch_norm(\n                is_training_bn=self.is_training_bn,\n                strategy=self.strategy,\n                data_format=self.data_format,\n                name='box-%d-bn-%d' % (i, level)))\n      self.bns.append(bn_per_level)\n\n      self.boxes = self.boxes_layer(\n          separable_conv, num_anchors, data_format, name='box-predict')\n\n  @tf.autograph.experimental.do_not_convert\n  def _conv_bn_act(self, image, i, level_id, training):\n    conv_op = self.conv_ops[i]\n    bn = self.bns[i][level_id]\n    act_type = self.act_type\n\n    @utils.recompute_grad(self.grad_checkpoint)\n    def _call(image):\n      original_image = image\n      image = conv_op(image)\n      image = bn(image, training=training)\n      if self.act_type:\n        image = utils.activation_fn(image, act_type)\n      if i > 0 and self.survival_prob:\n        image = utils.drop_connect(image, training, self.survival_prob)\n        image = image + original_image\n      return image\n\n    return _call(image)\n\n  def call(self, inputs, training):\n    \"\"\"Call boxnet.\"\"\"\n    box_outputs = []\n    for level_id in range(0, self.max_level - self.min_level + 1):\n      image = inputs[level_id]\n      for i in range(self.repeats):\n        image = self._conv_bn_act(image, i, level_id, training)\n\n      if self.feature_only:\n        box_outputs.append(image)\n      else:\n        box_outputs.append(self.boxes(image))\n\n    return box_outputs\n\n  @classmethod\n  def boxes_layer(cls, separable_conv, num_anchors, data_format, name):\n    \"\"\"Gets the conv2d layer in BoxNet class.\"\"\"\n    if separable_conv:\n      return tf.keras.layers.SeparableConv2D(\n          filters=4 * num_anchors,\n          depth_multiplier=1,\n          pointwise_initializer=tf.initializers.variance_scaling(),\n          depthwise_initializer=tf.initializers.variance_scaling(),\n          data_format=data_format,\n          kernel_size=3,\n          activation=None,\n          bias_initializer=tf.zeros_initializer(),\n          padding='same',\n          name=name)\n    else:\n      return tf.keras.layers.Conv2D(\n          filters=4 * num_anchors,\n          kernel_initializer=tf.random_normal_initializer(stddev=0.01),\n          data_format=data_format,\n          kernel_size=3,\n          activation=None,\n          bias_initializer=tf.zeros_initializer(),\n          padding='same',\n          name=name)\n\n\nclass SegmentationHead(tf.keras.layers.Layer):\n  \"\"\"Keras layer for semantic segmentation head.\"\"\"\n\n  def __init__(self,\n               num_classes,\n               num_filters,\n               min_level,\n               max_level,\n               data_format,\n               is_training_bn,\n               act_type,\n               strategy,\n               name='segmentation_head',\n               **kwargs):\n    \"\"\"Initialize SegmentationHead.\n\n    Args:\n      num_classes: number of classes.\n      num_filters: number of filters for \"intermediate\" layers.\n      min_level: minimum level for features.\n      max_level: maximum level for features.\n      data_format: string of 'channel_first' or 'channels_last'.\n      is_training_bn: True if we train the BatchNorm.\n      act_type: String of the activation used.\n      strategy: string to specify training strategy for TPU/GPU/CPU.\n      name: string of name.\n      **kwargs: other parameters.\n    \"\"\"\n    super().__init__(name=name, **kwargs)\n    self.act_type = act_type\n    self.con2d_ts = []\n    self.con2d_t_bns = []\n    for level in range(max_level - min_level):\n      self.con2d_ts.append(\n          tf.keras.layers.Conv2DTranspose(\n              num_filters,\n              3,\n              strides=2,\n              padding='same',\n              data_format=data_format,\n              use_bias=False))\n      self.con2d_t_bns.append(\n          util_keras.build_batch_norm(\n              is_training_bn=is_training_bn,\n              data_format=data_format,\n              strategy=strategy,\n              name='bn_' + str(level)))\n    self.head_transpose = tf.keras.layers.Conv2DTranspose(\n        num_classes, 3, strides=2, padding='same')\n\n  def call(self, feats, training):\n    x = feats[-1]\n    skips = list(reversed(feats[:-1]))\n\n    for con2d_t, con2d_t_bn, skip in zip(self.con2d_ts, self.con2d_t_bns,\n                                         skips):\n      x = con2d_t(x)\n      x = con2d_t_bn(x, training)\n      x = utils.activation_fn(x, self.act_type)\n      x = tf.concat([x, skip], axis=-1)\n\n    # This is the last layer of the model\n    return self.head_transpose(x)  # 64x64 -> 128x128\n\n\nclass FPNCells(tf.keras.layers.Layer):\n  \"\"\"FPN cells.\"\"\"\n\n  def __init__(self, config, name='fpn_cells'):\n    super().__init__(name=name)\n    self.config = config\n\n    if config.fpn_config:\n      self.fpn_config = config.fpn_config\n    else:\n      self.fpn_config = fpn_configs.get_fpn_config(config.fpn_name,\n                                                   config.min_level,\n                                                   config.max_level,\n                                                   config.fpn_weight_method)\n\n    self.cells = [\n        FPNCell(self.config, name='cell_%d' % rep)\n        for rep in range(self.config.fpn_cell_repeats)\n    ]\n\n  def call(self, feats, training):\n    for cell in self.cells:\n      cell_feats = cell(feats, training)\n      min_level = self.config.min_level\n      max_level = self.config.max_level\n\n      feats = []\n      for level in range(min_level, max_level + 1):\n        for i, fnode in enumerate(reversed(self.fpn_config.nodes)):\n          if fnode['feat_level'] == level:\n            feats.append(cell_feats[-1 - i])\n            break\n\n    return feats\n\n\nclass FPNCell(tf.keras.layers.Layer):\n  \"\"\"A single FPN cell.\"\"\"\n\n  def __init__(self, config, name='fpn_cell'):\n    super().__init__(name=name)\n    self.config = config\n    if config.fpn_config:\n      self.fpn_config = config.fpn_config\n    else:\n      self.fpn_config = fpn_configs.get_fpn_config(config.fpn_name,\n                                                   config.min_level,\n                                                   config.max_level,\n                                                   config.fpn_weight_method)\n    self.fnodes = []\n    for i, fnode_cfg in enumerate(self.fpn_config.nodes):\n      logging.info('fnode %d : %s', i, fnode_cfg)\n      fnode = FNode(\n          fnode_cfg['feat_level'] - self.config.min_level,\n          fnode_cfg['inputs_offsets'],\n          config.fpn_num_filters,\n          config.apply_bn_for_resampling,\n          config.is_training_bn,\n          config.conv_after_downsample,\n          config.conv_bn_act_pattern,\n          config.separable_conv,\n          config.act_type,\n          strategy=config.strategy,\n          weight_method=self.fpn_config.weight_method,\n          data_format=config.data_format,\n          model_optimizations=config.model_optimizations,\n          name='fnode%d' % i)\n      self.fnodes.append(fnode)\n\n  def call(self, feats, training):\n    @utils.recompute_grad(self.config.grad_checkpoint)\n    def _call(feats):\n      for fnode in self.fnodes:\n        feats = fnode(feats, training)\n      return feats\n    return _call(feats)\n\n\nclass EfficientDetNet(tf.keras.Model):\n  \"\"\"EfficientDet keras network without pre/post-processing.\"\"\"\n\n  def __init__(self,\n               model_name=None,\n               config=None,\n               name='',\n               feature_only=False):\n    \"\"\"Initialize model.\"\"\"\n    super().__init__(name=name)\n\n    config = config or hparams_config.get_efficientdet_config(model_name)\n    self.config = config\n\n    # Backbone.\n    backbone_name = config.backbone_name\n    is_training_bn = config.is_training_bn\n    if 'efficientnet' in backbone_name:\n      override_params = {\n          'batch_norm':\n              utils.batch_norm_class(is_training_bn, config.strategy),\n          'relu_fn':\n              functools.partial(utils.activation_fn, act_type=config.act_type),\n          'grad_checkpoint': self.config.grad_checkpoint\n      }\n      if 'b0' in backbone_name:\n        override_params['survival_prob'] = 0.0\n      if config.backbone_config is not None:\n        override_params['blocks_args'] = (\n            efficientnet_builder.BlockDecoder().encode(\n                config.backbone_config.blocks))\n      override_params['data_format'] = config.data_format\n      self.backbone = backbone_factory.get_model(\n          backbone_name, override_params=override_params)\n\n    # Feature network.\n    self.resample_layers = []  # additional resampling layers.\n    for level in range(6, config.max_level + 1):\n      # Adds a coarser level by downsampling the last feature map.\n      self.resample_layers.append(\n          ResampleFeatureMap(\n              feat_level=(level - config.min_level),\n              target_num_channels=config.fpn_num_filters,\n              apply_bn=config.apply_bn_for_resampling,\n              is_training_bn=config.is_training_bn,\n              conv_after_downsample=config.conv_after_downsample,\n              strategy=config.strategy,\n              data_format=config.data_format,\n              model_optimizations=config.model_optimizations,\n              name='resample_p%d' % level,\n          ))\n    self.fpn_cells = FPNCells(config)\n\n    # class/box output prediction network.\n    num_anchors = len(config.aspect_ratios) * config.num_scales\n    num_filters = config.fpn_num_filters\n    for head in config.heads:\n      if head == 'object_detection':\n        self.class_net = ClassNet(\n            num_classes=config.num_classes,\n            num_anchors=num_anchors,\n            num_filters=num_filters,\n            min_level=config.min_level,\n            max_level=config.max_level,\n            is_training_bn=config.is_training_bn,\n            act_type=config.act_type,\n            repeats=config.box_class_repeats,\n            separable_conv=config.separable_conv,\n            survival_prob=config.survival_prob,\n            strategy=config.strategy,\n            grad_checkpoint=config.grad_checkpoint,\n            data_format=config.data_format,\n            feature_only=feature_only)\n\n        self.box_net = BoxNet(\n            num_anchors=num_anchors,\n            num_filters=num_filters,\n            min_level=config.min_level,\n            max_level=config.max_level,\n            is_training_bn=config.is_training_bn,\n            act_type=config.act_type,\n            repeats=config.box_class_repeats,\n            separable_conv=config.separable_conv,\n            survival_prob=config.survival_prob,\n            strategy=config.strategy,\n            grad_checkpoint=config.grad_checkpoint,\n            data_format=config.data_format,\n            feature_only=feature_only)\n\n      if head == 'segmentation':\n        self.seg_head = SegmentationHead(\n            num_classes=config.seg_num_classes,\n            num_filters=num_filters,\n            min_level=config.min_level,\n            max_level=config.max_level,\n            is_training_bn=config.is_training_bn,\n            act_type=config.act_type,\n            strategy=config.strategy,\n            data_format=config.data_format)\n\n  def _init_set_name(self, name, zero_based=True):\n    \"\"\"A hack to allow empty model name for legacy checkpoint compitability.\"\"\"\n    if name == '':  # pylint: disable=g-explicit-bool-comparison\n      self._name = name\n    else:\n      self._name = super().__init__(name, zero_based)\n\n  def call(self, inputs, training):\n    \"\"\"Returns the network features.\n\n    Args:\n      inputs: Input image tensor, commonly with shape [batch, height, width, 3].\n      training: bool, whether in training model.\n\n    Returns:\n      A list of tensors based on the heads:\n        - If heads is empty, then just return fpn_features;\n        - If heads includes 'object_detection', then return bounding box and\n            class predictions.\n        - If heads includes 'segmentation', then return per-pixel class\n            predictions.\n    \"\"\"\n    config = self.config\n    # call backbone network.\n    all_feats = self.backbone(inputs, training=training, features_only=True)\n    all_feats[0] = inputs  # replace the [0] logits with inputs.\n    feats = all_feats[config.min_level:config.max_level + 1]\n\n    # Build additional input features that are not from backbone.\n    for resample_layer in self.resample_layers:\n      feats.append(resample_layer(feats[-1], training, None))\n\n    # call feature network.\n    fpn_feats = self.fpn_cells(feats, training)\n\n    # call class/box/seg output network.\n    outputs = []\n    if 'object_detection' in config.heads:\n      class_outputs = self.class_net(fpn_feats, training)\n      box_outputs = self.box_net(fpn_feats, training)\n      outputs.extend([class_outputs, box_outputs])\n    if 'segmentation' in config.heads:\n      seg_outputs = self.seg_head(fpn_feats, training)\n      outputs.append(seg_outputs)\n    return tuple(outputs) or fpn_feats\n\n\nclass EfficientDetModel(EfficientDetNet):\n  \"\"\"EfficientDet full keras model with pre and post processing.\"\"\"\n\n  def _preprocessing(self,\n                     raw_images,\n                     image_size,\n                     mean_rgb,\n                     stddev_rgb,\n                     mode=None):\n    \"\"\"Preprocess images before feeding to the network.\"\"\"\n    if not mode:\n      return raw_images, None\n\n    image_size = utils.parse_image_size(image_size)\n    if mode != 'infer':\n      # We only support inference for now.\n      raise ValueError('preprocessing must be infer or empty')\n\n    def map_fn(image):\n      input_processor = dataloader.DetectionInputProcessor(\n          image, image_size)\n      input_processor.normalize_image(mean_rgb, stddev_rgb)\n      input_processor.set_scale_factors_to_output_size()\n      image = input_processor.resize_and_crop_image()\n      image_scale = input_processor.image_scale_to_original\n      return image, image_scale\n\n    if raw_images.shape.as_list()[0]:  # fixed batch size.\n      batch_size = raw_images.shape.as_list()[0]\n      outputs = [map_fn(raw_images[i]) for i in range(batch_size)]\n      return [tf.stack(y) for y in zip(*outputs)]\n\n    # otherwise treat it as dynamic batch size.\n    return tf.vectorized_map(map_fn, raw_images)\n\n  def _postprocess(self, cls_outputs, box_outputs, scales, mode='global'):\n    \"\"\"Postprocess class and box predictions.\"\"\"\n    if not mode:\n      return cls_outputs, box_outputs\n\n    if mode == 'global':\n      return postprocess.postprocess_global(self.config.as_dict(), cls_outputs,\n                                            box_outputs, scales)\n    if mode == 'per_class':\n      return postprocess.postprocess_per_class(self.config.as_dict(),\n                                               cls_outputs, box_outputs, scales)\n    if mode == 'combined':\n      return postprocess.postprocess_combined(self.config.as_dict(),\n                                              cls_outputs, box_outputs, scales)\n    if mode == 'tflite':\n      if scales is not None:\n        # pre_mode should be None for TFLite.\n        raise ValueError('scales not supported for TFLite post-processing')\n      return postprocess.postprocess_tflite(self.config.as_dict(), cls_outputs,\n                                            box_outputs)\n    raise ValueError('Unsupported postprocess mode {}'.format(mode))\n\n  def call(self, inputs, training=False, pre_mode='infer', post_mode='global'):\n    \"\"\"Call this model.\n\n    Args:\n      inputs: a tensor with common shape [batch, height, width, channels].\n      training: If true, it is training mode. Otherwise, eval mode.\n      pre_mode: preprocessing mode, must be {None, 'infer'}.\n      post_mode: postprrocessing mode, must be {None, 'global', 'per_class'}.\n\n    Returns:\n      the output tensor list.\n    \"\"\"\n    config = self.config\n\n    # preprocess.\n    inputs, scales = self._preprocessing(inputs, config.image_size,\n                                         config.mean_rgb, config.stddev_rgb,\n                                         pre_mode)\n    # network.\n    outputs = super().call(inputs, training)\n\n    if 'object_detection' in config.heads and post_mode:\n      # postprocess for detection\n      det_outputs = self._postprocess(outputs[0], outputs[1], scales, post_mode)\n      outputs = det_outputs + outputs[2:]\n\n    return outputs\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/keras/eval.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Eval libraries.\"\"\"\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import coco_metric\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import dataloader\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import hparams_config\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import anchors\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import efficientdet_keras\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import label_util\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import postprocess\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import util_keras\n\n# Cloud TPU Cluster Resolvers\nflags.DEFINE_string('tpu', None, 'The Cloud TPU name.')\nflags.DEFINE_string('gcp_project', None, 'Project name.')\nflags.DEFINE_string('tpu_zone', None, 'GCE zone name.')\n\nflags.DEFINE_integer('eval_samples', None, 'Number of eval samples.')\nflags.DEFINE_string('val_file_pattern', None,\n                    'Glob for eval tfrecords, e.g. coco/val-*.tfrecord.')\nflags.DEFINE_string('val_json_file', None,\n                    'Groudtruth, e.g. annotations/instances_val2017.json.')\nflags.DEFINE_string('model_name', 'efficientdet-d0', 'Model name to use.')\nflags.DEFINE_string('model_dir', None, 'Location of the checkpoint to run.')\nflags.DEFINE_integer('batch_size', 8, 'GLobal batch size.')\nflags.DEFINE_string('hparams', '', 'Comma separated k=v pairs or a yaml file')\nFLAGS = flags.FLAGS\n\n\ndef main(_):\n  config = hparams_config.get_efficientdet_config(FLAGS.model_name)\n  config.override(FLAGS.hparams)\n  config.val_json_file = FLAGS.val_json_file\n  config.nms_configs.max_nms_inputs = anchors.MAX_DETECTION_POINTS\n  config.drop_remainder = False  # eval all examples w/o drop.\n  config.image_size = utils.parse_image_size(config['image_size'])\n\n  if config.strategy == 'tpu':\n    tpu_cluster_resolver = tf.distribute.cluster_resolver.TPUClusterResolver(\n        FLAGS.tpu, zone=FLAGS.tpu_zone, project=FLAGS.gcp_project)\n    tf.config.experimental_connect_to_cluster(tpu_cluster_resolver)\n    tf.tpu.experimental.initialize_tpu_system(tpu_cluster_resolver)\n    ds_strategy = tf.distribute.TPUStrategy(tpu_cluster_resolver)\n    logging.info('All devices: %s', tf.config.list_logical_devices('TPU'))\n  elif config.strategy == 'gpus':\n    ds_strategy = tf.distribute.MirroredStrategy()\n    logging.info('All devices: %s', tf.config.list_physical_devices('GPU'))\n  else:\n    if tf.config.list_physical_devices('GPU'):\n      ds_strategy = tf.distribute.OneDeviceStrategy('device:GPU:0')\n    else:\n      ds_strategy = tf.distribute.OneDeviceStrategy('device:CPU:0')\n\n  with ds_strategy.scope():\n    # Network\n    model = efficientdet_keras.EfficientDetNet(config=config)\n    model.build((None, *config.image_size, 3))\n    util_keras.restore_ckpt(model,\n                            tf.train.latest_checkpoint(FLAGS.model_dir),\n                            config.moving_average_decay,\n                            skip_mismatch=False)\n    @tf.function\n    def model_fn(images, labels):\n      cls_outputs, box_outputs = model(images, training=False)\n      detections = postprocess.generate_detections(config,\n                                                   cls_outputs,\n                                                   box_outputs,\n                                                   labels['image_scales'],\n                                                   labels['source_ids'])\n      tf.numpy_function(evaluator.update_state,\n                        [labels['groundtruth_data'],\n                         postprocess.transform_detections(detections)], [])\n\n    # Evaluator for AP calculation.\n    label_map = label_util.get_label_map(config.label_map)\n    evaluator = coco_metric.EvaluationMetric(\n        filename=config.val_json_file, label_map=label_map)\n\n    # dataset\n    batch_size = FLAGS.batch_size   # global batch size.\n    ds = dataloader.InputReader(\n        FLAGS.val_file_pattern,\n        is_training=False,\n        max_instances_per_image=config.max_instances_per_image)(\n            config, batch_size=batch_size)\n    if FLAGS.eval_samples:\n      ds = ds.take((FLAGS.eval_samples + batch_size - 1) // batch_size)\n    ds = ds_strategy.experimental_distribute_dataset(ds)\n\n    # evaluate all images.\n    eval_samples = FLAGS.eval_samples or 5000\n    pbar = tf.keras.utils.Progbar((eval_samples + batch_size - 1) // batch_size)\n    for i, (images, labels) in enumerate(ds):\n      ds_strategy.run(model_fn, (images, labels))\n      pbar.update(i)\n\n  # compute the final eval results.\n  metrics = evaluator.result()\n  metric_dict = {}\n  for i, name in enumerate(evaluator.metric_names):\n    metric_dict[name] = metrics[i]\n\n  if label_map:\n    for i, cid in enumerate(sorted(label_map.keys())):\n      name = 'AP_/%s' % label_map[cid]\n      metric_dict[name] = metrics[i + len(evaluator.metric_names)]\n  print(FLAGS.model_name, metric_dict)\n\n\nif __name__ == '__main__':\n  flags.mark_flag_as_required('val_file_pattern')\n  flags.mark_flag_as_required('model_dir')\n  logging.set_verbosity(logging.ERROR)\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/keras/eval_tflite.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Eval libraries. Used for TFLite model without post-processing.\"\"\"\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\nimport numpy as np\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import coco_metric\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import dataloader\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import hparams_config\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import anchors\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import label_util\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import postprocess\n\nFLAGS = flags.FLAGS\n\nDEFAULT_SCALE, DEFAULT_ZERO_POINT = 0, 0\n\n\ndef define_flags():\n  \"\"\"Define the flags.\"\"\"\n  flags.DEFINE_integer('eval_samples', None, 'Number of eval samples.')\n  flags.DEFINE_string('val_file_pattern', None,\n                      'Glob for eval tfrecords, e.g. coco/val-*.tfrecord.')\n  flags.DEFINE_string('val_json_file', None,\n                      'Groudtruth, e.g. annotations/instances_val2017.json.')\n  flags.DEFINE_string('model_name', 'efficientdet-d0', 'Model name to use.')\n  flags.DEFINE_string('tflite_path', None, 'Path to TFLite model.')\n  flags.DEFINE_bool(\n      'only_network', False,\n      'TFLite model only contains EfficientDetNet without post-processing NMS op.'\n  )\n  flags.DEFINE_bool('pre_class_nms', False, 'Use pre_class_nms for evaluation.')\n  flags.DEFINE_string('hparams', '', 'Comma separated k=v pairs or a yaml file')\n  flags.mark_flag_as_required('val_file_pattern')\n  flags.mark_flag_as_required('tflite_path')\n\n\nclass LiteRunner(object):\n  \"\"\"Runs inference with TF Lite model.\"\"\"\n\n  def __init__(self, tflite_model_path, only_network=False):\n    \"\"\"Initializes Lite runner with tflite model file.\n\n    Args:\n      tflite_model_path: Path to TFLite model file.\n      only_network: boolean, If True, TFLite model only contains EfficientDetNet\n        without post-processing NMS op. If False, TFLite model contains custom\n        NMS op.\n    \"\"\"\n    self.interpreter = tf.lite.Interpreter(tflite_model_path)\n    self.interpreter.allocate_tensors()\n    # Get input and output tensors.\n    self.input_details = self.interpreter.get_input_details()\n    self.output_details = self.interpreter.get_output_details()\n    self.only_network = only_network\n    if self.only_network:\n      # TFLite with only_network=True changed the order of output. Sort it\n      # according to tensor name.\n      # TFLite with only_network=False  doesn't change the order since the\n      # latest op is cutom NMS op.\n      self.output_details = sorted(self.output_details, key=lambda a: a['name'])\n\n  def run(self, image):\n    \"\"\"Runs inference with Lite model.\"\"\"\n    interpreter = self.interpreter\n    signature_fn = interpreter.get_signature_runner()\n    input_details = self.input_details\n    output_details = self.output_details\n\n    input_detail = input_details[0]\n    if input_detail['quantization'] != (DEFAULT_SCALE, DEFAULT_ZERO_POINT):\n      scale, zero_point = input_detail['quantization']\n      image = image / scale + zero_point\n      image = np.array(image, dtype=input_detail['dtype'])\n\n    output = signature_fn(images=image)\n\n    def get_output(idx):\n      output_detail = output_details[idx]\n      output_tensor = output[f'output_{idx}']\n      if output_detail['quantization'] != (DEFAULT_SCALE, DEFAULT_ZERO_POINT):\n        # Dequantize the output\n        scale, zero_point = output_detail['quantization']\n        output_tensor = output_tensor.astype(np.float32)\n        output_tensor = (output_tensor - zero_point) * scale\n      return output_tensor\n\n    output_size = len(output_details)\n    if not self.only_network:\n      # TFLite model with post-processing.\n      # Four Outputs:\n      #   num_boxes: a float32 tensor of size 1 containing the number of\n      #     detected boxes\n      #   detection_scores: a float32 tensor of shape [1, num_boxes]\n      #     with class scores\n      #   detection_classes: a float32 tensor of shape [1, num_boxes]\n      #     with class indices\n      #   detection_boxes: a float32 tensor of shape [1, num_boxes, 4] with box\n      #     locations\n      return [get_output(i) for i in range(output_size)]\n    else:\n      # TFLite model only contains network without post-processing.\n      num_boxes = int(output_size / 2)\n      cls_outputs, box_outputs = [], []\n      for i in range(num_boxes):\n        cls_outputs.append(get_output(i))\n        box_outputs.append(get_output(i + num_boxes))\n      return cls_outputs, box_outputs\n\n\ndef main(_):\n  config = hparams_config.get_efficientdet_config(FLAGS.model_name)\n  config.override(FLAGS.hparams)\n  config.val_json_file = FLAGS.val_json_file\n  config.nms_configs.max_nms_inputs = anchors.MAX_DETECTION_POINTS\n  config.drop_remainder = False  # eval all examples w/o drop.\n  config.image_size = utils.parse_image_size(config['image_size'])\n\n  # Evaluator for AP calculation.\n  label_map = label_util.get_label_map(config.label_map)\n  evaluator = coco_metric.EvaluationMetric(\n      filename=config.val_json_file, label_map=label_map)\n\n  # dataset\n  batch_size = 1\n  ds = dataloader.InputReader(\n      FLAGS.val_file_pattern,\n      is_training=False,\n      max_instances_per_image=config.max_instances_per_image)(\n          config, batch_size=batch_size)\n  eval_samples = FLAGS.eval_samples\n  if eval_samples:\n    ds = ds.take((eval_samples + batch_size - 1) // batch_size)\n\n  # Network\n  lite_runner = LiteRunner(FLAGS.tflite_path, FLAGS.only_network)\n  eval_samples = FLAGS.eval_samples or 5000\n  pbar = tf.keras.utils.Progbar((eval_samples + batch_size - 1) // batch_size)\n  for i, (images, labels) in enumerate(ds):\n    if not FLAGS.only_network:\n      _, nms_scores_bs, nms_classes_bs, nms_boxes_bs = lite_runner.run(images)\n      nms_classes_bs += postprocess.CLASS_OFFSET\n\n      height, width = utils.parse_image_size(config.image_size)\n      normalize_factor = tf.constant([height, width, height, width],\n                                     dtype=tf.float32)\n      nms_boxes_bs *= normalize_factor\n      if labels['image_scales'] is not None:\n        scales = tf.expand_dims(tf.expand_dims(labels['image_scales'], -1), -1)\n        nms_boxes_bs = nms_boxes_bs * tf.cast(scales, nms_boxes_bs.dtype)\n      detections = postprocess.generate_detections_from_nms_output(\n          nms_boxes_bs, nms_classes_bs, nms_scores_bs, labels['source_ids'])\n    else:\n      cls_outputs, box_outputs = lite_runner.run(images)\n      detections = postprocess.generate_detections(\n          config,\n          cls_outputs,\n          box_outputs,\n          labels['image_scales'],\n          labels['source_ids'],\n          pre_class_nms=FLAGS.pre_class_nms)\n\n    detections = postprocess.transform_detections(detections)\n    evaluator.update_state(labels['groundtruth_data'].numpy(),\n                           detections.numpy())\n    pbar.update(i)\n\n  # compute the final eval results.\n  metrics = evaluator.result()\n  metric_dict = {}\n  for i, name in enumerate(evaluator.metric_names):\n    metric_dict[name] = metrics[i]\n\n  if label_map:\n    for i, cid in enumerate(sorted(label_map.keys())):\n      name = 'AP_/%s' % label_map[cid]\n      metric_dict[name] = metrics[i + len(evaluator.metric_names)]\n  print(FLAGS.model_name, metric_dict)\n\n\nif __name__ == '__main__':\n  define_flags()\n  logging.set_verbosity(logging.WARNING)\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/keras/fpn_configs.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"BiFPN/QuFPN and other FPN configs.\n\nBiFPN is presented in the EfficientDet paper.\nQuFPN is proposed in https://github.com/google/automl/pull/580\n\"\"\"\nimport itertools\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import hparams_config\n\n\ndef bifpn_config(min_level, max_level, weight_method):\n  \"\"\"A dynamic bifpn config that can adapt to different min/max levels.\"\"\"\n  p = hparams_config.Config()\n  p.weight_method = weight_method or 'fastattn'\n\n  # Node id starts from the input features and monotonically increase whenever\n  # a new node is added. Here is an example for level P3 - P7:\n  #     P7 (4)              P7\" (12)\n  #     P6 (3)    P6' (5)   P6\" (11)\n  #     P5 (2)    P5' (6)   P5\" (10)\n  #     P4 (1)    P4' (7)   P4\" (9)\n  #     P3 (0)              P3\" (8)\n  # So output would be like:\n  # [\n  #   {'feat_level': 6, 'inputs_offsets': [3, 4]},  # for P6'\n  #   {'feat_level': 5, 'inputs_offsets': [2, 5]},  # for P5'\n  #   {'feat_level': 4, 'inputs_offsets': [1, 6]},  # for P4'\n  #   {'feat_level': 3, 'inputs_offsets': [0, 7]},  # for P3\"\n  #   {'feat_level': 4, 'inputs_offsets': [1, 7, 8]},  # for P4\"\n  #   {'feat_level': 5, 'inputs_offsets': [2, 6, 9]},  # for P5\"\n  #   {'feat_level': 6, 'inputs_offsets': [3, 5, 10]},  # for P6\"\n  #   {'feat_level': 7, 'inputs_offsets': [4, 11]},  # for P7\"\n  # ]\n  num_levels = max_level - min_level + 1\n  node_ids = {min_level + i: [i] for i in range(num_levels)}\n\n  level_last_id = lambda level: node_ids[level][-1]\n  level_all_ids = lambda level: node_ids[level]\n  id_cnt = itertools.count(num_levels)\n\n  p.nodes = []\n  for i in range(max_level - 1, min_level - 1, -1):\n    # top-down path.\n    p.nodes.append({\n        'feat_level': i,\n        'inputs_offsets': [level_last_id(i),\n                           level_last_id(i + 1)]\n    })\n    node_ids[i].append(next(id_cnt))\n\n  for i in range(min_level + 1, max_level + 1):\n    # bottom-up path.\n    p.nodes.append({\n        'feat_level': i,\n        'inputs_offsets': level_all_ids(i) + [level_last_id(i - 1)]\n    })\n    node_ids[i].append(next(id_cnt))\n\n  return p\n\n\ndef qufpn_config(min_level, max_level, weight_method=None):\n  \"\"\"A dynamic quad fpn config that can adapt to different min/max levels.\"\"\"\n  # It extends the idea of BiFPN, and has four paths:\n  #   (up_down -> bottom_up) + (bottom_up -> up_down).\n  # See test for an example for level 2 and 7.\n  p = hparams_config.Config()\n  p.weight_method = weight_method or 'fastattn'\n  p.quad_method = 'fastattn'\n  num_levels = max_level - min_level + 1\n  node_ids = {min_level + i: [i] for i in range(num_levels)}\n  level_last_id = lambda level: node_ids[level][-1]\n  level_all_ids = lambda level: node_ids[level]\n  level_first_id = lambda level: node_ids[level][0]\n  id_cnt = itertools.count(num_levels)\n\n  p.nodes = []\n  for i in range(max_level - 1, min_level - 1, -1):\n    # top-down path 1.\n    p.nodes.append({\n        'feat_level': i,\n        'inputs_offsets': [level_last_id(i),\n                           level_last_id(i + 1)],\n        'weight_method': p.weight_method\n    })\n    node_ids[i].append(next(id_cnt))\n  node_ids[max_level].append(node_ids[max_level][-1])\n\n  for i in range(min_level + 1, max_level):\n    # bottom-up path 2.\n    p.nodes.append({\n        'feat_level': i,\n        'inputs_offsets': level_all_ids(i) + [level_last_id(i - 1)],\n        'weight_method': p.weight_method\n    })\n    node_ids[i].append(next(id_cnt))\n\n  i = max_level\n  p.nodes.append({\n      'feat_level': i,\n      'inputs_offsets': [level_first_id(i)] + [level_last_id(i - 1)],\n      'weight_method': p.weight_method\n  })\n  node_ids[i].append(next(id_cnt))\n  node_ids[min_level].append(node_ids[min_level][-1])\n\n  for i in range(min_level + 1, max_level + 1, 1):\n    # bottom-up path 3.\n    p.nodes.append({\n        'feat_level': i,\n        'inputs_offsets': [\n            level_first_id(i),\n            level_last_id(i - 1) if i != min_level + 1 else level_first_id(i -\n                                                                           1)\n        ],\n        'weight_method': p.weight_method\n    })\n    node_ids[i].append(next(id_cnt))\n  node_ids[min_level].append(node_ids[min_level][-1])\n\n  for i in range(max_level - 1, min_level, -1):\n    # top-down path 4.\n    p.nodes.append({\n        'feat_level':\n            i,\n        'inputs_offsets': [node_ids[i][0]] + [node_ids[i][-1]] +\n                          [level_last_id(i + 1)],\n        'weight_method':\n            p.weight_method\n    })\n    node_ids[i].append(next(id_cnt))\n  i = min_level\n  p.nodes.append({\n      'feat_level': i,\n      'inputs_offsets': [node_ids[i][0]] + [level_last_id(i + 1)],\n      'weight_method': p.weight_method\n  })\n  node_ids[i].append(next(id_cnt))\n  node_ids[max_level].append(node_ids[max_level][-1])\n\n  for i in range(max_level, min_level - 1, -1):\n    # quad-add path.\n    p.nodes.append({\n        'feat_level': i,\n        'inputs_offsets': [node_ids[i][2], node_ids[i][4]],\n        'weight_method': p.quad_method\n    })\n    node_ids[i].append(next(id_cnt))\n\n  return p\n\n\ndef get_fpn_config(fpn_name, min_level, max_level, weight_method):\n  \"\"\"Get fpn related configuration.\"\"\"\n  if not fpn_name:\n    fpn_name = 'bifpn'\n  name_to_config = {\n      'bifpn': bifpn_config(min_level, max_level, weight_method),\n      'qufpn': qufpn_config(min_level, max_level, weight_method),\n      # legacy only: to be deprecated.\n      'bifpn_dyn': bifpn_config(min_level, max_level, weight_method),\n  }\n  return name_to_config[fpn_name]\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/keras/infer.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"A simple example on how to use keras model for inference.\"\"\"\nimport os\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\nimport numpy as np\nfrom PIL import Image\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import hparams_config\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import inference\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import efficientdet_keras\n\nflags.DEFINE_string('image_path', None, 'Location of test image.')\nflags.DEFINE_string('output_dir', None, 'Directory of annotated output images.')\nflags.DEFINE_string('model_dir', None, 'Location of the checkpoint to run.')\nflags.DEFINE_string('model_name', 'efficientdet-d0', 'Model name to use.')\nflags.DEFINE_string('hparams', '', 'Comma separated k=v pairs or a yaml file')\nflags.DEFINE_bool('debug', False, 'If true, run function in eager for debug.')\nflags.DEFINE_string('saved_model_dir', None, 'Saved model directory')\nFLAGS = flags.FLAGS\n\n\ndef main(_):\n\n  # pylint: disable=line-too-long\n  # Prepare images and checkpoints: please run these commands in shell.\n  # !mkdir tmp\n  # !wget https://user-images.githubusercontent.com/11736571/77320690-099af300-6d37-11ea-9d86-24f14dc2d540.png -O tmp/img.png\n  # !wget https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco/efficientdet-d0.tar.gz -O tmp/efficientdet-d0.tar.gz\n  # !tar zxf tmp/efficientdet-d0.tar.gz -C tmp\n  imgs = [np.array(Image.open(FLAGS.image_path))]\n  # Create model config.\n  config = hparams_config.get_efficientdet_config(FLAGS.model_name)\n  config.is_training_bn = False\n  config.image_size = '1920x1280'\n  config.nms_configs.score_thresh = 0.4\n  config.nms_configs.max_output_size = 100\n  config.override(FLAGS.hparams)\n\n  # Use 'mixed_float16' if running on GPUs.\n  policy = tf.keras.mixed_precision.Policy('float32')\n  tf.keras.mixed_precision.set_global_policy(policy)\n  tf.config.run_functions_eagerly(FLAGS.debug)\n\n  # Create and run the model.\n  model = efficientdet_keras.EfficientDetModel(config=config)\n  model.build((None, None, None, 3))\n  model.load_weights(tf.train.latest_checkpoint(FLAGS.model_dir))\n  model.summary()\n\n  class ExportModel(tf.Module):\n\n    def __init__(self, model):\n      super().__init__()\n      self.model = model\n\n    @tf.function\n    def f(self, imgs):\n      return self.model(imgs, training=False, post_mode='global')\n\n  imgs = tf.convert_to_tensor(imgs, dtype=tf.uint8)\n  export_model = ExportModel(model)\n  if FLAGS.saved_model_dir:\n    tf.saved_model.save(\n        export_model,\n        FLAGS.saved_model_dir,\n        signatures=export_model.f.get_concrete_function(\n            tf.TensorSpec(shape=(None, None, None, 3), dtype=tf.uint8)))\n    export_model = tf.saved_model.load(FLAGS.saved_model_dir)\n\n  boxes, scores, classes, valid_len = export_model.f(imgs)\n\n  # Visualize results.\n  for i, img in enumerate(imgs):\n    length = valid_len[i]\n    img = inference.visualize_image(\n        img,\n        boxes[i].numpy()[:length],\n        classes[i].numpy().astype(int)[:length],\n        scores[i].numpy()[:length],\n        label_map=config.label_map,\n        min_score_thresh=config.nms_configs.score_thresh,\n        max_boxes_to_draw=config.nms_configs.max_output_size)\n    output_image_path = os.path.join(FLAGS.output_dir, str(i) + '.jpg')\n    Image.fromarray(img).save(output_image_path)\n    print('writing annotated image to %s' % output_image_path)\n\n\nif __name__ == '__main__':\n  flags.mark_flag_as_required('image_path')\n  flags.mark_flag_as_required('output_dir')\n  flags.mark_flag_as_required('model_dir')\n  logging.set_verbosity(logging.ERROR)\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/keras/infer_lib.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\nr\"\"\"Inference related utilities.\"\"\"\nimport copy\nimport os\nimport time\nfrom typing import Text, Dict, Any, Optional\nfrom absl import logging\nimport numpy as np\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import dataloader\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import hparams_config\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import efficientdet_keras\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import label_util\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import util_keras\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.visualize import vis_utils\n\n\ndef visualize_image(image,\n                    boxes,\n                    classes,\n                    scores,\n                    label_map=None,\n                    min_score_thresh=0.01,\n                    max_boxes_to_draw=1000,\n                    line_thickness=2,\n                    **kwargs):\n  \"\"\"Visualizes a given image.\n\n  Args:\n    image: a image with shape [H, W, C].\n    boxes: a box prediction with shape [N, 4] ordered [ymin, xmin, ymax, xmax].\n    classes: a class prediction with shape [N].\n    scores: A list of float value with shape [N].\n    label_map: a dictionary from class id to name.\n    min_score_thresh: minimal score for showing. If claass probability is below\n      this threshold, then the object will not show up.\n    max_boxes_to_draw: maximum bounding box to draw.\n    line_thickness: how thick is the bounding box line.\n    **kwargs: extra parameters.\n\n  Returns:\n    output_image: an output image with annotated boxes and classes.\n  \"\"\"\n  label_map = label_util.get_label_map(label_map or 'coco')\n  category_index = {k: {'id': k, 'name': label_map[k]} for k in label_map}\n  img = np.array(image)\n  vis_utils.visualize_boxes_and_labels_on_image_array(\n      img,\n      boxes,\n      classes,\n      scores,\n      category_index,\n      min_score_thresh=min_score_thresh,\n      max_boxes_to_draw=max_boxes_to_draw,\n      line_thickness=line_thickness,\n      **kwargs)\n  return img\n\n\nclass ExportNetwork(tf.Module):\n\n  def __init__(self, model):\n    super().__init__()\n    self.model = model\n\n  @tf.function\n  def __call__(self, imgs):\n    return tf.nest.flatten(self.model(imgs, training=False))\n\n\nclass ExportModel(tf.Module):\n  \"\"\"Model to be exported as SavedModel/TFLite format.\"\"\"\n\n  def __init__(self, model, pre_mode='infer', post_mode='global'):\n    super().__init__()\n    self.model = model\n    self.pre_mode = pre_mode\n    self.post_mode = post_mode\n\n  @tf.function\n  def __call__(self, imgs):\n    return self.model(\n        imgs, training=False, pre_mode=self.pre_mode, post_mode=self.post_mode)\n\n\nclass ServingDriver:\n  \"\"\"A driver for serving single or batch images.\n\n  This driver supports serving with image files or arrays, with configurable\n  batch size.\n\n  Example 1. Serving streaming image contents:\n\n    driver = inference.ServingDriver(\n      'efficientdet-d0', '/tmp/efficientdet-d0', batch_size=1)\n    driver.build()\n    for m in image_iterator():\n      predictions = driver.serve_files([m])\n      boxes, scores, classes, _ = tf.nest.map_structure(np.array, predictions)\n      driver.visualize(m, boxes[0], scores[0], classes[0])\n      # m is the new image with annotated boxes.\n\n  Example 2. Serving batch image contents:\n\n    imgs = []\n    for f in ['/tmp/1.jpg', '/tmp/2.jpg']:\n      imgs.append(np.array(Image.open(f)))\n\n    driver = inference.ServingDriver(\n      'efficientdet-d0', '/tmp/efficientdet-d0', batch_size=len(imgs))\n    driver.build()\n    predictions = driver.serve(imgs)\n    boxes, scores, classes, _ = tf.nest.map_structure(np.array, predictions)\n    for i in range(len(imgs)):\n      driver.visualize(imgs[i], boxes[i], scores[i], classes[i])\n\n  Example 3: another way is to use SavedModel:\n\n    # step1: export a model.\n    driver = inference.ServingDriver('efficientdet-d0', '/tmp/efficientdet-d0')\n    driver.build()\n    driver.export('/tmp/saved_model_path')\n\n    # step2: Serve a model.\n    driver.load(self.saved_model_dir)\n    raw_images = []\n    for f in tf.io.gfile.glob('/tmp/images/*.jpg'):\n      raw_images.append(np.array(PIL.Image.open(f)))\n    detections = driver.serve(raw_images)\n    boxes, scores, classes, _ = tf.nest.map_structure(np.array, detections)\n    for i in range(len(imgs)):\n      driver.visualize(imgs[i], boxes[i], scores[i], classes[i])\n  \"\"\"\n\n  def __init__(self,\n               model_name: Text,\n               ckpt_path: Optional[Text] = None,\n               batch_size: int = 1,\n               only_network: bool = False,\n               model_params: Optional[Dict[Text, Any]] = None,\n               debug: bool = False):\n    \"\"\"Initialize the inference driver.\n\n    Args:\n      model_name: target model name, such as efficientdet-d0.\n      ckpt_path: checkpoint path, such as /tmp/efficientdet-d0/.\n      batch_size: batch size for inference.\n      only_network: only use the network without pre/post processing.\n      model_params: model parameters for overriding the config.\n      debug: bool, if true, run in debug mode.\n    \"\"\"\n    super().__init__()\n    self.model_name = model_name\n    self.ckpt_path = ckpt_path\n    self.batch_size = batch_size\n    self.only_network = only_network\n    self.debug = debug\n\n    self.params = hparams_config.get_detection_config(model_name).as_dict()\n\n    if model_params:\n      self.params.update(model_params)\n    self.params.update(dict(is_training_bn=False))\n    self.label_map = self.params.get('label_map', None)\n\n    self._model = None\n\n    mixed_precision = self.params.get('mixed_precision', None)\n    precision = utils.get_precision(\n        self.params.get('strategy', None), mixed_precision)\n    policy = tf.keras.mixed_precision.Policy(precision)\n    tf.keras.mixed_precision.set_global_policy(policy)\n\n  @property\n  def model(self):\n    if not self._model:\n      self.build()\n    return self._model\n\n  @model.setter\n  def model(self, model):\n    self._model = model\n\n  def build(self, params_override=None):\n    \"\"\"Build model and restore checkpoints.\"\"\"\n    params = copy.deepcopy(self.params)\n    if params_override:\n      params.update(params_override)\n    config = hparams_config.get_efficientdet_config(self.model_name)\n    config.override(params)\n    if self.only_network:\n      self.model = efficientdet_keras.EfficientDetNet(config=config)\n    else:\n      self.model = efficientdet_keras.EfficientDetModel(config=config)\n    image_size = utils.parse_image_size(params['image_size'])\n    self.model.build((self.batch_size, *image_size, 3))\n    util_keras.restore_ckpt(self.model, self.ckpt_path,\n                            self.params['moving_average_decay'],\n                            skip_mismatch=False)\n    if self.debug:\n      tf.config.run_functions_eagerly(self.debug)\n\n  def visualize(self, image, boxes, classes, scores, **kwargs):\n    \"\"\"Visualize prediction on image.\"\"\"\n    return visualize_image(image, boxes, classes.astype(int), scores,\n                           self.label_map, **kwargs)\n\n  def benchmark(self, image_arrays, bm_runs=10, trace_filename=None):\n    \"\"\"Benchmark inference latency/throughput.\n\n    Args:\n      image_arrays: a list of images in numpy array format.\n      bm_runs: Number of benchmark runs.\n      trace_filename: If None, specify the filename for saving trace.\n    \"\"\"\n    _, spec = self._get_model_and_spec()\n\n    @tf.function(input_signature=[spec])\n    def test_func(image_arrays):\n      return self.model(image_arrays)  # pylint: disable=not-callable\n\n    for _ in range(3):  # warmup 3 runs.\n      test_func(image_arrays)\n\n    start = time.perf_counter()\n    for _ in range(bm_runs):\n      test_func(image_arrays)\n    end = time.perf_counter()\n    inference_time = (end - start) / bm_runs\n\n    print('Per batch inference time: ', inference_time)\n    print('FPS: ', self.batch_size / inference_time)\n\n    if trace_filename:\n      options = tf.profiler.experimental.ProfilerOptions()\n      tf.profiler.experimental.start(trace_filename, options)\n      test_func(image_arrays)\n      tf.profiler.experimental.stop()\n\n  def serve(self, image_arrays):\n    \"\"\"Serve a list of image arrays.\n\n    Args:\n      image_arrays: A list of image content with each image has shape [height,\n        width, 3] and uint8 type.\n\n    Returns:\n      A list of detections.\n    \"\"\"\n    if isinstance(self.model, tf.lite.Interpreter):\n      input_details = self.model.get_input_details()\n      output_details = self.model.get_output_details()\n      self.model.set_tensor(input_details[0]['index'], np.array(image_arrays))\n      self.model.invoke()\n      return [self.model.get_tensor(x['index']) for x in output_details]\n    return self.model(image_arrays)  # pylint: disable=not-callable\n\n  def load(self, saved_model_dir_or_frozen_graph: Text):\n    \"\"\"Load the model using saved model or a frozen graph.\"\"\"\n    # Load saved model if it is a folder.\n    if tf.saved_model.contains_saved_model(saved_model_dir_or_frozen_graph):\n      self.model = tf.saved_model.load(saved_model_dir_or_frozen_graph)\n      return\n\n    if saved_model_dir_or_frozen_graph.endswith('.tflite'):\n      self.model = tf.lite.Interpreter(saved_model_dir_or_frozen_graph)\n      self.model.allocate_tensors()\n      return\n\n    # Load a frozen graph.\n    def wrap_frozen_graph(graph_def, inputs, outputs):\n      # https://www.tensorflow.org/guide/migrate\n      imports_graph_def_fn = lambda: tf.import_graph_def(graph_def, name='')\n      wrapped_import = tf.compat.v1.wrap_function(imports_graph_def_fn, [])\n      import_graph = wrapped_import.graph\n      return wrapped_import.prune(\n          tf.nest.map_structure(import_graph.as_graph_element, inputs),\n          tf.nest.map_structure(import_graph.as_graph_element, outputs))\n\n    graph_def = tf.Graph().as_graph_def()\n    with tf.io.gfile.GFile(saved_model_dir_or_frozen_graph, 'rb') as f:\n      graph_def.ParseFromString(f.read())\n\n    self.model = wrap_frozen_graph(\n        graph_def,\n        inputs='images:0',\n        outputs=['Identity:0', 'Identity_1:0', 'Identity_2:0', 'Identity_3:0'])\n\n  def freeze(self, func):\n    \"\"\"Freeze the graph.\"\"\"\n    # pylint: disable=g-import-not-at-top,disable=g-direct-tensorflow-import\n    from tensorflow.python.framework.convert_to_constants \\\n      import convert_variables_to_constants_v2_as_graph\n    _, graphdef = convert_variables_to_constants_v2_as_graph(func)\n    return graphdef\n\n  def _get_model_and_spec(self, tflite=None):\n    \"\"\"Get model instance and export spec.\"\"\"\n    if self.only_network or tflite:\n      image_size = utils.parse_image_size(self.params['image_size'])\n      spec = tf.TensorSpec(\n          shape=[self.batch_size, *image_size, 3],\n          dtype=tf.float32,\n          name='images')\n      if self.only_network:\n        export_model = ExportNetwork(self.model)\n      else:\n        # If export tflite, we should remove preprocessing since TFLite doesn't\n        # support dynamic shape.\n        logging.info('Export model without preprocessing.')\n        # This section is only used for TFLite, so we use the applicable\n        # pre_ & post_ modes.\n        export_model = ExportModel(\n            self.model, pre_mode=None, post_mode='tflite')\n      return export_model, spec\n    else:\n      spec = tf.TensorSpec(\n          shape=[self.batch_size, None, None, 3], dtype=tf.uint8, name='images')\n      export_model = ExportModel(self.model)\n      return export_model, spec\n\n  def export(self,\n             output_dir: Optional[Text] = None,\n             tensorrt: Optional[Text] = None,\n             tflite: Optional[Text] = None,\n             file_pattern: Optional[Text] = None,\n             num_calibration_steps: int = 2000):\n    \"\"\"Export a saved model, frozen graph, and potential tflite/tensorrt model.\n\n    Args:\n      output_dir: the output folder for saved model.\n      tensorrt: If not None, must be {'FP32', 'FP16', 'INT8'}.\n      tflite: Type for post-training quantization.\n      file_pattern: Glob for tfrecords, e.g. coco/val-*.tfrecord.\n      num_calibration_steps: Number of post-training quantization calibration\n        steps to run.\n    \"\"\"\n    export_model, input_spec = self._get_model_and_spec(tflite)\n    image_size = utils.parse_image_size(self.params['image_size'])\n    if output_dir:\n      tf.saved_model.save(\n          export_model,\n          output_dir,\n          signatures=export_model.__call__.get_concrete_function(input_spec))\n      logging.info('Model saved at %s', output_dir)\n\n      # also save freeze pb file.\n      graphdef = self.freeze(\n          export_model.__call__.get_concrete_function(input_spec))\n      proto_path = tf.io.write_graph(\n          graphdef, output_dir, self.model_name + '_frozen.pb', as_text=False)\n      logging.info('Frozen graph saved at %s', proto_path)\n\n    if tflite:\n      shape = (self.batch_size, *image_size, 3)\n      input_spec = tf.TensorSpec(\n          shape=shape, dtype=input_spec.dtype, name=input_spec.name)\n      # from_saved_model supports advanced converter features like op fusing.\n      converter = tf.lite.TFLiteConverter.from_saved_model(output_dir)\n      if tflite == 'FP32':\n        converter.optimizations = [tf.lite.Optimize.DEFAULT]\n        converter.target_spec.supported_types = [tf.float32]\n      elif tflite == 'FP16':\n        converter.optimizations = [tf.lite.Optimize.DEFAULT]\n        converter.target_spec.supported_types = [tf.float16]\n      elif tflite == 'INT8':\n        # Enables MLIR-based post-training quantization.\n        converter.experimental_new_quantizer = True\n        if file_pattern:\n          config = hparams_config.get_efficientdet_config(self.model_name)\n          config.override(self.params)\n          ds = dataloader.InputReader(\n              file_pattern,\n              is_training=False,\n              max_instances_per_image=config.max_instances_per_image)(\n                  config, batch_size=self.batch_size)\n\n          def representative_dataset_gen():\n            for image, _ in ds.take(num_calibration_steps):\n              yield [image]\n        else:  # Used for debugging, can remove later.\n          logging.warn('Use real representative dataset instead of fake ones.')\n          num_calibration_steps = 10\n          def representative_dataset_gen():  # rewrite this for real data.\n            for _ in range(num_calibration_steps):\n              yield [tf.ones(shape, dtype=input_spec.dtype)]\n\n        converter.representative_dataset = representative_dataset_gen\n        converter.optimizations = [tf.lite.Optimize.DEFAULT]\n        converter.inference_input_type = tf.uint8\n        # TFLite's custom NMS op isn't supported by post-training quant,\n        # so we add TFLITE_BUILTINS as well.\n        supported_ops = [\n            tf.lite.OpsSet.TFLITE_BUILTINS_INT8, tf.lite.OpsSet.TFLITE_BUILTINS\n        ]\n        converter.target_spec.supported_ops = supported_ops\n\n      else:\n        raise ValueError(f'Invalid tflite {tflite}: must be FP32, FP16, INT8.')\n\n      tflite_path = os.path.join(output_dir, tflite.lower() + '.tflite')\n      tflite_model = converter.convert()\n      tf.io.gfile.GFile(tflite_path, 'wb').write(tflite_model)\n      logging.info('TFLite is saved at %s', tflite_path)\n\n    if tensorrt:\n      trt_path = os.path.join(output_dir, 'tensorrt_' + tensorrt.lower())\n      conversion_params = tf.experimental.tensorrt.ConversionParams(\n          max_workspace_size_bytes=(2 << 20),\n          maximum_cached_engines=1,\n          precision_mode=tensorrt.upper())\n      converter = tf.experimental.tensorrt.Converter(\n          output_dir, conversion_params=conversion_params)\n      converter.convert()\n      converter.save(trt_path)\n      logging.info('TensorRT model is saved at %s', trt_path)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/keras/inspector.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\nr\"\"\"Tool to inspect a model.\"\"\"\nimport os\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\n\nimport numpy as np\nfrom PIL import Image\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import hparams_config\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import infer_lib\n\nflags.DEFINE_string('model_name', 'efficientdet-d0', 'Model.')\nflags.DEFINE_enum('mode', 'infer',\n                  ['infer', 'dry', 'export', 'benchmark', 'video'], 'Run mode.')\nflags.DEFINE_string('trace_filename', None, 'Trace file name.')\n\nflags.DEFINE_integer('bm_runs', 10, 'Number of benchmark runs.')\nflags.DEFINE_enum('tensorrt', '', ['', 'FP32', 'FP16', 'INT8'],\n                  'TensorRT mode.')\nflags.DEFINE_integer('batch_size', 1, 'Batch size for inference.')\nflags.DEFINE_integer('image_size', -1, 'Input image size for inference.')\n\nflags.DEFINE_string('model_dir', '_', 'checkpoint dir used for eval.')\nflags.DEFINE_string('export_ckpt', None, 'Output model ckpt path.')\n\nflags.DEFINE_string(\n    'hparams', '', 'Comma separated k=v pairs of hyperparameters or a module'\n    ' containing attributes to use as hyperparameters.')\n\nflags.DEFINE_string('input_image', None, 'Input image path for inference.')\nflags.DEFINE_string('output_image_dir', None, 'Output dir for inference.')\n\n# For video.\nflags.DEFINE_string('input_video', None, 'Input video path for inference.')\nflags.DEFINE_string('output_video', None,\n                    'Output video path. If None, play it online instead.')\n\n# For saved model.\nflags.DEFINE_string('saved_model_dir', None, 'Folder path for saved model.')\nflags.DEFINE_enum('tflite', '', ['', 'FP32', 'FP16', 'INT8'], 'tflite type.')\nflags.DEFINE_string('file_pattern', None,\n                    'Glob for tfrecords, e.g. coco/val-*.tfrecord.')\nflags.DEFINE_integer(\n    'num_calibration_steps', 2000,\n    'Number of post-training quantization calibration steps to run.')\nflags.DEFINE_bool('debug', False, 'Debug mode.')\nflags.DEFINE_bool('only_network', False, 'Model only contains network')\nFLAGS = flags.FLAGS\n\n\ndef main(_):\n  tf.config.run_functions_eagerly(FLAGS.debug)\n  devices = tf.config.list_physical_devices('GPU')\n  for device in devices:\n    tf.config.experimental.set_memory_growth(device, True)\n\n  model_config = hparams_config.get_detection_config(FLAGS.model_name)\n  model_config.override(FLAGS.hparams)  # Add custom overrides\n  model_config.is_training_bn = False\n  if FLAGS.image_size != -1:\n    model_config.image_size = FLAGS.image_size\n  model_config.image_size = utils.parse_image_size(model_config.image_size)\n\n  model_params = model_config.as_dict()\n  ckpt_path_or_file = FLAGS.model_dir\n  if tf.io.gfile.isdir(ckpt_path_or_file):\n    ckpt_path_or_file = tf.train.latest_checkpoint(ckpt_path_or_file)\n  driver = infer_lib.ServingDriver(FLAGS.model_name, ckpt_path_or_file,\n                                   FLAGS.batch_size or None,\n                                   FLAGS.only_network, model_params)\n  if FLAGS.mode == 'export':\n    if not FLAGS.saved_model_dir:\n      raise ValueError('Please specify --saved_model_dir=')\n    model_dir = FLAGS.saved_model_dir\n    if tf.io.gfile.exists(model_dir):\n      tf.io.gfile.rmtree(model_dir)\n    driver.export(model_dir, FLAGS.tensorrt, FLAGS.tflite, FLAGS.file_pattern,\n                  FLAGS.num_calibration_steps)\n    print('Model are exported to %s' % model_dir)\n  elif FLAGS.mode == 'infer':\n    image_file = tf.io.read_file(FLAGS.input_image)\n    image_arrays = tf.io.decode_image(image_file)\n    image_arrays.set_shape((None, None, 3))\n    image_arrays = tf.expand_dims(image_arrays, axis=0)\n    if FLAGS.saved_model_dir:\n      driver.load(FLAGS.saved_model_dir)\n      if FLAGS.saved_model_dir.endswith('.tflite'):\n        image_size = utils.parse_image_size(model_config.image_size)\n        image_arrays = tf.image.resize_with_pad(image_arrays, *image_size)\n        image_arrays = tf.cast(image_arrays, tf.uint8)\n    detections_bs = driver.serve(image_arrays)\n    boxes, scores, classes, _ = tf.nest.map_structure(np.array, detections_bs)\n    raw_image = Image.fromarray(np.array(image_arrays)[0])\n    img = driver.visualize(\n        raw_image,\n        boxes[0],\n        classes[0],\n        scores[0],\n        min_score_thresh=model_config.nms_configs.score_thresh or 0.4,\n        max_boxes_to_draw=model_config.nms_configs.max_output_size)\n    output_image_path = os.path.join(FLAGS.output_image_dir, '0.jpg')\n    Image.fromarray(img).save(output_image_path)\n    print('writing file to %s' % output_image_path)\n  elif FLAGS.mode == 'benchmark':\n    if FLAGS.saved_model_dir:\n      driver.load(FLAGS.saved_model_dir)\n\n    batch_size = FLAGS.batch_size or 1\n    if FLAGS.input_image:\n      image_file = tf.io.read_file(FLAGS.input_image)\n      image_arrays = tf.image.decode_image(image_file)\n      image_arrays.set_shape((None, None, 3))\n      image_arrays = tf.expand_dims(image_arrays, 0)\n      if batch_size > 1:\n        image_arrays = tf.tile(image_arrays, [batch_size, 1, 1, 1])\n    else:\n      # use synthetic data if no image is provided.\n      image_arrays = tf.ones((batch_size, *model_config.image_size, 3),\n                             dtype=tf.uint8)\n    if FLAGS.only_network:\n      image_arrays = tf.image.convert_image_dtype(image_arrays, tf.float32)\n      image_arrays = tf.image.resize(image_arrays, model_config.image_size)\n    driver.benchmark(image_arrays, FLAGS.bm_runs, FLAGS.trace_filename)\n  elif FLAGS.mode == 'dry':\n    # transfer to tf2 format ckpt\n    driver.build()\n    if FLAGS.export_ckpt:\n      driver.model.save_weights(FLAGS.export_ckpt)\n  elif FLAGS.mode == 'video':\n    import cv2  # pylint: disable=g-import-not-at-top\n    if FLAGS.saved_model_dir:\n      driver.load(FLAGS.saved_model_dir)\n    cap = cv2.VideoCapture(FLAGS.input_video)\n    if not cap.isOpened():\n      print('Error opening input video: {}'.format(FLAGS.input_video))\n\n    out_ptr = None\n    if FLAGS.output_video:\n      frame_width, frame_height = int(cap.get(3)), int(cap.get(4))\n      out_ptr = cv2.VideoWriter(FLAGS.output_video,\n                                cv2.VideoWriter_fourcc('m', 'p', '4', 'v'),\n                                cap.get(5), (frame_width, frame_height))\n\n    while cap.isOpened():\n      # Capture frame-by-frame\n      ret, frame = cap.read()\n      if not ret:\n        break\n\n      raw_frames = np.array([frame])\n      detections_bs = driver.serve(raw_frames)\n      boxes, scores, classes, _ = tf.nest.map_structure(np.array, detections_bs)\n      new_frame = driver.visualize(\n          raw_frames[0],\n          boxes[0],\n          classes[0],\n          scores[0],\n          min_score_thresh=model_config.nms_configs.score_thresh or 0.4,\n          max_boxes_to_draw=model_config.nms_configs.max_output_size)\n\n      if out_ptr:\n        # write frame into output file.\n        out_ptr.write(new_frame)\n      else:\n        # show the frame online, mainly used for real-time speed test.\n        cv2.imshow('Frame', new_frame)\n        # Press Q on keyboard to  exit\n        if cv2.waitKey(1) & 0xFF == ord('q'):\n          break\n\n\nif __name__ == '__main__':\n  logging.set_verbosity(logging.INFO)\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/keras/label_util.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"A few predefined label id mapping.\"\"\"\nimport tensorflow as tf\nimport yaml\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import hparams_config\n\ncoco = {\n    # 0: 'background',\n    1: 'person',\n    2: 'bicycle',\n    3: 'car',\n    4: 'motorcycle',\n    5: 'airplane',\n    6: 'bus',\n    7: 'train',\n    8: 'truck',\n    9: 'boat',\n    10: 'traffic light',\n    11: 'fire hydrant',\n    13: 'stop sign',\n    14: 'parking meter',\n    15: 'bench',\n    16: 'bird',\n    17: 'cat',\n    18: 'dog',\n    19: 'horse',\n    20: 'sheep',\n    21: 'cow',\n    22: 'elephant',\n    23: 'bear',\n    24: 'zebra',\n    25: 'giraffe',\n    27: 'backpack',\n    28: 'umbrella',\n    31: 'handbag',\n    32: 'tie',\n    33: 'suitcase',\n    34: 'frisbee',\n    35: 'skis',\n    36: 'snowboard',\n    37: 'sports ball',\n    38: 'kite',\n    39: 'baseball bat',\n    40: 'baseball glove',\n    41: 'skateboard',\n    42: 'surfboard',\n    43: 'tennis racket',\n    44: 'bottle',\n    46: 'wine glass',\n    47: 'cup',\n    48: 'fork',\n    49: 'knife',\n    50: 'spoon',\n    51: 'bowl',\n    52: 'banana',\n    53: 'apple',\n    54: 'sandwich',\n    55: 'orange',\n    56: 'broccoli',\n    57: 'carrot',\n    58: 'hot dog',\n    59: 'pizza',\n    60: 'donut',\n    61: 'cake',\n    62: 'chair',\n    63: 'couch',\n    64: 'potted plant',\n    65: 'bed',\n    67: 'dining table',\n    70: 'toilet',\n    72: 'tv',\n    73: 'laptop',\n    74: 'mouse',\n    75: 'remote',\n    76: 'keyboard',\n    77: 'cell phone',\n    78: 'microwave',\n    79: 'oven',\n    80: 'toaster',\n    81: 'sink',\n    82: 'refrigerator',\n    84: 'book',\n    85: 'clock',\n    86: 'vase',\n    87: 'scissors',\n    88: 'teddy bear',\n    89: 'hair drier',\n    90: 'toothbrush',\n}\n\nvoc = {\n    # 0: 'background',\n    1: 'aeroplane',\n    2: 'bicycle',\n    3: 'bird',\n    4: 'boat',\n    5: 'bottle',\n    6: 'bus',\n    7: 'car',\n    8: 'cat',\n    9: 'chair',\n    10: 'cow',\n    11: 'diningtable',\n    12: 'dog',\n    13: 'horse',\n    14: 'motorbike',\n    15: 'person',\n    16: 'pottedplant',\n    17: 'sheep',\n    18: 'sofa',\n    19: 'train',\n    20: 'tvmonitor',\n}\n\nwaymo = {\n    # 0: 'background',\n    1: 'vehicle',\n    2: 'pedestrian',\n    3: 'cyclist',\n}\n\n\ndef get_label_map(mapping):\n  \"\"\"Get label id map based on the name, filename, or dict.\"\"\"\n  # case 1: if it is None or dict, just return it.\n  if not mapping or isinstance(mapping, dict):\n    return mapping\n\n  if isinstance(mapping, hparams_config.Config):\n    return mapping.as_dict()\n\n  # case 2: if it is a yaml file, load it to a dict and return the dict.\n  assert isinstance(mapping, str), 'mapping must be dict or str.'\n  if mapping.endswith('.yaml'):\n    with tf.io.gfile.GFile(mapping) as f:\n      return yaml.load(f, Loader=yaml.FullLoader)\n\n  # case 3: it is a name of a predefined dataset.\n  return {'coco': coco, 'voc': voc, 'waymo': waymo}[mapping]\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/keras/postprocess.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# =============================================================================\n\"\"\"Postprocessing for anchor-based detection.\"\"\"\nimport functools\nfrom typing import List, Tuple\n\nfrom absl import logging\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import nms_np\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import anchors\nT = tf.Tensor  # a shortcut for typing check.\nCLASS_OFFSET = 1\n# TFLite-specific constants.\nTFLITE_MAX_CLASSES_PER_DETECTION = 1\nTFLITE_DETECTION_POSTPROCESS_FUNC = 'TFLite_Detection_PostProcess'\n# TFLite fast NMS == postprocess_global (less accurate)\n# TFLite regular NMS == postprocess_per_class\nTFLITE_USE_REGULAR_NMS = False\n\n\ndef to_list(inputs):\n  if isinstance(inputs, dict):\n    return [inputs[k] for k in sorted(inputs.keys())]\n  if isinstance(inputs, list):\n    return inputs\n\n\ndef batch_map_fn(map_fn, inputs, *args):\n  \"\"\"Apply map_fn at batch dimension.\"\"\"\n  if isinstance(inputs[0], (list, tuple)):\n    batch_size = len(inputs[0])\n  else:\n    batch_size = inputs[0].shape.as_list()[0]\n\n  if not batch_size:\n    # handle dynamic batch size: tf.vectorized_map is faster than tf.map_fn.\n    return tf.vectorized_map(map_fn, inputs, *args)\n\n  outputs = []\n  for i in range(batch_size):\n    outputs.append(map_fn([x[i] for x in inputs]))\n  return [tf.stack(y) for y in zip(*outputs)]\n\n\ndef clip_boxes(boxes: T, image_size: int) -> T:\n  \"\"\"Clip boxes to fit the image size.\"\"\"\n  image_size = utils.parse_image_size(image_size) * 2\n  return tf.clip_by_value(boxes, [0], image_size)\n\n\ndef merge_class_box_level_outputs(params, cls_outputs: List[T],\n                                  box_outputs: List[T]) -> Tuple[T, T]:\n  \"\"\"Concatenates class and box of all levels into one tensor.\"\"\"\n  cls_outputs_all, box_outputs_all = [], []\n  batch_size = tf.shape(cls_outputs[0])[0]\n  for level in range(0, params['max_level'] - params['min_level'] + 1):\n    if params['data_format'] == 'channels_first':\n      cls_outputs[level] = tf.transpose(cls_outputs[level], [0, 2, 3, 1])\n      box_outputs[level] = tf.transpose(box_outputs[level], [0, 2, 3, 1])\n    cls_outputs_all.append(\n        tf.reshape(cls_outputs[level], [batch_size, -1, params['num_classes']]))\n    box_outputs_all.append(tf.reshape(box_outputs[level], [batch_size, -1, 4]))\n  return tf.concat(cls_outputs_all, 1), tf.concat(box_outputs_all, 1)\n\n\ndef topk_class_boxes(params, cls_outputs: T,\n                     box_outputs: T) -> Tuple[T, T, T, T]:\n  \"\"\"Pick the topk class and box outputs.\"\"\"\n  batch_size = tf.shape(cls_outputs)[0]\n  num_classes = params['num_classes']\n\n  max_nms_inputs = params['nms_configs'].get('max_nms_inputs', 0)\n  if max_nms_inputs > 0:\n    # Prune anchors and detections to only keep max_nms_inputs.\n    # Due to some issues, top_k is currently slow in graph model.\n    logging.info('use max_nms_inputs for pre-nms topk.')\n    cls_outputs_reshape = tf.reshape(cls_outputs, [batch_size, -1])\n    _, cls_topk_indices = tf.math.top_k(\n        cls_outputs_reshape, k=max_nms_inputs, sorted=False)\n    indices = cls_topk_indices // num_classes\n    classes = cls_topk_indices % num_classes\n    cls_indices = tf.stack([indices, classes], axis=2)\n\n    cls_outputs_topk = tf.gather_nd(cls_outputs, cls_indices, batch_dims=1)\n    box_outputs_topk = tf.gather_nd(\n        box_outputs, tf.expand_dims(indices, 2), batch_dims=1)\n  else:\n    logging.info('use max_reduce for pre-nms topk.')\n    # Keep all anchors, but for each anchor, just keep the max probablity for\n    # each class.\n    cls_outputs_idx = tf.math.argmax(cls_outputs, axis=-1, output_type=tf.int32)\n    num_anchors = tf.shape(cls_outputs)[1]\n\n    classes = cls_outputs_idx\n    indices = tf.tile(\n        tf.expand_dims(tf.range(num_anchors), axis=0), [batch_size, 1])\n    cls_outputs_topk = tf.reduce_max(cls_outputs, -1)\n    box_outputs_topk = box_outputs\n\n  return cls_outputs_topk, box_outputs_topk, classes, indices\n\n\ndef pre_nms(params, cls_outputs, box_outputs, topk=True):\n  \"\"\"Detection post processing before nms.\n\n  It takes the multi-level class and box predictions from network, merge them\n  into unified tensors, and compute boxes, scores, and classes.\n\n  Args:\n    params: a dict of parameters.\n    cls_outputs: a list of tensors for classes, each tensor denotes a level of\n      logits with shape [N, H, W, num_class * num_anchors].\n    box_outputs: a list of tensors for boxes, each tensor ddenotes a level of\n      boxes with shape [N, H, W, 4 * num_anchors].\n    topk: if True, select topk before nms (mainly to speed up nms).\n\n  Returns:\n    A tuple of (boxes, scores, classes).\n  \"\"\"\n  # get boxes by apply bounding box regression to anchors.\n  eval_anchors = anchors.Anchors(params['min_level'], params['max_level'],\n                                 params['num_scales'], params['aspect_ratios'],\n                                 params['anchor_scale'], params['image_size'])\n\n  cls_outputs, box_outputs = merge_class_box_level_outputs(\n      params, cls_outputs, box_outputs)\n\n  if topk:\n    # select topK purely based on scores before NMS, in order to speed up nms.\n    cls_outputs, box_outputs, classes, indices = topk_class_boxes(\n        params, cls_outputs, box_outputs)\n    anchor_boxes = tf.gather(eval_anchors.boxes, indices)\n  else:\n    anchor_boxes = eval_anchors.boxes\n    classes = None\n\n  boxes = anchors.decode_box_outputs(box_outputs, anchor_boxes)\n  # convert logits to scores.\n  scores = tf.math.sigmoid(cls_outputs)\n  return boxes, scores, classes\n\n\ndef nms(params, boxes: T, scores: T, classes: T,\n        padded: bool) -> Tuple[T, T, T, T]:\n  \"\"\"Non-maximum suppression.\n\n  Args:\n    params: a dict of parameters.\n    boxes: a tensor with shape [N, 4], where N is the number of boxes. Box\n      format is [y_min, x_min, y_max, x_max].\n    scores: a tensor with shape [N].\n    classes: a tensor with shape [N].\n    padded: a bool vallue indicating whether the results are padded.\n\n  Returns:\n    A tuple (boxes, scores, classes, valid_lens), where valid_lens is a scalar\n    denoting the valid length of boxes/scores/classes outputs.\n  \"\"\"\n  nms_configs = params['nms_configs']\n  method = nms_configs['method']\n  max_output_size = nms_configs['max_output_size']\n\n  if method == 'hard' or not method:\n    # hard nms.\n    sigma = 0.0\n    iou_thresh = nms_configs['iou_thresh'] or 0.5\n    score_thresh = nms_configs['score_thresh'] or float('-inf')\n  elif method == 'gaussian':\n    sigma = nms_configs['sigma'] or 0.5\n    iou_thresh = 1.0\n    score_thresh = nms_configs['score_thresh'] or 0.001\n  else:\n    raise ValueError('Inference has invalid nms method {}'.format(method))\n\n  # TF API's sigma is twice as the paper's value, so here we divide it by 2:\n  # https://github.com/tensorflow/tensorflow/issues/40253.\n  nms_top_idx, nms_scores, nms_valid_lens = tf.raw_ops.NonMaxSuppressionV5(\n      boxes=boxes,\n      scores=scores,\n      max_output_size=max_output_size,\n      iou_threshold=iou_thresh,\n      score_threshold=score_thresh,\n      soft_nms_sigma=(sigma / 2),\n      pad_to_max_output_size=padded)\n\n  nms_boxes = tf.gather(boxes, nms_top_idx)\n  nms_classes = tf.cast(\n      tf.gather(classes, nms_top_idx) + CLASS_OFFSET, boxes.dtype)\n  return nms_boxes, nms_scores, nms_classes, nms_valid_lens\n\n\ndef postprocess_combined(params, cls_outputs, box_outputs, image_scales=None):\n  \"\"\"Post processing with combined NMS.\n\n  Leverage the tf combined NMS. It is fast on TensorRT, but slow on CPU/GPU.\n\n  Args:\n    params: a dict of parameters.\n    cls_outputs: a list of tensors for classes, each tensor denotes a level of\n      logits with shape [N, H, W, num_class * num_anchors].\n    box_outputs: a list of tensors for boxes, each tensor ddenotes a level of\n      boxes with shape [N, H, W, 4 * num_anchors]. Each box format is [y_min,\n      x_min, y_max, x_man].\n    image_scales: scaling factor or the final image and bounding boxes.\n\n  Returns:\n    A tuple of batch level (boxes, scores, classess, valid_len) after nms.\n  \"\"\"\n  cls_outputs = to_list(cls_outputs)\n  box_outputs = to_list(box_outputs)\n  # Don't filter any outputs because combine_nms need the raw information.\n  boxes, scores, _ = pre_nms(params, cls_outputs, box_outputs, topk=False)\n\n  max_output_size = params['nms_configs']['max_output_size']\n  score_thresh = params['nms_configs']['score_thresh'] or float('-inf')\n  nms_boxes, nms_scores, nms_classes, nms_valid_len = (\n      tf.image.combined_non_max_suppression(\n          tf.expand_dims(boxes, axis=2),\n          scores,\n          max_output_size,\n          max_output_size,\n          score_threshold=score_thresh,\n          clip_boxes=False))\n  nms_classes += CLASS_OFFSET\n  nms_boxes = clip_boxes(nms_boxes, params['image_size'])\n  if image_scales is not None:\n    scales = tf.expand_dims(tf.expand_dims(image_scales, -1), -1)\n    nms_boxes = nms_boxes * tf.cast(scales, nms_boxes.dtype)\n  return nms_boxes, nms_scores, nms_classes, nms_valid_len\n\n\ndef tflite_nms_implements_signature(params):\n  \"\"\"`experimental_implements` signature for TFLite's custom NMS op.\n\n  This signature encodes the arguments to correctly initialize TFLite's custom\n  post-processing op in the MLIR converter.\n  For details on `experimental_implements` see here:\n  https://www.tensorflow.org/api_docs/python/tf/function\n\n  Args:\n    params: a dict of parameters.\n\n  Returns:\n    String encoding of a map from attribute keys to values.\n  \"\"\"\n  scale_value = 1.0\n  nms_configs = params['nms_configs']\n  iou_thresh = nms_configs['iou_thresh'] or 0.5\n  score_thresh = nms_configs['score_thresh'] or float('-inf')\n  max_detections = params['tflite_max_detections']\n\n  implements_signature = [\n      'name: \"%s\"' % TFLITE_DETECTION_POSTPROCESS_FUNC,\n      'attr { key: \"max_detections\" value { i: %d } }' % max_detections,\n      'attr { key: \"max_classes_per_detection\" value { i: %d } }' %\n      TFLITE_MAX_CLASSES_PER_DETECTION,\n      'attr { key: \"use_regular_nms\" value { b: %s } }' %\n      str(TFLITE_USE_REGULAR_NMS).lower(),\n      'attr { key: \"nms_score_threshold\" value { f: %f } }' % score_thresh,\n      'attr { key: \"nms_iou_threshold\" value { f: %f } }' % iou_thresh,\n      'attr { key: \"y_scale\" value { f: %f } }' % scale_value,\n      'attr { key: \"x_scale\" value { f: %f } }' % scale_value,\n      'attr { key: \"h_scale\" value { f: %f } }' % scale_value,\n      'attr { key: \"w_scale\" value { f: %f } }' % scale_value,\n      'attr { key: \"num_classes\" value { i: %d } }' % params['num_classes']\n  ]\n  implements_signature = ' '.join(implements_signature)\n\n  return implements_signature\n\n\ndef tflite_pre_nms(params, cls_outputs, box_outputs):\n  \"\"\"Pre-NMS that is compatible with TFLite's custom NMS op.\n\n  For details, see tensorflow/lite/kernels/detection_postprocess.cc\n\n  Args:\n    params: a dict of parameters.\n    cls_outputs: a list of tensors for classes, each tensor denotes a level of\n      logits with shape [1, H, W, num_class * num_anchors].\n    box_outputs: a list of tensors for boxes, each tensor ddenotes a level of\n      boxes with shape [1, H, W, 4 * num_anchors]. Each box format is [y_min,\n      x_min, y_max, x_man].\n\n  Returns:\n    boxes: boxes encoded as {y_center, x_center, height, width}\n    scores: scores converted from `cls_outputs` logits using sigmoid\n    anchors: normalized anchors encoded as {y_center, x_center, height, width}\n  \"\"\"\n  cls_outputs = to_list(cls_outputs)\n  box_outputs = to_list(box_outputs)\n  cls_outputs, box_outputs = merge_class_box_level_outputs(\n      params, cls_outputs, box_outputs)\n  eval_anchors = anchors.Anchors(params['min_level'], params['max_level'],\n                                 params['num_scales'], params['aspect_ratios'],\n                                 params['anchor_scale'], params['image_size'])\n\n  # TODO(b/175166514): Consider computing Top-K boxes & anchors here. We don't\n  # do this currently since the resultant graph does not support TFLite\n  # delegates well. `topk_class_boxes` won't work as-is, since the outputs\n  # will need to be modified appropriately for TFLite op's consumption.\n\n  # TFLite's object detection APIs require normalized anchors.\n  height, width = utils.parse_image_size(params['image_size'])\n  normalize_factor = tf.constant([height, width, height, width],\n                                 dtype=tf.float32)\n  normalized_anchors = eval_anchors.boxes / normalize_factor\n  decoded_anchors = anchors.decode_anchors_to_centersize(\n      box_outputs, normalized_anchors)\n\n  # convert logits to scores.\n  scores = tf.math.sigmoid(cls_outputs)\n\n  return box_outputs, scores, decoded_anchors\n\n\ndef postprocess_tflite(params, cls_outputs, box_outputs):\n  \"\"\"Post processing for conversion to TFLite.\n\n  Mathematically same as postprocess_global, except that the last portion of the\n  TF graph constitutes a dummy `tf.function` that contains an annotation for\n  conversion to TFLite's custom NMS op. Using this custom op allows features\n  like post-training quantization & accelerator support.\n  NOTE: This function does NOT return a valid output, and is only meant to\n  generate a SavedModel for TFLite conversion via MLIR.\n  For TFLite op details, see tensorflow/lite/kernels/detection_postprocess.cc\n\n  Args:\n    params: a dict of parameters.\n    cls_outputs: a list of tensors for classes, each tensor denotes a level of\n      logits with shape [1, H, W, num_class * num_anchors].\n    box_outputs: a list of tensors for boxes, each tensor ddenotes a level of\n      boxes with shape [1, H, W, 4 * num_anchors]. Each box format is [y_min,\n      x_min, y_max, x_man].\n\n  Returns:\n    A (dummy) tuple of (boxes, scores, classess, valid_len).\n  \"\"\"\n  box_outputs, scores, decoded_anchors = tflite_pre_nms(params, cls_outputs,\n                                                        box_outputs)\n\n  # There is no TF equivalent for TFLite's custom post-processing op.\n  # So we add an 'empty' composite function here, that is legalized to the\n  # custom op with MLIR.\n  # For details, see:\n  # tensorflow/compiler/mlir/lite/utils/nms_utils.cc\n  @tf.function(experimental_implements=tflite_nms_implements_signature(params))\n  # pylint: disable=g-unused-argument,unused-argument\n  def dummy_post_processing(box_encodings, class_predictions, anchor_boxes):\n    boxes = tf.constant(0.0, dtype=tf.float32, name='boxes')\n    scores = tf.constant(0.0, dtype=tf.float32, name='scores')\n    classes = tf.constant(0.0, dtype=tf.float32, name='classes')\n    num_detections = tf.constant(0.0, dtype=tf.float32, name='num_detections')\n    return boxes, classes, scores, num_detections\n\n  return dummy_post_processing(box_outputs, scores, decoded_anchors)[::-1]\n\n\ndef postprocess_global(params, cls_outputs, box_outputs, image_scales=None):\n  \"\"\"Post processing with global NMS.\n\n  A fast but less accurate version of NMS. The idea is to treat the scores for\n  different classes in a unified way, and perform NMS globally for all classes.\n\n  Args:\n    params: a dict of parameters.\n    cls_outputs: a list of tensors for classes, each tensor denotes a level of\n      logits with shape [N, H, W, num_class * num_anchors].\n    box_outputs: a list of tensors for boxes, each tensor ddenotes a level of\n      boxes with shape [N, H, W, 4 * num_anchors]. Each box format is [y_min,\n      x_min, y_max, x_man].\n    image_scales: scaling factor or the final image and bounding boxes.\n\n  Returns:\n    A tuple of batch level (boxes, scores, classess, valid_len) after nms.\n  \"\"\"\n  cls_outputs = to_list(cls_outputs)\n  box_outputs = to_list(box_outputs)\n  boxes, scores, classes = pre_nms(params, cls_outputs, box_outputs)\n\n  def single_batch_fn(element):\n    return nms(params, element[0], element[1], element[2], True)\n\n  nms_boxes, nms_scores, nms_classes, nms_valid_len = batch_map_fn(\n      single_batch_fn, [boxes, scores, classes])\n  nms_boxes = clip_boxes(nms_boxes, params['image_size'])\n  if image_scales is not None:\n    scales = tf.expand_dims(tf.expand_dims(image_scales, -1), -1)\n    nms_boxes = nms_boxes * tf.cast(scales, nms_boxes.dtype)\n  return nms_boxes, nms_scores, nms_classes, nms_valid_len\n\n\ndef per_class_nms(params, boxes, scores, classes, image_scales=None):\n  \"\"\"Per-class nms, a utility for postprocess_per_class.\n\n  Args:\n    params: a dict of parameters.\n    boxes: A tensor with shape [N, K, 4], where N is batch_size, K is num_boxes.\n      Box format is [y_min, x_min, y_max, x_max].\n    scores: A tensor with shape [N, K].\n    classes: A tensor with shape [N, K].\n    image_scales: scaling factor or the final image and bounding boxes.\n\n  Returns:\n    A tuple of batch level (boxes, scores, classess, valid_len) after nms.\n  \"\"\"\n  def single_batch_fn(element):\n    \"\"\"A mapping function for a single batch.\"\"\"\n    boxes_i, scores_i, classes_i = element[0], element[1], element[2]\n    nms_boxes_cls, nms_scores_cls, nms_classes_cls = [], [], []\n    nms_valid_len_cls = []\n    for cid in range(params['num_classes']):\n      indices = tf.where(tf.equal(classes_i, cid))\n      if indices.shape[0] == 0:\n        continue\n      classes_cls = tf.gather_nd(classes_i, indices)\n      boxes_cls = tf.gather_nd(boxes_i, indices)\n      scores_cls = tf.gather_nd(scores_i, indices)\n\n      nms_boxes, nms_scores, nms_classes, nms_valid_len = nms(\n          params, boxes_cls, scores_cls, classes_cls, False)\n      nms_boxes_cls.append(nms_boxes)\n      nms_scores_cls.append(nms_scores)\n      nms_classes_cls.append(nms_classes)\n      nms_valid_len_cls.append(nms_valid_len)\n\n    # Pad zeros and select topk.\n    max_output_size = params['nms_configs'].get('max_output_size', 100)\n    nms_boxes_cls = tf.pad(\n        tf.concat(nms_boxes_cls, 0), [[0, max_output_size], [0, 0]])\n    nms_scores_cls = tf.pad(\n        tf.concat(nms_scores_cls, 0), [[0, max_output_size]])\n    nms_classes_cls = tf.pad(\n        tf.concat(nms_classes_cls, 0), [[0, max_output_size]])\n    nms_valid_len_cls = tf.stack(nms_valid_len_cls)\n\n    _, indices = tf.math.top_k(nms_scores_cls, k=max_output_size, sorted=True)\n\n    return tuple((\n        tf.gather(nms_boxes_cls, indices),\n        tf.gather(nms_scores_cls, indices),\n        tf.gather(nms_classes_cls, indices),\n        tf.minimum(max_output_size, tf.reduce_sum(nms_valid_len_cls))))\n    # end of single_batch_fn\n\n  nms_boxes, nms_scores, nms_classes, nms_valid_len = batch_map_fn(\n      single_batch_fn, [boxes, scores, classes])\n  if image_scales is not None:\n    scales = tf.expand_dims(tf.expand_dims(image_scales, -1), -1)\n    nms_boxes = nms_boxes * tf.cast(scales, nms_boxes.dtype)\n  return nms_boxes, nms_scores, nms_classes, nms_valid_len\n\n\ndef postprocess_per_class(params, cls_outputs, box_outputs, image_scales=None):\n  \"\"\"Post processing with per class NMS.\n\n  An accurate but relatively slow version of NMS. The idea is to perform NMS for\n  each class, and then combine them.\n\n  Args:\n    params: a dict of parameters.\n    cls_outputs: a list of tensors for classes, each tensor denotes a level of\n      logits with shape [N, H, W, num_class * num_anchors].\n    box_outputs: a list of tensors for boxes, each tensor ddenotes a level of\n      boxes with shape [N, H, W, 4 * num_anchors]. Each box format is [y_min,\n      x_min, y_max, x_man].\n    image_scales: scaling factor or the final image and bounding boxes.\n\n  Returns:\n    A tuple of batch level (boxes, scores, classess, valid_len) after nms.\n  \"\"\"\n  cls_outputs = to_list(cls_outputs)\n  box_outputs = to_list(box_outputs)\n  boxes, scores, classes = pre_nms(params, cls_outputs, box_outputs)\n  return per_class_nms(params, boxes, scores, classes, image_scales)\n\n\ndef generate_detections_from_nms_output(nms_boxes_bs,\n                                        nms_classes_bs,\n                                        nms_scores_bs,\n                                        image_ids,\n                                        original_image_widths=None,\n                                        flip=False):\n  \"\"\"Generating [id, x, y, w, h, score, class] from NMS outputs.\"\"\"\n\n  image_ids_bs = tf.cast(tf.expand_dims(image_ids, -1), nms_scores_bs.dtype)\n  if flip:\n    detections_bs = [\n        image_ids_bs * tf.ones_like(nms_scores_bs),\n        # the mirrored location of the left edge is the image width\n        # minus the position of the right edge\n        original_image_widths - nms_boxes_bs[:, :, 3],\n        nms_boxes_bs[:, :, 0],\n        # the mirrored location of the right edge is the image width\n        # minus the position of the left edge\n        original_image_widths - nms_boxes_bs[:, :, 1],\n        nms_boxes_bs[:, :, 2],\n        nms_scores_bs,\n        nms_classes_bs,\n    ]\n  else:\n    detections_bs = [\n        image_ids_bs * tf.ones_like(nms_scores_bs),\n        nms_boxes_bs[:, :, 1],\n        nms_boxes_bs[:, :, 0],\n        nms_boxes_bs[:, :, 3],\n        nms_boxes_bs[:, :, 2],\n        nms_scores_bs,\n        nms_classes_bs,\n    ]\n  return tf.stack(detections_bs, axis=-1, name='detections')\n\n\ndef generate_detections(params,\n                        cls_outputs,\n                        box_outputs,\n                        image_scales,\n                        image_ids,\n                        flip=False,\n                        pre_class_nms=True):\n  \"\"\"A legacy interface for generating [id, x, y, w, h, score, class].\"\"\"\n  _, width = utils.parse_image_size(params['image_size'])\n\n  original_image_widths = tf.expand_dims(image_scales, -1) * width\n\n  if params['nms_configs'].get('pyfunc', True):\n    # numpy based soft-nms gives better accuracy than the tensorflow builtin\n    # the reason why is unknown\n    detections_bs = []\n    boxes, scores, classes = pre_nms(params, cls_outputs, box_outputs)\n    for index in range(boxes.shape[0]):\n      nms_configs = params['nms_configs']\n      detections = tf.numpy_function(\n          functools.partial(nms_np.per_class_nms, nms_configs=nms_configs), [\n              boxes[index],\n              scores[index],\n              classes[index],\n              tf.slice(image_ids, [index], [1]),\n              tf.slice(image_scales, [index], [1]),\n              params['num_classes'],\n              nms_configs['max_output_size'],\n          ], tf.float32)\n\n      if flip:\n        detections = tf.stack([\n            detections[:, 0],\n            # the mirrored location of the left edge is the image width\n            # minus the position of the right edge\n            original_image_widths[index] - detections[:, 3],\n            detections[:, 2],\n            # the mirrored location of the right edge is the image width\n            # minus the position of the left edge\n            original_image_widths[index] - detections[:, 1],\n            detections[:, 4],\n            detections[:, 5],\n            detections[:, 6],\n        ], axis=-1)\n      detections_bs.append(detections)\n    return tf.stack(detections_bs, axis=0, name='detections')\n\n  if pre_class_nms:\n    postprocess = postprocess_per_class\n  else:\n    postprocess = postprocess_global\n  nms_boxes_bs, nms_scores_bs, nms_classes_bs, _ = postprocess(\n      params, cls_outputs, box_outputs, image_scales)\n\n  return generate_detections_from_nms_output(nms_boxes_bs, nms_classes_bs,\n                                             nms_scores_bs, image_ids,\n                                             original_image_widths, flip)\n\n\ndef transform_detections(detections):\n  \"\"\"A transforms detections in [id, x1, y1, x2, y2, score, class] form to [id, x, y, w, h, score, class].\"\"\"\n  return tf.stack(  #\n      [\n          detections[:, :, 0],\n          detections[:, :, 1],\n          detections[:, :, 2],\n          detections[:, :, 3] - detections[:, :, 1],\n          detections[:, :, 4] - detections[:, :, 2],\n          detections[:, :, 5],\n          detections[:, :, 6],\n      ],\n      axis=-1)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/keras/segmentation.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"A demo script to show to train a segmentation model.\"\"\"\nfrom absl import app\nfrom absl import logging\nimport tensorflow as tf\nimport tensorflow_datasets as tfds\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import hparams_config\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import efficientdet_keras\n\n\ndef create_mask(pred_mask):\n  pred_mask = tf.argmax(pred_mask, axis=-1)\n  pred_mask = pred_mask[..., tf.newaxis]\n  return pred_mask[0]\n\n\ndef normalize(input_image, input_mask):\n  input_image = tf.cast(input_image, tf.float32) / 255.0\n  input_mask -= 1\n  return input_image, input_mask\n\n\ndef load_image_train(datapoint):\n  \"\"\"Load images for training.\"\"\"\n  input_image = tf.image.resize(datapoint['image'], (512, 512))\n  input_mask = tf.image.resize(datapoint['segmentation_mask'], (128, 128))\n\n  if tf.random.uniform(()) > 0.5:\n    input_image = tf.image.flip_left_right(input_image)\n    input_mask = tf.image.flip_left_right(input_mask)\n\n  input_image, input_mask = normalize(input_image, input_mask)\n\n  return input_image, input_mask\n\n\ndef load_image_test(datapoint):\n  input_image = tf.image.resize(datapoint['image'], (512, 512))\n  input_mask = tf.image.resize(datapoint['segmentation_mask'], (128, 128))\n\n  input_image, input_mask = normalize(input_image, input_mask)\n\n  return input_image, input_mask\n\n\ndef main(_):\n  dataset, info = tfds.load('oxford_iiit_pet:3.*.*', with_info=True)\n  train_examples = info.splits['train'].num_examples\n  batch_size = 8\n  steps_per_epoch = train_examples // batch_size\n\n  train = dataset['train'].map(\n      load_image_train, num_parallel_calls=tf.data.experimental.AUTOTUNE)\n  test = dataset['test'].map(load_image_test)\n\n  train_dataset = train.cache().shuffle(1000).batch(batch_size).repeat()\n  train_dataset = train_dataset.prefetch(\n      buffer_size=tf.data.experimental.AUTOTUNE)\n  test_dataset = test.batch(batch_size)\n  config = hparams_config.get_efficientdet_config('efficientdet-d0')\n  config.heads = ['segmentation']\n  model = efficientdet_keras.EfficientDetNet(config=config)\n  model.build((1, 512, 512, 3))\n  model.compile(\n      optimizer='adam',\n      loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n      metrics=['accuracy'])\n\n  val_subsplits = 5\n  val_steps = info.splits['test'].num_examples // batch_size // val_subsplits\n  model.fit(\n      train_dataset,\n      epochs=20,\n      steps_per_epoch=steps_per_epoch,\n      validation_steps=val_steps,\n      validation_data=test_dataset,\n      callbacks=[])\n\n  model.save_weights(\n      './third_party/brain_automl/efficientdet/testdata/segmentation')\n\n  print(create_mask(model(tf.ones((1, 512, 512, 3)), False)))\n\n\nif __name__ == '__main__':\n  logging.set_verbosity(logging.WARNING)\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/keras/tfmot.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"A tool for model optimization.\"\"\"\nimport functools\n\nimport tensorflow_model_optimization as tfmot\nfrom tensorflow_model_optimization.python.core.quantization.keras import quantize_wrapper\nfrom tensorflow_model_optimization.python.core.quantization.keras.default_8bit import default_8bit_quantize_configs\n\n\ndef quantize(layer, quantize_config=None):\n  if quantize_config is None:\n    quantize_config = default_8bit_quantize_configs.Default8BitOutputQuantizeConfig(\n    )\n  return quantize_wrapper.QuantizeWrapper(\n      layer, quantize_config=quantize_config)\n\n\noptimzation_methods = {\n    'prune': tfmot.sparsity.keras.prune_low_magnitude,\n    'quantize': quantize\n}\n\n\ndef set_config(configs):\n  for key in configs:\n    if key == 'prune':\n      optimzation_methods[key] = functools.partial(\n          tfmot.sparsity.keras.prune_low_magnitude, **configs[key])\n    if key == 'quantize':\n      optimzation_methods[key] = functools.partial(quantize, **configs[key])\n\n\ndef get_method(method):\n  if method not in optimzation_methods:\n    raise KeyError(f'only support {optimzation_methods.keys()}')\n  return optimzation_methods[method]\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/keras/train.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"The main training script.\"\"\"\nimport os\nimport platform\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import dataloader\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import hparams_config\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import tfmot\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import train_lib\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import util_keras\n\nFLAGS = flags.FLAGS\n\n\ndef define_flags():\n  \"\"\"Define the flags.\"\"\"\n  # Cloud TPU Cluster Resolvers\n  flags.DEFINE_string(\n      'tpu',\n      default=None,\n      help='The Cloud TPU to use for training. This should be either the name '\n      'used when creating the Cloud TPU, or a grpc://ip.address.of.tpu:8470 '\n      'url.')\n  flags.DEFINE_string(\n      'gcp_project',\n      default=None,\n      help='Project name for the Cloud TPU-enabled project. If not specified, '\n      'we will attempt to automatically detect the GCE project from metadata.')\n  flags.DEFINE_string(\n      'tpu_zone',\n      default=None,\n      help='GCE zone where the Cloud TPU is located in. If not specified, we '\n      'will attempt to automatically detect the GCE project from metadata.')\n\n  # Model specific paramenters\n  flags.DEFINE_string(\n      'eval_master',\n      default='',\n      help='GRPC URL of the eval master. Set to an appropriate value when '\n      'running on CPU/GPU')\n  flags.DEFINE_string('eval_name', default=None, help='Eval job name')\n  flags.DEFINE_enum('strategy', '', ['tpu', 'gpus', ''],\n                    'Training: gpus for multi-gpu, if None, use TF default.')\n\n  flags.DEFINE_integer(\n      'num_cores', default=8, help='Number of TPU cores for training')\n\n  flags.DEFINE_bool('use_fake_data', False, 'Use fake input.')\n  flags.DEFINE_bool(\n      'use_xla', False,\n      'Use XLA even if strategy is not tpu. If strategy is tpu, always use XLA,'\n      ' and this flag has no effect.')\n  flags.DEFINE_string('model_dir', None, 'Location of model_dir')\n\n  flags.DEFINE_string('pretrained_ckpt', None,\n                      'Start training from this EfficientDet checkpoint.')\n\n  flags.DEFINE_string(\n      'hparams', '', 'Comma separated k=v pairs of hyperparameters or a module'\n      ' containing attributes to use as hyperparameters.')\n  flags.DEFINE_integer('batch_size', 64, 'training batch size')\n  flags.DEFINE_integer('eval_samples', 5000, 'The number of samples for '\n                       'evaluation.')\n  flags.DEFINE_integer('steps_per_execution', 1,\n                       'Number of steps per training execution.')\n  flags.DEFINE_string(\n      'train_file_pattern', None,\n      'Glob for train data files (e.g., COCO train - minival set)')\n  flags.DEFINE_string('val_file_pattern', None,\n                      'Glob for evaluation tfrecords (e.g., COCO val2017 set)')\n  flags.DEFINE_string(\n      'val_json_file', None,\n      'COCO validation JSON containing golden bounding boxes. If None, use the '\n      'ground truth from the dataloader. Ignored if testdev_dir is not None.')\n\n  flags.DEFINE_enum('mode', 'traineval', ['train', 'traineval'],\n                    'job mode: train, traineval.')\n  flags.DEFINE_string(\n      'hub_module_url', None, 'TF-Hub path/url to EfficientDet module.'\n      'If specified, pretrained_ckpt flag should not be used.')\n  flags.DEFINE_integer('num_examples_per_epoch', 120000,\n                       'Number of examples in one epoch')\n  flags.DEFINE_integer('num_epochs', None, 'Number of epochs for training')\n  flags.DEFINE_string('model_name', 'efficientdet-d0', 'Model name.')\n  flags.DEFINE_bool('debug', False, 'Enable debug mode')\n  flags.DEFINE_integer(\n      'tf_random_seed', 111111,\n      'Fixed random seed for deterministic execution across runs for debugging.'\n  )\n  flags.DEFINE_bool('profile', False, 'Enable profile mode')\n\n\ndef setup_model(model, config):\n  \"\"\"Build and compile model.\"\"\"\n  model.build((None, *config.image_size, 3))\n  model.compile(\n      steps_per_execution=config.steps_per_execution,\n      optimizer=train_lib.get_optimizer(config.as_dict()),\n      loss={\n          train_lib.BoxLoss.__name__:\n              train_lib.BoxLoss(\n                  config.delta, reduction=tf.keras.losses.Reduction.NONE),\n          train_lib.BoxIouLoss.__name__:\n              train_lib.BoxIouLoss(\n                  config.iou_loss_type,\n                  config.min_level,\n                  config.max_level,\n                  config.num_scales,\n                  config.aspect_ratios,\n                  config.anchor_scale,\n                  config.image_size,\n                  reduction=tf.keras.losses.Reduction.NONE),\n          train_lib.FocalLoss.__name__:\n              train_lib.FocalLoss(\n                  config.alpha,\n                  config.gamma,\n                  label_smoothing=config.label_smoothing,\n                  reduction=tf.keras.losses.Reduction.NONE),\n          tf.keras.losses.SparseCategoricalCrossentropy.__name__:\n              tf.keras.losses.SparseCategoricalCrossentropy(\n                  from_logits=True, reduction=tf.keras.losses.Reduction.NONE)\n      })\n  return model\n\n\ndef init_experimental(config):\n  \"\"\"Serialize train config to model directory.\"\"\"\n  tf.io.gfile.makedirs(config.model_dir)\n  config_file = os.path.join(config.model_dir, 'config.yaml')\n  if not tf.io.gfile.exists(config_file):\n    tf.io.gfile.GFile(config_file, 'w').write(str(config))\n\n\ndef main(_):\n  # Parse and override hparams\n  config = hparams_config.get_detection_config(FLAGS.model_name)\n  config.override(FLAGS.hparams)\n  if FLAGS.num_epochs:  # NOTE: remove this flag after updating all docs.\n    config.num_epochs = FLAGS.num_epochs\n\n  # Parse image size in case it is in string format.\n  config.image_size = utils.parse_image_size(config.image_size)\n\n  if FLAGS.use_xla and FLAGS.strategy != 'tpu':\n    tf.config.optimizer.set_jit(True)\n    for gpu in tf.config.list_physical_devices('GPU'):\n      tf.config.experimental.set_memory_growth(gpu, True)\n\n  if FLAGS.debug:\n    tf.debugging.set_log_device_placement(True)\n    os.environ['TF_DETERMINISTIC_OPS'] = '1'\n    tf.random.set_seed(FLAGS.tf_random_seed)\n    logging.set_verbosity(logging.DEBUG)\n\n  if FLAGS.strategy == 'tpu':\n    tpu_cluster_resolver = tf.distribute.cluster_resolver.TPUClusterResolver(\n        FLAGS.tpu, zone=FLAGS.tpu_zone, project=FLAGS.gcp_project)\n    tf.config.experimental_connect_to_cluster(tpu_cluster_resolver)\n    tf.tpu.experimental.initialize_tpu_system(tpu_cluster_resolver)\n    ds_strategy = tf.distribute.TPUStrategy(tpu_cluster_resolver)\n    logging.info('All devices: %s', tf.config.list_logical_devices('TPU'))\n  elif FLAGS.strategy == 'gpus':\n    gpus = tf.config.list_physical_devices('GPU')\n    if FLAGS.batch_size % len(gpus):\n      raise ValueError(\n          'Batch size divide gpus number must be interger, but got %f' %\n          (FLAGS.batch_size / len(gpus)))\n    if platform.system() == 'Windows':\n      # Windows doesn't support nccl use HierarchicalCopyAllReduce instead\n      # TODO(fsx950223): investigate HierarchicalCopyAllReduce performance issue\n      cross_device_ops = tf.distribute.HierarchicalCopyAllReduce()\n    else:\n      cross_device_ops = None\n    ds_strategy = tf.distribute.MirroredStrategy(\n        cross_device_ops=cross_device_ops)\n    logging.info('All devices: %s', gpus)\n  else:\n    if tf.config.list_physical_devices('GPU'):\n      ds_strategy = tf.distribute.OneDeviceStrategy('device:GPU:0')\n    else:\n      ds_strategy = tf.distribute.OneDeviceStrategy('device:CPU:0')\n\n  steps_per_epoch = FLAGS.num_examples_per_epoch // FLAGS.batch_size\n  params = dict(\n      profile=FLAGS.profile,\n      model_name=FLAGS.model_name,\n      steps_per_execution=FLAGS.steps_per_execution,\n      model_dir=FLAGS.model_dir,\n      steps_per_epoch=steps_per_epoch,\n      strategy=FLAGS.strategy,\n      batch_size=FLAGS.batch_size,\n      tf_random_seed=FLAGS.tf_random_seed,\n      debug=FLAGS.debug,\n      val_json_file=FLAGS.val_json_file,\n      eval_samples=FLAGS.eval_samples,\n      num_shards=ds_strategy.num_replicas_in_sync)\n  config.override(params, True)\n  # set mixed precision policy by keras api.\n  precision = utils.get_precision(config.strategy, config.mixed_precision)\n  policy = tf.keras.mixed_precision.Policy(precision)\n  tf.keras.mixed_precision.set_global_policy(policy)\n\n  def get_dataset(is_training, config):\n    file_pattern = (\n        FLAGS.train_file_pattern\n        if is_training else FLAGS.val_file_pattern)\n    if not file_pattern:\n      raise ValueError('No matching files.')\n\n    return dataloader.InputReader(\n        file_pattern,\n        is_training=is_training,\n        use_fake_data=FLAGS.use_fake_data,\n        max_instances_per_image=config.max_instances_per_image,\n        debug=FLAGS.debug)(\n            config.as_dict())\n\n  with ds_strategy.scope():\n    if config.model_optimizations:\n      tfmot.set_config(config.model_optimizations.as_dict())\n    if FLAGS.hub_module_url:\n      model = train_lib.EfficientDetNetTrainHub(\n          config=config, hub_module_url=FLAGS.hub_module_url)\n    else:\n      model = train_lib.EfficientDetNetTrain(config=config)\n    model = setup_model(model, config)\n    if FLAGS.debug:\n      tf.config.run_functions_eagerly(True)\n    if FLAGS.pretrained_ckpt and not FLAGS.hub_module_url:\n      ckpt_path = tf.train.latest_checkpoint(FLAGS.pretrained_ckpt)\n      util_keras.restore_ckpt(\n          model,\n          ckpt_path,\n          config.moving_average_decay,\n          exclude_layers=['class_net'])\n    init_experimental(config)\n    if 'train' in FLAGS.mode:\n      val_dataset = get_dataset(False, config) if 'eval' in FLAGS.mode else None\n      model.fit(\n          get_dataset(True, config),\n          epochs=config.num_epochs,\n          steps_per_epoch=steps_per_epoch,\n          callbacks=train_lib.get_callbacks(config.as_dict(), val_dataset),\n          validation_data=val_dataset,\n          validation_steps=(FLAGS.eval_samples // FLAGS.batch_size))\n    else:\n      # Continuous eval.\n      for ckpt in tf.train.checkpoints_iterator(\n          FLAGS.model_dir, min_interval_secs=180):\n        logging.info('Starting to evaluate.')\n        # Terminate eval job when final checkpoint is reached.\n        try:\n          current_epoch = int(os.path.basename(ckpt).split('-')[1])\n        except IndexError:\n          current_epoch = 0\n\n        val_dataset = get_dataset(False, config)\n        logging.info('start loading model.')\n        model.load_weights(tf.train.latest_checkpoint(FLAGS.model_dir))\n        logging.info('finish loading model.')\n        coco_eval = train_lib.COCOCallback(val_dataset, 1)\n        coco_eval.set_model(model)\n        eval_results = coco_eval.on_epoch_end(current_epoch)\n        logging.info('eval results for %s: %s', ckpt, eval_results)\n\n        try:\n          utils.archive_ckpt(eval_results, eval_results['AP'], ckpt)\n        except tf.errors.NotFoundError:\n          # Checkpoint might be not already deleted by the time eval finished.\n          logging.info('Checkpoint %s no longer exists, skipping.', ckpt)\n\n        if current_epoch >= config.num_epochs or not current_epoch:\n          logging.info('Eval epoch %d / %d', current_epoch, config.num_epochs)\n          break\n\n\nif __name__ == '__main__':\n  define_flags()\n  logging.set_verbosity(logging.INFO)\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/keras/train_lib.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Training related libraries.\"\"\"\nimport math\nimport os\nimport re\nfrom absl import logging\nimport neural_structured_learning as nsl\nimport numpy as np\n\nimport tensorflow as tf\nfrom tensorflow_addons.callbacks import AverageModelCheckpoint\nimport tensorflow_hub as hub\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import coco_metric\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import inference\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import iou_utils\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import anchors\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import efficientdet_keras\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import label_util\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import postprocess\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.keras import util_keras\nfrom tensorflow_model_optimization.python.core.sparsity.keras import pruning_wrapper\n\n\ndef _collect_prunable_layers(model):\n  \"\"\"Recursively collect the prunable layers in the model.\"\"\"\n  prunable_layers = []\n  for layer in model._flatten_layers(recursive=False, include_self=False):  # pylint: disable=protected-access\n    # A keras model may have other models as layers.\n    if isinstance(layer, pruning_wrapper.PruneLowMagnitude):\n      prunable_layers.append(layer)\n    elif isinstance(layer, (tf.keras.Model, tf.keras.layers.Layer)):\n      prunable_layers += _collect_prunable_layers(layer)\n\n  return prunable_layers\n\n\nclass UpdatePruningStep(tf.keras.callbacks.Callback):\n  \"\"\"Keras callback which updates pruning wrappers with the optimizer step.\n\n  This callback must be used when training a model which needs to be pruned. Not\n  doing so will throw an error.\n  Example:\n  ```python\n  model.fit(x, y,\n      callbacks=[UpdatePruningStep()])\n  ```\n  \"\"\"\n\n  def __init__(self):\n    super(UpdatePruningStep, self).__init__()\n    self.prunable_layers = []\n\n  def on_train_begin(self, logs=None):\n    # Collect all the prunable layers in the model.\n    self.prunable_layers = _collect_prunable_layers(self.model)\n    self.step = tf.keras.backend.get_value(self.model.optimizer.iterations)\n\n  def on_train_batch_begin(self, batch, logs=None):\n    tuples = []\n\n    for layer in self.prunable_layers:\n      if layer.built:\n        tuples.append((layer.pruning_step, self.step))\n\n    tf.keras.backend.batch_set_value(tuples)\n    self.step = self.step + 1\n\n  def on_epoch_end(self, batch, logs=None):\n    # At the end of every epoch, remask the weights. This ensures that when\n    # the model is saved after completion, the weights represent mask*weights.\n    weight_mask_ops = []\n\n    for layer in self.prunable_layers:\n      if layer.built and isinstance(layer, pruning_wrapper.PruneLowMagnitude):\n        if tf.executing_eagerly():\n          layer.pruning_obj.weight_mask_op()\n        else:\n          weight_mask_ops.append(layer.pruning_obj.weight_mask_op())\n\n    tf.keras.backend.batch_get_value(weight_mask_ops)\n\n\nclass PruningSummaries(tf.keras.callbacks.TensorBoard):\n  \"\"\"A Keras callback for adding pruning summaries to tensorboard.\n\n  Logs the sparsity(%) and threshold at a given iteration step.\n  \"\"\"\n\n  def __init__(self, log_dir, update_freq='epoch', **kwargs):\n    if not isinstance(log_dir, str) or not log_dir:\n      raise ValueError(\n          '`log_dir` must be a non-empty string. You passed `log_dir`='\n          '{input}.'.format(input=log_dir))\n\n    super().__init__(log_dir=log_dir, update_freq=update_freq, **kwargs)\n\n    log_dir = self.log_dir + '/metrics'\n    self._file_writer = tf.summary.create_file_writer(log_dir)\n\n  def _log_pruning_metrics(self, logs, step):\n    with self._file_writer.as_default():\n      for name, value in logs.items():\n        tf.summary.scalar(name, value, step=step)\n\n      self._file_writer.flush()\n\n  def on_epoch_begin(self, epoch, logs=None):\n    if logs is not None:\n      super().on_epoch_begin(epoch, logs)\n\n    pruning_logs = {}\n    params = []\n    prunable_layers = _collect_prunable_layers(self.model)\n    for layer in prunable_layers:\n      for _, mask, threshold in layer.pruning_vars:\n        params.append(mask)\n        params.append(threshold)\n\n    params.append(self.model.optimizer.iterations)\n\n    values = tf.keras.backend.batch_get_value(params)\n    iteration = values[-1]\n    del values[-1]\n    del params[-1]\n\n    param_value_pairs = list(zip(params, values))\n\n    for mask, mask_value in param_value_pairs[::2]:\n      pruning_logs.update({mask.name + '/sparsity': 1 - np.mean(mask_value)})\n\n    for threshold, threshold_value in param_value_pairs[1::2]:\n      pruning_logs.update({threshold.name + '/threshold': threshold_value})\n\n    self._log_pruning_metrics(pruning_logs, iteration)\n\n\ndef update_learning_rate_schedule_parameters(params):\n  \"\"\"Updates params that are related to the learning rate schedule.\"\"\"\n  batch_size = params['batch_size']\n  # Learning rate is proportional to the batch size\n  params['adjusted_learning_rate'] = (params['learning_rate'] * batch_size / 64)\n  steps_per_epoch = params['steps_per_epoch']\n  params['lr_warmup_step'] = int(params['lr_warmup_epoch'] * steps_per_epoch)\n  params['first_lr_drop_step'] = int(params['first_lr_drop_epoch'] *\n                                     steps_per_epoch)\n  params['second_lr_drop_step'] = int(params['second_lr_drop_epoch'] *\n                                      steps_per_epoch)\n  params['total_steps'] = int(params['num_epochs'] * steps_per_epoch)\n\n\nclass StepwiseLrSchedule(tf.optimizers.schedules.LearningRateSchedule):\n  \"\"\"Stepwise learning rate schedule.\"\"\"\n\n  def __init__(self, adjusted_lr: float, lr_warmup_init: float,\n               lr_warmup_step: int, first_lr_drop_step: int,\n               second_lr_drop_step: int):\n    \"\"\"Build a StepwiseLrSchedule.\n\n    Args:\n      adjusted_lr: `float`, The initial learning rate.\n      lr_warmup_init: `float`, The warm up learning rate.\n      lr_warmup_step: `int`, The warm up step.\n      first_lr_drop_step: `int`, First lr decay step.\n      second_lr_drop_step: `int`, Second lr decay step.\n    \"\"\"\n    super().__init__()\n    logging.info('LR schedule method: stepwise')\n    self.adjusted_lr = adjusted_lr\n    self.lr_warmup_init = lr_warmup_init\n    self.lr_warmup_step = lr_warmup_step\n    self.first_lr_drop_step = first_lr_drop_step\n    self.second_lr_drop_step = second_lr_drop_step\n\n  def __call__(self, step):\n    linear_warmup = (\n        self.lr_warmup_init +\n        (tf.cast(step, dtype=tf.float32) / self.lr_warmup_step *\n         (self.adjusted_lr - self.lr_warmup_init)))\n    learning_rate = tf.where(step < self.lr_warmup_step, linear_warmup,\n                             self.adjusted_lr)\n    lr_schedule = [[1.0, self.lr_warmup_step], [0.1, self.first_lr_drop_step],\n                   [0.01, self.second_lr_drop_step]]\n    for mult, start_global_step in lr_schedule:\n      learning_rate = tf.where(step < start_global_step, learning_rate,\n                               self.adjusted_lr * mult)\n    return learning_rate\n\n\nclass CosineLrSchedule(tf.optimizers.schedules.LearningRateSchedule):\n  \"\"\"Cosine learning rate schedule.\"\"\"\n\n  def __init__(self, adjusted_lr: float, lr_warmup_init: float,\n               lr_warmup_step: int, total_steps: int):\n    \"\"\"Build a CosineLrSchedule.\n\n    Args:\n      adjusted_lr: `float`, The initial learning rate.\n      lr_warmup_init: `float`, The warm up learning rate.\n      lr_warmup_step: `int`, The warm up step.\n      total_steps: `int`, Total train steps.\n    \"\"\"\n    super().__init__()\n    logging.info('LR schedule method: cosine')\n    self.adjusted_lr = adjusted_lr\n    self.lr_warmup_init = lr_warmup_init\n    self.lr_warmup_step = lr_warmup_step\n    self.decay_steps = tf.cast(total_steps - lr_warmup_step, tf.float32)\n\n  def __call__(self, step):\n    linear_warmup = (\n        self.lr_warmup_init +\n        (tf.cast(step, dtype=tf.float32) / self.lr_warmup_step *\n         (self.adjusted_lr - self.lr_warmup_init)))\n    cosine_lr = 0.5 * self.adjusted_lr * (\n        1 + tf.cos(math.pi * tf.cast(step, tf.float32) / self.decay_steps))\n    return tf.where(step < self.lr_warmup_step, linear_warmup, cosine_lr)\n\n\nclass PolynomialLrSchedule(tf.optimizers.schedules.LearningRateSchedule):\n  \"\"\"Polynomial learning rate schedule.\"\"\"\n\n  def __init__(self, adjusted_lr: float, lr_warmup_init: float,\n               lr_warmup_step: int, power: float, total_steps: int):\n    \"\"\"Build a PolynomialLrSchedule.\n\n    Args:\n      adjusted_lr: `float`, The initial learning rate.\n      lr_warmup_init: `float`, The warm up learning rate.\n      lr_warmup_step: `int`, The warm up step.\n      power: `float`, power.\n      total_steps: `int`, Total train steps.\n    \"\"\"\n    super().__init__()\n    logging.info('LR schedule method: polynomial')\n    self.adjusted_lr = adjusted_lr\n    self.lr_warmup_init = lr_warmup_init\n    self.lr_warmup_step = lr_warmup_step\n    self.power = power\n    self.total_steps = total_steps\n\n  def __call__(self, step):\n    linear_warmup = (\n        self.lr_warmup_init +\n        (tf.cast(step, dtype=tf.float32) / self.lr_warmup_step *\n         (self.adjusted_lr - self.lr_warmup_init)))\n    polynomial_lr = self.adjusted_lr * tf.pow(\n        1 - (tf.cast(step, dtype=tf.float32) / self.total_steps), self.power)\n    return tf.where(step < self.lr_warmup_step, linear_warmup, polynomial_lr)\n\n\ndef learning_rate_schedule(params):\n  \"\"\"Learning rate schedule based on global step.\"\"\"\n  update_learning_rate_schedule_parameters(params)\n  lr_decay_method = params['lr_decay_method']\n  if lr_decay_method == 'stepwise':\n    return StepwiseLrSchedule(params['adjusted_learning_rate'],\n                              params['lr_warmup_init'],\n                              params['lr_warmup_step'],\n                              params['first_lr_drop_step'],\n                              params['second_lr_drop_step'])\n\n  if lr_decay_method == 'cosine':\n    return CosineLrSchedule(params['adjusted_learning_rate'],\n                            params['lr_warmup_init'], params['lr_warmup_step'],\n                            params['total_steps'])\n\n  if lr_decay_method == 'polynomial':\n    return PolynomialLrSchedule(params['adjusted_learning_rate'],\n                                params['lr_warmup_init'],\n                                params['lr_warmup_step'],\n                                params['poly_lr_power'], params['total_steps'])\n\n  raise ValueError('unknown lr_decay_method: {}'.format(lr_decay_method))\n\n\ndef get_optimizer(params):\n  \"\"\"Get optimizer.\"\"\"\n  learning_rate = learning_rate_schedule(params)\n  momentum = params['momentum']\n  if params['optimizer'].lower() == 'sgd':\n    logging.info('Use SGD optimizer')\n    optimizer = tf.keras.optimizers.SGD(learning_rate, momentum=momentum)\n  elif params['optimizer'].lower() == 'adam':\n    logging.info('Use Adam optimizer')\n    optimizer = tf.keras.optimizers.Adam(learning_rate, beta_1=momentum)\n  else:\n    raise ValueError('optimizers should be adam or sgd')\n\n  moving_average_decay = params['moving_average_decay']\n  if moving_average_decay:\n    # TODO(tanmingxing): potentially add dynamic_decay for new tfa release.\n    from tensorflow_addons import optimizers as tfa_optimizers  # pylint: disable=g-import-not-at-top\n    optimizer = tfa_optimizers.MovingAverage(\n        optimizer, average_decay=moving_average_decay, dynamic_decay=True)\n  precision = utils.get_precision(params['strategy'], params['mixed_precision'])\n  if precision == 'mixed_float16' and params['loss_scale']:\n    optimizer = tf.keras.mixed_precision.LossScaleOptimizer(\n        optimizer,\n        initial_scale=params['loss_scale'])\n  return optimizer\n\n\nclass COCOCallback(tf.keras.callbacks.Callback):\n  \"\"\"A utility for COCO eval callback.\"\"\"\n\n  def __init__(self, test_dataset, update_freq=None):\n    super().__init__()\n    self.test_dataset = test_dataset\n    self.update_freq = update_freq\n\n  def set_model(self, model: tf.keras.Model):\n    self.model = model\n    config = model.config\n    self.config = config\n    label_map = label_util.get_label_map(config.label_map)\n    log_dir = os.path.join(config.model_dir, 'coco')\n    self.file_writer = tf.summary.create_file_writer(log_dir)\n    self.evaluator = coco_metric.EvaluationMetric(\n        filename=config.val_json_file, label_map=label_map)\n\n  @tf.function\n  def _get_detections(self, images, labels):\n    cls_outputs, box_outputs = self.model(images, training=False)\n    detections = postprocess.generate_detections(self.config,\n                                                 cls_outputs,\n                                                 box_outputs,\n                                                 labels['image_scales'],\n                                                 labels['source_ids'])\n    tf.numpy_function(self.evaluator.update_state,\n                      [labels['groundtruth_data'],\n                       postprocess.transform_detections(detections)], [])\n\n  def on_epoch_end(self, epoch, logs=None):\n    epoch += 1\n    if self.update_freq and epoch % self.update_freq == 0:\n      self.evaluator.reset_states()\n      strategy = tf.distribute.get_strategy()\n      count = self.config.eval_samples // self.config.batch_size\n      dataset = self.test_dataset.take(count)\n      dataset = strategy.experimental_distribute_dataset(dataset)\n      for (images, labels) in dataset:\n        strategy.run(self._get_detections, (images, labels))\n      metrics = self.evaluator.result()\n      eval_results = {}\n      with self.file_writer.as_default(), tf.summary.record_if(True):\n        for i, name in enumerate(self.evaluator.metric_names):\n          tf.summary.scalar(name, metrics[i], step=epoch)\n          eval_results[name] = metrics[i]\n      return eval_results\n\n\nclass DisplayCallback(tf.keras.callbacks.Callback):\n  \"\"\"Display inference result callback.\"\"\"\n\n  def __init__(self, sample_image, output_dir, update_freq=None):\n    super().__init__()\n    image_file = tf.io.read_file(sample_image)\n    self.sample_image = tf.expand_dims(\n        tf.image.decode_jpeg(image_file, channels=3), axis=0)\n    self.update_freq = update_freq\n    self.output_dir = output_dir\n\n  def set_model(self, model: tf.keras.Model):\n    self.model = model\n    config = model.config\n    log_dir = os.path.join(config.model_dir, 'test_images')\n    self.file_writer = tf.summary.create_file_writer(log_dir)\n    self.min_score_thresh = config.nms_configs['score_thresh'] or 0.4\n    self.max_boxes_to_draw = config.nms_configs['max_output_size'] or 100\n\n  def on_train_batch_end(self, batch, logs=None):\n    if self.update_freq and batch % self.update_freq == 0:\n      self._draw_inference(batch)\n\n  def _draw_inference(self, step):\n    self.model.__class__ = efficientdet_keras.EfficientDetModel\n    results = self.model(self.sample_image, training=False)\n    boxes, scores, classes, valid_len = tf.nest.map_structure(np.array, results)\n    length = valid_len[0]\n    image = inference.visualize_image(\n        self.sample_image[0],\n        boxes[0][:length],\n        classes[0].astype(int)[:length],\n        scores[0][:length],\n        label_map=self.model.config.label_map,\n        min_score_thresh=self.min_score_thresh,\n        max_boxes_to_draw=self.max_boxes_to_draw)\n\n    with self.file_writer.as_default():\n      tf.summary.image('Test image', tf.expand_dims(image, axis=0), step=step)\n    self.model.__class__ = EfficientDetNetTrain\n\n\ndef get_callbacks(params, val_dataset=None):\n  \"\"\"Get callbacks for given params.\"\"\"\n  if params['moving_average_decay']:\n    avg_callback = AverageModelCheckpoint(\n        filepath=os.path.join(params['model_dir'], 'emackpt-{epoch:d}'),\n        verbose=params['verbose'],\n        save_freq=params['save_freq'],\n        save_weights_only=True,\n        update_weights=False)\n    callbacks = [avg_callback]\n  else:\n    ckpt_callback = tf.keras.callbacks.ModelCheckpoint(\n        os.path.join(params['model_dir'], 'ckpt-{epoch:d}'),\n        verbose=params['verbose'],\n        save_freq=params['save_freq'],\n        save_weights_only=True)\n    callbacks = [ckpt_callback]\n  if params['model_optimizations'] and 'prune' in params['model_optimizations']:\n    prune_callback = UpdatePruningStep()\n    prune_summaries = PruningSummaries(\n        log_dir=params['model_dir'],\n        update_freq=params['steps_per_execution'],\n        profile_batch=2 if params['profile'] else 0)\n    callbacks += [prune_callback, prune_summaries]\n  else:\n    tb_callback = tf.keras.callbacks.TensorBoard(\n        log_dir=params['model_dir'],\n        update_freq=params['steps_per_execution'],\n        profile_batch=2 if params['profile'] else 0)\n    callbacks.append(tb_callback)\n  if params.get('sample_image', None):\n    display_callback = DisplayCallback(\n        params.get('sample_image', None), params['model_dir'],\n        params['img_summary_steps'])\n    callbacks.append(display_callback)\n  if (params.get('map_freq', None) and val_dataset and\n      params['strategy'] != 'tpu'):\n    coco_callback = COCOCallback(val_dataset, params['map_freq'])\n    callbacks.append(coco_callback)\n  return callbacks\n\n\nclass AdversarialLoss(tf.keras.losses.Loss):\n  \"\"\"Adversarial keras loss wrapper.\"\"\"\n\n  # TODO(fsx950223): WIP\n  def __init__(self, adv_config, *args, **kwargs):\n    super().__init__(*args, **kwargs)\n    self.adv_config = adv_config\n    self.model = None\n    self.loss_fn = None\n    self.tape = None\n    self.built = False\n\n  def build(self, model, loss_fn, tape):\n    self.model = model\n    self.loss_fn = loss_fn\n    self.tape = tape\n    self.built = True\n\n  def call(self, features, y, y_pred, labeled_loss):\n    return self.adv_config.multiplier * nsl.keras.adversarial_loss(\n        features,\n        y,\n        self.model,\n        self.loss_fn,\n        predictions=y_pred,\n        labeled_loss=self.labeled_loss,\n        gradient_tape=self.tape)\n\n\nclass FocalLoss(tf.keras.losses.Loss):\n  \"\"\"Compute the focal loss between `logits` and the golden `target` values.\n\n  Focal loss = -(1-pt)^gamma * log(pt)\n  where pt is the probability of being classified to the true class.\n  \"\"\"\n\n  def __init__(self, alpha, gamma, label_smoothing=0.0, **kwargs):\n    \"\"\"Initialize focal loss.\n\n    Args:\n      alpha: A float32 scalar multiplying alpha to the loss from positive\n        examples and (1-alpha) to the loss from negative examples.\n      gamma: A float32 scalar modulating loss from hard and easy examples.\n      label_smoothing: Float in [0, 1]. If > `0` then smooth the labels.\n      **kwargs: other params.\n    \"\"\"\n    super().__init__(**kwargs)\n    self.alpha = alpha\n    self.gamma = gamma\n    self.label_smoothing = label_smoothing\n\n  @tf.autograph.experimental.do_not_convert\n  def call(self, y, y_pred):\n    \"\"\"Compute focal loss for y and y_pred.\n\n    Args:\n      y: A tuple of (normalizer, y_true), where y_true is the target class.\n      y_pred: A float32 tensor [batch, height_in, width_in, num_predictions].\n\n    Returns:\n      the focal loss.\n    \"\"\"\n    normalizer, y_true = y\n    alpha = tf.convert_to_tensor(self.alpha, dtype=y_pred.dtype)\n    gamma = tf.convert_to_tensor(self.gamma, dtype=y_pred.dtype)\n\n    # compute focal loss multipliers before label smoothing, such that it will\n    # not blow up the loss.\n    pred_prob = tf.sigmoid(y_pred)\n    p_t = (y_true * pred_prob) + ((1 - y_true) * (1 - pred_prob))\n    alpha_factor = y_true * alpha + (1 - y_true) * (1 - alpha)\n    modulating_factor = (1.0 - p_t)**gamma\n\n    # apply label smoothing for cross_entropy for each entry.\n    y_true = y_true * (1.0 - self.label_smoothing) + 0.5 * self.label_smoothing\n    ce = tf.nn.sigmoid_cross_entropy_with_logits(labels=y_true, logits=y_pred)\n\n    # compute the final loss and return\n    return alpha_factor * modulating_factor * ce / normalizer\n\n\nclass BoxLoss(tf.keras.losses.Loss):\n  \"\"\"L2 box regression loss.\"\"\"\n\n  def __init__(self, delta=0.1, **kwargs):\n    \"\"\"Initialize box loss.\n\n    Args:\n      delta: `float`, the point where the huber loss function changes from a\n        quadratic to linear. It is typically around the mean value of regression\n        target. For instances, the regression targets of 512x512 input with 6\n        anchors on P3-P7 pyramid is about [0.1, 0.1, 0.2, 0.2].\n      **kwargs: other params.\n    \"\"\"\n    super().__init__(**kwargs)\n    self.huber = tf.keras.losses.Huber(\n        delta, reduction=tf.keras.losses.Reduction.NONE)\n\n  @tf.autograph.experimental.do_not_convert\n  def call(self, y_true, box_outputs):\n    num_positives, box_targets = y_true\n    normalizer = num_positives * 4.0\n    mask = tf.cast(box_targets != 0.0, box_outputs.dtype)\n    box_targets = tf.expand_dims(box_targets, axis=-1)\n    box_outputs = tf.expand_dims(box_outputs, axis=-1)\n    # TODO(fsx950223): remove cast when huber loss dtype is fixed.\n    box_loss = tf.cast(self.huber(box_targets, box_outputs),\n                       box_outputs.dtype) * mask\n    box_loss = tf.reduce_sum(box_loss) / normalizer\n    return box_loss\n\n\nclass BoxIouLoss(tf.keras.losses.Loss):\n  \"\"\"Box iou loss.\"\"\"\n\n  def __init__(self, iou_loss_type, min_level, max_level, num_scales,\n               aspect_ratios, anchor_scale, image_size, **kwargs):\n    super().__init__(**kwargs)\n    self.iou_loss_type = iou_loss_type\n    self.input_anchors = anchors.Anchors(min_level, max_level, num_scales,\n                                         aspect_ratios, anchor_scale,\n                                         image_size)\n\n  @tf.autograph.experimental.do_not_convert\n  def call(self, y_true, box_outputs):\n    anchor_boxes = tf.tile(\n        self.input_anchors.boxes,\n        [box_outputs.shape[0] // self.input_anchors.boxes.shape[0], 1])\n    num_positives, box_targets = y_true\n    normalizer = num_positives * 4.0\n    mask = tf.cast(box_targets != 0.0, box_outputs.dtype)\n    box_outputs = anchors.decode_box_outputs(box_outputs, anchor_boxes) * mask\n    box_targets = anchors.decode_box_outputs(box_targets, anchor_boxes) * mask\n    box_iou_loss = iou_utils.iou_loss(box_outputs, box_targets,\n                                      self.iou_loss_type)\n    box_iou_loss = tf.reduce_sum(box_iou_loss) / normalizer\n    return box_iou_loss\n\n\nclass EfficientDetNetTrain(efficientdet_keras.EfficientDetNet):\n  \"\"\"A customized trainer for EfficientDet.\n\n  see https://www.tensorflow.org/guide/keras/customizing_what_happens_in_fit\n  \"\"\"\n\n  def __init__(self, *args, **kwargs):\n    super().__init__(*args, **kwargs)\n    log_dir = os.path.join(self.config.model_dir, 'train_images')\n    self.summary_writer = tf.summary.create_file_writer(log_dir)\n\n  def _freeze_vars(self):\n    if self.config.var_freeze_expr:\n      return [\n          v for v in self.trainable_variables\n          if not re.match(self.config.var_freeze_expr, v.name)\n      ]\n    return self.trainable_variables\n\n  def _reg_l2_loss(self, weight_decay, regex=r'.*(kernel|weight):0$'):\n    \"\"\"Return regularization l2 loss loss.\"\"\"\n    var_match = re.compile(regex)\n    return weight_decay * tf.add_n([\n        tf.nn.l2_loss(v) for v in self._freeze_vars() if var_match.match(v.name)\n    ])\n\n  def _detection_loss(self, cls_outputs, box_outputs, labels, loss_vals):\n    \"\"\"Computes total detection loss.\n\n    Computes total detection loss including box and class loss from all levels.\n    Args:\n      cls_outputs: an OrderDict with keys representing levels and values\n        representing logits in [batch_size, height, width, num_anchors].\n      box_outputs: an OrderDict with keys representing levels and values\n        representing box regression targets in [batch_size, height, width,\n        num_anchors * 4].\n      labels: the dictionary that returned from dataloader that includes\n        groundtruth targets.\n      loss_vals: A dict of loss values.\n\n    Returns:\n      total_loss: an integer tensor representing total loss reducing from\n        class and box losses from all levels.\n      cls_loss: an integer tensor representing total class loss.\n      box_loss: an integer tensor representing total box regression loss.\n      box_iou_loss: an integer tensor representing total box iou loss.\n    \"\"\"\n    # Sum all positives in a batch for normalization and avoid zero\n    # num_positives_sum, which would lead to inf loss during training\n    dtype = cls_outputs[0].dtype\n    num_positives_sum = tf.reduce_sum(labels['mean_num_positives']) + 1.0\n    positives_momentum = self.config.positives_momentum or 0\n    if positives_momentum > 0:\n      # normalize the num_positive_examples for training stability.\n      moving_normalizer_var = tf.Variable(\n          0.0,\n          name='moving_normalizer',\n          dtype=dtype,\n          synchronization=tf.VariableSynchronization.ON_READ,\n          trainable=False,\n          aggregation=tf.VariableAggregation.MEAN)\n      num_positives_sum = tf.keras.backend.moving_average_update(\n          moving_normalizer_var,\n          num_positives_sum,\n          momentum=self.config.positives_momentum)\n    elif positives_momentum < 0:\n      num_positives_sum = utils.cross_replica_mean(num_positives_sum)\n    num_positives_sum = tf.cast(num_positives_sum, dtype)\n    levels = range(len(cls_outputs))\n    cls_losses = []\n    box_losses = []\n    for level in levels:\n      # Onehot encoding for classification labels.\n      cls_targets_at_level = tf.one_hot(\n          labels['cls_targets_%d' % (level + self.config.min_level)],\n          self.config.num_classes,\n          dtype=dtype)\n\n      if self.config.data_format == 'channels_first':\n        bs, _, width, height, _ = cls_targets_at_level.get_shape().as_list()\n        cls_targets_at_level = tf.reshape(cls_targets_at_level,\n                                          [bs, -1, width, height])\n      else:\n        bs, width, height, _, _ = cls_targets_at_level.get_shape().as_list()\n        cls_targets_at_level = tf.reshape(cls_targets_at_level,\n                                          [bs, width, height, -1])\n\n      class_loss_layer = self.loss.get(FocalLoss.__name__, None)\n      if class_loss_layer:\n        cls_loss = class_loss_layer([num_positives_sum, cls_targets_at_level],\n                                    cls_outputs[level])\n        if self.config.data_format == 'channels_first':\n          cls_loss = tf.reshape(\n              cls_loss, [bs, -1, width, height, self.config.num_classes])\n        else:\n          cls_loss = tf.reshape(\n              cls_loss, [bs, width, height, -1, self.config.num_classes])\n        cls_loss *= tf.cast(\n            tf.expand_dims(\n                tf.not_equal(\n                    labels['cls_targets_%d' % (level + self.config.min_level)],\n                    -2), -1), dtype)\n        cls_loss_sum = tf.reduce_sum(cls_loss)\n        cls_losses.append(tf.cast(cls_loss_sum, dtype))\n\n      if self.config.box_loss_weight and self.loss.get(BoxLoss.__name__, None):\n        box_targets_at_level = (\n            labels['box_targets_%d' % (level + self.config.min_level)])\n        box_loss_layer = self.loss[BoxLoss.__name__]\n        box_losses.append(\n            box_loss_layer([num_positives_sum, box_targets_at_level],\n                           box_outputs[level]))\n\n    if self.config.iou_loss_type:\n      box_outputs = tf.concat([tf.reshape(v, [-1, 4]) for v in box_outputs],\n                              axis=0)\n      box_targets = tf.concat([\n          tf.reshape(labels['box_targets_%d' %\n                            (level + self.config.min_level)], [-1, 4])\n          for level in levels\n      ],\n                              axis=0)\n      box_iou_loss_layer = self.loss[BoxIouLoss.__name__]\n      box_iou_loss = box_iou_loss_layer([num_positives_sum, box_targets],\n                                        box_outputs)\n      loss_vals['box_iou_loss'] = box_iou_loss\n    else:\n      box_iou_loss = 0\n\n    cls_loss = tf.add_n(cls_losses) if cls_losses else 0\n    box_loss = tf.add_n(box_losses) if box_losses else 0\n    total_loss = (\n        cls_loss + self.config.box_loss_weight * box_loss +\n        self.config.iou_loss_weight * box_iou_loss)\n    loss_vals['det_loss'] = total_loss\n    loss_vals['cls_loss'] = cls_loss\n    loss_vals['box_loss'] = box_loss\n    return total_loss\n\n  def train_step(self, data):\n    \"\"\"Train step.\n\n    Args:\n      data: Tuple of (images, labels). Image tensor with shape [batch_size,\n        height, width, 3]. The height and width are fixed and equal.Input labels\n        in a dictionary. The labels include class targets and box targets which\n        are dense label maps. The labels are generated from get_input_fn\n        function in data/dataloader.py.\n\n    Returns:\n      A dict record loss info.\n    \"\"\"\n    images, labels = data\n    if self.config.img_summary_steps:\n      with self.summary_writer.as_default():\n        tf.summary.image('input_image', images)\n    with tf.GradientTape() as tape:\n      if len(self.config.heads) == 2:\n        cls_outputs, box_outputs, seg_outputs = util_keras.fp16_to_fp32_nested(\n            self(images, training=True))\n        loss_dtype = cls_outputs[0].dtype\n      elif 'object_detection' in self.config.heads:\n        cls_outputs, box_outputs = util_keras.fp16_to_fp32_nested(\n            self(images, training=True))\n        loss_dtype = cls_outputs[0].dtype\n      elif 'segmentation' in self.config.heads:\n        seg_outputs, = util_keras.fp16_to_fp32_nested(\n            self(images, training=True))\n        loss_dtype = seg_outputs.dtype\n      else:\n        raise ValueError('No valid head found: {}'.format(self.config.heads))\n      labels = util_keras.fp16_to_fp32_nested(labels)\n\n      total_loss = 0\n      loss_vals = {}\n      if 'object_detection' in self.config.heads:\n        det_loss = self._detection_loss(cls_outputs, box_outputs, labels,\n                                        loss_vals)\n        total_loss += det_loss\n      if 'segmentation' in self.config.heads:\n        seg_loss_layer = (\n            self.loss[tf.keras.losses.SparseCategoricalCrossentropy.__name__])\n        seg_loss = seg_loss_layer(labels['image_masks'], seg_outputs)\n        total_loss += seg_loss\n        loss_vals['seg_loss'] = seg_loss\n\n      reg_l2_loss = self._reg_l2_loss(self.config.weight_decay)\n      loss_vals['reg_l2_loss'] = reg_l2_loss\n      total_loss += tf.cast(reg_l2_loss, loss_dtype)\n      if isinstance(self.optimizer,\n                    tf.keras.mixed_precision.LossScaleOptimizer):\n        scaled_loss = self.optimizer.get_scaled_loss(total_loss)\n        optimizer = self.optimizer.inner_optimizer\n      else:\n        scaled_loss = total_loss\n        optimizer = self.optimizer\n    loss_vals['loss'] = total_loss\n    if hasattr(tf.keras.optimizers, 'experimental') and isinstance(\n        optimizer, tf.keras.optimizers.experimental.Optimizer):\n      loss_vals['learning_rate'] = optimizer.learning_rate\n    else:\n      loss_vals['learning_rate'] = optimizer.learning_rate(optimizer.iterations)\n    trainable_vars = self._freeze_vars()\n    scaled_gradients = tape.gradient(scaled_loss, trainable_vars)\n    if isinstance(self.optimizer,\n                  tf.keras.mixed_precision.LossScaleOptimizer):\n      gradients = self.optimizer.get_unscaled_gradients(scaled_gradients)\n    else:\n      gradients = scaled_gradients\n    if self.config.clip_gradients_norm > 0:\n      clip_norm = abs(self.config.clip_gradients_norm)\n      gradients = [\n          tf.clip_by_norm(g, clip_norm) if g is not None else None\n          for g in gradients\n      ]\n      gradients, _ = tf.clip_by_global_norm(gradients, clip_norm)\n      loss_vals['gradient_norm'] = tf.linalg.global_norm(gradients)\n    self.optimizer.apply_gradients(zip(gradients, trainable_vars))\n    return loss_vals\n\n  def test_step(self, data):\n    \"\"\"Test step.\n\n    Args:\n      data: Tuple of (images, labels). Image tensor with shape [batch_size,\n        height, width, 3]. The height and width are fixed and equal.Input labels\n        in a dictionary. The labels include class targets and box targets which\n        are dense label maps. The labels are generated from get_input_fn\n        function in data/dataloader.py.\n\n    Returns:\n      A dict record loss info.\n    \"\"\"\n    images, labels = data\n    if len(self.config.heads) == 2:\n      cls_outputs, box_outputs, seg_outputs = util_keras.fp16_to_fp32_nested(\n          self(images, training=False))\n      loss_dtype = cls_outputs[0].dtype\n    elif 'object_detection' in self.config.heads:\n      cls_outputs, box_outputs = util_keras.fp16_to_fp32_nested(\n          self(images, training=False))\n      loss_dtype = cls_outputs[0].dtype\n    elif 'segmentation' in self.config.heads:\n      seg_outputs, = util_keras.fp16_to_fp32_nested(\n          self(images, training=False))\n      loss_dtype = seg_outputs.dtype\n    else:\n      raise ValueError('No valid head found: {}'.format(self.config.heads))\n\n    labels = util_keras.fp16_to_fp32_nested(labels)\n\n    total_loss = 0\n    loss_vals = {}\n    if 'object_detection' in self.config.heads:\n      det_loss = self._detection_loss(cls_outputs, box_outputs, labels,\n                                      loss_vals)\n      total_loss += det_loss\n    if 'segmentation' in self.config.heads:\n      seg_loss_layer = (\n          self.loss[tf.keras.losses.SparseCategoricalCrossentropy.__name__])\n      seg_loss = seg_loss_layer(labels['image_masks'], seg_outputs)\n      total_loss += seg_loss\n      loss_vals['seg_loss'] = seg_loss\n    reg_l2_loss = self._reg_l2_loss(self.config.weight_decay)\n    loss_vals['reg_l2_loss'] = reg_l2_loss\n    loss_vals['loss'] = total_loss + tf.cast(reg_l2_loss, loss_dtype)\n    return loss_vals\n\n\nclass EfficientDetNetTrainHub(EfficientDetNetTrain):\n  \"\"\"EfficientDetNetTrain for Hub module.\"\"\"\n\n  def __init__(self, config, hub_module_url, name=''):\n    super(efficientdet_keras.EfficientDetNet, self).__init__(name=name)\n    self.config = config\n    self.hub_module_url = hub_module_url\n    self.base_model = hub.KerasLayer(hub_module_url, trainable=True)\n\n    # class/box output prediction network.\n    num_anchors = len(config.aspect_ratios) * config.num_scales\n\n    conv2d_layer = efficientdet_keras.ClassNet.conv2d_layer(\n        config.separable_conv, config.data_format)\n    self.classes = efficientdet_keras.ClassNet.classes_layer(\n        conv2d_layer,\n        config.num_classes,\n        num_anchors,\n        name='class_net/class-predict')\n\n    self.boxes = efficientdet_keras.BoxNet.boxes_layer(\n        config.separable_conv,\n        num_anchors,\n        config.data_format,\n        name='box_net/box-predict')\n\n    log_dir = os.path.join(self.config.model_dir, 'train_images')\n    self.summary_writer = tf.summary.create_file_writer(log_dir)\n\n  def call(self, inputs, training):\n    cls_outputs, box_outputs = self.base_model(inputs, training=training)\n    for i in range(self.config.max_level - self.config.min_level + 1):\n      cls_outputs[i] = self.classes(cls_outputs[i])\n      box_outputs[i] = self.boxes(box_outputs[i])\n    return (cls_outputs, box_outputs)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/keras/util_keras.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Common keras utils.\"\"\"\nimport collections\n\nfrom typing import Optional, Text\nfrom absl import logging\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\n\n# Prefix variable name mapping from keras model to the hub module checkpoint.\nHUB_CPT_NAME = collections.OrderedDict([('class_net/class-predict/', 'classes'),\n                                        ('box_net/box-predict/', 'boxes'),\n                                        ('', 'base_model')])\n\n\ndef build_batch_norm(is_training_bn: bool,\n                     beta_initializer: Text = 'zeros',\n                     gamma_initializer: Text = 'ones',\n                     data_format: Text = 'channels_last',\n                     momentum: float = 0.99,\n                     epsilon: float = 1e-3,\n                     strategy: Optional[Text] = None,\n                     name: Text = 'tpu_batch_normalization'):\n  \"\"\"Build a batch normalization layer.\n\n  Args:\n    is_training_bn: `bool` for whether the model is training.\n    beta_initializer: `str`, beta initializer.\n    gamma_initializer: `str`, gamma initializer.\n    data_format: `str` either \"channels_first\" for `[batch, channels, height,\n      width]` or \"channels_last for `[batch, height, width, channels]`.\n    momentum: `float`, momentume of batch norm.\n    epsilon: `float`, small value for numerical stability.\n    strategy: `str`, whether to use tpu, gpus or other version of batch norm.\n    name: the name of the batch normalization layer\n\n  Returns:\n    A normalized `Tensor` with the same `data_format`.\n  \"\"\"\n  axis = 1 if data_format == 'channels_first' else -1\n  batch_norm_class = utils.batch_norm_class(is_training_bn, strategy)\n\n  bn_layer = batch_norm_class(\n      axis=axis,\n      momentum=momentum,\n      epsilon=epsilon,\n      center=True,\n      scale=True,\n      beta_initializer=beta_initializer,\n      gamma_initializer=gamma_initializer,\n      name=name)\n\n  return bn_layer\n\n\ndef get_ema_vars(model):\n  \"\"\"Get all exponential moving average (ema) variables.\"\"\"\n  ema_vars = model.trainable_weights\n  for v in model.weights:\n    # We maintain mva for batch norm moving mean and variance as well.\n    if 'moving_mean' in v.name or 'moving_variance' in v.name:\n      ema_vars.append(v)\n  ema_vars_dict = dict()\n  # Remove duplicate vars\n  for var in ema_vars:\n    ema_vars_dict[var.ref()] = var\n  return ema_vars_dict\n\n\ndef average_name(ema, var):\n  \"\"\"Returns the name of the `Variable` holding the average for `var`.\n\n  A hacker for tf2.\n\n  Args:\n    ema: A `ExponentialMovingAverage` object.\n    var: A `Variable` object.\n\n  Returns:\n    A string: The name of the variable that will be used or was used\n    by the `ExponentialMovingAverage class` to hold the moving average of `var`.\n  \"\"\"\n\n  if var.ref() in ema._averages:  # pylint: disable=protected-access\n    return ema._averages[var.ref()].name.split(':')[0]  # pylint: disable=protected-access\n  return tf.compat.v1.get_default_graph().unique_name(\n      var.name.split(':')[0] + '/' + ema.name, mark_as_used=False)\n\n\ndef load_from_hub_checkpoint(model, ckpt_path_or_file):\n  \"\"\"Loads EfficientDetNet weights from EfficientDetNetTrainHub checkpoint.\"\"\"\n\n  def _get_cpt_var_name(var_name):\n    for name_prefix, hub_name_prefix in HUB_CPT_NAME.items():\n      if var_name.startswith(name_prefix):\n        cpt_var_name = var_name[len(name_prefix):]  # remove the name_prefix\n        cpt_var_name = cpt_var_name.replace('/', '.S')\n        cpt_var_name = hub_name_prefix + '/' + cpt_var_name\n        if name_prefix:\n          cpt_var_name = cpt_var_name.replace(':0', '')\n        break\n\n    return cpt_var_name + '/.ATTRIBUTES/VARIABLE_VALUE'  # pytype: disable=name-error  # py310-upgrade\n\n  for var in model.weights:\n    cpt_var_name = _get_cpt_var_name(var.name)\n    var.assign(tf.train.load_variable(ckpt_path_or_file, cpt_var_name))\n\n    logging.log_first_n(\n        logging.INFO,\n        'Init %s from %s (%s)' % (var.name, cpt_var_name, ckpt_path_or_file),\n        10)\n\n\ndef restore_ckpt(model,\n                 ckpt_path_or_file,\n                 ema_decay=0.9998,\n                 skip_mismatch=True,\n                 exclude_layers=None):\n  \"\"\"Restore variables from a given checkpoint.\n\n  Args:\n    model: the keras model to be restored.\n    ckpt_path_or_file: the path or file for checkpoint.\n    ema_decay: ema decay rate. If None or zero or negative value, disable ema.\n    skip_mismatch: whether to skip variables if shape mismatch,\n      only works with tf1 checkpoint.\n    exclude_layers: string list exclude layer's variables,\n      only works with tf2 checkpoint.\n\n  Raises:\n    KeyError: if access unexpected variables.\n  \"\"\"\n  if ckpt_path_or_file == '_':\n    logging.info('Running test: do not load any ckpt.')\n    return\n  if tf.io.gfile.isdir(ckpt_path_or_file):\n    ckpt_path_or_file = tf.train.latest_checkpoint(ckpt_path_or_file)\n\n  var_list = tf.train.list_variables(ckpt_path_or_file)\n  if (var_list[0][0] ==\n      '_CHECKPOINTABLE_OBJECT_GRAPH'):\n    try:\n      # Use custom checkpoint solves mismatch shape issue.\n      keys = {var[0].split('/')[0] for var in var_list}\n      keys.discard('_CHECKPOINTABLE_OBJECT_GRAPH')\n      if exclude_layers:\n        exclude_layers = set(exclude_layers)\n        keys = keys.difference(exclude_layers)\n      ckpt = tf.train.Checkpoint(**{key: getattr(model, key, None)\n                                    for key in keys\n                                    if getattr(model, key, None)})\n      status = ckpt.restore(ckpt_path_or_file)\n      status.assert_nontrivial_match()\n    except AssertionError:\n      # The checkpoint for  EfficientDetNetTrainHub and EfficientDetNet are not\n      # the same. If we trained from EfficientDetNetTrainHub using hub module\n      # and then want to use the weight in EfficientDetNet, it needed to\n      # manually load the model checkpoint.\n      load_from_hub_checkpoint(model, ckpt_path_or_file)\n  else:\n    if ema_decay > 0:\n      ema = tf.train.ExponentialMovingAverage(decay=0.0)\n      ema_vars = get_ema_vars(model)\n      var_dict = {\n          average_name(ema, var): var for (ref, var) in ema_vars.items()\n      }\n    else:\n      ema_vars = get_ema_vars(model)\n      var_dict = {\n          var.name.split(':')[0]: var for (ref, var) in ema_vars.items()\n      }\n    # add variables that not in var_dict\n    for v in model.weights:\n      if v.ref() not in ema_vars:\n        var_dict[v.name.split(':')[0]] = v\n    # try to load graph-based checkpoint with ema support,\n    # else load checkpoint via keras.load_weights which doesn't support ema.\n    reader = tf.train.load_checkpoint(ckpt_path_or_file)\n    var_shape_map = reader.get_variable_to_shape_map()\n    for key, var in var_dict.items():\n      if key in var_shape_map:\n        if var_shape_map[key] != var.shape:\n          msg = 'Shape mismatch: %s' % key\n          if skip_mismatch:\n            logging.warning(msg)\n          else:\n            raise ValueError(msg)\n        else:\n          var.assign(reader.get_tensor(key), read_value=False)\n          logging.log_first_n(\n              logging.INFO, f'Init {var.name} from {key} ({ckpt_path_or_file})',\n              10)\n      else:\n        msg = 'Not found %s in %s' % (key, ckpt_path_or_file)\n        if skip_mismatch:\n          logging.warning(msg)\n        else:\n          raise KeyError(msg)\n\n\ndef fp16_to_fp32_nested(input_nested):\n  \"\"\"Convert fp16 tensors in a nested structure to fp32.\n\n  Args:\n    input_nested: A Python dict, values being Tensor or Python list/tuple of\n      Tensor or Non-Tensor.\n\n  Returns:\n    A Python dict with the same structure as `tensor_dict`,\n    with all bfloat16 tensors converted to float32.\n  \"\"\"\n  if isinstance(input_nested, tf.Tensor):\n    if input_nested.dtype in (tf.bfloat16, tf.float16):\n      return tf.cast(input_nested, dtype=tf.float32)\n    else:\n      return input_nested\n  elif isinstance(input_nested, (list, tuple)):\n    out_tensor_dict = [fp16_to_fp32_nested(t) for t in input_nested]\n  elif isinstance(input_nested, dict):\n    out_tensor_dict = {\n        k: fp16_to_fp32_nested(v) for k, v in input_nested.items()\n    }\n  else:\n    return input_nested\n  return out_tensor_dict\n\n\n### The following code breaks COCO training.\n# def get_batch_norm(bn_class):\n#   def _wrapper(*args, **kwargs):\n#     if not kwargs.get('name', None):\n#       kwargs['name'] = 'tpu_batch_normalization'\n#     return bn_class(*args, **kwargs)\n#   return _wrapper\n\n# if tf.compat.v1.executing_eagerly_outside_functions():\n#   utils.BatchNormalization = get_batch_norm(\n#       tf.keras.layers.BatchNormalization)\n#   utils.SyncBatchNormalization = get_batch_norm(\n#       tf.keras.layers.experimental.SyncBatchNormalization)\n#   utils.TpuBatchNormalization = get_batch_norm(\n#       tf.keras.layers.experimental.SyncBatchNormalization)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/keras/wbf.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"WBF for test-time augmentation.\"\"\"\nimport tensorflow as tf\n\n\ndef vectorized_iou(clusters, detection):\n  \"\"\"Calculates the ious for box with each element of clusters.\"\"\"\n  x11, y11, x12, y12 = tf.split(clusters[:, 1:5], 4, axis=1)\n  x21, y21, x22, y22 = tf.split(detection[1:5], 4)\n\n  xa = tf.maximum(x11, x21)\n  ya = tf.maximum(y11, y21)\n  xb = tf.minimum(x12, x22)\n  yb = tf.minimum(y12, y22)\n\n  inter_area = tf.maximum((xb - xa), 0) * tf.maximum((yb - ya), 0)\n\n  boxa_area = (x12 - x11) * (y12 - y11)\n  boxb_area = (x22 - x21) * (y22 - y21)\n\n  iou = inter_area / (boxa_area + boxb_area - inter_area)\n\n  return iou\n\n\ndef find_matching_cluster(clusters, detection):\n  \"\"\"Returns the index of the highest iou matching cluster for detection.\"\"\"\n  if not clusters:\n    return -1\n  ious = vectorized_iou(tf.stack(clusters), detection)\n  ious = tf.reshape(ious, [len(clusters)])\n  if tf.math.reduce_max(ious) < 0.55:\n    # returns -1 if no iou is higher than 0.55.\n    return -1\n  return tf.argmax(ious)\n\n\ndef weighted_average(samples, weights):\n  return tf.math.reduce_sum(samples * weights) / tf.math.reduce_sum(weights)\n\n\ndef average_detections(detections, num_models):\n  \"\"\"Takes a list of detections and returns the average, both in box co-ordinates and confidence.\"\"\"\n  num_detections = len(detections)\n  detections = tf.stack(detections)\n  return [\n      detections[0][0],\n      weighted_average(detections[:, 1], detections[:, 5]),\n      weighted_average(detections[:, 2], detections[:, 5]),\n      weighted_average(detections[:, 3], detections[:, 5]),\n      weighted_average(detections[:, 4], detections[:, 5]),\n      tf.math.reduce_mean(detections[:, 5]) * min(1, num_detections/num_models),\n      detections[0][6],\n  ]\n\n\ndef ensemble_detections(params, detections, num_models):\n  \"\"\"Ensembles a group of detections by clustering the detections and returning the average of the clusters.\"\"\"\n  all_clusters = []\n\n  for cid in range(params['num_classes']):\n    indices = tf.where(tf.equal(detections[:, 6], cid))\n    if indices.shape[0] == 0:\n      continue\n    class_detections = tf.gather_nd(detections, indices)\n\n    clusters = []\n    cluster_averages = []\n    for d in class_detections:\n      cluster_index = find_matching_cluster(cluster_averages, d)\n      if cluster_index == -1:\n        clusters.append([d])\n        cluster_averages.append(average_detections([d], num_models))\n      else:\n        clusters[cluster_index].append(d)\n        cluster_averages[cluster_index] = average_detections(\n            clusters[cluster_index], num_models)\n\n    all_clusters.extend(cluster_averages)\n\n  all_clusters.sort(reverse=True, key=lambda d: d[5])\n  return tf.stack(all_clusters)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/main.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"The main training script.\"\"\"\nimport multiprocessing\nimport os\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\nimport numpy as np\nimport tensorflow.compat.v1 as tf\nfrom tensorflow.compat.v1 import estimator as tf_estimator\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import dataloader\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import det_model_fn\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import hparams_config\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\n\nflags.DEFINE_string(\n    'tpu',\n    default=None,\n    help='The Cloud TPU to use for training. This should be either the name '\n    'used when creating the Cloud TPU, or a grpc://ip.address.of.tpu:8470 '\n    'url.')\nflags.DEFINE_string(\n    'gcp_project',\n    default=None,\n    help='Project name for the Cloud TPU-enabled project. If not specified, we '\n    'will attempt to automatically detect the GCE project from metadata.')\nflags.DEFINE_string(\n    'tpu_zone',\n    default=None,\n    help='GCE zone where the Cloud TPU is located in. If not specified, we '\n    'will attempt to automatically detect the GCE project from metadata.')\nflags.DEFINE_string('eval_name', default=None, help='Eval job name')\nflags.DEFINE_enum('strategy', None, ['tpu', 'gpus', ''],\n                  'Training: gpus for multi-gpu, if None, use TF default.')\n\nflags.DEFINE_bool('use_fake_data', False, 'Use fake input.')\nflags.DEFINE_bool(\n    'use_xla', False,\n    'Use XLA even if strategy is not tpu. If strategy is tpu, always use XLA, '\n    'and this flag has no effect.')\nflags.DEFINE_string('model_dir', None, 'Location of model_dir')\nflags.DEFINE_string(\n    'backbone_ckpt', '', 'Location of the ResNet50 checkpoint to use for model '\n    'initialization.')\nflags.DEFINE_string('ckpt', None,\n                    'Start training from this EfficientDet checkpoint.')\n\nflags.DEFINE_string(\n    'hparams', '', 'Comma separated k=v pairs of hyperparameters or a module'\n    ' containing attributes to use as hyperparameters.')\nflags.DEFINE_integer(\n    'num_cores', default=8, help='Number of TPU cores for training')\nflags.DEFINE_bool('use_spatial_partition', False, 'Use spatial partition.')\nflags.DEFINE_integer(\n    'num_cores_per_replica',\n    default=2,\n    help='Number of TPU cores per replica when using spatial partition.')\nflags.DEFINE_multi_integer(\n    'input_partition_dims', [1, 2, 1, 1],\n    'A list that describes the partition dims for all the tensors.')\nflags.DEFINE_integer('train_batch_size', 64, 'global training batch size')\nflags.DEFINE_integer('eval_batch_size', 1, 'global evaluation batch size')\nflags.DEFINE_integer('eval_samples', 5000, 'Number of samples for eval.')\nflags.DEFINE_integer('iterations_per_loop', 1000,\n                     'Number of iterations per TPU training loop')\nflags.DEFINE_integer('save_checkpoints_steps', 1000,\n                     'Number of iterations per checkpoint save')\nflags.DEFINE_string(\n    'train_file_pattern', None,\n    'Glob for training data files (e.g., COCO train - minival set)')\nflags.DEFINE_string('val_file_pattern', None,\n                    'Glob for evaluation tfrecords (e.g., COCO val2017 set)')\nflags.DEFINE_string(\n    'val_json_file', None,\n    'COCO validation JSON containing golden bounding boxes. If None, use the '\n    'ground truth from the dataloader. Ignored if testdev_dir is not None.')\nflags.DEFINE_string('testdev_dir', None,\n                    'COCO testdev dir. If not None, ignorer val_json_file.')\nflags.DEFINE_integer('num_examples_per_epoch', 120000,\n                     'Number of examples in one epoch')\nflags.DEFINE_integer('num_epochs', None, 'Number of epochs for training')\nflags.DEFINE_string('mode', 'train',\n                    'Mode to run: train or eval (default: train)')\nflags.DEFINE_string('model_name', 'efficientdet-d1', 'Model name.')\nflags.DEFINE_bool('eval_after_train', False, 'Run one eval after the '\n                  'training finishes.')\nflags.DEFINE_bool('profile', False, 'Profile training performance.')\nflags.DEFINE_integer(\n    'tf_random_seed', None, 'Sets the TF graph seed for deterministic execution'\n    ' across runs (for debugging).')\n\n# For Eval mode\nflags.DEFINE_integer('min_eval_interval', 180,\n                     'Minimum seconds between evaluations.')\nflags.DEFINE_integer(\n    'eval_timeout', None,\n    'Maximum seconds between checkpoints before evaluation terminates.')\n\n# for train_and_eval mode\nflags.DEFINE_bool(\n    'run_epoch_in_child_process', False,\n    'This option helps to rectify CPU memory leak. If True, every epoch is '\n    'run in a separate process for train and eval and memory will be cleared.'\n    'Drawback: need to kill 2 processes if trainining needs to be interrupted.')\n\nFLAGS = flags.FLAGS\n\n\ndef main(_):\n  if FLAGS.strategy == 'tpu':\n    tf.disable_eager_execution()\n    tpu_cluster_resolver = tf.distribute.cluster_resolver.TPUClusterResolver(\n        FLAGS.tpu, zone=FLAGS.tpu_zone, project=FLAGS.gcp_project)\n    tpu_grpc_url = tpu_cluster_resolver.get_master()\n    tf.Session.reset(tpu_grpc_url)\n  else:\n    tpu_cluster_resolver = None\n\n  # Check data path\n  if FLAGS.mode in ('train', 'train_and_eval'):\n    if FLAGS.train_file_pattern is None:\n      raise RuntimeError('Must specify --train_file_pattern for train.')\n  if FLAGS.mode in ('eval', 'train_and_eval'):\n    if FLAGS.val_file_pattern is None:\n      raise RuntimeError('Must specify --val_file_pattern for eval.')\n\n  # Parse and override hparams\n  config = hparams_config.get_detection_config(FLAGS.model_name)\n  config.override(FLAGS.hparams)\n  if FLAGS.num_epochs:  # NOTE: remove this flag after updating all docs.\n    config.num_epochs = FLAGS.num_epochs\n\n  # Parse image size in case it is in string format.\n  config.image_size = utils.parse_image_size(config.image_size)\n\n  # The following is for spatial partitioning. `features` has one tensor while\n  # `labels` had 4 + (`max_level` - `min_level` + 1) * 2 tensors. The input\n  # partition is performed on `features` and all partitionable tensors of\n  # `labels`, see the partition logic below.\n  # In the TPUEstimator context, the meaning of `shard` and `replica` is the\n  # same; follwing the API, here has mixed use of both.\n  if FLAGS.use_spatial_partition:\n    # Checks input_partition_dims agrees with num_cores_per_replica.\n    if FLAGS.num_cores_per_replica != np.prod(FLAGS.input_partition_dims):\n      raise RuntimeError('--num_cores_per_replica must be a product of array'\n                         'elements in --input_partition_dims.')\n\n    labels_partition_dims = {\n        'mean_num_positives': None,\n        'source_ids': None,\n        'groundtruth_data': None,\n        'image_scales': None,\n        'image_masks': None,\n    }\n    # The Input Partition Logic: We partition only the partition-able tensors.\n    feat_sizes = utils.get_feat_sizes(\n        config.get('image_size'), config.get('max_level'))\n    for level in range(config.get('min_level'), config.get('max_level') + 1):\n\n      def _can_partition(spatial_dim):\n        partitionable_index = np.where(\n            spatial_dim % np.array(FLAGS.input_partition_dims) == 0)\n        return len(partitionable_index[0]) == len(FLAGS.input_partition_dims)\n\n      spatial_dim = feat_sizes[level]\n      if _can_partition(spatial_dim['height']) and _can_partition(\n          spatial_dim['width']):\n        labels_partition_dims['box_targets_%d' %\n                              level] = FLAGS.input_partition_dims\n        labels_partition_dims['cls_targets_%d' %\n                              level] = FLAGS.input_partition_dims\n      else:\n        labels_partition_dims['box_targets_%d' % level] = None\n        labels_partition_dims['cls_targets_%d' % level] = None\n    num_cores_per_replica = FLAGS.num_cores_per_replica\n    input_partition_dims = [FLAGS.input_partition_dims, labels_partition_dims]\n    num_shards = FLAGS.num_cores // num_cores_per_replica\n  else:\n    num_cores_per_replica = None\n    input_partition_dims = None\n    num_shards = FLAGS.num_cores\n\n  params = dict(\n      config.as_dict(),\n      model_name=FLAGS.model_name,\n      iterations_per_loop=FLAGS.iterations_per_loop,\n      model_dir=FLAGS.model_dir,\n      num_shards=num_shards,\n      num_examples_per_epoch=FLAGS.num_examples_per_epoch,\n      strategy=FLAGS.strategy,\n      backbone_ckpt=FLAGS.backbone_ckpt,\n      ckpt=FLAGS.ckpt,\n      val_json_file=FLAGS.val_json_file,\n      testdev_dir=FLAGS.testdev_dir,\n      profile=FLAGS.profile,\n      mode=FLAGS.mode)\n  config_proto = tf.ConfigProto(\n      allow_soft_placement=True, log_device_placement=False)\n  if FLAGS.strategy != 'tpu':\n    if FLAGS.use_xla:\n      config_proto.graph_options.optimizer_options.global_jit_level = (\n          tf.OptimizerOptions.ON_1)\n      config_proto.gpu_options.allow_growth = True\n\n  model_dir = FLAGS.model_dir\n  model_fn_instance = det_model_fn.get_model_fn(FLAGS.model_name)\n  max_instances_per_image = config.max_instances_per_image\n  if FLAGS.eval_samples:\n    eval_steps = int((FLAGS.eval_samples + FLAGS.eval_batch_size - 1) //\n                     FLAGS.eval_batch_size)\n  else:\n    eval_steps = None\n  total_examples = int(config.num_epochs * FLAGS.num_examples_per_epoch)\n  train_steps = total_examples // FLAGS.train_batch_size\n  logging.info(params)\n\n  if not tf.io.gfile.exists(model_dir):\n    tf.io.gfile.makedirs(model_dir)\n\n  config_file = os.path.join(model_dir, 'config.yaml')\n  if not tf.io.gfile.exists(config_file):\n    tf.io.gfile.GFile(config_file, 'w').write(str(config))\n\n  train_input_fn = dataloader.InputReader(\n      FLAGS.train_file_pattern,\n      is_training=True,\n      use_fake_data=FLAGS.use_fake_data,\n      max_instances_per_image=max_instances_per_image)\n  eval_input_fn = dataloader.InputReader(\n      FLAGS.val_file_pattern,\n      is_training=False,\n      use_fake_data=FLAGS.use_fake_data,\n      max_instances_per_image=max_instances_per_image)\n\n  if FLAGS.strategy == 'tpu':\n    tpu_config = tf_estimator.tpu.TPUConfig(\n        FLAGS.iterations_per_loop if FLAGS.strategy == 'tpu' else 1,\n        num_cores_per_replica=num_cores_per_replica,\n        input_partition_dims=input_partition_dims,\n        per_host_input_for_training=tf_estimator.tpu.InputPipelineConfig\n        .PER_HOST_V2)\n    run_config = tf_estimator.tpu.RunConfig(\n        cluster=tpu_cluster_resolver,\n        model_dir=model_dir,\n        log_step_count_steps=FLAGS.iterations_per_loop,\n        session_config=config_proto,\n        tpu_config=tpu_config,\n        save_checkpoints_steps=FLAGS.save_checkpoints_steps,\n        tf_random_seed=FLAGS.tf_random_seed,\n    )\n    # TPUEstimator can do both train and eval.\n    train_est = tf_estimator.tpu.TPUEstimator(\n        model_fn=model_fn_instance,\n        train_batch_size=FLAGS.train_batch_size,\n        eval_batch_size=FLAGS.eval_batch_size,\n        config=run_config,\n        params=params)\n    eval_est = train_est\n  else:\n    strategy = None\n    if FLAGS.strategy == 'gpus':\n      strategy = tf.distribute.MirroredStrategy()\n    run_config = tf_estimator.RunConfig(\n        model_dir=model_dir,\n        train_distribute=strategy,\n        log_step_count_steps=FLAGS.iterations_per_loop,\n        session_config=config_proto,\n        save_checkpoints_steps=FLAGS.save_checkpoints_steps,\n        tf_random_seed=FLAGS.tf_random_seed,\n    )\n\n    def get_estimator(global_batch_size):\n      params['num_shards'] = getattr(strategy, 'num_replicas_in_sync', 1)\n      params['batch_size'] = global_batch_size // params['num_shards']\n      return tf_estimator.Estimator(\n          model_fn=model_fn_instance, config=run_config, params=params)\n\n    # train and eval need different estimator due to different batch size.\n    train_est = get_estimator(FLAGS.train_batch_size)\n    eval_est = get_estimator(FLAGS.eval_batch_size)\n\n  # start train/eval flow.\n  if FLAGS.mode == 'train':\n    train_est.train(input_fn=train_input_fn, max_steps=train_steps)\n    if FLAGS.eval_after_train:\n      eval_est.evaluate(input_fn=eval_input_fn, steps=eval_steps)\n\n  elif FLAGS.mode == 'eval':\n    # Run evaluation when there's a new checkpoint\n    for ckpt in tf.train.checkpoints_iterator(\n        FLAGS.model_dir,\n        min_interval_secs=FLAGS.min_eval_interval,\n        timeout=FLAGS.eval_timeout):\n\n      logging.info('Starting to evaluate.')\n      try:\n        eval_results = eval_est.evaluate(\n            eval_input_fn, steps=eval_steps, name=FLAGS.eval_name)\n        # Terminate eval job when final checkpoint is reached.\n        try:\n          current_step = int(os.path.basename(ckpt).split('-')[1])\n        except IndexError:\n          logging.info('%s has no global step info: stop!', ckpt)\n          break\n\n        utils.archive_ckpt(eval_results, eval_results['AP'], ckpt)\n        if current_step >= train_steps:\n          logging.info('Eval finished step %d/%d', current_step, train_steps)\n          break\n\n      except tf.errors.NotFoundError:\n        # Checkpoint might be not already deleted by the time eval finished.\n        # We simply skip ssuch case.\n        logging.info('Checkpoint %s no longer exists, skipping.', ckpt)\n\n  elif FLAGS.mode == 'train_and_eval':\n    ckpt = tf.train.latest_checkpoint(FLAGS.model_dir)\n    try:\n      step = int(os.path.basename(ckpt).split('-')[1])\n      current_epoch = (\n          step * FLAGS.train_batch_size // FLAGS.num_examples_per_epoch)\n      logging.info('found ckpt at step %d (epoch %d)', step, current_epoch)\n    except (IndexError, TypeError):\n      logging.info('Folder %s has no ckpt with valid step.', FLAGS.model_dir)\n      current_epoch = 0\n\n    def run_train_and_eval(e):\n      print('\\n   =====> Starting training, epoch: %d.' % e)\n      train_est.train(\n          input_fn=train_input_fn,\n          max_steps=e * FLAGS.num_examples_per_epoch // FLAGS.train_batch_size)\n      print('\\n   =====> Starting evaluation, epoch: %d.' % e)\n      eval_results = eval_est.evaluate(input_fn=eval_input_fn, steps=eval_steps)\n      ckpt = tf.train.latest_checkpoint(FLAGS.model_dir)\n      utils.archive_ckpt(eval_results, eval_results['AP'], ckpt)\n\n    epochs_per_cycle = 1  # higher number has less graph construction overhead.\n    for e in range(current_epoch + 1, config.num_epochs + 1, epochs_per_cycle):\n      if FLAGS.run_epoch_in_child_process:\n        p = multiprocessing.Process(target=run_train_and_eval, args=(e,))\n        p.start()\n        p.join()\n        if p.exitcode != 0:\n          return p.exitcode\n      else:\n        tf.compat.v1.reset_default_graph()\n        run_train_and_eval(e)\n\n  else:\n    logging.info('Invalid mode: %s', FLAGS.mode)\n\n\nif __name__ == '__main__':\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/model_inspect.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\nr\"\"\"Tool to inspect a model.\"\"\"\nimport os\nimport time\nfrom typing import Text, Tuple, List\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\n\nimport numpy as np\nfrom PIL import Image\nimport tensorflow.compat.v1 as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import hparams_config\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import inference\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import utils\nfrom tensorflow.python.client import timeline  # pylint: disable=g-direct-tensorflow-import\n\nflags.DEFINE_string('model_name', 'efficientdet-d0', 'Model.')\nflags.DEFINE_string('logdir', '/tmp/deff/', 'log directory.')\nflags.DEFINE_string('runmode', 'dry', 'Run mode: {freeze, bm, dry}')\nflags.DEFINE_string('trace_filename', None, 'Trace file name.')\n\nflags.DEFINE_integer('threads', 0, 'Number of threads.')\nflags.DEFINE_integer('bm_runs', 10, 'Number of benchmark runs.')\nflags.DEFINE_string('tensorrt', None, 'TensorRT mode: {None, FP32, FP16, INT8}')\nflags.DEFINE_bool('delete_logdir', True, 'Whether to delete logdir.')\nflags.DEFINE_bool('freeze', False, 'Freeze graph.')\nflags.DEFINE_bool('use_xla', False, 'Run with xla optimization.')\nflags.DEFINE_integer('batch_size', 1, 'Batch size for inference.')\n\nflags.DEFINE_string('ckpt_path', None, 'checkpoint dir used for eval.')\nflags.DEFINE_string('export_ckpt', None, 'Path for exporting new models.')\n\nflags.DEFINE_string(\n    'hparams', '', 'Comma separated k=v pairs of hyperparameters or a module'\n    ' containing attributes to use as hyperparameters.')\n\nflags.DEFINE_string('input_image', None, 'Input image path for inference.')\nflags.DEFINE_string('output_image_dir', None, 'Output dir for inference.')\n\n# For video.\nflags.DEFINE_string('input_video', None, 'Input video path for inference.')\nflags.DEFINE_string('output_video', None,\n                    'Output video path. If None, play it online instead.')\n\n# For visualization.\nflags.DEFINE_integer('line_thickness', None, 'Line thickness for box.')\nflags.DEFINE_integer('max_boxes_to_draw', 100, 'Max number of boxes to draw.')\nflags.DEFINE_float('min_score_thresh', 0.4, 'Score threshold to show box.')\nflags.DEFINE_string('nms_method', 'hard', 'nms method, hard or gaussian.')\n\n# For saved model.\nflags.DEFINE_string('saved_model_dir', '/tmp/saved_model',\n                    'Folder path for saved model.')\nflags.DEFINE_string('tflite_path', None, 'Path for exporting tflite file.')\n\nFLAGS = flags.FLAGS\n\n\nclass ModelInspector(object):\n  \"\"\"A simple helper class for inspecting a model.\"\"\"\n\n  def __init__(self,\n               model_name: Text,\n               logdir: Text,\n               tensorrt: Text = False,\n               use_xla: bool = False,\n               ckpt_path: Text = None,\n               export_ckpt: Text = None,\n               saved_model_dir: Text = None,\n               tflite_path: Text = None,\n               batch_size: int = 1,\n               hparams: Text = '',\n               **kwargs):  # pytype: disable=annotation-type-mismatch\n    self.model_name = model_name\n    self.logdir = logdir\n    self.tensorrt = tensorrt\n    self.use_xla = use_xla\n    self.ckpt_path = ckpt_path\n    self.export_ckpt = export_ckpt\n    self.saved_model_dir = saved_model_dir\n    self.tflite_path = tflite_path\n\n    model_config = hparams_config.get_detection_config(model_name)\n    model_config.override(hparams)  # Add custom overrides\n    model_config.is_training_bn = False\n    model_config.image_size = utils.parse_image_size(model_config.image_size)\n\n    # If batch size is 0, then build a graph with dynamic batch size.\n    self.batch_size = batch_size or None\n    self.labels_shape = [batch_size, model_config.num_classes]\n\n    # A hack to make flag consistent with nms configs.\n    if kwargs.get('score_thresh', None):\n      model_config.nms_configs.score_thresh = kwargs['score_thresh']\n    if kwargs.get('nms_method', None):\n      model_config.nms_configs.method = kwargs['nms_method']\n    if kwargs.get('max_output_size', None):\n      model_config.nms_configs.max_output_size = kwargs['max_output_size']\n\n    height, width = model_config.image_size\n    if model_config.data_format == 'channels_first':\n      self.inputs_shape = [batch_size, 3, height, width]\n    else:\n      self.inputs_shape = [batch_size, height, width, 3]\n\n    self.model_config = model_config\n\n  def build_model(self, inputs: tf.Tensor) -> List[tf.Tensor]:\n    \"\"\"Build model with inputs and labels and print out model stats.\"\"\"\n    logging.info('start building model')\n    cls_outputs, box_outputs = inference.build_model(\n        self.model_name,\n        inputs,\n        **self.model_config)\n\n    # Write to tfevent for tensorboard.\n    train_writer = tf.summary.FileWriter(self.logdir)\n    train_writer.add_graph(tf.get_default_graph())\n    train_writer.flush()\n\n    all_outputs = list(cls_outputs.values()) + list(box_outputs.values())\n    return all_outputs\n\n  def export_saved_model(self, **kwargs):\n    \"\"\"Export a saved model for inference.\"\"\"\n    tf.enable_resource_variables()\n    driver = inference.ServingDriver(\n        self.model_name,\n        self.ckpt_path,\n        batch_size=self.batch_size,\n        use_xla=self.use_xla,\n        model_params=self.model_config.as_dict(),\n        **kwargs)\n    driver.build()\n    driver.export(self.saved_model_dir, self.tflite_path, self.tensorrt)\n\n  def saved_model_inference(self, image_path_pattern, output_dir, **kwargs):\n    \"\"\"Perform inference for the given saved model.\"\"\"\n    driver = inference.ServingDriver(\n        self.model_name,\n        self.ckpt_path,\n        batch_size=self.batch_size,\n        use_xla=self.use_xla,\n        model_params=self.model_config.as_dict(),\n        **kwargs)\n    driver.load(self.saved_model_dir)\n\n    # Serving time batch size should be fixed.\n    batch_size = self.batch_size or 1\n    all_files = list(tf.io.gfile.glob(image_path_pattern))\n    print('all_files=', all_files)\n    num_batches = (len(all_files) + batch_size - 1) // batch_size\n\n    for i in range(num_batches):\n      batch_files = all_files[i * batch_size:(i + 1) * batch_size]\n      height, width = self.model_config.image_size\n      images = [Image.open(f) for f in batch_files]\n      if len(set([m.size for m in images])) > 1:\n        # Resize only if images in the same batch have different sizes.\n        images = [m.resize(height, width) for m in images]\n      raw_images = [np.array(m) for m in images]\n      size_before_pad = len(raw_images)\n      if size_before_pad < batch_size:\n        padding_size = batch_size - size_before_pad\n        raw_images += [np.zeros_like(raw_images[0])] * padding_size\n\n      detections_bs = driver.serve_images(raw_images)\n      for j in range(size_before_pad):\n        img = driver.visualize(raw_images[j], detections_bs[j], **kwargs)\n        img_id = str(i * batch_size + j)\n        output_image_path = os.path.join(output_dir, img_id + '.jpg')\n        Image.fromarray(img).save(output_image_path)\n        print('writing file to %s' % output_image_path)\n\n  def saved_model_benchmark(self,\n                            image_path_pattern,\n                            trace_filename=None,\n                            **kwargs):\n    \"\"\"Perform inference for the given saved model.\"\"\"\n    driver = inference.ServingDriver(\n        self.model_name,\n        self.ckpt_path,\n        batch_size=self.batch_size,\n        use_xla=self.use_xla,\n        model_params=self.model_config.as_dict(),\n        **kwargs)\n    driver.load(self.saved_model_dir)\n    raw_images = []\n    all_files = list(tf.io.gfile.glob(image_path_pattern))\n    if len(all_files) < self.batch_size:\n      all_files = all_files * (self.batch_size // len(all_files) + 1)\n    raw_images = [np.array(Image.open(f)) for f in all_files[:self.batch_size]]\n    driver.benchmark(raw_images, trace_filename)\n\n  def saved_model_video(self, video_path: Text, output_video: Text, **kwargs):\n    \"\"\"Perform video inference for the given saved model.\"\"\"\n    import cv2  # pylint: disable=g-import-not-at-top\n\n    driver = inference.ServingDriver(\n        self.model_name,\n        self.ckpt_path,\n        batch_size=1,\n        use_xla=self.use_xla,\n        model_params=self.model_config.as_dict())\n    driver.load(self.saved_model_dir)\n\n    cap = cv2.VideoCapture(video_path)\n    if not cap.isOpened():\n      print('Error opening input video: {}'.format(video_path))\n\n    out_ptr = None\n    if output_video:\n      frame_width, frame_height = int(cap.get(3)), int(cap.get(4))\n      out_ptr = cv2.VideoWriter(output_video,\n                                cv2.VideoWriter_fourcc('m', 'p', '4', 'v'), 25,\n                                (frame_width, frame_height))\n\n    while cap.isOpened():\n      # Capture frame-by-frame\n      ret, frame = cap.read()\n      if not ret:\n        break\n\n      raw_frames = [np.array(frame)]\n      detections_bs = driver.serve_images(raw_frames)\n      new_frame = driver.visualize(raw_frames[0], detections_bs[0], **kwargs)\n\n      if out_ptr:\n        # write frame into output file.\n        out_ptr.write(new_frame)\n      else:\n        # show the frame online, mainly used for real-time speed test.\n        cv2.imshow('Frame', new_frame)\n        # Press Q on keyboard to  exit\n        if cv2.waitKey(1) & 0xFF == ord('q'):\n          break\n\n  def inference_single_image(self, image_image_path, output_dir, **kwargs):\n    driver = inference.InferenceDriver(self.model_name, self.ckpt_path,\n                                       self.model_config.as_dict())\n    driver.inference(image_image_path, output_dir, **kwargs)\n\n  def build_and_save_model(self):\n    \"\"\"build and save the model into self.logdir.\"\"\"\n    with tf.Graph().as_default(), tf.Session() as sess:\n      # Build model with inputs and labels.\n      inputs = tf.placeholder(tf.float32, name='input', shape=self.inputs_shape)\n      outputs = self.build_model(inputs)\n\n      # Run the model\n      inputs_val = np.random.rand(*self.inputs_shape).astype(float)\n      labels_val = np.zeros(self.labels_shape).astype(np.int64)\n      labels_val[:, 0] = 1\n\n      if self.ckpt_path:\n        # Load the true weights if available.\n        inference.restore_ckpt(sess, self.ckpt_path,\n                               self.model_config.moving_average_decay,\n                               self.export_ckpt)\n      else:\n        sess.run(tf.global_variables_initializer())\n        # Run a single train step.\n        sess.run(outputs, feed_dict={inputs: inputs_val})\n\n      all_saver = tf.train.Saver(save_relative_paths=True)\n      all_saver.save(sess, os.path.join(self.logdir, self.model_name))\n\n      tf_graph = os.path.join(self.logdir, self.model_name + '_train.pb')\n      with tf.io.gfile.GFile(tf_graph, 'wb') as f:\n        f.write(sess.graph_def.SerializeToString())\n\n  def eval_ckpt(self):\n    \"\"\"build and save the model into self.logdir.\"\"\"\n    with tf.Graph().as_default(), tf.Session() as sess:\n      # Build model with inputs and labels.\n      inputs = tf.placeholder(tf.float32, name='input', shape=self.inputs_shape)\n      self.build_model(inputs)\n      inference.restore_ckpt(sess, self.ckpt_path,\n                             self.model_config.moving_average_decay,\n                             self.export_ckpt)\n\n  def freeze_model(self) -> Tuple[Text, Text]:\n    \"\"\"Freeze model and convert them into tflite and tf graph.\"\"\"\n    with tf.Graph().as_default(), tf.Session() as sess:\n      inputs = tf.placeholder(tf.float32, name='input', shape=self.inputs_shape)\n      outputs = self.build_model(inputs)\n\n      if self.ckpt_path:\n        # Load the true weights if available.\n        inference.restore_ckpt(sess, self.ckpt_path,\n                               self.model_config.moving_average_decay,\n                               self.export_ckpt)\n      else:\n        # Load random weights if not checkpoint is not available.\n        self.build_and_save_model()\n        checkpoint = tf.train.latest_checkpoint(self.logdir)\n        logging.info('Loading checkpoint: %s', checkpoint)\n        saver = tf.train.Saver()\n        saver.restore(sess, checkpoint)\n\n      # export frozen graph.\n      output_node_names = [node.op.name for node in outputs]\n      graphdef = tf.graph_util.convert_variables_to_constants(\n          sess, sess.graph_def, output_node_names)\n\n      tf_graph = os.path.join(self.logdir, self.model_name + '_frozen.pb')\n      tf.io.gfile.GFile(tf_graph, 'wb').write(graphdef.SerializeToString())\n\n      # export savaed model.\n      output_dict = {'class_predict_%d' % i: outputs[i] for i in range(5)}\n      output_dict.update({'box_predict_%d' % i: outputs[5+i] for i in range(5)})\n      signature_def_map = {\n          'serving_default':\n              tf.saved_model.predict_signature_def(\n                  {'input': inputs},\n                  output_dict,\n              )\n      }\n      output_dir = os.path.join(self.logdir, 'savedmodel')\n      b = tf.saved_model.Builder(output_dir)\n      b.add_meta_graph_and_variables(\n          sess,\n          tags=['serve'],\n          signature_def_map=signature_def_map,\n          assets_collection=tf.get_collection(tf.GraphKeys.ASSET_FILEPATHS),\n          clear_devices=True)\n      b.save()\n      logging.info('Model saved at %s', output_dir)\n\n    return graphdef\n\n  def benchmark_model(self,\n                      warmup_runs,\n                      bm_runs,\n                      num_threads,\n                      trace_filename=None):\n    \"\"\"Benchmark model.\"\"\"\n    if self.tensorrt:\n      print('Using tensorrt ', self.tensorrt)\n      graphdef = self.freeze_model()\n\n    if num_threads > 0:\n      print('num_threads for benchmarking: {}'.format(num_threads))\n      sess_config = tf.ConfigProto(\n          intra_op_parallelism_threads=num_threads,\n          inter_op_parallelism_threads=1)\n    else:\n      sess_config = tf.ConfigProto()\n\n    sess_config.graph_options.rewrite_options.dependency_optimization = 2\n    if self.use_xla:\n      sess_config.graph_options.optimizer_options.global_jit_level = (\n          tf.OptimizerOptions.ON_2)\n\n    with tf.Graph().as_default(), tf.Session(config=sess_config) as sess:\n      inputs = tf.placeholder(tf.float32, name='input', shape=self.inputs_shape)\n      output = self.build_model(inputs)\n\n      img = np.random.uniform(size=self.inputs_shape)\n\n      sess.run(tf.global_variables_initializer())\n      if self.tensorrt:\n        fetches = [inputs.name] + [i.name for i in output]\n        goutput = self.convert_tr(graphdef, fetches)\n        inputs, output = goutput[0], goutput[1:]\n\n      if not self.use_xla:\n        # Don't use tf.group because XLA removes the whole graph for tf.group.\n        output = tf.group(*output)\n      else:\n        output = tf.add_n([tf.reduce_sum(x) for x in output])\n\n      output_name = [output.name]\n      input_name = inputs.name\n      graphdef = tf.graph_util.convert_variables_to_constants(\n          sess, sess.graph_def, output_name)\n\n    with tf.Graph().as_default(), tf.Session(config=sess_config) as sess:\n      tf.import_graph_def(graphdef, name='')\n\n      for i in range(warmup_runs):\n        start_time = time.time()\n        sess.run(output_name, feed_dict={input_name: img})\n        logging.info('Warm up: {} {:.4f}s'.format(i, time.time() - start_time))\n\n      print('Start benchmark runs total={}'.format(bm_runs))\n      start = time.perf_counter()\n      for i in range(bm_runs):\n        sess.run(output_name, feed_dict={input_name: img})\n      end = time.perf_counter()\n      inference_time = (end - start) / bm_runs\n      print('Per batch inference time: ', inference_time)\n      print('FPS: ', self.batch_size / inference_time)\n\n      if trace_filename:\n        run_options = tf.RunOptions()\n        run_options.trace_level = tf.RunOptions.FULL_TRACE\n        run_metadata = tf.RunMetadata()\n        sess.run(\n            output_name,\n            feed_dict={input_name: img},\n            options=run_options,\n            run_metadata=run_metadata)\n        logging.info('Dumping trace to %s', trace_filename)\n        trace_dir = os.path.dirname(trace_filename)\n        if not tf.io.gfile.exists(trace_dir):\n          tf.io.gfile.makedirs(trace_dir)\n        with tf.io.gfile.GFile(trace_filename, 'w') as trace_file:\n          trace = timeline.Timeline(step_stats=run_metadata.step_stats)\n          trace_file.write(trace.generate_chrome_trace_format(show_memory=True))\n\n  def convert_tr(self, graph_def, fetches):\n    \"\"\"Convert to TensorRT.\"\"\"\n    from tensorflow.python.compiler.tensorrt import trt  # pylint: disable=g-direct-tensorflow-import,g-import-not-at-top\n    converter = trt.TrtGraphConverter(\n        nodes_denylist=[t.split(':')[0] for t in fetches],\n        input_graph_def=graph_def,\n        precision_mode=self.tensorrt)\n    infer_graph = converter.convert()\n    goutput = tf.import_graph_def(infer_graph, return_elements=fetches)\n    return goutput\n\n  def run_model(self, runmode, **kwargs):\n    \"\"\"Run the model on devices.\"\"\"\n    if runmode == 'dry':\n      self.build_and_save_model()\n    elif runmode == 'freeze':\n      self.freeze_model()\n    elif runmode == 'ckpt':\n      self.eval_ckpt()\n    elif runmode == 'saved_model_benchmark':\n      self.saved_model_benchmark(\n          kwargs['input_image'],\n          trace_filename=kwargs.get('trace_filename', None))\n    elif runmode in ('infer', 'saved_model', 'saved_model_infer',\n                     'saved_model_video'):\n      config_dict = {}\n      if kwargs.get('line_thickness', None):\n        config_dict['line_thickness'] = kwargs.get('line_thickness')\n      if kwargs.get('max_boxes_to_draw', None):\n        config_dict['max_boxes_to_draw'] = kwargs.get('max_boxes_to_draw')\n      if kwargs.get('min_score_thresh', None):\n        config_dict['min_score_thresh'] = kwargs.get('min_score_thresh')\n\n      if runmode == 'saved_model':\n        self.export_saved_model(**config_dict)\n      elif runmode == 'infer':\n        self.inference_single_image(kwargs['input_image'],\n                                    kwargs['output_image_dir'], **config_dict)\n      elif runmode == 'saved_model_infer':\n        self.saved_model_inference(kwargs['input_image'],\n                                   kwargs['output_image_dir'], **config_dict)\n      elif runmode == 'saved_model_video':\n        self.saved_model_video(kwargs['input_video'], kwargs['output_video'],\n                               **config_dict)\n    elif runmode == 'bm':\n      self.benchmark_model(\n          warmup_runs=5,\n          bm_runs=kwargs.get('bm_runs', 10),\n          num_threads=kwargs.get('threads', 0),\n          trace_filename=kwargs.get('trace_filename', None))\n    else:\n      raise ValueError('Unkown runmode {}'.format(runmode))\n\n\ndef main(_):\n  if tf.io.gfile.exists(FLAGS.logdir) and FLAGS.delete_logdir:\n    logging.info('Deleting log dir ...')\n    tf.io.gfile.rmtree(FLAGS.logdir)\n\n  inspector = ModelInspector(\n      model_name=FLAGS.model_name,\n      logdir=FLAGS.logdir,\n      tensorrt=FLAGS.tensorrt,\n      use_xla=FLAGS.use_xla,\n      ckpt_path=FLAGS.ckpt_path,\n      export_ckpt=FLAGS.export_ckpt,\n      saved_model_dir=FLAGS.saved_model_dir,\n      tflite_path=FLAGS.tflite_path,\n      batch_size=FLAGS.batch_size,\n      hparams=FLAGS.hparams,\n      score_thresh=FLAGS.min_score_thresh,\n      max_output_size=FLAGS.max_boxes_to_draw,\n      nms_method=FLAGS.nms_method)\n  inspector.run_model(\n      FLAGS.runmode,\n      input_image=FLAGS.input_image,\n      output_image_dir=FLAGS.output_image_dir,\n      input_video=FLAGS.input_video,\n      output_video=FLAGS.output_video,\n      line_thickness=FLAGS.line_thickness,\n      max_boxes_to_draw=FLAGS.max_boxes_to_draw,\n      min_score_thresh=FLAGS.min_score_thresh,\n      nms_method=FLAGS.nms_method,\n      bm_runs=FLAGS.bm_runs,\n      threads=FLAGS.threads,\n      trace_filename=FLAGS.trace_filename)\n\n\nif __name__ == '__main__':\n  logging.set_verbosity(logging.WARNING)\n  tf.enable_v2_tensorshape()\n  tf.disable_eager_execution()\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/nms_np.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Anchor definition.\"\"\"\nimport numpy as np\n\n# The minimum score to consider a logit for identifying detections.\nMIN_CLASS_SCORE = -5.0\n\n# The score for a dummy detection\n_DUMMY_DETECTION_SCORE = -1e5\n\n# The maximum number of (anchor,class) pairs to keep for non-max suppression.\nMAX_DETECTION_POINTS = 5000\n\n\ndef diou_nms(dets, iou_thresh=None):\n  \"\"\"DIOU non-maximum suppression.\n\n  diou = iou - square of euclidian distance of box centers\n     / square of diagonal of smallest enclosing bounding box\n\n  Reference: https://arxiv.org/pdf/1911.08287.pdf\n\n  Args:\n    dets: detection with shape (num, 5) and format [x1, y1, x2, y2, score].\n    iou_thresh: IOU threshold,\n\n  Returns:\n    numpy.array: Retained boxes.\n  \"\"\"\n  iou_thresh = iou_thresh or 0.5\n  x1 = dets[:, 0]\n  y1 = dets[:, 1]\n  x2 = dets[:, 2]\n  y2 = dets[:, 3]\n  scores = dets[:, 4]\n\n  areas = (x2 - x1 + 1) * (y2 - y1 + 1)\n  order = scores.argsort()[::-1]\n\n  center_x = (x1 + x2) / 2\n  center_y = (y1 + y2) / 2\n\n  keep = []\n  while order.size > 0:\n    i = order[0]\n    keep.append(i)\n    xx1 = np.maximum(x1[i], x1[order[1:]])\n    yy1 = np.maximum(y1[i], y1[order[1:]])\n    xx2 = np.minimum(x2[i], x2[order[1:]])\n    yy2 = np.minimum(y2[i], y2[order[1:]])\n\n    w = np.maximum(0.0, xx2 - xx1 + 1)\n    h = np.maximum(0.0, yy2 - yy1 + 1)\n    intersection = w * h\n    iou = intersection / (areas[i] + areas[order[1:]] - intersection)\n\n    smallest_enclosing_box_x1 = np.minimum(x1[i], x1[order[1:]])\n    smallest_enclosing_box_x2 = np.maximum(x2[i], x2[order[1:]])\n    smallest_enclosing_box_y1 = np.minimum(y1[i], y1[order[1:]])\n    smallest_enclosing_box_y2 = np.maximum(y2[i], y2[order[1:]])\n\n    square_of_the_diagonal = (\n        (smallest_enclosing_box_x2 - smallest_enclosing_box_x1)**2 +\n        (smallest_enclosing_box_y2 - smallest_enclosing_box_y1)**2)\n\n    square_of_center_distance = ((center_x[i] - center_x[order[1:]])**2 +\n                                 (center_y[i] - center_y[order[1:]])**2)\n\n    # Add 1e-10 for numerical stability.\n    diou = iou - square_of_center_distance / (square_of_the_diagonal  + 1e-10)\n    inds = np.where(diou <= iou_thresh)[0]\n    order = order[inds + 1]\n  return dets[keep]\n\n\ndef hard_nms(dets, iou_thresh=None):\n  \"\"\"The basic hard non-maximum suppression.\n\n  Args:\n    dets: detection with shape (num, 5) and format [x1, y1, x2, y2, score].\n    iou_thresh: IOU threshold,\n\n  Returns:\n    numpy.array: Retained boxes.\n  \"\"\"\n  iou_thresh = iou_thresh or 0.5\n  x1 = dets[:, 0]\n  y1 = dets[:, 1]\n  x2 = dets[:, 2]\n  y2 = dets[:, 3]\n  scores = dets[:, 4]\n\n  areas = (x2 - x1 + 1) * (y2 - y1 + 1)\n  order = scores.argsort()[::-1]\n\n  keep = []\n  while order.size > 0:\n    i = order[0]\n    keep.append(i)\n    xx1 = np.maximum(x1[i], x1[order[1:]])\n    yy1 = np.maximum(y1[i], y1[order[1:]])\n    xx2 = np.minimum(x2[i], x2[order[1:]])\n    yy2 = np.minimum(y2[i], y2[order[1:]])\n\n    w = np.maximum(0.0, xx2 - xx1 + 1)\n    h = np.maximum(0.0, yy2 - yy1 + 1)\n    intersection = w * h\n    overlap = intersection / (areas[i] + areas[order[1:]] - intersection)\n\n    inds = np.where(overlap <= iou_thresh)[0]\n    order = order[inds + 1]\n\n  return dets[keep]\n\n\ndef soft_nms(dets, nms_configs):\n  \"\"\"Soft non-maximum suppression.\n\n  [1] Soft-NMS -- Improving Object Detection With One Line of Code.\n    https://arxiv.org/abs/1704.04503\n\n  Args:\n    dets: detection with shape (num, 5) and format [x1, y1, x2, y2, score].\n    nms_configs: a dict config that may contain the following members\n      * method: one of {`linear`, `gaussian`, 'hard'}. Use `gaussian` if None.\n      * iou_thresh (float): IOU threshold, only for `linear`, `hard`.\n      * sigma: Gaussian parameter, only for method 'gaussian'.\n      * score_thresh (float): Box score threshold for final boxes.\n\n  Returns:\n    numpy.array: Retained boxes.\n  \"\"\"\n  method = nms_configs['method']\n  # Default sigma and iou_thresh are from the original soft-nms paper.\n  sigma = nms_configs['sigma'] or 0.5\n  iou_thresh = nms_configs['iou_thresh'] or 0.3\n  score_thresh = nms_configs['score_thresh'] or 0.001\n\n  x1 = dets[:, 0]\n  y1 = dets[:, 1]\n  x2 = dets[:, 2]\n  y2 = dets[:, 3]\n\n  areas = (x2 - x1 + 1) * (y2 - y1 + 1)\n  # expand dets with areas, and the second dimension is\n  # x1, y1, x2, y2, score, area\n  dets = np.concatenate((dets, areas[:, None]), axis=1)\n\n  retained_box = []\n  while dets.size > 0:\n    max_idx = np.argmax(dets[:, 4], axis=0)\n    dets[[0, max_idx], :] = dets[[max_idx, 0], :]\n    retained_box.append(dets[0, :-1])\n\n    xx1 = np.maximum(dets[0, 0], dets[1:, 0])\n    yy1 = np.maximum(dets[0, 1], dets[1:, 1])\n    xx2 = np.minimum(dets[0, 2], dets[1:, 2])\n    yy2 = np.minimum(dets[0, 3], dets[1:, 3])\n\n    w = np.maximum(xx2 - xx1 + 1, 0.0)\n    h = np.maximum(yy2 - yy1 + 1, 0.0)\n    inter = w * h\n    iou = inter / (dets[0, 5] + dets[1:, 5] - inter)\n\n    if method == 'linear':\n      weight = np.ones_like(iou)\n      weight[iou > iou_thresh] -= iou[iou > iou_thresh]\n    elif method == 'gaussian':\n      weight = np.exp(-(iou * iou) / sigma)\n    else:  # traditional nms\n      weight = np.ones_like(iou)\n      weight[iou > iou_thresh] = 0\n\n    dets[1:, 4] *= weight\n    retained_idx = np.where(dets[1:, 4] >= score_thresh)[0]\n    dets = dets[retained_idx + 1, :]\n\n  return np.vstack(retained_box)\n\n\ndef nms(dets, nms_configs):\n  \"\"\"Non-maximum suppression.\n\n  Args:\n    dets: detection with shape (num, 5) and format [x1, y1, x2, y2, score].\n    nms_configs: a dict config that may contain parameters.\n\n  Returns:\n    numpy.array: Retained boxes.\n  \"\"\"\n\n  nms_configs = nms_configs or {}\n  method = nms_configs['method']\n\n  if method == 'hard' or not method:\n    return hard_nms(dets, nms_configs['iou_thresh'])\n\n  if method == 'diou':\n    return diou_nms(dets, nms_configs['iou_thresh'])\n\n  if method in ('linear', 'gaussian'):\n    return soft_nms(dets, nms_configs)\n\n  raise ValueError('Unknown NMS method: {}'.format(method))\n\n\ndef per_class_nms(boxes, scores, classes, image_id, image_scale, num_classes,\n                  max_boxes_to_draw, nms_configs):\n  \"\"\"Perform per class nms.\"\"\"\n  boxes = boxes[:, [1, 0, 3, 2]]\n  detections = []\n  for c in range(num_classes):\n    indices = np.where(classes == c)[0]\n    if indices.shape[0] == 0:\n      continue\n    boxes_cls = boxes[indices, :]\n    scores_cls = scores[indices]\n    # Select top-scoring boxes in each class and apply non-maximum suppression\n    # (nms) for boxes in the same class. The selected boxes from each class are\n    # then concatenated for the final detection outputs.\n    all_detections_cls = np.column_stack((boxes_cls, scores_cls))\n    top_detections_cls = nms(all_detections_cls, nms_configs)\n    top_detections_cls = np.column_stack(\n        (np.repeat(image_id, len(top_detections_cls)),\n         top_detections_cls,\n         np.repeat(c + 1, len(top_detections_cls)))\n    )\n    detections.append(top_detections_cls)\n\n  def _generate_dummy_detections(number):\n    detections_dummy = np.zeros((number, 7), dtype=np.float32)\n    detections_dummy[:, 0] = image_id[0]\n    detections_dummy[:, 5] = _DUMMY_DETECTION_SCORE\n    return detections_dummy\n\n  if detections:\n    detections = np.vstack(detections)\n    # take final 100 detections\n    indices = np.argsort(-detections[:, -2])\n    detections = np.array(\n        detections[indices[0:max_boxes_to_draw]], dtype=np.float32)\n    # Add dummy detections to fill up to 100 detections\n    n = max(max_boxes_to_draw - len(detections), 0)\n    detections_dummy = _generate_dummy_detections(n)\n    detections = np.vstack([detections, detections_dummy])\n  else:\n    detections = _generate_dummy_detections(max_boxes_to_draw)\n\n  detections[:, 1:5] *= image_scale\n\n  return detections\n\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/object_detection/__init__.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/object_detection/argmax_matcher.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Argmax matcher implementation.\n\nThis class takes a similarity matrix and matches columns to rows based on the\nmaximum value per column. One can specify matched_thresholds and\nto prevent columns from matching to rows (generally resulting in a negative\ntraining example) and unmatched_theshold to ignore the match (generally\nresulting in neither a positive or negative training example).\n\nThis matcher is used in Fast(er)-RCNN.\n\nNote: matchers are used in TargetAssigners. There is a create_target_assigner\nfactory function for popular implementations.\n\"\"\"\nimport tensorflow.compat.v1 as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.object_detection import matcher\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.object_detection import shape_utils\n\n\nclass ArgMaxMatcher(matcher.Matcher):\n  \"\"\"Matcher based on highest value.\n\n  This class computes matches from a similarity matrix. Each column is matched\n  to a single row.\n\n  To support object detection target assignment this class enables setting both\n  matched_threshold (upper threshold) and unmatched_threshold (lower thresholds)\n  defining three categories of similarity which define whether examples are\n  positive, negative, or ignored:\n  (1) similarity >= matched_threshold: Highest similarity. Matched/Positive!\n  (2) matched_threshold > similarity >= unmatched_threshold: Medium similarity.\n          Depending on negatives_lower_than_unmatched, this is either\n          Unmatched/Negative OR Ignore.\n  (3) unmatched_threshold > similarity: Lowest similarity. Depending on flag\n          negatives_lower_than_unmatched, either Unmatched/Negative OR Ignore.\n  For ignored matches this class sets the values in the Match object to -2.\n  \"\"\"\n\n  def __init__(self,\n               matched_threshold,\n               unmatched_threshold=None,\n               negatives_lower_than_unmatched=True,\n               force_match_for_each_row=False):\n    \"\"\"Construct ArgMaxMatcher.\n\n    Args:\n      matched_threshold: Threshold for positive matches. Positive if\n        sim >= matched_threshold, where sim is the maximum value of the\n        similarity matrix for a given column. Set to None for no threshold.\n      unmatched_threshold: Threshold for negative matches. Negative if\n        sim < unmatched_threshold. Defaults to matched_threshold\n        when set to None.\n      negatives_lower_than_unmatched: Boolean which defaults to True. If True\n        then negative matches are the ones below the unmatched_threshold,\n        whereas ignored matches are in between the matched and unmatched\n        threshold. If False, then negative matches are in between the matched\n        and unmatched threshold, and everything lower than unmatched is ignored.\n      force_match_for_each_row: If True, ensures that each row is matched to\n        at least one column (which is not guaranteed otherwise if the\n        matched_threshold is high). Defaults to False. See\n        argmax_matcher_test.testMatcherForceMatch() for an example.\n\n    Raises:\n      ValueError: if unmatched_threshold is set but matched_threshold is not set\n        or if unmatched_threshold > matched_threshold.\n    \"\"\"\n    if (matched_threshold is None) and (unmatched_threshold is not None):\n      raise ValueError('Need to also define matched_threshold when'\n                       'unmatched_threshold is defined')\n    self._matched_threshold = matched_threshold\n    if unmatched_threshold is None:\n      self._unmatched_threshold = matched_threshold\n    else:\n      if unmatched_threshold > matched_threshold:\n        raise ValueError('unmatched_threshold needs to be smaller or equal'\n                         'to matched_threshold')\n      self._unmatched_threshold = unmatched_threshold\n    if not negatives_lower_than_unmatched:\n      if self._unmatched_threshold == self._matched_threshold:\n        raise ValueError('When negatives are in between matched and '\n                         'unmatched thresholds, these cannot be of equal '\n                         'value. matched: %s, unmatched: %s',\n                         self._matched_threshold, self._unmatched_threshold)\n    self._force_match_for_each_row = force_match_for_each_row\n    self._negatives_lower_than_unmatched = negatives_lower_than_unmatched\n\n  def _match(self, similarity_matrix):\n    \"\"\"Tries to match each column of the similarity matrix to a row.\n\n    Args:\n      similarity_matrix: tensor of shape [N, M] representing any similarity\n        metric.\n\n    Returns:\n      Match object with corresponding matches for each of M columns.\n    \"\"\"\n\n    def _match_when_rows_are_empty():\n      \"\"\"Performs matching when the rows of similarity matrix are empty.\n\n      When the rows are empty, all detections are false positives. So we return\n      a tensor of -1's to indicate that the columns do not match to any rows.\n\n      Returns:\n        matches:  int32 tensor indicating the row each column matches to.\n      \"\"\"\n      similarity_matrix_shape = shape_utils.combined_static_and_dynamic_shape(\n          similarity_matrix)\n      return -1 * tf.ones([similarity_matrix_shape[1]], dtype=tf.int32)\n\n    def _match_when_rows_are_non_empty():\n      \"\"\"Performs matching when the rows of similarity matrix are non empty.\n\n      Returns:\n        matches:  int32 tensor indicating the row each column matches to.\n      \"\"\"\n      # Matches for each column\n      matches = tf.argmax(similarity_matrix, 0, output_type=tf.int32)\n\n      # Deal with matched and unmatched threshold\n      if self._matched_threshold is not None:\n        # Get logical indices of ignored and unmatched columns as tf.int64\n        matched_vals = tf.reduce_max(similarity_matrix, 0)\n        below_unmatched_threshold = tf.greater(self._unmatched_threshold,\n                                               matched_vals)\n        between_thresholds = tf.logical_and(\n            tf.greater_equal(matched_vals, self._unmatched_threshold),\n            tf.greater(self._matched_threshold, matched_vals))\n\n        if self._negatives_lower_than_unmatched:\n          matches = self._set_values_using_indicator(matches,\n                                                     below_unmatched_threshold,\n                                                     -1)\n          matches = self._set_values_using_indicator(matches,\n                                                     between_thresholds,\n                                                     -2)\n        else:\n          matches = self._set_values_using_indicator(matches,\n                                                     below_unmatched_threshold,\n                                                     -2)\n          matches = self._set_values_using_indicator(matches,\n                                                     between_thresholds,\n                                                     -1)\n\n      if self._force_match_for_each_row:\n        similarity_matrix_shape = shape_utils.combined_static_and_dynamic_shape(\n            similarity_matrix)\n        force_match_column_ids = tf.argmax(similarity_matrix, 1,\n                                           output_type=tf.int32)\n        force_match_column_indicators = tf.one_hot(\n            force_match_column_ids, depth=similarity_matrix_shape[1])\n        force_match_row_ids = tf.argmax(force_match_column_indicators, 0,\n                                        output_type=tf.int32)\n        force_match_column_mask = tf.cast(\n            tf.reduce_max(force_match_column_indicators, 0), tf.bool)\n        final_matches = tf.where(force_match_column_mask,\n                                 force_match_row_ids, matches)\n        return final_matches\n      else:\n        return matches\n\n    if similarity_matrix.shape.is_fully_defined():\n      if similarity_matrix.shape[0] == 0:\n        return _match_when_rows_are_empty()\n      else:\n        return _match_when_rows_are_non_empty()\n    else:\n      return tf.cond(\n          tf.greater(tf.shape(similarity_matrix)[0], 0),\n          _match_when_rows_are_non_empty, _match_when_rows_are_empty)\n\n  def _set_values_using_indicator(self, x, indicator, val):\n    \"\"\"Set the indicated fields of x to val.\n\n    Args:\n      x: tensor.\n      indicator: boolean with same shape as x.\n      val: scalar with value to set.\n\n    Returns:\n      modified tensor.\n    \"\"\"\n    indicator = tf.cast(indicator, x.dtype)\n    return x * (1 - indicator) + val * indicator\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/object_detection/box_coder.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Base box coder.\n\nBox coders convert between coordinate frames, namely image-centric\n(with (0,0) on the top left of image) and anchor-centric (with (0,0) being\ndefined by a specific anchor).\n\nUsers of a BoxCoder can call two methods:\n encode: which encodes a box with respect to a given anchor\n  (or rather, a tensor of boxes wrt a corresponding tensor of anchors) and\n decode: which inverts this encoding with a decode operation.\nIn both cases, the arguments are assumed to be in 1-1 correspondence already;\nit is not the job of a BoxCoder to perform matching.\n\"\"\"\nfrom abc import ABCMeta\nfrom abc import abstractmethod\nfrom abc import abstractproperty\n\nimport tensorflow.compat.v1 as tf\n\n\n# Box coder types.\nFASTER_RCNN = 'faster_rcnn'\nKEYPOINT = 'keypoint'\nMEAN_STDDEV = 'mean_stddev'\nSQUARE = 'square'\n\n\nclass BoxCoder(object):\n  \"\"\"Abstract base class for box coder.\"\"\"\n  __metaclass__ = ABCMeta\n\n  @abstractproperty\n  def code_size(self):\n    \"\"\"Return the size of each code.\n\n    This number is a constant and should agree with the output of the `encode`\n    op (e.g. if rel_codes is the output of self.encode(...), then it should have\n    shape [N, code_size()]).  This abstractproperty should be overridden by\n    implementations.\n\n    Returns:\n      an integer constant\n    \"\"\"\n    pass\n\n  def encode(self, boxes, anchors):\n    \"\"\"Encode a box list relative to an anchor collection.\n\n    Args:\n      boxes: BoxList holding N boxes to be encoded\n      anchors: BoxList of N anchors\n\n    Returns:\n      a tensor representing N relative-encoded boxes\n    \"\"\"\n    with tf.name_scope('Encode'):\n      return self._encode(boxes, anchors)\n\n  def decode(self, rel_codes, anchors):\n    \"\"\"Decode boxes that are encoded relative to an anchor collection.\n\n    Args:\n      rel_codes: a tensor representing N relative-encoded boxes\n      anchors: BoxList of anchors\n\n    Returns:\n      boxlist: BoxList holding N boxes encoded in the ordinary way (i.e.,\n        with corners y_min, x_min, y_max, x_max)\n    \"\"\"\n    with tf.name_scope('Decode'):\n      return self._decode(rel_codes, anchors)\n\n  @abstractmethod\n  def _encode(self, boxes, anchors):\n    \"\"\"Method to be overridden by implementations.\n\n    Args:\n      boxes: BoxList holding N boxes to be encoded\n      anchors: BoxList of N anchors\n\n    Returns:\n      a tensor representing N relative-encoded boxes\n    \"\"\"\n    pass\n\n  @abstractmethod\n  def _decode(self, rel_codes, anchors):\n    \"\"\"Method to be overridden by implementations.\n\n    Args:\n      rel_codes: a tensor representing N relative-encoded boxes\n      anchors: BoxList of anchors\n\n    Returns:\n      boxlist: BoxList holding N boxes encoded in the ordinary way (i.e.,\n        with corners y_min, x_min, y_max, x_max)\n    \"\"\"\n    pass\n\n\ndef batch_decode(encoded_boxes, box_coder, anchors):\n  \"\"\"Decode a batch of encoded boxes.\n\n  This op takes a batch of encoded bounding boxes and transforms\n  them to a batch of bounding boxes specified by their corners in\n  the order of [y_min, x_min, y_max, x_max].\n\n  Args:\n    encoded_boxes: a float32 tensor of shape [batch_size, num_anchors,\n      code_size] representing the location of the objects.\n    box_coder: a BoxCoder object.\n    anchors: a BoxList of anchors used to encode `encoded_boxes`.\n\n  Returns:\n    decoded_boxes: a float32 tensor of shape [batch_size, num_anchors,\n      coder_size] representing the corners of the objects in the order\n      of [y_min, x_min, y_max, x_max].\n\n  Raises:\n    ValueError: if batch sizes of the inputs are inconsistent, or if\n    the number of anchors inferred from encoded_boxes and anchors are\n    inconsistent.\n  \"\"\"\n  encoded_boxes.get_shape().assert_has_rank(3)\n  if encoded_boxes.get_shape()[1].value != anchors.num_boxes_static():\n    raise ValueError('The number of anchors inferred from encoded_boxes'\n                     ' and anchors are inconsistent: shape[1] of encoded_boxes'\n                     ' %s should be equal to the number of anchors: %s.' %\n                     (encoded_boxes.get_shape()[1].value,\n                      anchors.num_boxes_static()))\n\n  decoded_boxes = tf.stack([\n      box_coder.decode(boxes, anchors).get()\n      for boxes in tf.unstack(encoded_boxes)\n  ])\n  return decoded_boxes\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/object_detection/box_list.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Bounding Box List definition.\n\nBoxList represents a list of bounding boxes as tensorflow\ntensors, where each bounding box is represented as a row of 4 numbers,\n[y_min, x_min, y_max, x_max].  It is assumed that all bounding boxes\nwithin a given list correspond to a single image.  See also\nbox_list_ops.py for common box related operations (such as area, iou, etc).\n\nOptionally, users can add additional related fields (such as weights).\nWe assume the following things to be true about fields:\n* they correspond to boxes in the box_list along the 0th dimension\n* they have inferable rank at graph construction time\n* all dimensions except for possibly the 0th can be inferred\n  (i.e., not None) at graph construction time.\n\nSome other notes:\n  * Following tensorflow conventions, we use height, width ordering,\n  and correspondingly, y,x (or ymin, xmin, ymax, xmax) ordering\n  * Tensors are always provided as (flat) [N, 4] tensors.\n\"\"\"\n\nimport tensorflow.compat.v1 as tf\n\n\nclass BoxList(object):\n  \"\"\"Box collection.\"\"\"\n\n  def __init__(self, boxes):\n    \"\"\"Constructs box collection.\n\n    Args:\n      boxes: a tensor of shape [N, 4] representing box corners\n\n    Raises:\n      ValueError: if invalid dimensions for bbox data or if bbox data is not in\n          float32 format.\n    \"\"\"\n    if len(boxes.get_shape()) != 2 or boxes.get_shape()[-1] != 4:\n      raise ValueError('Invalid dimensions for box data.')\n    if boxes.dtype != tf.float32:\n      raise ValueError('Invalid tensor type: should be tf.float32')\n    self.data = {'boxes': boxes}\n\n  def num_boxes(self):\n    \"\"\"Returns number of boxes held in collection.\n\n    Returns:\n      a tensor representing the number of boxes held in the collection.\n    \"\"\"\n    return tf.shape(self.data['boxes'])[0]\n\n  def num_boxes_static(self):\n    \"\"\"Returns number of boxes held in collection.\n\n    This number is inferred at graph construction time rather than run-time.\n\n    Returns:\n      Number of boxes held in collection (integer) or None if this is not\n        inferable at graph construction time.\n    \"\"\"\n    return self.data['boxes'].get_shape().as_list()[0]\n\n  def get_all_fields(self):\n    \"\"\"Returns all fields.\"\"\"\n    return self.data.keys()\n\n  def get_extra_fields(self):\n    \"\"\"Returns all non-box fields (i.e., everything not named 'boxes').\"\"\"\n    return [k for k in self.data.keys() if k != 'boxes']\n\n  def add_field(self, field, field_data):\n    \"\"\"Add field to box list.\n\n    This method can be used to add related box data such as\n    weights/labels, etc.\n\n    Args:\n      field: a string key to access the data via `get`\n      field_data: a tensor containing the data to store in the BoxList\n    \"\"\"\n    self.data[field] = field_data\n\n  def has_field(self, field):\n    return field in self.data\n\n  def get(self):\n    \"\"\"Convenience function for accessing box coordinates.\n\n    Returns:\n      a tensor with shape [N, 4] representing box coordinates.\n    \"\"\"\n    return self.get_field('boxes')\n\n  def set(self, boxes):\n    \"\"\"Convenience function for setting box coordinates.\n\n    Args:\n      boxes: a tensor of shape [N, 4] representing box corners\n\n    Raises:\n      ValueError: if invalid dimensions for bbox data\n    \"\"\"\n    if len(boxes.get_shape()) != 2 or boxes.get_shape()[-1] != 4:\n      raise ValueError('Invalid dimensions for box data.')\n    self.data['boxes'] = boxes\n\n  def get_field(self, field):\n    \"\"\"Accesses a box collection and associated fields.\n\n    This function returns specified field with object; if no field is specified,\n    it returns the box coordinates.\n\n    Args:\n      field: this optional string parameter can be used to specify\n        a related field to be accessed.\n\n    Returns:\n      a tensor representing the box collection or an associated field.\n\n    Raises:\n      ValueError: if invalid field\n    \"\"\"\n    if not self.has_field(field):\n      raise ValueError('field ' + str(field) + ' does not exist')\n    return self.data[field]\n\n  def set_field(self, field, value):\n    \"\"\"Sets the value of a field.\n\n    Updates the field of a box_list with a given value.\n\n    Args:\n      field: (string) name of the field to set value.\n      value: the value to assign to the field.\n\n    Raises:\n      ValueError: if the box_list does not have specified field.\n    \"\"\"\n    if not self.has_field(field):\n      raise ValueError('field %s does not exist' % field)\n    self.data[field] = value\n\n  def get_center_coordinates_and_sizes(self, scope=None):\n    \"\"\"Computes the center coordinates, height and width of the boxes.\n\n    Args:\n      scope: name scope of the function.\n\n    Returns:\n      a list of 4 1-D tensors [ycenter, xcenter, height, width].\n    \"\"\"\n    with tf.name_scope(scope, 'get_center_coordinates_and_sizes'):\n      box_corners = self.get()\n      ymin, xmin, ymax, xmax = tf.unstack(tf.transpose(box_corners))\n      width = xmax - xmin\n      height = ymax - ymin\n      ycenter = ymin + height / 2.\n      xcenter = xmin + width / 2.\n      return [ycenter, xcenter, height, width]\n\n  def transpose_coordinates(self, scope=None):\n    \"\"\"Transpose the coordinate representation in a boxlist.\n\n    Args:\n      scope: name scope of the function.\n    \"\"\"\n    with tf.name_scope(scope, 'transpose_coordinates'):\n      y_min, x_min, y_max, x_max = tf.split(\n          value=self.get(), num_or_size_splits=4, axis=1)\n      self.set(tf.concat([x_min, y_min, x_max, y_max], 1))\n\n  def as_tensor_dict(self, fields=None):\n    \"\"\"Retrieves specified fields as a dictionary of tensors.\n\n    Args:\n      fields: (optional) list of fields to return in the dictionary.\n        If None (default), all fields are returned.\n\n    Returns:\n      tensor_dict: A dictionary of tensors specified by fields.\n\n    Raises:\n      ValueError: if specified field is not contained in boxlist.\n    \"\"\"\n    tensor_dict = {}\n    if fields is None:\n      fields = self.get_all_fields()\n    for field in fields:\n      if not self.has_field(field):\n        raise ValueError('boxlist must contain all specified fields')\n      tensor_dict[field] = self.get_field(field)\n    return tensor_dict\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/object_detection/faster_rcnn_box_coder.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Faster RCNN box coder.\n\nFaster RCNN box coder follows the coding schema described below:\n  ty = (y - ya) / ha\n  tx = (x - xa) / wa\n  th = log(h / ha)\n  tw = log(w / wa)\n  where x, y, w, h denote the box's center coordinates, width and height\n  respectively. Similarly, xa, ya, wa, ha denote the anchor's center\n  coordinates, width and height. tx, ty, tw and th denote the anchor-encoded\n  center, width and height respectively.\n\n  See http://arxiv.org/abs/1506.01497 for details.\n\"\"\"\n\nimport tensorflow.compat.v1 as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.object_detection import box_coder\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.object_detection import box_list\n\nEPSILON = 1e-8\n\n\nclass FasterRcnnBoxCoder(box_coder.BoxCoder):\n  \"\"\"Faster RCNN box coder.\"\"\"\n\n  def __init__(self, scale_factors=None):\n    \"\"\"Constructor for FasterRcnnBoxCoder.\n\n    Args:\n      scale_factors: List of 4 positive scalars to scale ty, tx, th and tw.\n        If set to None, does not perform scaling. For Faster RCNN,\n        the open-source implementation recommends using [10.0, 10.0, 5.0, 5.0].\n    \"\"\"\n    if scale_factors:\n      assert len(scale_factors) == 4\n      for scalar in scale_factors:\n        assert scalar > 0\n    self._scale_factors = scale_factors\n\n  @property\n  def code_size(self):\n    return 4\n\n  def _encode(self, boxes, anchors):\n    \"\"\"Encode a box collection with respect to anchor collection.\n\n    Args:\n      boxes: BoxList holding N boxes to be encoded.\n      anchors: BoxList of anchors.\n\n    Returns:\n      a tensor representing N anchor-encoded boxes of the format\n      [ty, tx, th, tw].\n    \"\"\"\n    # Convert anchors to the center coordinate representation.\n    ycenter_a, xcenter_a, ha, wa = anchors.get_center_coordinates_and_sizes()\n    ycenter, xcenter, h, w = boxes.get_center_coordinates_and_sizes()\n    # Avoid NaN in division and log below.\n    ha = tf.maximum(EPSILON, ha)\n    wa = tf.maximum(EPSILON, wa)\n    h = tf.maximum(EPSILON, h)\n    w = tf.maximum(EPSILON, w)\n\n    tx = (xcenter - xcenter_a) / wa\n    ty = (ycenter - ycenter_a) / ha\n    tw = tf.log(w / wa)\n    th = tf.log(h / ha)\n    # Scales location targets as used in paper for joint training.\n    if self._scale_factors:\n      ty *= self._scale_factors[0]\n      tx *= self._scale_factors[1]\n      th *= self._scale_factors[2]\n      tw *= self._scale_factors[3]\n    return tf.transpose(tf.stack([ty, tx, th, tw]))\n\n  def _decode(self, rel_codes, anchors):\n    \"\"\"Decode relative codes to boxes.\n\n    Args:\n      rel_codes: a tensor representing N anchor-encoded boxes.\n      anchors: BoxList of anchors.\n\n    Returns:\n      boxes: BoxList holding N bounding boxes.\n    \"\"\"\n    ycenter_a, xcenter_a, ha, wa = anchors.get_center_coordinates_and_sizes()\n\n    ty, tx, th, tw = tf.unstack(tf.transpose(rel_codes))\n    if self._scale_factors:\n      ty /= self._scale_factors[0]\n      tx /= self._scale_factors[1]\n      th /= self._scale_factors[2]\n      tw /= self._scale_factors[3]\n    w = tf.exp(tw) * wa\n    h = tf.exp(th) * ha\n    ycenter = ty * ha + ycenter_a\n    xcenter = tx * wa + xcenter_a\n    ymin = ycenter - h / 2.\n    xmin = xcenter - w / 2.\n    ymax = ycenter + h / 2.\n    xmax = xcenter + w / 2.\n    return box_list.BoxList(tf.transpose(tf.stack([ymin, xmin, ymax, xmax])))\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/object_detection/matcher.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Matcher interface and Match class.\n\nThis module defines the Matcher interface and the Match object. The job of the\nmatcher is to match row and column indices based on the similarity matrix and\nother optional parameters. Each column is matched to at most one row. There\nare three possibilities for the matching:\n\n1) match: A column matches a row.\n2) no_match: A column does not match any row.\n3) ignore: A column that is neither 'match' nor no_match.\n\nThe ignore case is regularly encountered in object detection: when an anchor has\na relatively small overlap with a ground-truth box, one neither wants to\nconsider this box a positive example (match) nor a negative example (no match).\n\nThe Match class is used to store the match results and it provides simple apis\nto query the results.\n\"\"\"\nimport abc\nimport tensorflow.compat.v1 as tf\n\n\nclass Match(object):\n  \"\"\"Class to store results from the matcher.\n\n  This class is used to store the results from the matcher. It provides\n  convenient methods to query the matching results.\n  \"\"\"\n\n  def __init__(self, match_results):\n    \"\"\"Constructs a Match object.\n\n    Args:\n      match_results: Integer tensor of shape [N] with (1) match_results[i]>=0,\n        meaning that column i is matched with row match_results[i].\n        (2) match_results[i]=-1, meaning that column i is not matched.\n        (3) match_results[i]=-2, meaning that column i is ignored.\n\n    Raises:\n      ValueError: if match_results does not have rank 1 or is not an\n        integer int32 scalar tensor\n    \"\"\"\n    if match_results.shape.ndims != 1:\n      raise ValueError('match_results should have rank 1')\n    if match_results.dtype != tf.int32:\n      raise ValueError('match_results should be an int32 or int64 scalar '\n                       'tensor')\n    self._match_results = match_results\n\n  @property\n  def match_results(self):\n    \"\"\"The accessor for match results.\n\n    Returns:\n      the tensor which encodes the match results.\n    \"\"\"\n    return self._match_results\n\n  def matched_column_indices(self):\n    \"\"\"Returns column indices that match to some row.\n\n    The indices returned by this op are always sorted in increasing order.\n\n    Returns:\n      column_indices: int32 tensor of shape [K] with column indices.\n    \"\"\"\n    return self._reshape_and_cast(tf.where(tf.greater(self._match_results, -1)))\n\n  def matched_column_indicator(self):\n    \"\"\"Returns column indices that are matched.\n\n    Returns:\n      column_indices: int32 tensor of shape [K] with column indices.\n    \"\"\"\n    return tf.greater_equal(self._match_results, 0)\n\n  def num_matched_columns(self):\n    \"\"\"Returns number (int32 scalar tensor) of matched columns.\"\"\"\n    return tf.shape(self.matched_column_indices())[0]\n\n  def unmatched_column_indices(self):\n    \"\"\"Returns column indices that do not match any row.\n\n    The indices returned by this op are always sorted in increasing order.\n\n    Returns:\n      column_indices: int32 tensor of shape [K] with column indices.\n    \"\"\"\n    return self._reshape_and_cast(tf.where(tf.equal(self._match_results, -1)))\n\n  def unmatched_column_indicator(self):\n    \"\"\"Returns column indices that are unmatched.\n\n    Returns:\n      column_indices: int32 tensor of shape [K] with column indices.\n    \"\"\"\n    return tf.equal(self._match_results, -1)\n\n  def num_unmatched_columns(self):\n    \"\"\"Returns number (int32 scalar tensor) of unmatched columns.\"\"\"\n    return tf.shape(self.unmatched_column_indices())[0]\n\n  def ignored_column_indices(self):\n    \"\"\"Returns column indices that are ignored (neither Matched nor Unmatched).\n\n    The indices returned by this op are always sorted in increasing order.\n\n    Returns:\n      column_indices: int32 tensor of shape [K] with column indices.\n    \"\"\"\n    return self._reshape_and_cast(tf.where(self.ignored_column_indicator()))\n\n  def ignored_column_indicator(self):\n    \"\"\"Returns boolean column indicator where True means the column is ignored.\n\n    Returns:\n      column_indicator: boolean vector which is True for all ignored column\n      indices.\n    \"\"\"\n    return tf.equal(self._match_results, -2)\n\n  def num_ignored_columns(self):\n    \"\"\"Returns number (int32 scalar tensor) of matched columns.\"\"\"\n    return tf.shape(self.ignored_column_indices())[0]\n\n  def unmatched_or_ignored_column_indices(self):\n    \"\"\"Returns column indices that are unmatched or ignored.\n\n    The indices returned by this op are always sorted in increasing order.\n\n    Returns:\n      column_indices: int32 tensor of shape [K] with column indices.\n    \"\"\"\n    return self._reshape_and_cast(tf.where(tf.greater(0, self._match_results)))\n\n  def matched_row_indices(self):\n    \"\"\"Returns row indices that match some column.\n\n    The indices returned by this op are ordered so as to be in correspondence\n    with the output of matched_column_indicator().  For example if\n    self.matched_column_indicator() is [0,2], and self.matched_row_indices() is\n    [7, 3], then we know that column 0 was matched to row 7 and column 2 was\n    matched to row 3.\n\n    Returns:\n      row_indices: int32 tensor of shape [K] with row indices.\n    \"\"\"\n    return self._reshape_and_cast(\n        tf.gather(self._match_results, self.matched_column_indices()))\n\n  def _reshape_and_cast(self, t):\n    return tf.cast(tf.reshape(t, [-1]), tf.int32)\n\n  def gather_based_on_match(self, input_tensor, unmatched_value,\n                            ignored_value):\n    \"\"\"Gathers elements from `input_tensor` based on match results.\n\n    For columns that are matched to a row, gathered_tensor[col] is set to\n    input_tensor[match_results[col]]. For columns that are unmatched,\n    gathered_tensor[col] is set to unmatched_value. Finally, for columns that\n    are ignored gathered_tensor[col] is set to ignored_value.\n\n    Note that the input_tensor.shape[1:] must match with unmatched_value.shape\n    and ignored_value.shape\n\n    Args:\n      input_tensor: Tensor to gather values from.\n      unmatched_value: Constant tensor value for unmatched columns.\n      ignored_value: Constant tensor value for ignored columns.\n\n    Returns:\n      gathered_tensor: A tensor containing values gathered from input_tensor.\n        The shape of the gathered tensor is [match_results.shape[0]] +\n        input_tensor.shape[1:].\n    \"\"\"\n    input_tensor = tf.concat([tf.stack([ignored_value, unmatched_value]),\n                              input_tensor], axis=0)\n    gather_indices = tf.maximum(self.match_results + 2, 0)\n    gathered_tensor = tf.gather(input_tensor, gather_indices)\n    return gathered_tensor\n\n\nclass Matcher(object):\n  \"\"\"Abstract base class for matcher.\n  \"\"\"\n  __metaclass__ = abc.ABCMeta\n\n  def match(self, similarity_matrix, scope=None, **params):\n    \"\"\"Computes matches among row and column indices and returns the result.\n\n    Computes matches among the row and column indices based on the similarity\n    matrix and optional arguments.\n\n    Args:\n      similarity_matrix: Float tensor of shape [N, M] with pairwise similarity\n        where higher value means more similar.\n      scope: Op scope name. Defaults to 'Match' if None.\n      **params: Additional keyword arguments for specific implementations of\n        the Matcher.\n\n    Returns:\n      A Match object with the results of matching.\n    \"\"\"\n    with tf.name_scope(scope, 'Match', [similarity_matrix, params]) as scope:\n      return Match(self._match(similarity_matrix, **params))\n\n  @abc.abstractmethod\n  def _match(self, similarity_matrix, **params):\n    \"\"\"Method to be overridden by implementations.\n\n    Args:\n      similarity_matrix: Float tensor of shape [N, M] with pairwise similarity\n        where higher value means more similar.\n      **params: Additional keyword arguments for specific implementations of\n        the Matcher.\n\n    Returns:\n      match_results: Integer tensor of shape [M]: match_results[i]>=0 means\n        that column i is matched to row match_results[i], match_results[i]=-1\n        means that the column is not matched. match_results[i]=-2 means that\n        the column is ignored (usually this happens when there is a very weak\n        match which one neither wants as positive nor negative example).\n    \"\"\"\n    pass\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/object_detection/preprocessor.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Preprocess images and bounding boxes for detection.\n\nWe perform two sets of operations in preprocessing stage:\n(a) operations that are applied to both training and testing data,\n(b) operations that are applied only to training data for the purpose of\n    data augmentation.\n\nA preprocessing function receives a set of inputs,\ne.g. an image and bounding boxes,\nperforms an operation on them, and returns them.\nSome examples are: randomly cropping the image, randomly mirroring the image,\n                   randomly changing the brightness, contrast, hue and\n                   randomly jittering the bounding boxes.\n\nThe image is a rank 4 tensor: [1, height, width, channels] with\ndtype=tf.float32. The groundtruth_boxes is a rank 2 tensor: [N, 4] where\nin each row there is a box with [ymin xmin ymax xmax].\nBoxes are in normalized coordinates meaning\ntheir coordinate values range in [0, 1]\n\nImportant Note: In tensor_dict, images is a rank 4 tensor, but preprocessing\nfunctions receive a rank 3 tensor for processing the image. Thus, inside the\npreprocess function we squeeze the image to become a rank 3 tensor and then\nwe pass it to the functions. At the end of the preprocess we expand the image\nback to rank 4.\n\"\"\"\n\nimport tensorflow.compat.v1 as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.object_detection import box_list\n\n\ndef _flip_boxes_left_right(boxes):\n  \"\"\"Left-right flip the boxes.\n\n  Args:\n    boxes: rank 2 float32 tensor containing the bounding boxes -> [N, 4].\n           Boxes are in normalized form meaning their coordinates vary\n           between [0, 1].\n           Each row is in the form of [ymin, xmin, ymax, xmax].\n\n  Returns:\n    Flipped boxes.\n  \"\"\"\n  ymin, xmin, ymax, xmax = tf.split(value=boxes, num_or_size_splits=4, axis=1)\n  flipped_xmin = tf.subtract(1.0, xmax)\n  flipped_xmax = tf.subtract(1.0, xmin)\n  flipped_boxes = tf.concat([ymin, flipped_xmin, ymax, flipped_xmax], 1)\n  return flipped_boxes\n\n\ndef _flip_masks_left_right(masks):\n  \"\"\"Left-right flip masks.\n\n  Args:\n    masks: rank 3 float32 tensor with shape\n      [num_instances, height, width] representing instance masks.\n\n  Returns:\n    flipped masks: rank 3 float32 tensor with shape\n      [num_instances, height, width] representing instance masks.\n  \"\"\"\n  return masks[:, :, ::-1]\n\n\ndef keypoint_flip_horizontal(keypoints, flip_point, flip_permutation,\n                             scope=None):\n  \"\"\"Flips the keypoints horizontally around the flip_point.\n\n  This operation flips the x coordinate for each keypoint around the flip_point\n  and also permutes the keypoints in a manner specified by flip_permutation.\n\n  Args:\n    keypoints: a tensor of shape [num_instances, num_keypoints, 2]\n    flip_point:  (float) scalar tensor representing the x coordinate to flip the\n      keypoints around.\n    flip_permutation: rank 1 int32 tensor containing the keypoint flip\n      permutation. This specifies the mapping from original keypoint indices\n      to the flipped keypoint indices. This is used primarily for keypoints\n      that are not reflection invariant. E.g. Suppose there are 3 keypoints\n      representing ['head', 'right_eye', 'left_eye'], then a logical choice for\n      flip_permutation might be [0, 2, 1] since we want to swap the 'left_eye'\n      and 'right_eye' after a horizontal flip.\n    scope: name scope.\n\n  Returns:\n    new_keypoints: a tensor of shape [num_instances, num_keypoints, 2]\n  \"\"\"\n  with tf.name_scope(scope, 'FlipHorizontal'):\n    keypoints = tf.transpose(keypoints, [1, 0, 2])\n    keypoints = tf.gather(keypoints, flip_permutation)\n    v, u = tf.split(value=keypoints, num_or_size_splits=2, axis=2)\n    u = flip_point * 2.0 - u\n    new_keypoints = tf.concat([v, u], 2)\n    new_keypoints = tf.transpose(new_keypoints, [1, 0, 2])\n    return new_keypoints\n\n\ndef random_horizontal_flip(image,\n                           boxes=None,\n                           masks=None,\n                           keypoints=None,\n                           keypoint_flip_permutation=None,\n                           seed=None):\n  \"\"\"Randomly flips the image and detections horizontally.\n\n  The probability of flipping the image is 50%.\n\n  Args:\n    image: rank 3 float32 tensor with shape [height, width, channels].\n    boxes: (optional) rank 2 float32 tensor with shape [N, 4]\n           containing the bounding boxes.\n           Boxes are in normalized form meaning their coordinates vary\n           between [0, 1].\n           Each row is in the form of [ymin, xmin, ymax, xmax].\n    masks: (optional) rank 3 float32 tensor with shape\n           [num_instances, height, width] containing instance masks. The masks\n           are of the same height, width as the input `image`.\n    keypoints: (optional) rank 3 float32 tensor with shape\n               [num_instances, num_keypoints, 2]. The keypoints are in y-x\n               normalized coordinates.\n    keypoint_flip_permutation: rank 1 int32 tensor containing the keypoint flip\n                               permutation.\n    seed: random seed\n\n  Returns:\n    image: image which is the same shape as input image.\n\n    If boxes, masks, keypoints, and keypoint_flip_permutation are not None,\n    the function also returns the following tensors.\n\n    boxes: rank 2 float32 tensor containing the bounding boxes -> [N, 4].\n           Boxes are in normalized form meaning their coordinates vary\n           between [0, 1].\n    masks: rank 3 float32 tensor with shape [num_instances, height, width]\n           containing instance masks.\n    keypoints: rank 3 float32 tensor with shape\n               [num_instances, num_keypoints, 2]\n\n  Raises:\n    ValueError: if keypoints are provided but keypoint_flip_permutation is not.\n  \"\"\"\n\n  def _flip_image(image):\n    # flip image\n    image_flipped = tf.image.flip_left_right(image)\n    return image_flipped\n\n  if keypoints is not None and keypoint_flip_permutation is None:\n    raise ValueError(\n        'keypoints are provided but keypoints_flip_permutation is not provided')\n\n  with tf.name_scope('RandomHorizontalFlip', values=[image, boxes]):\n    result = []\n    # random variable defining whether to do flip or not\n    do_a_flip_random = tf.greater(tf.random_uniform([], seed=seed), 0.5)\n\n    # flip image\n    image = tf.cond(do_a_flip_random, lambda: _flip_image(image), lambda: image)\n    result.append(image)\n\n    # flip boxes\n    if boxes is not None:\n      boxes = tf.cond(do_a_flip_random, lambda: _flip_boxes_left_right(boxes),\n                      lambda: boxes)\n      result.append(boxes)\n\n    # flip masks\n    if masks is not None:\n      masks = tf.cond(do_a_flip_random, lambda: _flip_masks_left_right(masks),\n                      lambda: masks)\n      result.append(masks)\n\n    # flip keypoints\n    if keypoints is not None and keypoint_flip_permutation is not None:\n      permutation = keypoint_flip_permutation\n      keypoints = tf.cond(\n          do_a_flip_random,\n          lambda: keypoint_flip_horizontal(keypoints, 0.5, permutation),\n          lambda: keypoints)\n      result.append(keypoints)\n\n    return tuple(result)\n\n\ndef _compute_new_static_size(image, min_dimension, max_dimension):\n  \"\"\"Compute new static shape for resize_to_range method.\"\"\"\n  image_shape = image.get_shape().as_list()\n  orig_height = image_shape[0]\n  orig_width = image_shape[1]\n  num_channels = image_shape[2]\n  orig_min_dim = min(orig_height, orig_width)\n  # Calculates the larger of the possible sizes\n  large_scale_factor = min_dimension / float(orig_min_dim)\n  # Scaling orig_(height|width) by large_scale_factor will make the smaller\n  # dimension equal to min_dimension, save for floating point rounding errors.\n  # For reasonably-sized images, taking the nearest integer will reliably\n  # eliminate this error.\n  large_height = int(round(orig_height * large_scale_factor))\n  large_width = int(round(orig_width * large_scale_factor))\n  large_size = [large_height, large_width]\n  if max_dimension:\n    # Calculates the smaller of the possible sizes, use that if the larger\n    # is too big.\n    orig_max_dim = max(orig_height, orig_width)\n    small_scale_factor = max_dimension / float(orig_max_dim)\n    # Scaling orig_(height|width) by small_scale_factor will make the larger\n    # dimension equal to max_dimension, save for floating point rounding\n    # errors. For reasonably-sized images, taking the nearest integer will\n    # reliably eliminate this error.\n    small_height = int(round(orig_height * small_scale_factor))\n    small_width = int(round(orig_width * small_scale_factor))\n    small_size = [small_height, small_width]\n    new_size = large_size\n    if max(large_size) > max_dimension:\n      new_size = small_size\n  else:\n    new_size = large_size\n  return tf.constant(new_size + [num_channels])\n\n\ndef _compute_new_dynamic_size(image, min_dimension, max_dimension):\n  \"\"\"Compute new dynamic shape for resize_to_range method.\"\"\"\n  image_shape = tf.shape(image)\n  orig_height = tf.to_float(image_shape[0])\n  orig_width = tf.to_float(image_shape[1])\n  num_channels = image_shape[2]\n  orig_min_dim = tf.minimum(orig_height, orig_width)\n  # Calculates the larger of the possible sizes\n  min_dimension = tf.constant(min_dimension, dtype=tf.float32)\n  large_scale_factor = min_dimension / orig_min_dim\n  # Scaling orig_(height|width) by large_scale_factor will make the smaller\n  # dimension equal to min_dimension, save for floating point rounding errors.\n  # For reasonably-sized images, taking the nearest integer will reliably\n  # eliminate this error.\n  large_height = tf.to_int32(tf.round(orig_height * large_scale_factor))\n  large_width = tf.to_int32(tf.round(orig_width * large_scale_factor))\n  large_size = tf.stack([large_height, large_width])\n  if max_dimension:\n    # Calculates the smaller of the possible sizes, use that if the larger\n    # is too big.\n    orig_max_dim = tf.maximum(orig_height, orig_width)\n    max_dimension = tf.constant(max_dimension, dtype=tf.float32)\n    small_scale_factor = max_dimension / orig_max_dim\n    # Scaling orig_(height|width) by small_scale_factor will make the larger\n    # dimension equal to max_dimension, save for floating point rounding\n    # errors. For reasonably-sized images, taking the nearest integer will\n    # reliably eliminate this error.\n    small_height = tf.to_int32(tf.round(orig_height * small_scale_factor))\n    small_width = tf.to_int32(tf.round(orig_width * small_scale_factor))\n    small_size = tf.stack([small_height, small_width])\n    new_size = tf.cond(\n        tf.to_float(tf.reduce_max(large_size)) > max_dimension,\n        lambda: small_size, lambda: large_size)\n  else:\n    new_size = large_size\n  return tf.stack(tf.unstack(new_size) + [num_channels])\n\n\ndef resize_to_range(image,\n                    masks=None,\n                    min_dimension=None,\n                    max_dimension=None,\n                    method=tf.image.ResizeMethod.BILINEAR,\n                    align_corners=False,\n                    pad_to_max_dimension=False):\n  \"\"\"Resizes an image so its dimensions are within the provided value.\n\n  The output size can be described by two cases:\n  1. If the image can be rescaled so its minimum dimension is equal to the\n     provided value without the other dimension exceeding max_dimension,\n     then do so.\n  2. Otherwise, resize so the largest dimension is equal to max_dimension.\n\n  Args:\n    image: A 3D tensor of shape [height, width, channels]\n    masks: (optional) rank 3 float32 tensor with shape\n           [num_instances, height, width] containing instance masks.\n    min_dimension: (optional) (scalar) desired size of the smaller image\n                   dimension.\n    max_dimension: (optional) (scalar) maximum allowed size\n                   of the larger image dimension.\n    method: (optional) interpolation method used in resizing. Defaults to\n            BILINEAR.\n    align_corners: bool. If true, exactly align all 4 corners of the input\n                   and output. Defaults to False.\n    pad_to_max_dimension: Whether to resize the image and pad it with zeros\n      so the resulting image is of the spatial size\n      [max_dimension, max_dimension]. If masks are included they are padded\n      similarly.\n\n  Returns:\n    Note that the position of the resized_image_shape changes based on whether\n    masks are present.\n    resized_image: A 3D tensor of shape [new_height, new_width, channels],\n      where the image has been resized (with bilinear interpolation) so that\n      min(new_height, new_width) == min_dimension or\n      max(new_height, new_width) == max_dimension.\n    resized_masks: If masks is not None, also outputs masks. A 3D tensor of\n      shape [num_instances, new_height, new_width].\n    resized_image_shape: A 1D tensor of shape [3] containing shape of the\n      resized image.\n\n  Raises:\n    ValueError: if the image is not a 3D tensor.\n  \"\"\"\n  if len(image.get_shape()) != 3:\n    raise ValueError('Image should be 3D tensor')\n\n  with tf.name_scope('ResizeToRange', values=[image, min_dimension]):\n    if image.get_shape().is_fully_defined():\n      new_size = _compute_new_static_size(image, min_dimension, max_dimension)\n    else:\n      new_size = _compute_new_dynamic_size(image, min_dimension, max_dimension)\n    new_image = tf.image.resize_images(\n        image, new_size[:-1], method=method, align_corners=align_corners)\n\n    if pad_to_max_dimension:\n      new_image = tf.image.pad_to_bounding_box(\n          new_image, 0, 0, max_dimension, max_dimension)\n\n    result = [new_image]\n    if masks is not None:\n      new_masks = tf.expand_dims(masks, 3)\n      new_masks = tf.image.resize_images(\n          new_masks,\n          new_size[:-1],\n          method=tf.image.ResizeMethod.NEAREST_NEIGHBOR,\n          align_corners=align_corners)\n      new_masks = tf.squeeze(new_masks, 3)\n      if pad_to_max_dimension:\n        new_masks = tf.image.pad_to_bounding_box(\n            new_masks, 0, 0, max_dimension, max_dimension)\n      result.append(new_masks)\n\n    result.append(new_size)\n    return result\n\n\ndef _copy_extra_fields(boxlist_to_copy_to, boxlist_to_copy_from):\n  \"\"\"Copies the extra fields of boxlist_to_copy_from to boxlist_to_copy_to.\n\n  Args:\n    boxlist_to_copy_to: BoxList to which extra fields are copied.\n    boxlist_to_copy_from: BoxList from which fields are copied.\n\n  Returns:\n    boxlist_to_copy_to with extra fields.\n  \"\"\"\n  for field in boxlist_to_copy_from.get_extra_fields():\n    boxlist_to_copy_to.add_field(field, boxlist_to_copy_from.get_field(field))\n  return boxlist_to_copy_to\n\n\ndef box_list_scale(boxlist, y_scale, x_scale, scope=None):\n  \"\"\"scale box coordinates in x and y dimensions.\n\n  Args:\n    boxlist: BoxList holding N boxes\n    y_scale: (float) scalar tensor\n    x_scale: (float) scalar tensor\n    scope: name scope.\n\n  Returns:\n    boxlist: BoxList holding N boxes\n  \"\"\"\n  with tf.name_scope(scope, 'Scale'):\n    y_scale = tf.cast(y_scale, tf.float32)\n    x_scale = tf.cast(x_scale, tf.float32)\n    y_min, x_min, y_max, x_max = tf.split(\n        value=boxlist.get(), num_or_size_splits=4, axis=1)\n    y_min = y_scale * y_min\n    y_max = y_scale * y_max\n    x_min = x_scale * x_min\n    x_max = x_scale * x_max\n    scaled_boxlist = box_list.BoxList(\n        tf.concat([y_min, x_min, y_max, x_max], 1))\n    return _copy_extra_fields(scaled_boxlist, boxlist)\n\n\ndef keypoint_scale(keypoints, y_scale, x_scale, scope=None):\n  \"\"\"Scales keypoint coordinates in x and y dimensions.\n\n  Args:\n    keypoints: a tensor of shape [num_instances, num_keypoints, 2]\n    y_scale: (float) scalar tensor\n    x_scale: (float) scalar tensor\n    scope: name scope.\n\n  Returns:\n    new_keypoints: a tensor of shape [num_instances, num_keypoints, 2]\n  \"\"\"\n  with tf.name_scope(scope, 'Scale'):\n    y_scale = tf.cast(y_scale, tf.float32)\n    x_scale = tf.cast(x_scale, tf.float32)\n    new_keypoints = keypoints * [[[y_scale, x_scale]]]\n    return new_keypoints\n\n\ndef scale_boxes_to_pixel_coordinates(image, boxes, keypoints=None):\n  \"\"\"Scales boxes from normalized to pixel coordinates.\n\n  Args:\n    image: A 3D float32 tensor of shape [height, width, channels].\n    boxes: A 2D float32 tensor of shape [num_boxes, 4] containing the bounding\n      boxes in normalized coordinates. Each row is of the form\n      [ymin, xmin, ymax, xmax].\n    keypoints: (optional) rank 3 float32 tensor with shape\n      [num_instances, num_keypoints, 2]. The keypoints are in y-x normalized\n      coordinates.\n\n  Returns:\n    image: unchanged input image.\n    scaled_boxes: a 2D float32 tensor of shape [num_boxes, 4] containing the\n      bounding boxes in pixel coordinates.\n    scaled_keypoints: a 3D float32 tensor with shape\n      [num_instances, num_keypoints, 2] containing the keypoints in pixel\n      coordinates.\n  \"\"\"\n  boxlist = box_list.BoxList(boxes)\n  image_height = tf.shape(image)[0]\n  image_width = tf.shape(image)[1]\n  scaled_boxes = box_list_scale(boxlist, image_height, image_width).get()\n  result = [image, scaled_boxes]\n  if keypoints is not None:\n    scaled_keypoints = keypoint_scale(keypoints, image_height, image_width)\n    result.append(scaled_keypoints)\n  return tuple(result)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/object_detection/region_similarity_calculator.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Region Similarity Calculators for BoxLists.\n\nRegion Similarity Calculators compare a pairwise measure of similarity\nbetween the boxes in two BoxLists.\n\"\"\"\nfrom abc import ABCMeta\nfrom abc import abstractmethod\n\nimport tensorflow.compat.v1 as tf\n\n\ndef area(boxlist, scope=None):\n  \"\"\"Computes area of boxes.\n\n  Args:\n    boxlist: BoxList holding N boxes\n    scope: name scope.\n\n  Returns:\n    a tensor with shape [N] representing box areas.\n  \"\"\"\n  with tf.name_scope(scope, 'Area'):\n    y_min, x_min, y_max, x_max = tf.split(\n        value=boxlist.get(), num_or_size_splits=4, axis=1)\n    return tf.squeeze((y_max - y_min) * (x_max - x_min), [1])\n\n\ndef intersection(boxlist1, boxlist2, scope=None):\n  \"\"\"Compute pairwise intersection areas between boxes.\n\n  Args:\n    boxlist1: BoxList holding N boxes\n    boxlist2: BoxList holding M boxes\n    scope: name scope.\n\n  Returns:\n    a tensor with shape [N, M] representing pairwise intersections\n  \"\"\"\n  with tf.name_scope(scope, 'Intersection'):\n    y_min1, x_min1, y_max1, x_max1 = tf.split(\n        value=boxlist1.get(), num_or_size_splits=4, axis=1)\n    y_min2, x_min2, y_max2, x_max2 = tf.split(\n        value=boxlist2.get(), num_or_size_splits=4, axis=1)\n    all_pairs_min_ymax = tf.minimum(y_max1, tf.transpose(y_max2))\n    all_pairs_max_ymin = tf.maximum(y_min1, tf.transpose(y_min2))\n    intersect_heights = tf.maximum(0.0, all_pairs_min_ymax - all_pairs_max_ymin)\n    all_pairs_min_xmax = tf.minimum(x_max1, tf.transpose(x_max2))\n    all_pairs_max_xmin = tf.maximum(x_min1, tf.transpose(x_min2))\n    intersect_widths = tf.maximum(0.0, all_pairs_min_xmax - all_pairs_max_xmin)\n    return intersect_heights * intersect_widths\n\n\ndef iou(boxlist1, boxlist2, scope=None):\n  \"\"\"Computes pairwise intersection-over-union between box collections.\n\n  Args:\n    boxlist1: BoxList holding N boxes\n    boxlist2: BoxList holding M boxes\n    scope: name scope.\n\n  Returns:\n    a tensor with shape [N, M] representing pairwise iou scores.\n  \"\"\"\n  with tf.name_scope(scope, 'IOU'):\n    intersections = intersection(boxlist1, boxlist2)\n    areas1 = area(boxlist1)\n    areas2 = area(boxlist2)\n    unions = (\n        tf.expand_dims(areas1, 1) + tf.expand_dims(areas2, 0) - intersections)\n    return tf.where(\n        tf.equal(intersections, 0.0),\n        tf.zeros_like(intersections), tf.truediv(intersections, unions))\n\n\nclass RegionSimilarityCalculator(object):\n  \"\"\"Abstract base class for region similarity calculator.\"\"\"\n  __metaclass__ = ABCMeta\n\n  def compare(self, boxlist1, boxlist2, scope=None):\n    \"\"\"Computes matrix of pairwise similarity between BoxLists.\n\n    This op (to be overridden) computes a measure of pairwise similarity between\n    the boxes in the given BoxLists. Higher values indicate more similarity.\n\n    Note that this method simply measures similarity and does not explicitly\n    perform a matching.\n\n    Args:\n      boxlist1: BoxList holding N boxes.\n      boxlist2: BoxList holding M boxes.\n      scope: Op scope name. Defaults to 'Compare' if None.\n\n    Returns:\n      a (float32) tensor of shape [N, M] with pairwise similarity score.\n    \"\"\"\n    with tf.name_scope(scope, 'Compare', [boxlist1, boxlist2]) as scope:\n      return self._compare(boxlist1, boxlist2)\n\n  @abstractmethod\n  def _compare(self, boxlist1, boxlist2):\n    pass\n\n\nclass IouSimilarity(RegionSimilarityCalculator):\n  \"\"\"Class to compute similarity based on Intersection over Union (IOU) metric.\n\n  This class computes pairwise similarity between two BoxLists based on IOU.\n  \"\"\"\n\n  def _compare(self, boxlist1, boxlist2):\n    \"\"\"Compute pairwise IOU similarity between the two BoxLists.\n\n    Args:\n      boxlist1: BoxList holding N boxes.\n      boxlist2: BoxList holding M boxes.\n\n    Returns:\n      A tensor with shape [N, M] representing pairwise iou scores.\n    \"\"\"\n    return iou(boxlist1, boxlist2)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/object_detection/shape_utils.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Utils used to manipulate tensor shapes.\"\"\"\n\nimport tensorflow.compat.v1 as tf\n\n\ndef assert_shape_equal(shape_a, shape_b):\n  \"\"\"Asserts that shape_a and shape_b are equal.\n\n  If the shapes are static, raises a ValueError when the shapes\n  mismatch.\n\n  If the shapes are dynamic, raises a tf InvalidArgumentError when the shapes\n  mismatch.\n\n  Args:\n    shape_a: a list containing shape of the first tensor.\n    shape_b: a list containing shape of the second tensor.\n\n  Returns:\n    Either a tf.no_op() when shapes are all static and a tf.assert_equal() op\n    when the shapes are dynamic.\n\n  Raises:\n    ValueError: When shapes are both static and unequal.\n  \"\"\"\n  if (all(isinstance(dim, int) for dim in shape_a) and\n      all(isinstance(dim, int) for dim in shape_b)):\n    if shape_a != shape_b:\n      raise ValueError('Unequal shapes {}, {}'.format(shape_a, shape_b))\n    else: return tf.no_op()\n  else:\n    return tf.assert_equal(shape_a, shape_b)\n\n\ndef combined_static_and_dynamic_shape(tensor):\n  \"\"\"Returns a list containing static and dynamic values for the dimensions.\n\n  Returns a list of static and dynamic values for shape dimensions. This is\n  useful to preserve static shapes when available in reshape operation.\n\n  Args:\n    tensor: A tensor of any type.\n\n  Returns:\n    A list of size tensor.shape.ndims containing integers or a scalar tensor.\n  \"\"\"\n  static_tensor_shape = tensor.shape.as_list()\n  dynamic_tensor_shape = tf.shape(tensor)\n  combined_shape = []\n  for index, dim in enumerate(static_tensor_shape):\n    if dim is not None:\n      combined_shape.append(dim)\n    else:\n      combined_shape.append(dynamic_tensor_shape[index])\n  return combined_shape\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/object_detection/target_assigner.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Base target assigner module.\n\nThe job of a TargetAssigner is, for a given set of anchors (bounding boxes) and\ngroundtruth detections (bounding boxes), to assign classification and regression\ntargets to each anchor as well as weights to each anchor (specifying, e.g.,\nwhich anchors should not contribute to training loss).\n\nIt assigns classification/regression targets by performing the following steps:\n1) Computing pairwise similarity between anchors and groundtruth boxes using a\n  provided RegionSimilarity Calculator\n2) Computing a matching based on the similarity matrix using a provided Matcher\n3) Assigning regression targets based on the matching and a provided BoxCoder\n4) Assigning classification targets based on the matching and groundtruth labels\n\nNote that TargetAssigners only operate on detections from a single\nimage at a time, so any logic for applying a TargetAssigner to multiple\nimages must be handled externally.\n\"\"\"\nimport tensorflow.compat.v1 as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.object_detection import box_list\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.object_detection import shape_utils\n\n\nKEYPOINTS_FIELD_NAME = 'keypoints'\n\n\nclass TargetAssigner(object):\n  \"\"\"Target assigner to compute classification and regression targets.\"\"\"\n\n  def __init__(self, similarity_calc, matcher, box_coder,\n               negative_class_weight=1.0, unmatched_cls_target=None):\n    \"\"\"Construct Object Detection Target Assigner.\n\n    Args:\n      similarity_calc: a RegionSimilarityCalculator\n      matcher: Matcher used to match groundtruth to anchors.\n      box_coder: BoxCoder used to encode matching groundtruth boxes with\n        respect to anchors.\n      negative_class_weight: classification weight to be associated to negative\n        anchors (default: 1.0). The weight must be in [0., 1.].\n      unmatched_cls_target: a float32 tensor with shape [d_1, d_2, ..., d_k]\n        which is consistent with the classification target for each\n        anchor (and can be empty for scalar targets).  This shape must thus be\n        compatible with the groundtruth labels that are passed to the \"assign\"\n        function (which have shape [num_gt_boxes, d_1, d_2, ..., d_k]).\n        If set to None, unmatched_cls_target is set to be [0] for each anchor.\n\n    Raises:\n      ValueError: if similarity_calc is not a RegionSimilarityCalculator or\n        if matcher is not a Matcher or if box_coder is not a BoxCoder\n    \"\"\"\n    self._similarity_calc = similarity_calc\n    self._matcher = matcher\n    self._box_coder = box_coder\n    self._negative_class_weight = negative_class_weight\n    if unmatched_cls_target is None:\n      self._unmatched_cls_target = tf.constant([0], tf.float32)\n    else:\n      self._unmatched_cls_target = unmatched_cls_target\n\n  @property\n  def box_coder(self):\n    return self._box_coder\n\n  def assign(self, anchors, groundtruth_boxes, groundtruth_labels=None,\n             groundtruth_weights=None, **params):\n    \"\"\"Assign classification and regression targets to each anchor.\n\n    For a given set of anchors and groundtruth detections, match anchors\n    to groundtruth_boxes and assign classification and regression targets to\n    each anchor as well as weights based on the resulting match (specifying,\n    e.g., which anchors should not contribute to training loss).\n\n    Anchors that are not matched to anything are given a classification target\n    of self._unmatched_cls_target which can be specified via the constructor.\n\n    Args:\n      anchors: a BoxList representing N anchors\n      groundtruth_boxes: a BoxList representing M groundtruth boxes\n      groundtruth_labels:  a tensor of shape [M, d_1, ... d_k]\n        with labels for each of the ground_truth boxes. The subshape\n        [d_1, ... d_k] can be empty (corresponding to scalar inputs).  When set\n        to None, groundtruth_labels assumes a binary problem where all\n        ground_truth boxes get a positive label (of 1).\n      groundtruth_weights: a float tensor of shape [M] indicating the weight to\n        assign to all anchors match to a particular groundtruth box. The weights\n        must be in [0., 1.]. If None, all weights are set to 1.\n      **params: Additional keyword arguments for specific implementations of\n              the Matcher.\n\n    Returns:\n      cls_targets: a float32 tensor with shape [num_anchors, d_1, d_2 ... d_k],\n        where the subshape [d_1, ..., d_k] is compatible with groundtruth_labels\n        which has shape [num_gt_boxes, d_1, d_2, ... d_k].\n      cls_weights: a float32 tensor with shape [num_anchors]\n      reg_targets: a float32 tensor with shape [num_anchors, box_code_dimension]\n      reg_weights: a float32 tensor with shape [num_anchors]\n      match: a matcher.Match object encoding the match between anchors and\n        groundtruth boxes, with rows corresponding to groundtruth boxes\n        and columns corresponding to anchors.\n\n    Raises:\n      ValueError: if anchors or groundtruth_boxes are not of type\n        box_list.BoxList\n    \"\"\"\n    if not isinstance(anchors, box_list.BoxList):\n      raise ValueError('anchors must be an BoxList')\n    if not isinstance(groundtruth_boxes, box_list.BoxList):\n      raise ValueError('groundtruth_boxes must be an BoxList')\n\n    if groundtruth_labels is None:\n      groundtruth_labels = tf.ones(tf.expand_dims(groundtruth_boxes.num_boxes(),\n                                                  0))\n      groundtruth_labels = tf.expand_dims(groundtruth_labels, -1)\n    unmatched_shape_assert = shape_utils.assert_shape_equal(\n        shape_utils.combined_static_and_dynamic_shape(groundtruth_labels)[1:],\n        shape_utils.combined_static_and_dynamic_shape(\n            self._unmatched_cls_target))\n    labels_and_box_shapes_assert = shape_utils.assert_shape_equal(\n        shape_utils.combined_static_and_dynamic_shape(\n            groundtruth_labels)[:1],\n        shape_utils.combined_static_and_dynamic_shape(\n            groundtruth_boxes.get())[:1])\n\n    if groundtruth_weights is None:\n      num_gt_boxes = groundtruth_boxes.num_boxes_static()\n      if not num_gt_boxes:\n        num_gt_boxes = groundtruth_boxes.num_boxes()\n      groundtruth_weights = tf.ones([num_gt_boxes], dtype=tf.float32)\n    with tf.control_dependencies(\n        [unmatched_shape_assert, labels_and_box_shapes_assert]):\n      match_quality_matrix = self._similarity_calc.compare(groundtruth_boxes,\n                                                           anchors)\n      match = self._matcher.match(match_quality_matrix, **params)\n      reg_targets = self._create_regression_targets(anchors,\n                                                    groundtruth_boxes,\n                                                    match)\n      cls_targets = self._create_classification_targets(groundtruth_labels,\n                                                        match)\n      reg_weights = self._create_regression_weights(match, groundtruth_weights)\n      cls_weights = self._create_classification_weights(match,\n                                                        groundtruth_weights)\n\n    num_anchors = anchors.num_boxes_static()\n    if num_anchors is not None:\n      reg_targets = self._reset_target_shape(reg_targets, num_anchors)\n      cls_targets = self._reset_target_shape(cls_targets, num_anchors)\n      reg_weights = self._reset_target_shape(reg_weights, num_anchors)\n      cls_weights = self._reset_target_shape(cls_weights, num_anchors)\n\n    return cls_targets, cls_weights, reg_targets, reg_weights, match\n\n  def _reset_target_shape(self, target, num_anchors):\n    \"\"\"Sets the static shape of the target.\n\n    Args:\n      target: the target tensor. Its first dimension will be overwritten.\n      num_anchors: the number of anchors, which is used to override the target's\n        first dimension.\n\n    Returns:\n      A tensor with the shape info filled in.\n    \"\"\"\n    target_shape = target.get_shape().as_list()\n    target_shape[0] = num_anchors\n    target.set_shape(target_shape)\n    return target\n\n  def _create_regression_targets(self, anchors, groundtruth_boxes, match):\n    \"\"\"Returns a regression target for each anchor.\n\n    Args:\n      anchors: a BoxList representing N anchors\n      groundtruth_boxes: a BoxList representing M groundtruth_boxes\n      match: a matcher.Match object\n\n    Returns:\n      reg_targets: a float32 tensor with shape [N, box_code_dimension]\n    \"\"\"\n    matched_gt_boxes = match.gather_based_on_match(\n        groundtruth_boxes.get(),\n        unmatched_value=tf.zeros(4),\n        ignored_value=tf.zeros(4))\n    matched_gt_boxlist = box_list.BoxList(matched_gt_boxes)\n    if groundtruth_boxes.has_field(KEYPOINTS_FIELD_NAME):\n      groundtruth_keypoints = groundtruth_boxes.get_field(KEYPOINTS_FIELD_NAME)\n      matched_keypoints = match.gather_based_on_match(\n          groundtruth_keypoints,\n          unmatched_value=tf.zeros(groundtruth_keypoints.get_shape()[1:]),\n          ignored_value=tf.zeros(groundtruth_keypoints.get_shape()[1:]))\n      matched_gt_boxlist.add_field(KEYPOINTS_FIELD_NAME, matched_keypoints)\n    matched_reg_targets = self._box_coder.encode(matched_gt_boxlist, anchors)\n    match_results_shape = shape_utils.combined_static_and_dynamic_shape(\n        match.match_results)\n\n    # Zero out the unmatched and ignored regression targets.\n    unmatched_ignored_reg_targets = tf.tile(\n        self._default_regression_target(), [match_results_shape[0], 1])\n    matched_anchors_mask = match.matched_column_indicator()\n    reg_targets = tf.where(matched_anchors_mask,\n                           matched_reg_targets,\n                           unmatched_ignored_reg_targets)\n    return reg_targets\n\n  def _default_regression_target(self):\n    \"\"\"Returns the default target for anchors to regress to.\n\n    Default regression targets are set to zero (though in\n    this implementation what these targets are set to should\n    not matter as the regression weight of any box set to\n    regress to the default target is zero).\n\n    Returns:\n      default_target: a float32 tensor with shape [1, box_code_dimension]\n    \"\"\"\n    return tf.constant([self._box_coder.code_size*[0]], tf.float32)\n\n  def _create_classification_targets(self, groundtruth_labels, match):\n    \"\"\"Create classification targets for each anchor.\n\n    Assign a classification target of for each anchor to the matching\n    groundtruth label that is provided by match.  Anchors that are not matched\n    to anything are given the target self._unmatched_cls_target\n\n    Args:\n      groundtruth_labels:  a tensor of shape [num_gt_boxes, d_1, ... d_k]\n        with labels for each of the ground_truth boxes. The subshape\n        [d_1, ... d_k] can be empty (corresponding to scalar labels).\n      match: a matcher.Match object that provides a matching between anchors\n        and groundtruth boxes.\n\n    Returns:\n      a float32 tensor with shape [num_anchors, d_1, d_2 ... d_k], where the\n      subshape [d_1, ..., d_k] is compatible with groundtruth_labels which has\n      shape [num_gt_boxes, d_1, d_2, ... d_k].\n    \"\"\"\n    return match.gather_based_on_match(\n        groundtruth_labels,\n        unmatched_value=self._unmatched_cls_target,\n        ignored_value=self._unmatched_cls_target)\n\n  def _create_regression_weights(self, match, groundtruth_weights):\n    \"\"\"Set regression weight for each anchor.\n\n    Only positive anchors are set to contribute to the regression loss, so this\n    method returns a weight of 1 for every positive anchor and 0 for every\n    negative anchor.\n\n    Args:\n      match: a matcher.Match object that provides a matching between anchors\n        and groundtruth boxes.\n      groundtruth_weights: a float tensor of shape [M] indicating the weight to\n        assign to all anchors match to a particular groundtruth box.\n\n    Returns:\n      a float32 tensor with shape [num_anchors] representing regression weights.\n    \"\"\"\n    return match.gather_based_on_match(\n        groundtruth_weights, ignored_value=0., unmatched_value=0.)\n\n  def _create_classification_weights(self,\n                                     match,\n                                     groundtruth_weights):\n    \"\"\"Create classification weights for each anchor.\n\n    Positive (matched) anchors are associated with a weight of\n    positive_class_weight and negative (unmatched) anchors are associated with\n    a weight of negative_class_weight. When anchors are ignored, weights are set\n    to zero. By default, both positive/negative weights are set to 1.0,\n    but they can be adjusted to handle class imbalance (which is almost always\n    the case in object detection).\n\n    Args:\n      match: a matcher.Match object that provides a matching between anchors\n        and groundtruth boxes.\n      groundtruth_weights: a float tensor of shape [M] indicating the weight to\n        assign to all anchors match to a particular groundtruth box.\n\n    Returns:\n      a float32 tensor with shape [num_anchors] representing classification\n      weights.\n    \"\"\"\n    return match.gather_based_on_match(\n        groundtruth_weights,\n        ignored_value=0.,\n        unmatched_value=self._negative_class_weight)\n\n  def get_box_coder(self):\n    \"\"\"Get BoxCoder of this TargetAssigner.\n\n    Returns:\n      BoxCoder object.\n    \"\"\"\n    return self._box_coder\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/object_detection/tf_example_decoder.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Tensorflow Example proto decoder for object detection.\n\nA decoder to decode string tensors containing serialized tensorflow.Example\nprotos for object detection.\n\"\"\"\n\nimport tensorflow.compat.v1 as tf\n\n\ndef _get_source_id_from_encoded_image(parsed_tensors):\n  return tf.strings.as_string(\n      tf.strings.to_hash_bucket_fast(parsed_tensors['image/encoded'],\n                                     2**63 - 1))\n\n\nclass TfExampleDecoder(object):\n  \"\"\"Tensorflow Example proto decoder.\"\"\"\n\n  def __init__(self, include_mask=False, regenerate_source_id=False):\n    self._include_mask = include_mask\n    self._regenerate_source_id = regenerate_source_id\n    self._keys_to_features = {\n        'image/encoded': tf.FixedLenFeature((), tf.string),\n        'image/source_id': tf.FixedLenFeature((), tf.string, ''),\n        'image/height': tf.FixedLenFeature((), tf.int64, -1),\n        'image/width': tf.FixedLenFeature((), tf.int64, -1),\n        'image/object/bbox/xmin': tf.VarLenFeature(tf.float32),\n        'image/object/bbox/xmax': tf.VarLenFeature(tf.float32),\n        'image/object/bbox/ymin': tf.VarLenFeature(tf.float32),\n        'image/object/bbox/ymax': tf.VarLenFeature(tf.float32),\n        'image/object/class/label': tf.VarLenFeature(tf.int64),\n        'image/object/area': tf.VarLenFeature(tf.float32),\n        'image/object/is_crowd': tf.VarLenFeature(tf.int64),\n    }\n    if include_mask:\n      self._keys_to_features.update({\n          'image/object/mask':\n              tf.VarLenFeature(tf.string),\n      })\n\n  def _decode_image(self, parsed_tensors):\n    \"\"\"Decodes the image and set its static shape.\"\"\"\n    image = tf.io.decode_image(parsed_tensors['image/encoded'], channels=3)\n    image.set_shape([None, None, 3])\n    return image\n\n  def _decode_boxes(self, parsed_tensors):\n    \"\"\"Concat box coordinates in the format of [ymin, xmin, ymax, xmax].\"\"\"\n    xmin = parsed_tensors['image/object/bbox/xmin']\n    xmax = parsed_tensors['image/object/bbox/xmax']\n    ymin = parsed_tensors['image/object/bbox/ymin']\n    ymax = parsed_tensors['image/object/bbox/ymax']\n    return tf.stack([ymin, xmin, ymax, xmax], axis=-1)\n\n  def _decode_masks(self, parsed_tensors):\n    \"\"\"Decode a set of PNG masks to the tf.float32 tensors.\"\"\"\n    def _decode_png_mask(png_bytes):\n      mask = tf.squeeze(\n          tf.io.decode_png(png_bytes, channels=1, dtype=tf.uint8), axis=-1)\n      mask = tf.cast(mask, dtype=tf.float32)\n      mask.set_shape([None, None])\n      return mask\n\n    height = parsed_tensors['image/height']\n    width = parsed_tensors['image/width']\n    masks = parsed_tensors['image/object/mask']\n    return tf.cond(\n        tf.greater(tf.shape(masks)[0], 0),\n        lambda: tf.map_fn(_decode_png_mask, masks, dtype=tf.float32),\n        lambda: tf.zeros([0, height, width], dtype=tf.float32))\n\n  def _decode_areas(self, parsed_tensors):\n    xmin = parsed_tensors['image/object/bbox/xmin']\n    xmax = parsed_tensors['image/object/bbox/xmax']\n    ymin = parsed_tensors['image/object/bbox/ymin']\n    ymax = parsed_tensors['image/object/bbox/ymax']\n    return tf.cond(\n        tf.greater(tf.shape(parsed_tensors['image/object/area'])[0], 0),\n        lambda: parsed_tensors['image/object/area'],\n        lambda: (xmax - xmin) * (ymax - ymin))\n\n  def decode(self, serialized_example):\n    \"\"\"Decode the serialized example.\n\n    Args:\n      serialized_example: a single serialized tf.Example string.\n\n    Returns:\n      decoded_tensors: a dictionary of tensors with the following fields:\n        - image: a uint8 tensor of shape [None, None, 3].\n        - source_id: a string scalar tensor.\n        - height: an integer scalar tensor.\n        - width: an integer scalar tensor.\n        - groundtruth_classes: a int64 tensor of shape [None].\n        - groundtruth_is_crowd: a bool tensor of shape [None].\n        - groundtruth_area: a float32 tensor of shape [None].\n        - groundtruth_boxes: a float32 tensor of shape [None, 4].\n        - groundtruth_instance_masks: a float32 tensor of shape\n            [None, None, None].\n        - groundtruth_instance_masks_png: a string tensor of shape [None].\n    \"\"\"\n    parsed_tensors = tf.io.parse_single_example(\n        serialized_example, self._keys_to_features)\n    for k in parsed_tensors:\n      if isinstance(parsed_tensors[k], tf.SparseTensor):\n        if parsed_tensors[k].dtype == tf.string:\n          parsed_tensors[k] = tf.sparse_tensor_to_dense(\n              parsed_tensors[k], default_value='')\n        else:\n          parsed_tensors[k] = tf.sparse_tensor_to_dense(\n              parsed_tensors[k], default_value=0)\n\n    image = self._decode_image(parsed_tensors)\n    boxes = self._decode_boxes(parsed_tensors)\n    areas = self._decode_areas(parsed_tensors)\n\n    decode_image_shape = tf.logical_or(\n        tf.equal(parsed_tensors['image/height'], -1),\n        tf.equal(parsed_tensors['image/width'], -1))\n    image_shape = tf.cast(tf.shape(image), dtype=tf.int64)\n\n    parsed_tensors['image/height'] = tf.where(decode_image_shape,\n                                              image_shape[0],\n                                              parsed_tensors['image/height'])\n    parsed_tensors['image/width'] = tf.where(decode_image_shape, image_shape[1],\n                                             parsed_tensors['image/width'])\n\n    is_crowds = tf.cond(\n        tf.greater(tf.shape(parsed_tensors['image/object/is_crowd'])[0], 0),\n        lambda: tf.cast(parsed_tensors['image/object/is_crowd'], dtype=tf.bool),\n        lambda: tf.zeros_like(parsed_tensors['image/object/class/label'], dtype=tf.bool))  # pylint: disable=line-too-long\n    if self._regenerate_source_id:\n      source_id = _get_source_id_from_encoded_image(parsed_tensors)\n    else:\n      source_id = tf.cond(\n          tf.greater(tf.strings.length(parsed_tensors['image/source_id']),\n                     0), lambda: parsed_tensors['image/source_id'],\n          lambda: _get_source_id_from_encoded_image(parsed_tensors))\n    if self._include_mask:\n      masks = self._decode_masks(parsed_tensors)\n\n    decoded_tensors = {\n        'image': image,\n        'source_id': source_id,\n        'height': parsed_tensors['image/height'],\n        'width': parsed_tensors['image/width'],\n        'groundtruth_classes': parsed_tensors['image/object/class/label'],\n        'groundtruth_is_crowd': is_crowds,\n        'groundtruth_area': areas,\n        'groundtruth_boxes': boxes,\n    }\n    if self._include_mask:\n      decoded_tensors.update({\n          'groundtruth_instance_masks': masks,\n          'groundtruth_instance_masks_png': parsed_tensors['image/object/mask'],\n      })\n    return decoded_tensors\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/requirements.txt",
    "content": "lxml>=4.6.1\nabsl-py>=0.10.0\nmatplotlib>=3.0.3\nnumpy>=1.19.4\nPillow>=6.0.0\nPyYAML>=5.1\nsix>=1.15.0\ntensorflow>=2.4.0\ntensorflow-addons>=0.12\ntensorflow-hub>=0.11\nneural-structured-learning>=1.3.1\ntensorflow-model-optimization>=0.5\nCython>=0.29.13\ngit+https://github.com/cocodataset/cocoapi.git#subdirectory=PythonAPI\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/run_tflite.py",
    "content": "# Copyright 2021 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\nr\"\"\"Run TF Lite model.\"\"\"\nfrom absl import app\nfrom absl import flags\n\nfrom PIL import Image\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet import inference\n\nFLAGS = flags.FLAGS\n\n\ndef define_flags():\n  \"\"\"Define flags.\"\"\"\n  flags.DEFINE_string('tflite_path', None, 'Path of tflite file.')\n  flags.DEFINE_string('sample_image', None, 'Sample image path')\n  flags.DEFINE_string('output_image', None, 'Output image path')\n  flags.DEFINE_string('image_size', '512x512', 'Image size \"WxH\".')\n\n\ndef load_image(image_path, image_size):\n  \"\"\"Loads an image, and returns numpy.ndarray.\n\n  Args:\n    image_path: str, path to image.\n    image_size: list of int, representing [width, height].\n\n  Returns:\n    image_batch: numpy.ndarray of shape [1, H, W, C].\n  \"\"\"\n  input_data = tf.io.gfile.GFile(image_path, 'rb').read()\n  image = tf.io.decode_image(input_data, channels=3, dtype=tf.uint8)\n  image = tf.image.resize(\n      image, image_size, method='bilinear', antialias=True)\n  return tf.expand_dims(tf.cast(image, tf.uint8), 0).numpy()\n\n\ndef save_visualized_image(image, prediction, output_path):\n  \"\"\"Saves the visualized image with prediction.\n\n  Args:\n    image: numpy.ndarray of shape [H, W, C].\n    prediction: numpy.ndarray of shape [num_predictions, 7].\n    output_path: str, output image path.\n  \"\"\"\n  output_image = inference.visualize_image_prediction(\n      image,\n      prediction,\n      label_map='coco')\n  Image.fromarray(output_image).save(output_path)\n\n\nclass TFLiteRunner:\n  \"\"\"Wrapper to run TFLite model.\"\"\"\n\n  def __init__(self, model_path):\n    \"\"\"Init.\n\n    Args:\n      model_path: str, path to tflite model.\n    \"\"\"\n    self.interpreter = tf.lite.Interpreter(model_path=model_path)\n    self.interpreter.allocate_tensors()\n    self.input_index = self.interpreter.get_input_details()[0]['index']\n    self.output_index = self.interpreter.get_output_details()[0]['index']\n\n  def run(self, image):\n    \"\"\"Run inference on a single images.\n\n    Args:\n      image: numpy.ndarray of shape [1, H, W, C].\n\n    Returns:\n      prediction: numpy.ndarray of shape [1, num_detections, 7].\n    \"\"\"\n    self.interpreter.set_tensor(self.input_index, image)\n    self.interpreter.invoke()\n    return self.interpreter.get_tensor(self.output_index)\n\n\ndef main(_):\n  image_size = [int(dim) for dim in FLAGS.image_size.split('x')]\n  image = load_image(FLAGS.sample_image, image_size)\n\n  runner = TFLiteRunner(FLAGS.tflite_path)\n  prediction = runner.run(image)\n\n  save_visualized_image(image[0], prediction[0], FLAGS.output_image)\n\n\nif __name__ == '__main__':\n  define_flags()\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/tensorrt.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\nr\"\"\"Simple tools for TensorRT.\n\nExample usage:\n\n$ export ROOT=/tmp/d4\n$ python model_inspect.py --runmode=freeze --model_name=efficientdet-d4 \\\n    --logdir=$ROOT  # --hparams=xyz.yaml\n$ python tensorrt.py --tf_savedmodel_dir=$ROOT/savedmodel \\\n    --trt_savedmodel_dir=$ROOT/trtmodel\n\"\"\"\nimport time\nfrom absl import app\nfrom absl import flags\nimport numpy as np\nimport tensorflow.compat.v1 as tf\n# pylint: disable=g-direct-tensorflow-import\nfrom tensorflow.python.compiler.tensorrt import trt_convert as trt\n\nflags.DEFINE_string('tf_savedmodel_dir', None, 'TensorFlow saved model dir.')\nflags.DEFINE_string('trt_savedmodel_dir', None, 'TensorRT saved model dir.')\nFLAGS = flags.FLAGS\n\n\ndef convert2trt(tf_savedmodel_dir: str, trt_savedmodel_dir: str):\n  converter = trt.TrtGraphConverter(\n      input_saved_model_dir=tf_savedmodel_dir,\n      max_workspace_size_bytes=(2 << 20),\n      precision_mode='FP16',\n      maximum_cached_engines=1)\n  converter.convert()\n  converter.save(trt_savedmodel_dir)\n\n\ndef benchmark(trt_savedmodel_dir: str, warmup_runs: int = 5, bm_runs: int = 20):\n  \"\"\"Benchmark TRT latency for a given TRT saved model.\"\"\"\n  with tf.Session() as sess:\n    # First load the Saved Model into the session\n    tf.saved_model.loader.load(sess, [tf.saved_model.tag_constants.SERVING],\n                               trt_savedmodel_dir)\n    graph = tf.get_default_graph()\n    input_shape = graph.get_tensor_by_name('input:0').shape\n    x = np.ones(input_shape).astype(np.float32)\n    ss = lambda i: '' if i == 0 else '_%d' % i\n    outputs = ['box_net/box-predict%s/BiasAdd:0' % ss(i) for i in range(1)]\n    outputs += ['class_net/class-predict%s/BiasAdd:0' % ss(i) for i in range(5)]\n    # Apply reduce_sum to avoid massive data move between GPU and CPU.\n    outputs = [tf.reduce_sum(graph.get_tensor_by_name(i)) for i in outputs]\n\n    # warmup\n    for _ in range(warmup_runs):\n      sess.run(outputs, feed_dict={'input:0': x})\n    # benchmark\n    s = time.perf_counter()\n    for _ in range(bm_runs):\n      sess.run(outputs, feed_dict={'input:0': x})\n    e = time.perf_counter()\n    print('Benchmark latency=%.4f  FPS=%.2f', (e - s) / bm_runs,\n          bm_runs / (e - s))\n\n\ndef main(_):\n  if FLAGS.tf_savedmodel_dir:\n    convert2trt(FLAGS.tf_savedmodel_dir, FLAGS.trt_savedmodel_dir)\n  benchmark(FLAGS.trt_savedmodel_dir, FLAGS.warmup_runs, FLAGS.bm_runs)\n\n\nif __name__ == '__main__':\n  flags.mark_flag_as_required('trt_savedmodel_dir')\n  tf.disable_v2_behavior()\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/tutorial.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"colab_type\": \"text\",\n        \"id\": \"V8-yl-s-WKMG\"\n      },\n      \"source\": [\n        \"# EfficientDet Tutorial: inference, eval, and training \\n\",\n        \"\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"<table align=\\\"left\\\"><td>\\n\",\n        \"  <a target=\\\"_blank\\\"  href=\\\"https://github.com/google/automl/blob/master/efficientdet/tutorial.ipynb\\\">\\n\",\n        \"    <img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on github\\n\",\n        \"  </a>\\n\",\n        \"</td><td>\\n\",\n        \"  <a target=\\\"_blank\\\"  href=\\\"https://colab.sandbox.google.com/github/google/automl/blob/master/efficientdet/tutorial.ipynb\\\">\\n\",\n        \"    <img width=32px src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"</td></table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"muwOCNHaq85j\",\n        \"colab_type\": \"text\"\n      },\n      \"source\": [\n        \"# 0. Install and view graph.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"colab_type\": \"text\",\n        \"id\": \"dggLVarNxxvC\"\n      },\n      \"source\": [\n        \"## 0.1 Install package and download source code/image.\\n\",\n        \"\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"metadata\": {\n        \"colab_type\": \"code\",\n        \"id\": \"hGL97-GXjSUw\",\n        \"colab\": {}\n      },\n      \"source\": [\n        \"%%capture\\n\",\n        \"#@title\\n\",\n        \"import os\\n\",\n        \"import sys\\n\",\n        \"import tensorflow.compat.v1 as tf\\n\",\n        \"\\n\",\n        \"# Download source code.\\n\",\n        \"if \\\"efficientdet\\\" not in os.getcwd():\\n\",\n        \"  !git clone --depth 1 https://github.com/google/automl\\n\",\n        \"  os.chdir('automl/efficientdet')\\n\",\n        \"  sys.path.append('.')\\n\",\n        \"  !pip install -r requirements.txt\\n\",\n        \"  !pip install -U 'git+https://github.com/cocodataset/cocoapi.git#subdirectory=PythonAPI'\\n\",\n        \"else:\\n\",\n        \"  !git pull\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": []\n    },\n    {\n      \"cell_type\": \"code\",\n      \"metadata\": {\n        \"id\": \"Tow-ic7H3d7i\",\n        \"colab_type\": \"code\",\n        \"outputId\": \"cd7629ea-6dab-4302-8cf3-eb8a94d83f65\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 408\n        }\n      },\n      \"source\": [\n        \"MODEL = 'efficientdet-d0'  #@param\\n\",\n        \"\\n\",\n        \"def download(m):\\n\",\n        \"  if m not in os.listdir():\\n\",\n        \"    !wget https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco/{m}.tar.gz\\n\",\n        \"    !tar zxf {m}.tar.gz\\n\",\n        \"  ckpt_path = os.path.join(os.getcwd(), m)\\n\",\n        \"  return ckpt_path\\n\",\n        \"\\n\",\n        \"# Download checkpoint.\\n\",\n        \"ckpt_path = download(MODEL)\\n\",\n        \"print('Use model in {}'.format(ckpt_path))\\n\",\n        \"\\n\",\n        \"# Prepare image and visualization settings.\\n\",\n        \"image_url =  'https://user-images.githubusercontent.com/11736571/77320690-099af300-6d37-11ea-9d86-24f14dc2d540.png'#@param\\n\",\n        \"image_name = 'img.png' #@param\\n\",\n        \"!wget {image_url} -O img.png\\n\",\n        \"import os\\n\",\n        \"img_path = os.path.join(os.getcwd(), 'img.png')\\n\",\n        \"\\n\",\n        \"min_score_thresh = 0.35  #@param\\n\",\n        \"max_boxes_to_draw = 200  #@param\\n\",\n        \"line_thickness =   2#@param\\n\",\n        \"\\n\",\n        \"import PIL\\n\",\n        \"# Get the largest of height/width and round to 128.\\n\",\n        \"image_size = max(PIL.Image.open(img_path).size)\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"--2020-06-01 05:07:16--  https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco/efficientdet-d0.tar.gz\\n\",\n            \"Resolving storage.googleapis.com (storage.googleapis.com)... 173.194.217.128, 2607:f8b0:400c:c05::80\\n\",\n            \"Connecting to storage.googleapis.com (storage.googleapis.com)|173.194.217.128|:443... connected.\\n\",\n            \"HTTP request sent, awaiting response... 200 OK\\n\",\n            \"Length: 28994253 (28M) [application/octet-stream]\\n\",\n            \"Saving to: ‘efficientdet-d0.tar.gz’\\n\",\n            \"\\n\",\n            \"\\refficientdet-d0.tar   0%[                    ]       0  --.-KB/s               \\refficientdet-d0.tar 100%[===================>]  27.65M   164MB/s    in 0.2s    \\n\",\n            \"\\n\",\n            \"2020-06-01 05:07:16 (164 MB/s) - ‘efficientdet-d0.tar.gz’ saved [28994253/28994253]\\n\",\n            \"\\n\",\n            \"Use model in /content/automl/efficientdet/efficientdet-d0\\n\",\n            \"--2020-06-01 05:07:18--  https://user-images.githubusercontent.com/11736571/77320690-099af300-6d37-11ea-9d86-24f14dc2d540.png\\n\",\n            \"Resolving user-images.githubusercontent.com (user-images.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\\n\",\n            \"Connecting to user-images.githubusercontent.com (user-images.githubusercontent.com)|151.101.0.133|:443... connected.\\n\",\n            \"HTTP request sent, awaiting response... 200 OK\\n\",\n            \"Length: 4080549 (3.9M) [image/png]\\n\",\n            \"Saving to: ‘img.png’\\n\",\n            \"\\n\",\n            \"img.png             100%[===================>]   3.89M  12.8MB/s    in 0.3s    \\n\",\n            \"\\n\",\n            \"2020-06-01 05:07:18 (12.8 MB/s) - ‘img.png’ saved [4080549/4080549]\\n\",\n            \"\\n\"\n          ],\n          \"name\": \"stdout\"\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"GvdjcYpUVuQ5\",\n        \"colab_type\": \"text\"\n      },\n      \"source\": [\n        \"## 0.2 View graph in TensorBoard\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"metadata\": {\n        \"id\": \"U2oz3r1LUDzr\",\n        \"colab_type\": \"code\",\n        \"colab\": {}\n      },\n      \"source\": [\n        \"!python model_inspect.py --model_name={MODEL} --logdir=logs &> /dev/null\\n\",\n        \"%load_ext tensorboard\\n\",\n        \"%tensorboard --logdir logs\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": []\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"vZk2dwOxrGhY\",\n        \"colab_type\": \"text\"\n      },\n      \"source\": [\n        \"# 1. inference\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"_VaF_j7jdVCK\",\n        \"colab_type\": \"text\"\n      },\n      \"source\": [\n        \"## 1.1 Benchmark network latency\\n\",\n        \"There are two types of latency:\\n\",\n        \"network latency and end-to-end latency.\\n\",\n        \"\\n\",\n        \"*   network latency: from the first conv op to the network class and box prediction.\\n\",\n        \"*   end-to-end latency: from image preprocessing, network, to the final postprocessing to generate a annotated new image.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"metadata\": {\n        \"id\": \"R_3gL01UbDLH\",\n        \"colab_type\": \"code\",\n        \"colab\": {}\n      },\n      \"source\": [\n        \"# benchmaak network latency\\n\",\n        \"!python model_inspect.py --runmode=bm --model_name=efficientdet-d4 --hparams=\\\"mixed_precision=true\\\"\\n\",\n        \"\\n\",\n        \"# With colab + Tesla T4 GPU, here are the batch size 1 latency summary:\\n\",\n        \"# D0 (AP=33.5):  14.9ms,  FPS = 67.2   (batch size 8 FPS=)\\n\",\n        \"# D1 (AP=39.6):  22.7ms,  FPS = 44.1   (batch size 8 FPS=)\\n\",\n        \"# D2 (AP=43.0):  27.9ms,  FPS = 35.8   (batch size 8 FPS=)\\n\",\n        \"# D3 (AP=45.8):  48.1ms,  FPS = 20.8   (batch size 8 FPS=)\\n\",\n        \"# D4 (AP=49.4):  81.9ms,  FPS = 12.2   (batch size 8 FPS=)\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": []\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"VW95IodKovEu\",\n        \"colab_type\": \"text\"\n      },\n      \"source\": [\n        \"## 1.2 Benchmark end-to-end latency\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"metadata\": {\n        \"id\": \"NSf6SrZcdavN\",\n        \"colab_type\": \"code\",\n        \"outputId\": \"cd5925ff-1b96-4561-dbb1-03334ded45e7\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 1000\n        }\n      },\n      \"source\": [\n        \"# Benchmark end-to-end latency (: preprocess + network + posprocess).\\n\",\n        \"#\\n\",\n        \"# With colab + Tesla T4 GPU, here are the batch size 1 latency summary:\\n\",\n        \"# D0 (AP=33.5): 22.7ms,  FPS = 43.1   (batch size 4, FPS=)\\n\",\n        \"# D1 (AP=39.6): 34.3ms,  FPS = 29.2   (batch size 4, FPS=)\\n\",\n        \"# D2 (AP=43.0): 42.5ms,  FPS = 23.5   (batch size 4, FPS=)\\n\",\n        \"# D3 (AP=45.8): 64.8ms,  FPS = 15.4   (batch size 4, FPS=)\\n\",\n        \"# D4 (AP=49.4): 93.7ms,  FPS = 10.7   (batch size 4, FPS=)\\n\",\n        \"\\n\",\n        \"m = 'efficientdet-d4'  # @param\\n\",\n        \"batch_size = 1  # @param\\n\",\n        \"m_path = download(m)\\n\",\n        \"\\n\",\n        \"saved_model_dir = 'savedmodel'\\n\",\n        \"!rm -rf {saved_model_dir}\\n\",\n        \"!python model_inspect.py --runmode=saved_model --model_name={m} \\\\\\n\",\n        \"  --ckpt_path={m_path} --saved_model_dir={saved_model_dir} \\\\\\n\",\n        \"  --batch_size={batch_size}  --hparams=\\\"mixed_precision=true\\\"\\n\",\n        \"!python model_inspect.py --runmode=saved_model_benchmark --model_name={m} \\\\\\n\",\n        \"  --ckpt_path={m_path} --saved_model_dir={saved_model_dir}/{m}_frozen.pb \\\\\\n\",\n        \"  --batch_size={batch_size}  --hparams=\\\"mixed_precision=true\\\" --input_image=testdata/img1.jpg\\n\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"--2020-06-01 05:42:55--  https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco/efficientdet-d4.tar.gz\\n\",\n            \"Resolving storage.googleapis.com (storage.googleapis.com)... 173.194.216.128, 2607:f8b0:400c:c01::80\\n\",\n            \"Connecting to storage.googleapis.com (storage.googleapis.com)|173.194.216.128|:443... connected.\\n\",\n            \"HTTP request sent, awaiting response... 200 OK\\n\",\n            \"Length: 152915674 (146M) [application/octet-stream]\\n\",\n            \"Saving to: ‘efficientdet-d4.tar.gz’\\n\",\n            \"\\n\",\n            \"efficientdet-d4.tar 100%[===================>] 145.83M   129MB/s    in 1.1s    \\n\",\n            \"\\n\",\n            \"2020-06-01 05:42:57 (129 MB/s) - ‘efficientdet-d4.tar.gz’ saved [152915674/152915674]\\n\",\n            \"\\n\",\n            \"2020-06-01 05:43:01.074313: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:43:03.448788: I tensorflow/core/platform/profile_utils/cpu_utils.cc:102] CPU Frequency: 2300000000 Hz\\n\",\n            \"2020-06-01 05:43:03.448968: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x1981100 initialized for platform Host (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-06-01 05:43:03.449000: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Host, Default Version\\n\",\n            \"2020-06-01 05:43:03.450763: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcuda.so.1\\n\",\n            \"2020-06-01 05:43:03.592469: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:43:03.593133: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x19812c0 initialized for platform CUDA (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-06-01 05:43:03.593171: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Tesla T4, Compute Capability 7.5\\n\",\n            \"2020-06-01 05:43:03.593434: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:43:03.593956: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1561] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla T4 computeCapability: 7.5\\n\",\n            \"coreClock: 1.59GHz coreCount: 40 deviceMemorySize: 14.73GiB deviceMemoryBandwidth: 298.08GiB/s\\n\",\n            \"2020-06-01 05:43:03.594020: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:43:03.595686: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"2020-06-01 05:43:03.597259: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2020-06-01 05:43:03.597631: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2020-06-01 05:43:03.599125: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2020-06-01 05:43:03.599845: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\\n\",\n            \"2020-06-01 05:43:03.602709: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 05:43:03.602851: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:43:03.603445: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:43:03.603962: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1703] Adding visible gpu devices: 0\\n\",\n            \"2020-06-01 05:43:03.604014: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:43:04.140562: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1102] Device interconnect StreamExecutor with strength 1 edge matrix:\\n\",\n            \"2020-06-01 05:43:04.140629: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1108]      0 \\n\",\n            \"2020-06-01 05:43:04.140646: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1121] 0:   N \\n\",\n            \"2020-06-01 05:43:04.140884: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:43:04.141536: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:43:04.142083: W tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:39] Overriding allow_growth setting because the TF_FORCE_GPU_ALLOW_GROWTH environment variable is set. Original config value was 0.\\n\",\n            \"2020-06-01 05:43:04.142134: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1247] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 13970 MB memory) -> physical GPU (device: 0, name: Tesla T4, pci bus id: 0000:00:04.0, compute capability: 7.5)\\n\",\n            \"2020-06-01 05:43:04.227307: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:43:04.227940: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1561] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla T4 computeCapability: 7.5\\n\",\n            \"coreClock: 1.59GHz coreCount: 40 deviceMemorySize: 14.73GiB deviceMemoryBandwidth: 298.08GiB/s\\n\",\n            \"2020-06-01 05:43:04.228013: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:43:04.228062: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"2020-06-01 05:43:04.228087: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2020-06-01 05:43:04.228109: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2020-06-01 05:43:04.228130: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2020-06-01 05:43:04.228151: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\\n\",\n            \"2020-06-01 05:43:04.228172: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 05:43:04.228304: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:43:04.229034: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:43:04.229563: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1703] Adding visible gpu devices: 0\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:1666: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"If using Keras pass *_constraint arguments to layers.\\n\",\n            \"W0601 05:43:06.053900 140469416372096 deprecation.py:506] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:1666: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"If using Keras pass *_constraint arguments to layers.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/profiler/internal/flops_registry.py:142: tensor_shape_from_node_def_name (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.tensor_shape_from_node_def_name`\\n\",\n            \"W0601 05:43:11.643087 140469416372096 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/profiler/internal/flops_registry.py:142: tensor_shape_from_node_def_name (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.tensor_shape_from_node_def_name`\\n\",\n            \"3 ops no flops stats due to incomplete shapes.\\n\",\n            \"Parsing Inputs...\\n\",\n            \"Incomplete shape.\\n\",\n            \"Incomplete shape.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:138: conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.Conv2D` instead.\\n\",\n            \"W0601 05:43:12.194021 140469416372096 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:138: conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.Conv2D` instead.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/layers/convolutional.py:424: Layer.apply (from tensorflow.python.keras.engine.base_layer_v1) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Please use `layer.__call__` method instead.\\n\",\n            \"W0601 05:43:12.197279 140469416372096 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/layers/convolutional.py:424: Layer.apply (from tensorflow.python.keras.engine.base_layer_v1) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Please use `layer.__call__` method instead.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:164: max_pooling2d (from tensorflow.python.layers.pooling) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use keras.layers.MaxPooling2D instead.\\n\",\n            \"W0601 05:43:12.273774 140469416372096 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:164: max_pooling2d (from tensorflow.python.layers.pooling) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use keras.layers.MaxPooling2D instead.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:685: separable_conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.SeparableConv2D` instead.\\n\",\n            \"W0601 05:43:12.303184 140469416372096 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:685: separable_conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.SeparableConv2D` instead.\\n\",\n            \"3 ops no flops stats due to incomplete shapes.\\n\",\n            \"Parsing Inputs...\\n\",\n            \"Incomplete shape.\\n\",\n            \"Incomplete shape.\\n\",\n            \"3 ops no flops stats due to incomplete shapes.\\n\",\n            \"Parsing Inputs...\\n\",\n            \"Incomplete shape.\\n\",\n            \"Incomplete shape.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/training/moving_averages.py:444: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.\\n\",\n            \"W0601 05:43:29.570928 140469416372096 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/training/moving_averages.py:444: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/saved_model/signature_def_utils_impl.py:201: build_tensor_info (from tensorflow.python.saved_model.utils_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.utils.build_tensor_info or tf.compat.v1.saved_model.build_tensor_info.\\n\",\n            \"W0601 05:43:59.930180 140469416372096 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/saved_model/signature_def_utils_impl.py:201: build_tensor_info (from tensorflow.python.saved_model.utils_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.utils.build_tensor_info or tf.compat.v1.saved_model.build_tensor_info.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/inference.py:691: convert_variables_to_constants (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.convert_variables_to_constants`\\n\",\n            \"W0601 05:44:11.018419 140469416372096 deprecation.py:323] From /content/automl/efficientdet/inference.py:691: convert_variables_to_constants (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.convert_variables_to_constants`\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/graph_util_impl.py:359: extract_sub_graph (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.extract_sub_graph`\\n\",\n            \"W0601 05:44:11.018674 140469416372096 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/graph_util_impl.py:359: extract_sub_graph (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.extract_sub_graph`\\n\",\n            \"2020-06-01 05:44:16.731031: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:44:19.085146: I tensorflow/core/platform/profile_utils/cpu_utils.cc:102] CPU Frequency: 2300000000 Hz\\n\",\n            \"2020-06-01 05:44:19.085373: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x251f100 initialized for platform Host (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-06-01 05:44:19.085408: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Host, Default Version\\n\",\n            \"2020-06-01 05:44:19.087291: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcuda.so.1\\n\",\n            \"2020-06-01 05:44:19.230882: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:44:19.231588: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x251f2c0 initialized for platform CUDA (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-06-01 05:44:19.231624: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Tesla T4, Compute Capability 7.5\\n\",\n            \"2020-06-01 05:44:19.231847: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:44:19.232455: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1561] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla T4 computeCapability: 7.5\\n\",\n            \"coreClock: 1.59GHz coreCount: 40 deviceMemorySize: 14.73GiB deviceMemoryBandwidth: 298.08GiB/s\\n\",\n            \"2020-06-01 05:44:19.232525: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:44:19.234063: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"2020-06-01 05:44:19.235648: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2020-06-01 05:44:19.235996: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2020-06-01 05:44:19.237520: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2020-06-01 05:44:19.238227: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\\n\",\n            \"2020-06-01 05:44:19.241182: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 05:44:19.241357: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:44:19.242002: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:44:19.242545: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1703] Adding visible gpu devices: 0\\n\",\n            \"2020-06-01 05:44:19.242614: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:44:19.784102: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1102] Device interconnect StreamExecutor with strength 1 edge matrix:\\n\",\n            \"2020-06-01 05:44:19.784159: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1108]      0 \\n\",\n            \"2020-06-01 05:44:19.784173: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1121] 0:   N \\n\",\n            \"2020-06-01 05:44:19.784431: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:44:19.785121: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:44:19.785723: W tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:39] Overriding allow_growth setting because the TF_FORCE_GPU_ALLOW_GROWTH environment variable is set. Original config value was 0.\\n\",\n            \"2020-06-01 05:44:19.785773: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1247] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 13970 MB memory) -> physical GPU (device: 0, name: Tesla T4, pci bus id: 0000:00:04.0, compute capability: 7.5)\\n\",\n            \"2020-06-01 05:44:22.260525: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 05:44:23.974135: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"Per batch inference time:  0.09369662179997248\\n\",\n            \"FPS:  10.672743379530186\\n\"\n          ],\n          \"name\": \"stdout\"\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"jGKs3w2_ZXnu\",\n        \"colab_type\": \"text\"\n      },\n      \"source\": [\n        \"## 1.3 Inference images.\\n\",\n        \"\\n\",\n        \"---\\n\",\n        \"\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"metadata\": {\n        \"id\": \"tlh_S6M9ahe5\",\n        \"colab_type\": \"code\",\n        \"outputId\": \"be8ff756-2b62-46a8-de85-3379903b1340\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 1000\n        }\n      },\n      \"source\": [\n        \"# first export a saved model.\\n\",\n        \"saved_model_dir = 'savedmodel'\\n\",\n        \"!rm -rf {saved_model_dir}\\n\",\n        \"!python model_inspect.py --runmode=saved_model --model_name={MODEL} \\\\\\n\",\n        \"  --ckpt_path={ckpt_path} --saved_model_dir={saved_model_dir}\\n\",\n        \"\\n\",\n        \"# Then run saved_model_infer to do inference.\\n\",\n        \"# Notably: batch_size, image_size must be the same as when it is exported.\\n\",\n        \"serve_image_out = 'serve_image_out'\\n\",\n        \"!mkdir {serve_image_out}\\n\",\n        \"\\n\",\n        \"!python model_inspect.py --runmode=saved_model_infer \\\\\\n\",\n        \"  --saved_model_dir={saved_model_dir} \\\\\\n\",\n        \"  --model_name={MODEL}  --input_image=testdata/img1.jpg  \\\\\\n\",\n        \"  --output_image_dir={serve_image_out} \\\\\\n\",\n        \"  --min_score_thresh={min_score_thresh}  --max_boxes_to_draw={max_boxes_to_draw}\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"2020-05-31 22:18:29.834258: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-05-31 22:18:31.984806: I tensorflow/core/platform/profile_utils/cpu_utils.cc:102] CPU Frequency: 2300000000 Hz\\n\",\n            \"2020-05-31 22:18:31.984976: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x20ff100 initialized for platform Host (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-05-31 22:18:31.985007: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Host, Default Version\\n\",\n            \"2020-05-31 22:18:31.986636: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcuda.so.1\\n\",\n            \"2020-05-31 22:18:32.117308: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-05-31 22:18:32.117967: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x20ff2c0 initialized for platform CUDA (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-05-31 22:18:32.117998: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Tesla T4, Compute Capability 7.5\\n\",\n            \"2020-05-31 22:18:32.118194: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-05-31 22:18:32.118719: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1561] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla T4 computeCapability: 7.5\\n\",\n            \"coreClock: 1.59GHz coreCount: 40 deviceMemorySize: 14.73GiB deviceMemoryBandwidth: 298.08GiB/s\\n\",\n            \"2020-05-31 22:18:32.118768: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-05-31 22:18:32.120078: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"2020-05-31 22:18:32.121500: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2020-05-31 22:18:32.121824: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2020-05-31 22:18:32.123138: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2020-05-31 22:18:32.123796: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\\n\",\n            \"2020-05-31 22:18:32.126344: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-05-31 22:18:32.126500: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-05-31 22:18:32.127069: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-05-31 22:18:32.127552: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1703] Adding visible gpu devices: 0\\n\",\n            \"2020-05-31 22:18:32.127597: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-05-31 22:18:32.624626: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1102] Device interconnect StreamExecutor with strength 1 edge matrix:\\n\",\n            \"2020-05-31 22:18:32.624685: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1108]      0 \\n\",\n            \"2020-05-31 22:18:32.624697: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1121] 0:   N \\n\",\n            \"2020-05-31 22:18:32.624908: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-05-31 22:18:32.625519: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-05-31 22:18:32.626014: W tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:39] Overriding allow_growth setting because the TF_FORCE_GPU_ALLOW_GROWTH environment variable is set. Original config value was 0.\\n\",\n            \"2020-05-31 22:18:32.626056: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1247] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 13970 MB memory) -> physical GPU (device: 0, name: Tesla T4, pci bus id: 0000:00:04.0, compute capability: 7.5)\\n\",\n            \"2020-05-31 22:18:32.674343: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-05-31 22:18:32.674888: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1561] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla T4 computeCapability: 7.5\\n\",\n            \"coreClock: 1.59GHz coreCount: 40 deviceMemorySize: 14.73GiB deviceMemoryBandwidth: 298.08GiB/s\\n\",\n            \"2020-05-31 22:18:32.674931: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-05-31 22:18:32.674979: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"2020-05-31 22:18:32.675000: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2020-05-31 22:18:32.675019: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2020-05-31 22:18:32.675037: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2020-05-31 22:18:32.675055: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\\n\",\n            \"2020-05-31 22:18:32.675073: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-05-31 22:18:32.675164: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-05-31 22:18:32.675714: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-05-31 22:18:32.676183: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1703] Adding visible gpu devices: 0\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:1666: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"If using Keras pass *_constraint arguments to layers.\\n\",\n            \"W0531 22:18:34.165251 139695374006144 deprecation.py:506] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:1666: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"If using Keras pass *_constraint arguments to layers.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/profiler/internal/flops_registry.py:142: tensor_shape_from_node_def_name (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.tensor_shape_from_node_def_name`\\n\",\n            \"W0531 22:18:37.391232 139695374006144 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/profiler/internal/flops_registry.py:142: tensor_shape_from_node_def_name (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.tensor_shape_from_node_def_name`\\n\",\n            \"3 ops no flops stats due to incomplete shapes.\\n\",\n            \"Parsing Inputs...\\n\",\n            \"Incomplete shape.\\n\",\n            \"Incomplete shape.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:138: conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.Conv2D` instead.\\n\",\n            \"W0531 22:18:37.628946 139695374006144 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:138: conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.Conv2D` instead.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/layers/convolutional.py:424: Layer.apply (from tensorflow.python.keras.engine.base_layer_v1) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Please use `layer.__call__` method instead.\\n\",\n            \"W0531 22:18:37.632798 139695374006144 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/layers/convolutional.py:424: Layer.apply (from tensorflow.python.keras.engine.base_layer_v1) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Please use `layer.__call__` method instead.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:164: max_pooling2d (from tensorflow.python.layers.pooling) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use keras.layers.MaxPooling2D instead.\\n\",\n            \"W0531 22:18:37.685599 139695374006144 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:164: max_pooling2d (from tensorflow.python.layers.pooling) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use keras.layers.MaxPooling2D instead.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:685: separable_conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.SeparableConv2D` instead.\\n\",\n            \"W0531 22:18:37.708653 139695374006144 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:685: separable_conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.SeparableConv2D` instead.\\n\",\n            \"3 ops no flops stats due to incomplete shapes.\\n\",\n            \"Parsing Inputs...\\n\",\n            \"Incomplete shape.\\n\",\n            \"Incomplete shape.\\n\",\n            \"3 ops no flops stats due to incomplete shapes.\\n\",\n            \"Parsing Inputs...\\n\",\n            \"Incomplete shape.\\n\",\n            \"Incomplete shape.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/training/moving_averages.py:444: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.\\n\",\n            \"W0531 22:18:44.171763 139695374006144 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/training/moving_averages.py:444: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/saved_model/signature_def_utils_impl.py:201: build_tensor_info (from tensorflow.python.saved_model.utils_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.utils.build_tensor_info or tf.compat.v1.saved_model.build_tensor_info.\\n\",\n            \"W0531 22:18:57.940969 139695374006144 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/saved_model/signature_def_utils_impl.py:201: build_tensor_info (from tensorflow.python.saved_model.utils_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.utils.build_tensor_info or tf.compat.v1.saved_model.build_tensor_info.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/inference.py:690: convert_variables_to_constants (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.convert_variables_to_constants`\\n\",\n            \"W0531 22:19:03.135875 139695374006144 deprecation.py:323] From /content/automl/efficientdet/inference.py:690: convert_variables_to_constants (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.convert_variables_to_constants`\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/graph_util_impl.py:359: extract_sub_graph (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.extract_sub_graph`\\n\",\n            \"W0531 22:19:03.136129 139695374006144 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/graph_util_impl.py:359: extract_sub_graph (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.extract_sub_graph`\\n\",\n            \"2020-05-31 22:19:06.848442: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-05-31 22:19:09.071057: I tensorflow/core/platform/profile_utils/cpu_utils.cc:102] CPU Frequency: 2300000000 Hz\\n\",\n            \"2020-05-31 22:19:09.071237: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x12bf100 initialized for platform Host (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-05-31 22:19:09.071269: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Host, Default Version\\n\",\n            \"2020-05-31 22:19:09.073086: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcuda.so.1\\n\",\n            \"2020-05-31 22:19:09.205153: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-05-31 22:19:09.205811: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x12bf2c0 initialized for platform CUDA (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-05-31 22:19:09.205841: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Tesla T4, Compute Capability 7.5\\n\",\n            \"2020-05-31 22:19:09.206047: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-05-31 22:19:09.206568: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1561] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla T4 computeCapability: 7.5\\n\",\n            \"coreClock: 1.59GHz coreCount: 40 deviceMemorySize: 14.73GiB deviceMemoryBandwidth: 298.08GiB/s\\n\",\n            \"2020-05-31 22:19:09.206617: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-05-31 22:19:09.207943: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"2020-05-31 22:19:09.209330: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2020-05-31 22:19:09.209664: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2020-05-31 22:19:09.210969: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2020-05-31 22:19:09.211593: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\\n\",\n            \"2020-05-31 22:19:09.214126: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-05-31 22:19:09.214251: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-05-31 22:19:09.214834: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-05-31 22:19:09.215304: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1703] Adding visible gpu devices: 0\\n\",\n            \"2020-05-31 22:19:09.215348: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-05-31 22:19:09.724754: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1102] Device interconnect StreamExecutor with strength 1 edge matrix:\\n\",\n            \"2020-05-31 22:19:09.724808: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1108]      0 \\n\",\n            \"2020-05-31 22:19:09.724820: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1121] 0:   N \\n\",\n            \"2020-05-31 22:19:09.725035: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-05-31 22:19:09.725656: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-05-31 22:19:09.726145: W tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:39] Overriding allow_growth setting because the TF_FORCE_GPU_ALLOW_GROWTH environment variable is set. Original config value was 0.\\n\",\n            \"2020-05-31 22:19:09.726185: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1247] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 13970 MB memory) -> physical GPU (device: 0, name: Tesla T4, pci bus id: 0000:00:04.0, compute capability: 7.5)\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/inference.py:678: load (from tensorflow.python.saved_model.loader_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.loader.load or tf.compat.v1.saved_model.load. There will be a new function for importing SavedModels in Tensorflow 2.0.\\n\",\n            \"W0531 22:19:09.726969 139773881436032 deprecation.py:323] From /content/automl/efficientdet/inference.py:678: load (from tensorflow.python.saved_model.loader_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.loader.load or tf.compat.v1.saved_model.load. There will be a new function for importing SavedModels in Tensorflow 2.0.\\n\",\n            \"all_files= ['testdata/img1.jpg']\\n\",\n            \"2020-05-31 22:19:15.098209: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-05-31 22:19:16.755365: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\"\n          ],\n          \"name\": \"stdout\"\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"metadata\": {\n        \"id\": \"1q2x8s8GpUJz\",\n        \"colab_type\": \"code\",\n        \"colab\": {}\n      },\n      \"source\": [\n        \"from IPython import display\\n\",\n        \"display.display(display.Image(os.path.join(serve_image_out, '0.jpg')))\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": []\n    },\n    {\n      \"cell_type\": \"code\",\n      \"metadata\": {\n        \"id\": \"fHU46tfckaZo\",\n        \"colab_type\": \"code\",\n        \"outputId\": \"1766aecc-1b89-4142-9fd7-2bac421d6411\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 1000\n        }\n      },\n      \"source\": [\n        \"# In case you need to specify different image size or batch size or #boxes, then\\n\",\n        \"# you need to export a new saved model and run the inferernce.\\n\",\n        \"\\n\",\n        \"serve_image_out = 'serve_image_out'\\n\",\n        \"!mkdir {serve_image_out}\\n\",\n        \"saved_model_dir = 'savedmodel'\\n\",\n        \"!rm -rf {saved_model_dir}\\n\",\n        \"\\n\",\n        \"# Step 1: export model\\n\",\n        \"!python model_inspect.py --runmode=saved_model \\\\\\n\",\n        \"  --model_name=efficientdet-d0 --ckpt_path=efficientdet-d0 \\\\\\n\",\n        \"  --hparams=\\\"image_size=1920x1280\\\" --saved_model_dir={saved_model_dir}\\n\",\n        \"\\n\",\n        \"# Step 2: do inference with saved model.\\n\",\n        \"!python model_inspect.py --runmode=saved_model_infer \\\\\\n\",\n        \"  --model_name=efficientdet-d0 --saved_model_dir={saved_model_dir} \\\\\\n\",\n        \"  --input_image=img.png --output_image_dir={serve_image_out} \\\\\\n\",\n        \"  --min_score_thresh={min_score_thresh}  --max_boxes_to_draw={max_boxes_to_draw}\\n\",\n        \"\\n\",\n        \"from IPython import display\\n\",\n        \"display.display(display.Image(os.path.join(serve_image_out, '0.jpg')))\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"2020-06-01 05:46:14.071332: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:46:16.401257: I tensorflow/core/platform/profile_utils/cpu_utils.cc:102] CPU Frequency: 2300000000 Hz\\n\",\n            \"2020-06-01 05:46:16.401496: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x270f100 initialized for platform Host (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-06-01 05:46:16.401531: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Host, Default Version\\n\",\n            \"2020-06-01 05:46:16.403436: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcuda.so.1\\n\",\n            \"2020-06-01 05:46:16.547796: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:46:16.548486: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x270f2c0 initialized for platform CUDA (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-06-01 05:46:16.548520: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Tesla T4, Compute Capability 7.5\\n\",\n            \"2020-06-01 05:46:16.548728: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:46:16.549250: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1561] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla T4 computeCapability: 7.5\\n\",\n            \"coreClock: 1.59GHz coreCount: 40 deviceMemorySize: 14.73GiB deviceMemoryBandwidth: 298.08GiB/s\\n\",\n            \"2020-06-01 05:46:16.549316: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:46:16.550811: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"2020-06-01 05:46:16.552313: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2020-06-01 05:46:16.552651: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2020-06-01 05:46:16.554048: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2020-06-01 05:46:16.554769: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\\n\",\n            \"2020-06-01 05:46:16.557801: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 05:46:16.557946: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:46:16.558600: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:46:16.559170: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1703] Adding visible gpu devices: 0\\n\",\n            \"2020-06-01 05:46:16.559220: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:46:17.087089: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1102] Device interconnect StreamExecutor with strength 1 edge matrix:\\n\",\n            \"2020-06-01 05:46:17.087152: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1108]      0 \\n\",\n            \"2020-06-01 05:46:17.087166: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1121] 0:   N \\n\",\n            \"2020-06-01 05:46:17.087410: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:46:17.088037: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:46:17.088655: W tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:39] Overriding allow_growth setting because the TF_FORCE_GPU_ALLOW_GROWTH environment variable is set. Original config value was 0.\\n\",\n            \"2020-06-01 05:46:17.088701: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1247] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 13970 MB memory) -> physical GPU (device: 0, name: Tesla T4, pci bus id: 0000:00:04.0, compute capability: 7.5)\\n\",\n            \"2020-06-01 05:46:17.142073: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:46:17.142901: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1561] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla T4 computeCapability: 7.5\\n\",\n            \"coreClock: 1.59GHz coreCount: 40 deviceMemorySize: 14.73GiB deviceMemoryBandwidth: 298.08GiB/s\\n\",\n            \"2020-06-01 05:46:17.142958: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:46:17.143004: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"2020-06-01 05:46:17.143029: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2020-06-01 05:46:17.143049: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2020-06-01 05:46:17.143070: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2020-06-01 05:46:17.143090: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\\n\",\n            \"2020-06-01 05:46:17.143110: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 05:46:17.143227: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:46:17.144030: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:46:17.144772: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1703] Adding visible gpu devices: 0\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:1666: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"If using Keras pass *_constraint arguments to layers.\\n\",\n            \"W0601 05:46:18.736636 139905342916480 deprecation.py:506] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:1666: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"If using Keras pass *_constraint arguments to layers.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/profiler/internal/flops_registry.py:142: tensor_shape_from_node_def_name (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.tensor_shape_from_node_def_name`\\n\",\n            \"W0601 05:46:22.133676 139905342916480 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/profiler/internal/flops_registry.py:142: tensor_shape_from_node_def_name (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.tensor_shape_from_node_def_name`\\n\",\n            \"3 ops no flops stats due to incomplete shapes.\\n\",\n            \"Parsing Inputs...\\n\",\n            \"Incomplete shape.\\n\",\n            \"Incomplete shape.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:138: conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.Conv2D` instead.\\n\",\n            \"W0601 05:46:22.451671 139905342916480 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:138: conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.Conv2D` instead.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/layers/convolutional.py:424: Layer.apply (from tensorflow.python.keras.engine.base_layer_v1) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Please use `layer.__call__` method instead.\\n\",\n            \"W0601 05:46:22.454935 139905342916480 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/layers/convolutional.py:424: Layer.apply (from tensorflow.python.keras.engine.base_layer_v1) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Please use `layer.__call__` method instead.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:164: max_pooling2d (from tensorflow.python.layers.pooling) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use keras.layers.MaxPooling2D instead.\\n\",\n            \"W0601 05:46:22.505055 139905342916480 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:164: max_pooling2d (from tensorflow.python.layers.pooling) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use keras.layers.MaxPooling2D instead.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:685: separable_conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.SeparableConv2D` instead.\\n\",\n            \"W0601 05:46:22.525332 139905342916480 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:685: separable_conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.SeparableConv2D` instead.\\n\",\n            \"3 ops no flops stats due to incomplete shapes.\\n\",\n            \"Parsing Inputs...\\n\",\n            \"Incomplete shape.\\n\",\n            \"Incomplete shape.\\n\",\n            \"3 ops no flops stats due to incomplete shapes.\\n\",\n            \"Parsing Inputs...\\n\",\n            \"Incomplete shape.\\n\",\n            \"Incomplete shape.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/training/moving_averages.py:444: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.\\n\",\n            \"W0601 05:46:29.380645 139905342916480 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/training/moving_averages.py:444: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/saved_model/signature_def_utils_impl.py:201: build_tensor_info (from tensorflow.python.saved_model.utils_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.utils.build_tensor_info or tf.compat.v1.saved_model.build_tensor_info.\\n\",\n            \"W0601 05:46:44.401570 139905342916480 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/saved_model/signature_def_utils_impl.py:201: build_tensor_info (from tensorflow.python.saved_model.utils_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.utils.build_tensor_info or tf.compat.v1.saved_model.build_tensor_info.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/inference.py:691: convert_variables_to_constants (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.convert_variables_to_constants`\\n\",\n            \"W0601 05:46:50.026528 139905342916480 deprecation.py:323] From /content/automl/efficientdet/inference.py:691: convert_variables_to_constants (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.convert_variables_to_constants`\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/graph_util_impl.py:359: extract_sub_graph (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.extract_sub_graph`\\n\",\n            \"W0601 05:46:50.026841 139905342916480 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/graph_util_impl.py:359: extract_sub_graph (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.extract_sub_graph`\\n\",\n            \"2020-06-01 05:46:54.256374: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:46:56.608253: I tensorflow/core/platform/profile_utils/cpu_utils.cc:102] CPU Frequency: 2300000000 Hz\\n\",\n            \"2020-06-01 05:46:56.608499: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x2fef100 initialized for platform Host (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-06-01 05:46:56.608535: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Host, Default Version\\n\",\n            \"2020-06-01 05:46:56.610375: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcuda.so.1\\n\",\n            \"2020-06-01 05:46:56.757873: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:46:56.758619: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x2fef2c0 initialized for platform CUDA (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-06-01 05:46:56.758659: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Tesla T4, Compute Capability 7.5\\n\",\n            \"2020-06-01 05:46:56.759121: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:46:56.759876: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1561] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla T4 computeCapability: 7.5\\n\",\n            \"coreClock: 1.59GHz coreCount: 40 deviceMemorySize: 14.73GiB deviceMemoryBandwidth: 298.08GiB/s\\n\",\n            \"2020-06-01 05:46:56.759940: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:46:56.761691: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"2020-06-01 05:46:56.763507: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2020-06-01 05:46:56.763818: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2020-06-01 05:46:56.765201: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2020-06-01 05:46:56.765873: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\\n\",\n            \"2020-06-01 05:46:56.768656: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 05:46:56.768788: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:46:56.769388: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:46:56.769953: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1703] Adding visible gpu devices: 0\\n\",\n            \"2020-06-01 05:46:56.770002: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:46:57.314572: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1102] Device interconnect StreamExecutor with strength 1 edge matrix:\\n\",\n            \"2020-06-01 05:46:57.314626: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1108]      0 \\n\",\n            \"2020-06-01 05:46:57.314640: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1121] 0:   N \\n\",\n            \"2020-06-01 05:46:57.314882: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:46:57.315517: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:46:57.316053: W tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:39] Overriding allow_growth setting because the TF_FORCE_GPU_ALLOW_GROWTH environment variable is set. Original config value was 0.\\n\",\n            \"2020-06-01 05:46:57.316097: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1247] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 13970 MB memory) -> physical GPU (device: 0, name: Tesla T4, pci bus id: 0000:00:04.0, compute capability: 7.5)\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/inference.py:679: load (from tensorflow.python.saved_model.loader_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.loader.load or tf.compat.v1.saved_model.load. There will be a new function for importing SavedModels in Tensorflow 2.0.\\n\",\n            \"W0601 05:46:57.316836 140378130581376 deprecation.py:323] From /content/automl/efficientdet/inference.py:679: load (from tensorflow.python.saved_model.loader_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.loader.load or tf.compat.v1.saved_model.load. There will be a new function for importing SavedModels in Tensorflow 2.0.\\n\",\n            \"all_files= ['./img.png']\\n\",\n            \"2020-06-01 05:47:03.094128: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 05:47:05.000097: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\"\n          ],\n          \"name\": \"stdout\"\n        },\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"image/jpeg\": \"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\\n\",\n            \"text/plain\": [\n              \"<IPython.core.display.Image object>\"\n            ]\n          },\n          \"metadata\": {\n            \"tags\": []\n          }\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Vxm-kvfuAZne\",\n        \"colab_type\": \"text\"\n      },\n      \"source\": [\n        \"## 1.4 Inference video\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"metadata\": {\n        \"id\": \"3Pdnd1kQAgKY\",\n        \"colab_type\": \"code\",\n        \"outputId\": \"349bcbf2-4ad4-46d9-836b-17e5e1ea03b5\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 1000\n        }\n      },\n      \"source\": [\n        \"# step 0: download video\\n\",\n        \"video_url = 'https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/data/video480p.mov'  # @param\\n\",\n        \"!wget {video_url} -O input.mov\\n\",\n        \"\\n\",\n        \"# Step 1: export model\\n\",\n        \"saved_model_dir = 'savedmodel'\\n\",\n        \"!rm -rf {saved_model_dir}\\n\",\n        \"\\n\",\n        \"!python model_inspect.py --runmode=saved_model \\\\\\n\",\n        \"  --model_name=efficientdet-d0 --ckpt_path=efficientdet-d0 \\\\\\n\",\n        \"  --saved_model_dir={saved_model_dir} --hparams=\\\"mixed_precision=true\\\"\\n\",\n        \"\\n\",\n        \"# Step 2: do inference with saved model using saved_model_video\\n\",\n        \"!python model_inspect.py --runmode=saved_model_video \\\\\\n\",\n        \"  --model_name=efficientdet-d0   --ckpt_path=efficientdet-d0 \\\\\\n\",\n        \"  --saved_model_dir={saved_model_dir} --hparams=\\\"mixed_precision=true\\\" \\\\\\n\",\n        \"  --input_video=input.mov --output_video=output.mov\\n\",\n        \"# Then you can view the output.mov\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"--2020-06-01 05:08:08--  https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/data/video480p.mov\\n\",\n            \"Resolving storage.googleapis.com (storage.googleapis.com)... 172.217.204.128, 2607:f8b0:400c:c05::80\\n\",\n            \"Connecting to storage.googleapis.com (storage.googleapis.com)|172.217.204.128|:443... connected.\\n\",\n            \"HTTP request sent, awaiting response... 200 OK\\n\",\n            \"Length: 18511760 (18M) [application/octet-stream]\\n\",\n            \"Saving to: ‘input.mov’\\n\",\n            \"\\n\",\n            \"\\rinput.mov             0%[                    ]       0  --.-KB/s               \\rinput.mov           100%[===================>]  17.65M  --.-KB/s    in 0.1s    \\n\",\n            \"\\n\",\n            \"2020-06-01 05:08:08 (119 MB/s) - ‘input.mov’ saved [18511760/18511760]\\n\",\n            \"\\n\",\n            \"2020-06-01 05:08:10.640913: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:08:12.964736: I tensorflow/core/platform/profile_utils/cpu_utils.cc:102] CPU Frequency: 2300000000 Hz\\n\",\n            \"2020-06-01 05:08:12.965102: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x2c41480 initialized for platform Host (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-06-01 05:08:12.965147: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Host, Default Version\\n\",\n            \"2020-06-01 05:08:12.969921: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcuda.so.1\\n\",\n            \"2020-06-01 05:08:13.189523: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:08:13.190261: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x2c41640 initialized for platform CUDA (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-06-01 05:08:13.190315: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Tesla T4, Compute Capability 7.5\\n\",\n            \"2020-06-01 05:08:13.191600: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:08:13.192165: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1561] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla T4 computeCapability: 7.5\\n\",\n            \"coreClock: 1.59GHz coreCount: 40 deviceMemorySize: 14.73GiB deviceMemoryBandwidth: 298.08GiB/s\\n\",\n            \"2020-06-01 05:08:13.192213: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:08:13.416369: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"2020-06-01 05:08:13.542814: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2020-06-01 05:08:13.564000: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2020-06-01 05:08:13.832152: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2020-06-01 05:08:13.852017: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\\n\",\n            \"2020-06-01 05:08:14.369495: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 05:08:14.369747: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:08:14.370506: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:08:14.371069: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1703] Adding visible gpu devices: 0\\n\",\n            \"2020-06-01 05:08:14.374734: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:08:21.144501: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1102] Device interconnect StreamExecutor with strength 1 edge matrix:\\n\",\n            \"2020-06-01 05:08:21.144562: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1108]      0 \\n\",\n            \"2020-06-01 05:08:21.144576: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1121] 0:   N \\n\",\n            \"2020-06-01 05:08:21.150487: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:08:21.151227: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:08:21.151811: W tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:39] Overriding allow_growth setting because the TF_FORCE_GPU_ALLOW_GROWTH environment variable is set. Original config value was 0.\\n\",\n            \"2020-06-01 05:08:21.151864: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1247] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 13970 MB memory) -> physical GPU (device: 0, name: Tesla T4, pci bus id: 0000:00:04.0, compute capability: 7.5)\\n\",\n            \"2020-06-01 05:08:21.223963: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:08:21.224553: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1561] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla T4 computeCapability: 7.5\\n\",\n            \"coreClock: 1.59GHz coreCount: 40 deviceMemorySize: 14.73GiB deviceMemoryBandwidth: 298.08GiB/s\\n\",\n            \"2020-06-01 05:08:21.224604: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:08:21.224650: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"2020-06-01 05:08:21.224675: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2020-06-01 05:08:21.224698: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2020-06-01 05:08:21.224720: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2020-06-01 05:08:21.224742: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\\n\",\n            \"2020-06-01 05:08:21.224764: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 05:08:21.224874: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:08:21.225451: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:08:21.225958: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1703] Adding visible gpu devices: 0\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:1666: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"If using Keras pass *_constraint arguments to layers.\\n\",\n            \"W0601 05:08:22.807971 140451268405120 deprecation.py:506] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:1666: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"If using Keras pass *_constraint arguments to layers.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/profiler/internal/flops_registry.py:142: tensor_shape_from_node_def_name (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.tensor_shape_from_node_def_name`\\n\",\n            \"W0601 05:08:26.245461 140451268405120 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/profiler/internal/flops_registry.py:142: tensor_shape_from_node_def_name (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.tensor_shape_from_node_def_name`\\n\",\n            \"3 ops no flops stats due to incomplete shapes.\\n\",\n            \"Parsing Inputs...\\n\",\n            \"Incomplete shape.\\n\",\n            \"Incomplete shape.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:138: conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.Conv2D` instead.\\n\",\n            \"W0601 05:08:26.472386 140451268405120 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:138: conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.Conv2D` instead.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/layers/convolutional.py:424: Layer.apply (from tensorflow.python.keras.engine.base_layer_v1) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Please use `layer.__call__` method instead.\\n\",\n            \"W0601 05:08:26.475308 140451268405120 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/layers/convolutional.py:424: Layer.apply (from tensorflow.python.keras.engine.base_layer_v1) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Please use `layer.__call__` method instead.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:164: max_pooling2d (from tensorflow.python.layers.pooling) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use keras.layers.MaxPooling2D instead.\\n\",\n            \"W0601 05:08:26.531509 140451268405120 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:164: max_pooling2d (from tensorflow.python.layers.pooling) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use keras.layers.MaxPooling2D instead.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:685: separable_conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.SeparableConv2D` instead.\\n\",\n            \"W0601 05:08:26.551836 140451268405120 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:685: separable_conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.SeparableConv2D` instead.\\n\",\n            \"3 ops no flops stats due to incomplete shapes.\\n\",\n            \"Parsing Inputs...\\n\",\n            \"Incomplete shape.\\n\",\n            \"Incomplete shape.\\n\",\n            \"3 ops no flops stats due to incomplete shapes.\\n\",\n            \"Parsing Inputs...\\n\",\n            \"Incomplete shape.\\n\",\n            \"Incomplete shape.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/training/moving_averages.py:444: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.\\n\",\n            \"W0601 05:08:33.364987 140451268405120 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/training/moving_averages.py:444: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/saved_model/signature_def_utils_impl.py:201: build_tensor_info (from tensorflow.python.saved_model.utils_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.utils.build_tensor_info or tf.compat.v1.saved_model.build_tensor_info.\\n\",\n            \"W0601 05:08:47.828152 140451268405120 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/saved_model/signature_def_utils_impl.py:201: build_tensor_info (from tensorflow.python.saved_model.utils_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.utils.build_tensor_info or tf.compat.v1.saved_model.build_tensor_info.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/inference.py:691: convert_variables_to_constants (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.convert_variables_to_constants`\\n\",\n            \"W0601 05:08:53.265198 140451268405120 deprecation.py:323] From /content/automl/efficientdet/inference.py:691: convert_variables_to_constants (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.convert_variables_to_constants`\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/graph_util_impl.py:359: extract_sub_graph (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.extract_sub_graph`\\n\",\n            \"W0601 05:08:53.265506 140451268405120 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/graph_util_impl.py:359: extract_sub_graph (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.extract_sub_graph`\\n\",\n            \"2020-06-01 05:08:56.710234: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:08:59.535479: I tensorflow/core/platform/profile_utils/cpu_utils.cc:102] CPU Frequency: 2300000000 Hz\\n\",\n            \"2020-06-01 05:08:59.535664: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x30d3100 initialized for platform Host (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-06-01 05:08:59.535696: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Host, Default Version\\n\",\n            \"2020-06-01 05:08:59.537584: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcuda.so.1\\n\",\n            \"2020-06-01 05:08:59.671441: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:08:59.672074: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x30d32c0 initialized for platform CUDA (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-06-01 05:08:59.672104: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Tesla T4, Compute Capability 7.5\\n\",\n            \"2020-06-01 05:08:59.672340: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:08:59.672877: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1561] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla T4 computeCapability: 7.5\\n\",\n            \"coreClock: 1.59GHz coreCount: 40 deviceMemorySize: 14.73GiB deviceMemoryBandwidth: 298.08GiB/s\\n\",\n            \"2020-06-01 05:08:59.672919: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:08:59.674215: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"2020-06-01 05:08:59.675592: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2020-06-01 05:08:59.675899: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2020-06-01 05:08:59.677196: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2020-06-01 05:08:59.677865: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\\n\",\n            \"2020-06-01 05:08:59.680574: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 05:08:59.680727: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:08:59.681340: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:08:59.681854: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1703] Adding visible gpu devices: 0\\n\",\n            \"2020-06-01 05:08:59.681902: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:09:00.205193: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1102] Device interconnect StreamExecutor with strength 1 edge matrix:\\n\",\n            \"2020-06-01 05:09:00.205250: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1108]      0 \\n\",\n            \"2020-06-01 05:09:00.205280: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1121] 0:   N \\n\",\n            \"2020-06-01 05:09:00.205510: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:09:00.206118: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:09:00.206641: W tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:39] Overriding allow_growth setting because the TF_FORCE_GPU_ALLOW_GROWTH environment variable is set. Original config value was 0.\\n\",\n            \"2020-06-01 05:09:00.206699: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1247] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 13970 MB memory) -> physical GPU (device: 0, name: Tesla T4, pci bus id: 0000:00:04.0, compute capability: 7.5)\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/inference.py:679: load (from tensorflow.python.saved_model.loader_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.loader.load or tf.compat.v1.saved_model.load. There will be a new function for importing SavedModels in Tensorflow 2.0.\\n\",\n            \"W0601 05:09:00.207572 140294127970176 deprecation.py:323] From /content/automl/efficientdet/inference.py:679: load (from tensorflow.python.saved_model.loader_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.loader.load or tf.compat.v1.saved_model.load. There will be a new function for importing SavedModels in Tensorflow 2.0.\\n\",\n            \"2020-06-01 05:09:06.088973: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 05:09:11.133459: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\"\n          ],\n          \"name\": \"stdout\"\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"RW26DwfirQQN\",\n        \"colab_type\": \"text\"\n      },\n      \"source\": [\n        \"# 3. COCO evaluation\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"colab_type\": \"text\",\n        \"id\": \"cfn_tRFOWKMO\"\n      },\n      \"source\": [\n        \"## 3.1 COCO evaluation on validation set.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"metadata\": {\n        \"id\": \"2s6E8IsVN0pB\",\n        \"colab_type\": \"code\",\n        \"outputId\": \"c980908a-8110-47d0-b0f8-287cb938dfd6\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 1000\n        }\n      },\n      \"source\": [\n        \"if 'val2017' not in os.listdir():\\n\",\n        \"  !wget http://images.cocodataset.org/zips/val2017.zip\\n\",\n        \"  !wget http://images.cocodataset.org/annotations/annotations_trainval2017.zip\\n\",\n        \"  !unzip -q val2017.zip\\n\",\n        \"  !unzip annotations_trainval2017.zip\\n\",\n        \"\\n\",\n        \"  !mkdir tfrecord\\n\",\n        \"  !PYTHONPATH=\\\".:$PYTHONPATH\\\"  python dataset/create_coco_tfrecord.py \\\\\\n\",\n        \"      --image_dir=val2017 \\\\\\n\",\n        \"      --caption_annotations_file=annotations/captions_val2017.json \\\\\\n\",\n        \"      --output_file_prefix=tfrecord/val \\\\\\n\",\n        \"      --num_shards=32\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"--2020-06-01 03:22:59--  http://images.cocodataset.org/zips/val2017.zip\\n\",\n            \"Resolving images.cocodataset.org (images.cocodataset.org)... 52.216.239.115\\n\",\n            \"Connecting to images.cocodataset.org (images.cocodataset.org)|52.216.239.115|:80... connected.\\n\",\n            \"HTTP request sent, awaiting response... 200 OK\\n\",\n            \"Length: 815585330 (778M) [application/zip]\\n\",\n            \"Saving to: ‘val2017.zip’\\n\",\n            \"\\n\",\n            \"val2017.zip         100%[===================>] 777.80M  95.2MB/s    in 8.1s    \\n\",\n            \"\\n\",\n            \"2020-06-01 03:23:07 (96.1 MB/s) - ‘val2017.zip’ saved [815585330/815585330]\\n\",\n            \"\\n\",\n            \"--2020-06-01 03:23:08--  http://images.cocodataset.org/annotations/annotations_trainval2017.zip\\n\",\n            \"Resolving images.cocodataset.org (images.cocodataset.org)... 52.216.163.67\\n\",\n            \"Connecting to images.cocodataset.org (images.cocodataset.org)|52.216.163.67|:80... connected.\\n\",\n            \"HTTP request sent, awaiting response... 200 OK\\n\",\n            \"Length: 252907541 (241M) [application/zip]\\n\",\n            \"Saving to: ‘annotations_trainval2017.zip’\\n\",\n            \"\\n\",\n            \"annotations_trainva 100%[===================>] 241.19M  96.2MB/s    in 2.5s    \\n\",\n            \"\\n\",\n            \"2020-06-01 03:23:11 (96.2 MB/s) - ‘annotations_trainval2017.zip’ saved [252907541/252907541]\\n\",\n            \"\\n\",\n            \"Archive:  annotations_trainval2017.zip\\n\",\n            \"  inflating: annotations/instances_train2017.json  \\n\",\n            \"  inflating: annotations/instances_val2017.json  \\n\",\n            \"  inflating: annotations/captions_train2017.json  \\n\",\n            \"  inflating: annotations/captions_val2017.json  \\n\",\n            \"  inflating: annotations/person_keypoints_train2017.json  \\n\",\n            \"  inflating: annotations/person_keypoints_val2017.json  \\n\",\n            \"2020-06-01 03:23:39.581065: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"I0601 03:23:41.048378 140017000511360 create_coco_tfrecord.py:288] writing to output path: tfrecord/val\\n\",\n            \"I0601 03:23:42.145988 140017000511360 create_coco_tfrecord.py:240] Building caption index.\\n\",\n            \"I0601 03:23:42.152811 140017000511360 create_coco_tfrecord.py:252] 0 images are missing captions.\\n\",\n            \"I0601 03:23:42.188177 140017000511360 create_coco_tfrecord.py:326] On image 0 of 5000\\n\",\n            \"I0601 03:23:42.304900 140017000511360 create_coco_tfrecord.py:326] On image 100 of 5000\\n\",\n            \"I0601 03:23:42.408726 140017000511360 create_coco_tfrecord.py:326] On image 200 of 5000\\n\",\n            \"I0601 03:23:42.516799 140017000511360 create_coco_tfrecord.py:326] On image 300 of 5000\\n\",\n            \"I0601 03:23:42.623024 140017000511360 create_coco_tfrecord.py:326] On image 400 of 5000\\n\",\n            \"I0601 03:23:42.721782 140017000511360 create_coco_tfrecord.py:326] On image 500 of 5000\\n\",\n            \"I0601 03:23:42.887866 140017000511360 create_coco_tfrecord.py:326] On image 600 of 5000\\n\",\n            \"I0601 03:23:43.005725 140017000511360 create_coco_tfrecord.py:326] On image 700 of 5000\\n\",\n            \"I0601 03:23:43.122723 140017000511360 create_coco_tfrecord.py:326] On image 800 of 5000\\n\",\n            \"I0601 03:23:43.256930 140017000511360 create_coco_tfrecord.py:326] On image 900 of 5000\\n\",\n            \"I0601 03:23:43.386459 140017000511360 create_coco_tfrecord.py:326] On image 1000 of 5000\\n\",\n            \"I0601 03:23:43.513791 140017000511360 create_coco_tfrecord.py:326] On image 1100 of 5000\\n\",\n            \"I0601 03:23:43.634377 140017000511360 create_coco_tfrecord.py:326] On image 1200 of 5000\\n\",\n            \"I0601 03:23:43.748250 140017000511360 create_coco_tfrecord.py:326] On image 1300 of 5000\\n\",\n            \"I0601 03:23:43.852913 140017000511360 create_coco_tfrecord.py:326] On image 1400 of 5000\\n\",\n            \"I0601 03:23:43.977952 140017000511360 create_coco_tfrecord.py:326] On image 1500 of 5000\\n\",\n            \"I0601 03:23:44.099335 140017000511360 create_coco_tfrecord.py:326] On image 1600 of 5000\\n\",\n            \"I0601 03:23:44.223793 140017000511360 create_coco_tfrecord.py:326] On image 1700 of 5000\\n\",\n            \"I0601 03:23:44.339614 140017000511360 create_coco_tfrecord.py:326] On image 1800 of 5000\\n\",\n            \"I0601 03:23:44.453861 140017000511360 create_coco_tfrecord.py:326] On image 1900 of 5000\\n\",\n            \"I0601 03:23:44.560936 140017000511360 create_coco_tfrecord.py:326] On image 2000 of 5000\\n\",\n            \"I0601 03:23:44.825568 140017000511360 create_coco_tfrecord.py:326] On image 2100 of 5000\\n\",\n            \"I0601 03:23:45.217720 140017000511360 create_coco_tfrecord.py:326] On image 2200 of 5000\\n\",\n            \"I0601 03:23:45.526688 140017000511360 create_coco_tfrecord.py:326] On image 2300 of 5000\\n\",\n            \"I0601 03:23:45.661227 140017000511360 create_coco_tfrecord.py:326] On image 2400 of 5000\\n\",\n            \"I0601 03:23:45.872304 140017000511360 create_coco_tfrecord.py:326] On image 2500 of 5000\\n\",\n            \"I0601 03:23:46.325947 140017000511360 create_coco_tfrecord.py:326] On image 2600 of 5000\\n\",\n            \"I0601 03:23:46.455390 140017000511360 create_coco_tfrecord.py:326] On image 2700 of 5000\\n\",\n            \"I0601 03:23:46.934686 140017000511360 create_coco_tfrecord.py:326] On image 2800 of 5000\\n\",\n            \"I0601 03:23:47.429552 140017000511360 create_coco_tfrecord.py:326] On image 2900 of 5000\\n\",\n            \"I0601 03:23:47.901286 140017000511360 create_coco_tfrecord.py:326] On image 3000 of 5000\\n\",\n            \"I0601 03:23:48.331453 140017000511360 create_coco_tfrecord.py:326] On image 3100 of 5000\\n\",\n            \"I0601 03:23:48.768493 140017000511360 create_coco_tfrecord.py:326] On image 3200 of 5000\\n\",\n            \"I0601 03:23:49.232179 140017000511360 create_coco_tfrecord.py:326] On image 3300 of 5000\\n\",\n            \"I0601 03:23:49.664278 140017000511360 create_coco_tfrecord.py:326] On image 3400 of 5000\\n\",\n            \"I0601 03:23:50.062376 140017000511360 create_coco_tfrecord.py:326] On image 3500 of 5000\\n\",\n            \"I0601 03:23:50.444368 140017000511360 create_coco_tfrecord.py:326] On image 3600 of 5000\\n\",\n            \"I0601 03:23:50.883323 140017000511360 create_coco_tfrecord.py:326] On image 3700 of 5000\\n\",\n            \"I0601 03:23:51.289299 140017000511360 create_coco_tfrecord.py:326] On image 3800 of 5000\\n\",\n            \"I0601 03:23:51.697535 140017000511360 create_coco_tfrecord.py:326] On image 3900 of 5000\\n\",\n            \"I0601 03:23:52.112248 140017000511360 create_coco_tfrecord.py:326] On image 4000 of 5000\\n\",\n            \"I0601 03:23:52.515160 140017000511360 create_coco_tfrecord.py:326] On image 4100 of 5000\\n\",\n            \"I0601 03:23:52.949388 140017000511360 create_coco_tfrecord.py:326] On image 4200 of 5000\\n\",\n            \"I0601 03:23:53.368419 140017000511360 create_coco_tfrecord.py:326] On image 4300 of 5000\\n\",\n            \"I0601 03:23:53.819533 140017000511360 create_coco_tfrecord.py:326] On image 4400 of 5000\\n\",\n            \"I0601 03:23:54.243495 140017000511360 create_coco_tfrecord.py:326] On image 4500 of 5000\\n\",\n            \"I0601 03:23:54.630336 140017000511360 create_coco_tfrecord.py:326] On image 4600 of 5000\\n\",\n            \"I0601 03:23:55.044274 140017000511360 create_coco_tfrecord.py:326] On image 4700 of 5000\\n\",\n            \"I0601 03:23:55.431686 140017000511360 create_coco_tfrecord.py:326] On image 4800 of 5000\\n\",\n            \"I0601 03:23:55.863472 140017000511360 create_coco_tfrecord.py:326] On image 4900 of 5000\\n\",\n            \"I0601 03:23:56.302472 140017000511360 create_coco_tfrecord.py:338] Finished writing, skipped 0 annotations.\\n\"\n          ],\n          \"name\": \"stdout\"\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"metadata\": {\n        \"id\": \"eLHZUY3jQpZr\",\n        \"colab_type\": \"code\",\n        \"outputId\": \"b56106a2-10d9-4707-8b93-d2c6fef49e1d\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 1000\n        }\n      },\n      \"source\": [\n        \"# Evalute on validation set (takes about 10 mins for efficientdet-d0)\\n\",\n        \"!python main.py --mode=eval  \\\\\\n\",\n        \"    --model_name={MODEL}  --model_dir={ckpt_path}  \\\\\\n\",\n        \"    --val_file_pattern=tfrecord/val*  \\\\\\n\",\n        \"    --val_json_file=annotations/instances_val2017.json\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"2020-06-01 03:28:25.377482: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"WARNING:tensorflow:From main.py:234: The name tf.estimator.tpu.TPUConfig is deprecated. Please use tf.compat.v1.estimator.tpu.TPUConfig instead.\\n\",\n            \"\\n\",\n            \"W0601 03:28:27.532853 140219029243776 module_wrapper.py:138] From main.py:234: The name tf.estimator.tpu.TPUConfig is deprecated. Please use tf.compat.v1.estimator.tpu.TPUConfig instead.\\n\",\n            \"\\n\",\n            \"WARNING:tensorflow:From main.py:239: The name tf.estimator.tpu.InputPipelineConfig is deprecated. Please use tf.compat.v1.estimator.tpu.InputPipelineConfig instead.\\n\",\n            \"\\n\",\n            \"W0601 03:28:27.533198 140219029243776 module_wrapper.py:138] From main.py:239: The name tf.estimator.tpu.InputPipelineConfig is deprecated. Please use tf.compat.v1.estimator.tpu.InputPipelineConfig instead.\\n\",\n            \"\\n\",\n            \"WARNING:tensorflow:From main.py:247: The name tf.estimator.tpu.RunConfig is deprecated. Please use tf.compat.v1.estimator.tpu.RunConfig instead.\\n\",\n            \"\\n\",\n            \"W0601 03:28:27.533394 140219029243776 module_wrapper.py:138] From main.py:247: The name tf.estimator.tpu.RunConfig is deprecated. Please use tf.compat.v1.estimator.tpu.RunConfig instead.\\n\",\n            \"\\n\",\n            \"I0601 03:28:27.533618 140219029243776 main.py:262] {'name': 'efficientdet-d0', 'act_type': 'swish', 'image_size': (512, 512), 'target_size': None, 'input_rand_hflip': True, 'train_scale_min': 0.1, 'train_scale_max': 2.0, 'autoaugment_policy': None, 'use_augmix': False, 'augmix_params': (3, -1, 1), 'num_classes': 90, 'skip_crowd_during_training': True, 'label_id_mapping': None, 'max_instances_per_image': 100, 'min_level': 3, 'max_level': 7, 'num_scales': 3, 'aspect_ratios': [(1.0, 1.0), (1.4, 0.7), (0.7, 1.4)], 'anchor_scale': 4.0, 'is_training_bn': True, 'momentum': 0.9, 'optimizer': 'sgd', 'learning_rate': 0.08, 'lr_warmup_init': 0.008, 'lr_warmup_epoch': 1.0, 'first_lr_drop_epoch': 200.0, 'second_lr_drop_epoch': 250.0, 'poly_lr_power': 0.9, 'clip_gradients_norm': 10.0, 'num_epochs': 300, 'data_format': 'channels_last', 'alpha': 0.25, 'gamma': 1.5, 'delta': 0.1, 'box_loss_weight': 50.0, 'iou_loss_type': None, 'iou_loss_weight': 1.0, 'weight_decay': 4e-05, 'strategy': None, 'precision': None, 'box_class_repeats': 3, 'fpn_cell_repeats': 3, 'fpn_num_filters': 64, 'separable_conv': True, 'apply_bn_for_resampling': True, 'conv_after_downsample': False, 'conv_bn_act_pattern': False, 'use_native_resize_op': True, 'pooling_type': None, 'fpn_name': None, 'fpn_weight_method': None, 'fpn_config': None, 'survival_prob': None, 'img_summary_steps': None, 'lr_decay_method': 'cosine', 'moving_average_decay': 0.9998, 'ckpt_var_scope': None, 'var_exclude_expr': '.*/class-predict/.*', 'backbone_name': 'efficientnet-b0', 'backbone_config': None, 'var_freeze_expr': None, 'resnet_depth': 50, 'model_name': 'efficientdet-d0', 'iterations_per_loop': 100, 'model_dir': '/content/automl/efficientdet/efficientdet-d0', 'num_shards': 8, 'num_examples_per_epoch': 120000, 'backbone_ckpt': '', 'ckpt': None, 'val_json_file': 'annotations/instances_val2017.json', 'testdev_dir': None, 'mode': 'eval'}\\n\",\n            \"WARNING:tensorflow:From main.py:317: The name tf.estimator.tpu.TPUEstimator is deprecated. Please use tf.compat.v1.estimator.tpu.TPUEstimator instead.\\n\",\n            \"\\n\",\n            \"W0601 03:28:27.533797 140219029243776 module_wrapper.py:138] From main.py:317: The name tf.estimator.tpu.TPUEstimator is deprecated. Please use tf.compat.v1.estimator.tpu.TPUEstimator instead.\\n\",\n            \"\\n\",\n            \"INFO:tensorflow:Using config: {'_model_dir': '/content/automl/efficientdet/efficientdet-d0', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true\\n\",\n            \", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': None, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1, '_tpu_config': TPUConfig(iterations_per_loop=100, num_shards=8, num_cores_per_replica=None, per_host_input_for_training=3, tpu_job_name=None, initial_infeed_sleep_secs=None, input_partition_dims=None, eval_training_input_configuration=2, experimental_host_call_every_n_steps=1), '_cluster': None}\\n\",\n            \"I0601 03:28:27.534311 140219029243776 estimator.py:191] Using config: {'_model_dir': '/content/automl/efficientdet/efficientdet-d0', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true\\n\",\n            \", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': None, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1, '_tpu_config': TPUConfig(iterations_per_loop=100, num_shards=8, num_cores_per_replica=None, per_host_input_for_training=3, tpu_job_name=None, initial_infeed_sleep_secs=None, input_partition_dims=None, eval_training_input_configuration=2, experimental_host_call_every_n_steps=1), '_cluster': None}\\n\",\n            \"INFO:tensorflow:_TPUContext: eval_on_tpu True\\n\",\n            \"I0601 03:28:27.535187 140219029243776 tpu_context.py:216] _TPUContext: eval_on_tpu True\\n\",\n            \"WARNING:tensorflow:eval_on_tpu ignored because use_tpu is False.\\n\",\n            \"W0601 03:28:27.535668 140219029243776 tpu_context.py:218] eval_on_tpu ignored because use_tpu is False.\\n\",\n            \"INFO:tensorflow:Waiting for new checkpoint at /content/automl/efficientdet/efficientdet-d0\\n\",\n            \"I0601 03:28:27.535777 140219029243776 checkpoint_utils.py:125] Waiting for new checkpoint at /content/automl/efficientdet/efficientdet-d0\\n\",\n            \"INFO:tensorflow:Found new checkpoint at /content/automl/efficientdet/efficientdet-d0/model\\n\",\n            \"I0601 03:28:27.537644 140219029243776 checkpoint_utils.py:134] Found new checkpoint at /content/automl/efficientdet/efficientdet-d0/model\\n\",\n            \"I0601 03:28:27.537798 140219029243776 main.py:337] Starting to evaluate.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:1666: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"If using Keras pass *_constraint arguments to layers.\\n\",\n            \"W0601 03:28:27.544394 140219029243776 deprecation.py:506] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:1666: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"If using Keras pass *_constraint arguments to layers.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/dataloader.py:363: parallel_interleave (from tensorflow.python.data.experimental.ops.interleave_ops) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.data.Dataset.interleave(map_func, cycle_length, block_length, num_parallel_calls=tf.data.experimental.AUTOTUNE)` instead. If sloppy execution is desired, use `tf.data.Options.experimental_deterministic`.\\n\",\n            \"W0601 03:28:27.563604 140219029243776 deprecation.py:323] From /content/automl/efficientdet/dataloader.py:363: parallel_interleave (from tensorflow.python.data.experimental.ops.interleave_ops) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.data.Dataset.interleave(map_func, cycle_length, block_length, num_parallel_calls=tf.data.experimental.AUTOTUNE)` instead. If sloppy execution is desired, use `tf.data.Options.experimental_deterministic`.\\n\",\n            \"2020-06-01 03:28:27.667099: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcuda.so.1\\n\",\n            \"2020-06-01 03:28:27.702213: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 03:28:27.702782: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1561] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla T4 computeCapability: 7.5\\n\",\n            \"coreClock: 1.59GHz coreCount: 40 deviceMemorySize: 14.73GiB deviceMemoryBandwidth: 298.08GiB/s\\n\",\n            \"2020-06-01 03:28:27.702825: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 03:28:27.704274: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"2020-06-01 03:28:27.705746: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2020-06-01 03:28:27.706108: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2020-06-01 03:28:27.707539: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2020-06-01 03:28:27.708273: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\\n\",\n            \"2020-06-01 03:28:27.711294: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 03:28:27.711441: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 03:28:27.712031: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 03:28:27.712554: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1703] Adding visible gpu devices: 0\\n\",\n            \"INFO:tensorflow:Calling model_fn.\\n\",\n            \"I0601 03:28:28.324099 140219029243776 estimator.py:1169] Calling model_fn.\\n\",\n            \"INFO:tensorflow:Running eval on CPU/GPU\\n\",\n            \"I0601 03:28:28.324348 140219029243776 tpu_estimator.py:3171] Running eval on CPU/GPU\\n\",\n            \"I0601 03:28:28.324886 140219029243776 efficientdet_arch.py:720] act_type: swish\\n\",\n            \"alpha: 0.25\\n\",\n            \"anchor_scale: 4.0\\n\",\n            \"apply_bn_for_resampling: true\\n\",\n            \"aspect_ratios:\\n\",\n            \"- !!python/tuple\\n\",\n            \"    - 1.0\\n\",\n            \"    - 1.0\\n\",\n            \"- !!python/tuple\\n\",\n            \"    - 1.4\\n\",\n            \"    - 0.7\\n\",\n            \"- !!python/tuple\\n\",\n            \"    - 0.7\\n\",\n            \"    - 1.4\\n\",\n            \"augmix_params: !!python/tuple\\n\",\n            \"- 3\\n\",\n            \"- -1\\n\",\n            \"- 1\\n\",\n            \"autoaugment_policy: null\\n\",\n            \"backbone_ckpt: ''\\n\",\n            \"backbone_config: null\\n\",\n            \"backbone_name: efficientnet-b0\\n\",\n            \"batch_size: 1\\n\",\n            \"box_class_repeats: 3\\n\",\n            \"box_loss_weight: 50.0\\n\",\n            \"ckpt: null\\n\",\n            \"ckpt_var_scope: null\\n\",\n            \"clip_gradients_norm: 10.0\\n\",\n            \"conv_after_downsample: false\\n\",\n            \"conv_bn_act_pattern: false\\n\",\n            \"data_format: channels_last\\n\",\n            \"delta: 0.1\\n\",\n            \"first_lr_drop_epoch: 200.0\\n\",\n            \"fpn_cell_repeats: 3\\n\",\n            \"fpn_config: null\\n\",\n            \"fpn_name: null\\n\",\n            \"fpn_num_filters: 64\\n\",\n            \"fpn_weight_method: null\\n\",\n            \"gamma: 1.5\\n\",\n            \"image_size: !!python/tuple\\n\",\n            \"- 512\\n\",\n            \"- 512\\n\",\n            \"img_summary_steps: null\\n\",\n            \"input_rand_hflip: false\\n\",\n            \"iou_loss_type: null\\n\",\n            \"iou_loss_weight: 1.0\\n\",\n            \"is_training_bn: false\\n\",\n            \"iterations_per_loop: 100\\n\",\n            \"label_id_mapping: null\\n\",\n            \"learning_rate: 0.08\\n\",\n            \"lr_decay_method: cosine\\n\",\n            \"lr_warmup_epoch: 1.0\\n\",\n            \"lr_warmup_init: 0.008\\n\",\n            \"max_instances_per_image: 100\\n\",\n            \"max_level: 7\\n\",\n            \"min_level: 3\\n\",\n            \"mode: eval\\n\",\n            \"model_dir: /content/automl/efficientdet/efficientdet-d0\\n\",\n            \"model_name: efficientdet-d0\\n\",\n            \"momentum: 0.9\\n\",\n            \"moving_average_decay: 0.9998\\n\",\n            \"name: efficientdet-d0\\n\",\n            \"num_classes: 90\\n\",\n            \"num_epochs: 300\\n\",\n            \"num_examples_per_epoch: 120000\\n\",\n            \"num_scales: 3\\n\",\n            \"num_shards: 8\\n\",\n            \"optimizer: sgd\\n\",\n            \"poly_lr_power: 0.9\\n\",\n            \"pooling_type: null\\n\",\n            \"precision: null\\n\",\n            \"resnet_depth: 50\\n\",\n            \"second_lr_drop_epoch: 250.0\\n\",\n            \"separable_conv: true\\n\",\n            \"skip_crowd_during_training: true\\n\",\n            \"strategy: null\\n\",\n            \"survival_prob: null\\n\",\n            \"target_size: null\\n\",\n            \"testdev_dir: null\\n\",\n            \"train_scale_max: 2.0\\n\",\n            \"train_scale_min: 0.1\\n\",\n            \"use_augmix: false\\n\",\n            \"use_native_resize_op: true\\n\",\n            \"use_tpu: false\\n\",\n            \"val_json_file: annotations/instances_val2017.json\\n\",\n            \"var_exclude_expr: .*/class-predict/.*\\n\",\n            \"var_freeze_expr: null\\n\",\n            \"weight_decay: 4.0e-05\\n\",\n            \"\\n\",\n            \"I0601 03:28:28.329483 140219029243776 efficientnet_builder.py:224] global_params= GlobalParams(batch_norm_momentum=0.99, batch_norm_epsilon=0.001, dropout_rate=0.2, data_format='channels_last', num_classes=1000, width_coefficient=1.0, depth_coefficient=1.0, depth_divisor=8, min_depth=None, survival_prob=0.0, relu_fn=functools.partial(<function activation_fn at 0x7f86f29f3d90>, act_type='swish'), batch_norm=<class 'utils.BatchNormalization'>, use_se=True, local_pooling=None, condconv_num_experts=None, clip_projection_output=False, blocks_args=['r1_k3_s11_e1_i32_o16_se0.25', 'r2_k3_s22_e6_i16_o24_se0.25', 'r2_k5_s22_e6_i24_o40_se0.25', 'r3_k3_s22_e6_i40_o80_se0.25', 'r3_k5_s11_e6_i80_o112_se0.25', 'r4_k5_s22_e6_i112_o192_se0.25', 'r1_k3_s11_e6_i192_o320_se0.25'], fix_head_stem=None)\\n\",\n            \"I0601 03:28:31.123013 140219029243776 api.py:587] Built stem layers with output shape: (1, 256, 256, 32)\\n\",\n            \"I0601 03:28:33.629385 140219029243776 api.py:587] Block mb_conv_block  input shape: (1, 256, 256, 32)\\n\",\n            \"I0601 03:28:33.661346 140219029243776 api.py:587] DWConv shape: (1, 256, 256, 32)\\n\",\n            \"I0601 03:28:33.812110 140219029243776 api.py:587] Built Squeeze and Excitation with tensor shape: (1, 1, 1, 32)\\n\",\n            \"I0601 03:28:33.841851 140219029243776 api.py:587] Project shape: (1, 256, 256, 16)\\n\",\n            \"I0601 03:28:33.890565 140219029243776 api.py:587] Block mb_conv_block_1  input shape: (1, 256, 256, 16)\\n\",\n            \"I0601 03:28:33.921314 140219029243776 api.py:587] Expand shape: (1, 256, 256, 96)\\n\",\n            \"I0601 03:28:33.951348 140219029243776 api.py:587] DWConv shape: (1, 128, 128, 96)\\n\",\n            \"I0601 03:28:33.984775 140219029243776 api.py:587] Built Squeeze and Excitation with tensor shape: (1, 1, 1, 96)\\n\",\n            \"I0601 03:28:34.015750 140219029243776 api.py:587] Project shape: (1, 128, 128, 24)\\n\",\n            \"I0601 03:28:34.024907 140219029243776 api.py:587] Block mb_conv_block_2  input shape: (1, 128, 128, 24)\\n\",\n            \"I0601 03:28:34.058333 140219029243776 api.py:587] Expand shape: (1, 128, 128, 144)\\n\",\n            \"I0601 03:28:34.089240 140219029243776 api.py:587] DWConv shape: (1, 128, 128, 144)\\n\",\n            \"I0601 03:28:34.121758 140219029243776 api.py:587] Built Squeeze and Excitation with tensor shape: (1, 1, 1, 144)\\n\",\n            \"I0601 03:28:34.151883 140219029243776 api.py:587] Project shape: (1, 128, 128, 24)\\n\",\n            \"I0601 03:28:34.161335 140219029243776 api.py:587] Block mb_conv_block_3  input shape: (1, 128, 128, 24)\\n\",\n            \"I0601 03:28:34.194899 140219029243776 api.py:587] Expand shape: (1, 128, 128, 144)\\n\",\n            \"I0601 03:28:34.227180 140219029243776 api.py:587] DWConv shape: (1, 64, 64, 144)\\n\",\n            \"I0601 03:28:34.267751 140219029243776 api.py:587] Built Squeeze and Excitation with tensor shape: (1, 1, 1, 144)\\n\",\n            \"I0601 03:28:34.301784 140219029243776 api.py:587] Project shape: (1, 64, 64, 40)\\n\",\n            \"I0601 03:28:34.313288 140219029243776 api.py:587] Block mb_conv_block_4  input shape: (1, 64, 64, 40)\\n\",\n            \"I0601 03:28:34.347522 140219029243776 api.py:587] Expand shape: (1, 64, 64, 240)\\n\",\n            \"I0601 03:28:34.378578 140219029243776 api.py:587] DWConv shape: (1, 64, 64, 240)\\n\",\n            \"I0601 03:28:34.412726 140219029243776 api.py:587] Built Squeeze and Excitation with tensor shape: (1, 1, 1, 240)\\n\",\n            \"I0601 03:28:34.442892 140219029243776 api.py:587] Project shape: (1, 64, 64, 40)\\n\",\n            \"I0601 03:28:34.453632 140219029243776 api.py:587] Block mb_conv_block_5  input shape: (1, 64, 64, 40)\\n\",\n            \"I0601 03:28:34.491663 140219029243776 api.py:587] Expand shape: (1, 64, 64, 240)\\n\",\n            \"I0601 03:28:34.523128 140219029243776 api.py:587] DWConv shape: (1, 32, 32, 240)\\n\",\n            \"I0601 03:28:34.555319 140219029243776 api.py:587] Built Squeeze and Excitation with tensor shape: (1, 1, 1, 240)\\n\",\n            \"I0601 03:28:34.589114 140219029243776 api.py:587] Project shape: (1, 32, 32, 80)\\n\",\n            \"I0601 03:28:34.601770 140219029243776 api.py:587] Block mb_conv_block_6  input shape: (1, 32, 32, 80)\\n\",\n            \"I0601 03:28:34.633069 140219029243776 api.py:587] Expand shape: (1, 32, 32, 480)\\n\",\n            \"I0601 03:28:34.669409 140219029243776 api.py:587] DWConv shape: (1, 32, 32, 480)\\n\",\n            \"I0601 03:28:34.706420 140219029243776 api.py:587] Built Squeeze and Excitation with tensor shape: (1, 1, 1, 480)\\n\",\n            \"I0601 03:28:34.737901 140219029243776 api.py:587] Project shape: (1, 32, 32, 80)\\n\",\n            \"I0601 03:28:34.747094 140219029243776 api.py:587] Block mb_conv_block_7  input shape: (1, 32, 32, 80)\\n\",\n            \"I0601 03:28:34.778591 140219029243776 api.py:587] Expand shape: (1, 32, 32, 480)\\n\",\n            \"I0601 03:28:34.811817 140219029243776 api.py:587] DWConv shape: (1, 32, 32, 480)\\n\",\n            \"I0601 03:28:34.843950 140219029243776 api.py:587] Built Squeeze and Excitation with tensor shape: (1, 1, 1, 480)\\n\",\n            \"I0601 03:28:34.877232 140219029243776 api.py:587] Project shape: (1, 32, 32, 80)\\n\",\n            \"I0601 03:28:34.886362 140219029243776 api.py:587] Block mb_conv_block_8  input shape: (1, 32, 32, 80)\\n\",\n            \"I0601 03:28:34.917936 140219029243776 api.py:587] Expand shape: (1, 32, 32, 480)\\n\",\n            \"I0601 03:28:34.949104 140219029243776 api.py:587] DWConv shape: (1, 32, 32, 480)\\n\",\n            \"I0601 03:28:34.983436 140219029243776 api.py:587] Built Squeeze and Excitation with tensor shape: (1, 1, 1, 480)\\n\",\n            \"I0601 03:28:35.017930 140219029243776 api.py:587] Project shape: (1, 32, 32, 112)\\n\",\n            \"I0601 03:28:35.027091 140219029243776 api.py:587] Block mb_conv_block_9  input shape: (1, 32, 32, 112)\\n\",\n            \"I0601 03:28:35.066807 140219029243776 api.py:587] Expand shape: (1, 32, 32, 672)\\n\",\n            \"I0601 03:28:35.101598 140219029243776 api.py:587] DWConv shape: (1, 32, 32, 672)\\n\",\n            \"I0601 03:28:35.134581 140219029243776 api.py:587] Built Squeeze and Excitation with tensor shape: (1, 1, 1, 672)\\n\",\n            \"I0601 03:28:35.164892 140219029243776 api.py:587] Project shape: (1, 32, 32, 112)\\n\",\n            \"I0601 03:28:35.174525 140219029243776 api.py:587] Block mb_conv_block_10  input shape: (1, 32, 32, 112)\\n\",\n            \"I0601 03:28:35.207780 140219029243776 api.py:587] Expand shape: (1, 32, 32, 672)\\n\",\n            \"I0601 03:28:35.238986 140219029243776 api.py:587] DWConv shape: (1, 32, 32, 672)\\n\",\n            \"I0601 03:28:35.271428 140219029243776 api.py:587] Built Squeeze and Excitation with tensor shape: (1, 1, 1, 672)\\n\",\n            \"I0601 03:28:35.307202 140219029243776 api.py:587] Project shape: (1, 32, 32, 112)\\n\",\n            \"I0601 03:28:35.317617 140219029243776 api.py:587] Block mb_conv_block_11  input shape: (1, 32, 32, 112)\\n\",\n            \"I0601 03:28:35.349665 140219029243776 api.py:587] Expand shape: (1, 32, 32, 672)\\n\",\n            \"I0601 03:28:35.383484 140219029243776 api.py:587] DWConv shape: (1, 16, 16, 672)\\n\",\n            \"I0601 03:28:35.418318 140219029243776 api.py:587] Built Squeeze and Excitation with tensor shape: (1, 1, 1, 672)\\n\",\n            \"I0601 03:28:35.448390 140219029243776 api.py:587] Project shape: (1, 16, 16, 192)\\n\",\n            \"I0601 03:28:35.458056 140219029243776 api.py:587] Block mb_conv_block_12  input shape: (1, 16, 16, 192)\\n\",\n            \"I0601 03:28:35.499689 140219029243776 api.py:587] Expand shape: (1, 16, 16, 1152)\\n\",\n            \"I0601 03:28:35.536298 140219029243776 api.py:587] DWConv shape: (1, 16, 16, 1152)\\n\",\n            \"I0601 03:28:35.569783 140219029243776 api.py:587] Built Squeeze and Excitation with tensor shape: (1, 1, 1, 1152)\\n\",\n            \"I0601 03:28:35.603983 140219029243776 api.py:587] Project shape: (1, 16, 16, 192)\\n\",\n            \"I0601 03:28:35.613524 140219029243776 api.py:587] Block mb_conv_block_13  input shape: (1, 16, 16, 192)\\n\",\n            \"I0601 03:28:35.648805 140219029243776 api.py:587] Expand shape: (1, 16, 16, 1152)\\n\",\n            \"I0601 03:28:35.689206 140219029243776 api.py:587] DWConv shape: (1, 16, 16, 1152)\\n\",\n            \"I0601 03:28:35.725426 140219029243776 api.py:587] Built Squeeze and Excitation with tensor shape: (1, 1, 1, 1152)\\n\",\n            \"I0601 03:28:35.757848 140219029243776 api.py:587] Project shape: (1, 16, 16, 192)\\n\",\n            \"I0601 03:28:35.767347 140219029243776 api.py:587] Block mb_conv_block_14  input shape: (1, 16, 16, 192)\\n\",\n            \"I0601 03:28:35.807080 140219029243776 api.py:587] Expand shape: (1, 16, 16, 1152)\\n\",\n            \"I0601 03:28:35.846334 140219029243776 api.py:587] DWConv shape: (1, 16, 16, 1152)\\n\",\n            \"I0601 03:28:35.888501 140219029243776 api.py:587] Built Squeeze and Excitation with tensor shape: (1, 1, 1, 1152)\\n\",\n            \"I0601 03:28:35.929219 140219029243776 api.py:587] Project shape: (1, 16, 16, 192)\\n\",\n            \"I0601 03:28:35.940203 140219029243776 api.py:587] Block mb_conv_block_15  input shape: (1, 16, 16, 192)\\n\",\n            \"I0601 03:28:35.979359 140219029243776 api.py:587] Expand shape: (1, 16, 16, 1152)\\n\",\n            \"I0601 03:28:36.017061 140219029243776 api.py:587] DWConv shape: (1, 16, 16, 1152)\\n\",\n            \"I0601 03:28:36.052354 140219029243776 api.py:587] Built Squeeze and Excitation with tensor shape: (1, 1, 1, 1152)\\n\",\n            \"I0601 03:28:36.084471 140219029243776 api.py:587] Project shape: (1, 16, 16, 320)\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/profiler/internal/flops_registry.py:142: tensor_shape_from_node_def_name (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.tensor_shape_from_node_def_name`\\n\",\n            \"W0601 03:28:36.094776 140219029243776 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/profiler/internal/flops_registry.py:142: tensor_shape_from_node_def_name (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.tensor_shape_from_node_def_name`\\n\",\n            \"Parsing Inputs...\\n\",\n            \"I0601 03:28:36.338026 140219029243776 efficientdet_arch.py:725] backbone params/flops = 3.595388M, 1.933562589B\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:138: conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.Conv2D` instead.\\n\",\n            \"W0601 03:28:36.338469 140219029243776 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:138: conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.Conv2D` instead.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/layers/convolutional.py:424: Layer.apply (from tensorflow.python.keras.engine.base_layer_v1) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Please use `layer.__call__` method instead.\\n\",\n            \"W0601 03:28:36.341403 140219029243776 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/layers/convolutional.py:424: Layer.apply (from tensorflow.python.keras.engine.base_layer_v1) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Please use `layer.__call__` method instead.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:164: max_pooling2d (from tensorflow.python.layers.pooling) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use keras.layers.MaxPooling2D instead.\\n\",\n            \"W0601 03:28:36.389999 140219029243776 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:164: max_pooling2d (from tensorflow.python.layers.pooling) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use keras.layers.MaxPooling2D instead.\\n\",\n            \"I0601 03:28:36.397655 140219029243776 efficientdet_arch.py:471] building cell 0\\n\",\n            \"I0601 03:28:36.397964 140219029243776 efficientdet_arch.py:648] fnode 0 : {'feat_level': 6, 'inputs_offsets': [3, 4]}\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:685: separable_conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.SeparableConv2D` instead.\\n\",\n            \"W0601 03:28:36.410160 140219029243776 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:685: separable_conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.SeparableConv2D` instead.\\n\",\n            \"I0601 03:28:36.463976 140219029243776 efficientdet_arch.py:648] fnode 1 : {'feat_level': 5, 'inputs_offsets': [2, 5]}\\n\",\n            \"I0601 03:28:36.579131 140219029243776 efficientdet_arch.py:648] fnode 2 : {'feat_level': 4, 'inputs_offsets': [1, 6]}\\n\",\n            \"I0601 03:28:36.694639 140219029243776 efficientdet_arch.py:648] fnode 3 : {'feat_level': 3, 'inputs_offsets': [0, 7]}\\n\",\n            \"I0601 03:28:36.810347 140219029243776 efficientdet_arch.py:648] fnode 4 : {'feat_level': 4, 'inputs_offsets': [1, 7, 8]}\\n\",\n            \"I0601 03:28:36.929757 140219029243776 efficientdet_arch.py:648] fnode 5 : {'feat_level': 5, 'inputs_offsets': [2, 6, 9]}\\n\",\n            \"I0601 03:28:37.053891 140219029243776 efficientdet_arch.py:648] fnode 6 : {'feat_level': 6, 'inputs_offsets': [3, 5, 10]}\\n\",\n            \"I0601 03:28:37.132705 140219029243776 efficientdet_arch.py:648] fnode 7 : {'feat_level': 7, 'inputs_offsets': [4, 11]}\\n\",\n            \"I0601 03:28:37.212006 140219029243776 efficientdet_arch.py:471] building cell 1\\n\",\n            \"I0601 03:28:37.212349 140219029243776 efficientdet_arch.py:648] fnode 0 : {'feat_level': 6, 'inputs_offsets': [3, 4]}\\n\",\n            \"I0601 03:28:37.284093 140219029243776 efficientdet_arch.py:648] fnode 1 : {'feat_level': 5, 'inputs_offsets': [2, 5]}\\n\",\n            \"I0601 03:28:37.357714 140219029243776 efficientdet_arch.py:648] fnode 2 : {'feat_level': 4, 'inputs_offsets': [1, 6]}\\n\",\n            \"I0601 03:28:37.438060 140219029243776 efficientdet_arch.py:648] fnode 3 : {'feat_level': 3, 'inputs_offsets': [0, 7]}\\n\",\n            \"I0601 03:28:37.510998 140219029243776 efficientdet_arch.py:648] fnode 4 : {'feat_level': 4, 'inputs_offsets': [1, 7, 8]}\\n\",\n            \"I0601 03:28:37.601469 140219029243776 efficientdet_arch.py:648] fnode 5 : {'feat_level': 5, 'inputs_offsets': [2, 6, 9]}\\n\",\n            \"I0601 03:28:37.687102 140219029243776 efficientdet_arch.py:648] fnode 6 : {'feat_level': 6, 'inputs_offsets': [3, 5, 10]}\\n\",\n            \"I0601 03:28:37.770025 140219029243776 efficientdet_arch.py:648] fnode 7 : {'feat_level': 7, 'inputs_offsets': [4, 11]}\\n\",\n            \"I0601 03:28:37.852831 140219029243776 efficientdet_arch.py:471] building cell 2\\n\",\n            \"I0601 03:28:37.853177 140219029243776 efficientdet_arch.py:648] fnode 0 : {'feat_level': 6, 'inputs_offsets': [3, 4]}\\n\",\n            \"I0601 03:28:37.931208 140219029243776 efficientdet_arch.py:648] fnode 1 : {'feat_level': 5, 'inputs_offsets': [2, 5]}\\n\",\n            \"I0601 03:28:38.012127 140219029243776 efficientdet_arch.py:648] fnode 2 : {'feat_level': 4, 'inputs_offsets': [1, 6]}\\n\",\n            \"I0601 03:28:38.090590 140219029243776 efficientdet_arch.py:648] fnode 3 : {'feat_level': 3, 'inputs_offsets': [0, 7]}\\n\",\n            \"I0601 03:28:38.170805 140219029243776 efficientdet_arch.py:648] fnode 4 : {'feat_level': 4, 'inputs_offsets': [1, 7, 8]}\\n\",\n            \"I0601 03:28:38.261656 140219029243776 efficientdet_arch.py:648] fnode 5 : {'feat_level': 5, 'inputs_offsets': [2, 6, 9]}\\n\",\n            \"I0601 03:28:38.449078 140219029243776 efficientdet_arch.py:648] fnode 6 : {'feat_level': 6, 'inputs_offsets': [3, 5, 10]}\\n\",\n            \"I0601 03:28:38.541515 140219029243776 efficientdet_arch.py:648] fnode 7 : {'feat_level': 7, 'inputs_offsets': [4, 11]}\\n\",\n            \"Parsing Inputs...\\n\",\n            \"I0601 03:28:39.043738 140219029243776 efficientdet_arch.py:730] backbone+fpn params/flops = 3.791669M, 2.076332296B\\n\",\n            \"Parsing Inputs...\\n\",\n            \"I0601 03:28:42.122260 140219029243776 efficientdet_arch.py:735] backbone+fpn+box params/flops = 3.880067M, 2.535456184B\\n\",\n            \"I0601 03:28:42.122621 140219029243776 det_model_fn.py:97] LR schedule method: cosine\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/det_model_fn.py:659: The name tf.estimator.tpu.TPUEstimatorSpec is deprecated. Please use tf.compat.v1.estimator.tpu.TPUEstimatorSpec instead.\\n\",\n            \"\\n\",\n            \"W0601 03:28:42.439553 140219029243776 module_wrapper.py:138] From /content/automl/efficientdet/det_model_fn.py:659: The name tf.estimator.tpu.TPUEstimatorSpec is deprecated. Please use tf.compat.v1.estimator.tpu.TPUEstimatorSpec instead.\\n\",\n            \"\\n\",\n            \"I0601 03:28:42.469991 140219029243776 det_model_fn.py:589] Eval val with groudtruths annotations/instances_val2017.json.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/anchors.py:587: py_func (from tensorflow.python.ops.script_ops) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"tf.py_func is deprecated in TF V2. Instead, there are two\\n\",\n            \"    options available in V2.\\n\",\n            \"    - tf.py_function takes a python function which manipulates tf eager\\n\",\n            \"    tensors instead of numpy arrays. It's easy to convert a tf eager tensor to\\n\",\n            \"    an ndarray (just call tensor.numpy()) but having access to eager tensors\\n\",\n            \"    means `tf.py_function`s can use accelerators such as GPUs as well as\\n\",\n            \"    being differentiable using a gradient tape.\\n\",\n            \"    - tf.numpy_function maintains the semantics of the deprecated tf.py_func\\n\",\n            \"    (it is not differentiable, and manipulates numpy arrays). It drops the\\n\",\n            \"    stateful argument making all functions stateful.\\n\",\n            \"    \\n\",\n            \"W0601 03:28:42.478416 140219029243776 deprecation.py:323] From /content/automl/efficientdet/anchors.py:587: py_func (from tensorflow.python.ops.script_ops) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"tf.py_func is deprecated in TF V2. Instead, there are two\\n\",\n            \"    options available in V2.\\n\",\n            \"    - tf.py_function takes a python function which manipulates tf eager\\n\",\n            \"    tensors instead of numpy arrays. It's easy to convert a tf eager tensor to\\n\",\n            \"    an ndarray (just call tensor.numpy()) but having access to eager tensors\\n\",\n            \"    means `tf.py_function`s can use accelerators such as GPUs as well as\\n\",\n            \"    being differentiable using a gradient tape.\\n\",\n            \"    - tf.numpy_function maintains the semantics of the deprecated tf.py_func\\n\",\n            \"    (it is not differentiable, and manipulates numpy arrays). It drops the\\n\",\n            \"    stateful argument making all functions stateful.\\n\",\n            \"    \\n\",\n            \"loading annotations into memory...\\n\",\n            \"Done (t=0.64s)\\n\",\n            \"creating index...\\n\",\n            \"index created!\\n\",\n            \"I0601 03:28:43.161083 140219029243776 det_model_fn.py:652] Load EMA vars with ema_decay=0.999800\\n\",\n            \"INFO:tensorflow:Done calling model_fn.\\n\",\n            \"I0601 03:28:43.667938 140219029243776 estimator.py:1171] Done calling model_fn.\\n\",\n            \"INFO:tensorflow:Starting evaluation at 2020-06-01T03:28:43Z\\n\",\n            \"I0601 03:28:43.685333 140219029243776 evaluation.py:255] Starting evaluation at 2020-06-01T03:28:43Z\\n\",\n            \"INFO:tensorflow:Graph was finalized.\\n\",\n            \"I0601 03:28:44.014962 140219029243776 monitored_session.py:246] Graph was finalized.\\n\",\n            \"2020-06-01 03:28:44.020247: I tensorflow/core/platform/profile_utils/cpu_utils.cc:102] CPU Frequency: 2300000000 Hz\\n\",\n            \"2020-06-01 03:28:44.020470: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x1849100 initialized for platform Host (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-06-01 03:28:44.020501: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Host, Default Version\\n\",\n            \"2020-06-01 03:28:44.116278: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 03:28:44.116927: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x18492c0 initialized for platform CUDA (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-06-01 03:28:44.116960: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Tesla T4, Compute Capability 7.5\\n\",\n            \"2020-06-01 03:28:44.117208: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 03:28:44.117714: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1561] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla T4 computeCapability: 7.5\\n\",\n            \"coreClock: 1.59GHz coreCount: 40 deviceMemorySize: 14.73GiB deviceMemoryBandwidth: 298.08GiB/s\\n\",\n            \"2020-06-01 03:28:44.117767: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 03:28:44.117812: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"2020-06-01 03:28:44.117833: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2020-06-01 03:28:44.117852: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2020-06-01 03:28:44.117870: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2020-06-01 03:28:44.117893: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\\n\",\n            \"2020-06-01 03:28:44.117911: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 03:28:44.118006: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 03:28:44.118585: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 03:28:44.119077: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1703] Adding visible gpu devices: 0\\n\",\n            \"2020-06-01 03:28:44.119129: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 03:28:44.614816: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1102] Device interconnect StreamExecutor with strength 1 edge matrix:\\n\",\n            \"2020-06-01 03:28:44.614873: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1108]      0 \\n\",\n            \"2020-06-01 03:28:44.614886: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1121] 0:   N \\n\",\n            \"2020-06-01 03:28:44.615122: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 03:28:44.615721: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 03:28:44.616235: W tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:39] Overriding allow_growth setting because the TF_FORCE_GPU_ALLOW_GROWTH environment variable is set. Original config value was 0.\\n\",\n            \"2020-06-01 03:28:44.616290: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1247] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 13970 MB memory) -> physical GPU (device: 0, name: Tesla T4, pci bus id: 0000:00:04.0, compute capability: 7.5)\\n\",\n            \"INFO:tensorflow:Restoring parameters from /content/automl/efficientdet/efficientdet-d0/model\\n\",\n            \"I0601 03:28:44.617812 140219029243776 saver.py:1293] Restoring parameters from /content/automl/efficientdet/efficientdet-d0/model\\n\",\n            \"INFO:tensorflow:Running local_init_op.\\n\",\n            \"I0601 03:28:45.706003 140219029243776 session_manager.py:505] Running local_init_op.\\n\",\n            \"INFO:tensorflow:Done running local_init_op.\\n\",\n            \"I0601 03:28:45.794684 140219029243776 session_manager.py:508] Done running local_init_op.\\n\",\n            \"2020-06-01 03:28:48.918356: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 03:28:50.688065: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"INFO:tensorflow:Evaluation [500/5000]\\n\",\n            \"I0601 03:29:40.522579 140219029243776 evaluation.py:167] Evaluation [500/5000]\\n\",\n            \"INFO:tensorflow:Evaluation [1000/5000]\\n\",\n            \"I0601 03:30:29.711343 140219029243776 evaluation.py:167] Evaluation [1000/5000]\\n\",\n            \"INFO:tensorflow:Evaluation [1500/5000]\\n\",\n            \"I0601 03:31:20.120194 140219029243776 evaluation.py:167] Evaluation [1500/5000]\\n\",\n            \"INFO:tensorflow:Evaluation [2000/5000]\\n\",\n            \"I0601 03:32:09.853394 140219029243776 evaluation.py:167] Evaluation [2000/5000]\\n\",\n            \"INFO:tensorflow:Evaluation [2500/5000]\\n\",\n            \"I0601 03:32:58.698869 140219029243776 evaluation.py:167] Evaluation [2500/5000]\\n\",\n            \"INFO:tensorflow:Evaluation [3000/5000]\\n\",\n            \"I0601 03:33:49.175371 140219029243776 evaluation.py:167] Evaluation [3000/5000]\\n\",\n            \"INFO:tensorflow:Evaluation [3500/5000]\\n\",\n            \"I0601 03:34:39.156866 140219029243776 evaluation.py:167] Evaluation [3500/5000]\\n\",\n            \"INFO:tensorflow:Evaluation [4000/5000]\\n\",\n            \"I0601 03:35:28.423717 140219029243776 evaluation.py:167] Evaluation [4000/5000]\\n\",\n            \"INFO:tensorflow:Evaluation [4500/5000]\\n\",\n            \"I0601 03:36:17.885743 140219029243776 evaluation.py:167] Evaluation [4500/5000]\\n\",\n            \"INFO:tensorflow:Evaluation [5000/5000]\\n\",\n            \"I0601 03:37:07.498711 140219029243776 evaluation.py:167] Evaluation [5000/5000]\\n\",\n            \"Loading and preparing results...\\n\",\n            \"Converting ndarray to lists...\\n\",\n            \"(500000, 7)\\n\",\n            \"0/500000\\n\",\n            \"DONE (t=3.43s)\\n\",\n            \"creating index...\\n\",\n            \"index created!\\n\",\n            \"Running per image evaluation...\\n\",\n            \"Evaluate annotation type *bbox*\\n\",\n            \"DONE (t=68.06s).\\n\",\n            \"Accumulating evaluation results...\\n\",\n            \"DONE (t=9.95s).\\n\",\n            \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.335\\n\",\n            \" Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=100 ] = 0.515\\n\",\n            \" Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=100 ] = 0.353\\n\",\n            \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.125\\n\",\n            \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.388\\n\",\n            \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.526\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=  1 ] = 0.287\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets= 10 ] = 0.439\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.465\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.193\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.549\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.685\\n\",\n            \"INFO:tensorflow:Inference Time : 587.28611s\\n\",\n            \"I0601 03:38:30.971660 140219029243776 evaluation.py:273] Inference Time : 587.28611s\\n\",\n            \"INFO:tensorflow:Finished evaluation at 2020-06-01-03:38:30\\n\",\n            \"I0601 03:38:30.971896 140219029243776 evaluation.py:276] Finished evaluation at 2020-06-01-03:38:30\\n\",\n            \"INFO:tensorflow:Saving dict for global step 0: AP = 0.33491418, AP50 = 0.5154238, AP75 = 0.35295796, APl = 0.52646726, APm = 0.38791642, APs = 0.12475659, ARl = 0.6851952, ARm = 0.5492734, ARmax1 = 0.2873492, ARmax10 = 0.43856248, ARmax100 = 0.46534252, ARs = 0.19252816, box_loss = 0.0, cls_loss = 30.711409, global_step = 0, loss = 30.806612\\n\",\n            \"I0601 03:38:30.972132 140219029243776 estimator.py:2066] Saving dict for global step 0: AP = 0.33491418, AP50 = 0.5154238, AP75 = 0.35295796, APl = 0.52646726, APm = 0.38791642, APs = 0.12475659, ARl = 0.6851952, ARm = 0.5492734, ARmax1 = 0.2873492, ARmax10 = 0.43856248, ARmax100 = 0.46534252, ARs = 0.19252816, box_loss = 0.0, cls_loss = 30.711409, global_step = 0, loss = 30.806612\\n\",\n            \"INFO:tensorflow:Saving 'checkpoint_path' summary for global step 0: /content/automl/efficientdet/efficientdet-d0/model\\n\",\n            \"I0601 03:38:31.859352 140219029243776 estimator.py:2127] Saving 'checkpoint_path' summary for global step 0: /content/automl/efficientdet/efficientdet-d0/model\\n\",\n            \"INFO:tensorflow:evaluation_loop marked as finished\\n\",\n            \"I0601 03:38:31.860275 140219029243776 error_handling.py:115] evaluation_loop marked as finished\\n\",\n            \"I0601 03:38:31.860512 140219029243776 main.py:346] Eval results: {'AP': 0.33491418, 'AP50': 0.5154238, 'AP75': 0.35295796, 'APl': 0.52646726, 'APm': 0.38791642, 'APs': 0.12475659, 'ARl': 0.6851952, 'ARm': 0.5492734, 'ARmax1': 0.2873492, 'ARmax10': 0.43856248, 'ARmax100': 0.46534252, 'ARs': 0.19252816, 'box_loss': 0.0, 'cls_loss': 30.711409, 'loss': 30.806612, 'global_step': 0}\\n\",\n            \"I0601 03:38:31.860629 140219029243776 main.py:352] /content/automl/efficientdet/efficientdet-d0/model has no global step info: stop!\\n\"\n          ],\n          \"name\": \"stdout\"\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"mDp_acD1pUcx\",\n        \"colab_type\": \"text\"\n      },\n      \"source\": [\n        \"## 3.2 COCO evaluation on test-dev.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"metadata\": {\n        \"id\": \"9RI_dvx5pbBK\",\n        \"colab_type\": \"code\",\n        \"colab\": {}\n      },\n      \"source\": [\n        \"# Eval on test-dev is slow (~40 mins), please be cautious. \\n\",\n        \"RUN_EXPENSIVE_TEST_DEV_EVAL = True  #@param\\n\",\n        \"\\n\",\n        \"if RUN_EXPENSIVE_TEST_DEV_EVAL == True:\\n\",\n        \"  !rm *.zip *.tar tfrecord/ val2017/   # Cleanup disk space\\n\",\n        \"  # Download and convert test-dev data.\\n\",\n        \"  if \\\"test2017\\\" not in os.listdir():\\n\",\n        \"    !wget http://images.cocodataset.org/zips/test2017.zip\\n\",\n        \"    !unzip -q test2017.zip\\n\",\n        \"    !wget http://images.cocodataset.org/annotations/image_info_test2017.zip\\n\",\n        \"    !unzip image_info_test2017.zip\\n\",\n        \"\\n\",\n        \"    !mkdir tfrecord\\n\",\n        \"    !PYTHONPATH=\\\".:$PYTHONPATH\\\"  python dataset/create_coco_tfrecord.py \\\\\\n\",\n        \"          --image_dir=test2017 \\\\\\n\",\n        \"          --image_info_file=annotations/image_info_test-dev2017.json \\\\\\n\",\n        \"          --output_file_prefix=tfrecord/testdev \\\\\\n\",\n        \"          --num_shards=32\\n\",\n        \"\\n\",\n        \"  # Evalute on validation set: non-empty testdev_dir is the key pararmeter.\\n\",\n        \"  # Also, test-dev has 20288 images rather than val 5000 images.\\n\",\n        \"  !mkdir testdev_output\\n\",\n        \"  !python main.py --mode=eval  \\\\\\n\",\n        \"      --model_name={MODEL}  --model_dir={ckpt_path}  \\\\\\n\",\n        \"      --val_file_pattern=tfrecord/testdev*  \\\\\\n\",\n        \"      --eval_batch_size=8  --eval_samples=20288 \\\\\\n\",\n        \"      --testdev_dir='testdev_output'\\n\",\n        \"  !rm -rf test2017  # delete images to release disk space.\\n\",\n        \"  # Now you can submit testdev_output/detections_test-dev2017_test_results.json to\\n\",\n        \"  # coco server: https://competitions.codalab.org/competitions/20794#participate\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": []\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"RW90fiMiyg4n\",\n        \"colab_type\": \"text\"\n      },\n      \"source\": [\n        \"# 4. Training EfficientDets on PASCAL.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"C98Ye0MEyuKD\",\n        \"colab_type\": \"text\"\n      },\n      \"source\": [\n        \"## 4.1 Prepare data\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"metadata\": {\n        \"id\": \"6PC6QrMlylOF\",\n        \"colab_type\": \"code\",\n        \"outputId\": \"e53e1697-7250-450b-fe94-cb1a4cd9eefc\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 1000\n        }\n      },\n      \"source\": [\n        \"# Get pascal voc 2012 trainval data\\n\",\n        \"import os\\n\",\n        \"if 'VOCdevkit' not in os.listdir():\\n\",\n        \"  !wget http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar\\n\",\n        \"  !tar xf VOCtrainval_11-May-2012.tar\\n\",\n        \"\\n\",\n        \"  !mkdir tfrecord\\n\",\n        \"  !PYTHONPATH=\\\".:$PYTHONPATH\\\"  python dataset/create_pascal_tfrecord.py  \\\\\\n\",\n        \"    --data_dir=VOCdevkit --year=VOC2012  --output_path=tfrecord/pascal\\n\",\n        \"\\n\",\n        \"# Pascal has 5717 train images with 100 shards epoch, here we use a single shard\\n\",\n        \"# for demo, but users should use all shards pascal-*-of-00100.tfrecord.\\n\",\n        \"file_pattern = 'pascal-00000-of-00100.tfrecord'  # @param\\n\",\n        \"images_per_epoch = 57 * len(tf.io.gfile.glob('tfrecord/' + file_pattern))\\n\",\n        \"images_per_epoch = images_per_epoch // 8 * 8  # round to 64.\\n\",\n        \"print('images_per_epoch = {}'.format(images_per_epoch))\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"--2020-06-01 05:10:36--  http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar\\n\",\n            \"Resolving host.robots.ox.ac.uk (host.robots.ox.ac.uk)... 129.67.94.152\\n\",\n            \"Connecting to host.robots.ox.ac.uk (host.robots.ox.ac.uk)|129.67.94.152|:80... connected.\\n\",\n            \"HTTP request sent, awaiting response... Read error (Connection reset by peer) in headers.\\n\",\n            \"Retrying.\\n\",\n            \"\\n\",\n            \"--2020-06-01 05:11:57--  (try: 2)  http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar\\n\",\n            \"Connecting to host.robots.ox.ac.uk (host.robots.ox.ac.uk)|129.67.94.152|:80... connected.\\n\",\n            \"HTTP request sent, awaiting response... 200 OK\\n\",\n            \"Length: 1999639040 (1.9G) [application/x-tar]\\n\",\n            \"Saving to: ‘VOCtrainval_11-May-2012.tar’\\n\",\n            \"\\n\",\n            \"VOCtrainval_11-May- 100%[===================>]   1.86G  13.7MB/s    in 2m 22s  \\n\",\n            \"\\n\",\n            \"2020-06-01 05:20:26 (13.5 MB/s) - ‘VOCtrainval_11-May-2012.tar’ saved [1999639040/1999639040]\\n\",\n            \"\\n\",\n            \"2020-06-01 05:21:21.622800: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"I0601 05:21:23.172357 140062335764352 create_pascal_tfrecord.py:254] writing to output path: tfrecord/pascal\\n\",\n            \"I0601 05:21:23.176307 140062335764352 create_pascal_tfrecord.py:278] Reading from PASCAL VOC2012 dataset.\\n\",\n            \"I0601 05:21:23.187967 140062335764352 create_pascal_tfrecord.py:287] On image 0 of 5717\\n\",\n            \"/content/automl/efficientdet/dataset/tfrecord_util.py:78: FutureWarning: The behavior of this method will change in future versions. Use specific 'len(elem)' or 'elem is not None' test instead.\\n\",\n            \"  if not xml:\\n\",\n            \"I0601 05:21:23.309382 140062335764352 create_pascal_tfrecord.py:287] On image 100 of 5717\\n\",\n            \"I0601 05:21:23.427004 140062335764352 create_pascal_tfrecord.py:287] On image 200 of 5717\\n\",\n            \"I0601 05:21:23.540375 140062335764352 create_pascal_tfrecord.py:287] On image 300 of 5717\\n\",\n            \"I0601 05:21:23.662686 140062335764352 create_pascal_tfrecord.py:287] On image 400 of 5717\\n\",\n            \"I0601 05:21:23.777309 140062335764352 create_pascal_tfrecord.py:287] On image 500 of 5717\\n\",\n            \"I0601 05:21:23.893491 140062335764352 create_pascal_tfrecord.py:287] On image 600 of 5717\\n\",\n            \"I0601 05:21:24.012030 140062335764352 create_pascal_tfrecord.py:287] On image 700 of 5717\\n\",\n            \"I0601 05:21:24.131017 140062335764352 create_pascal_tfrecord.py:287] On image 800 of 5717\\n\",\n            \"I0601 05:21:24.249218 140062335764352 create_pascal_tfrecord.py:287] On image 900 of 5717\\n\",\n            \"I0601 05:21:24.367812 140062335764352 create_pascal_tfrecord.py:287] On image 1000 of 5717\\n\",\n            \"I0601 05:21:24.524589 140062335764352 create_pascal_tfrecord.py:287] On image 1100 of 5717\\n\",\n            \"I0601 05:21:24.671167 140062335764352 create_pascal_tfrecord.py:287] On image 1200 of 5717\\n\",\n            \"I0601 05:21:24.795677 140062335764352 create_pascal_tfrecord.py:287] On image 1300 of 5717\\n\",\n            \"I0601 05:21:24.915077 140062335764352 create_pascal_tfrecord.py:287] On image 1400 of 5717\\n\",\n            \"I0601 05:21:25.049030 140062335764352 create_pascal_tfrecord.py:287] On image 1500 of 5717\\n\",\n            \"I0601 05:21:25.182934 140062335764352 create_pascal_tfrecord.py:287] On image 1600 of 5717\\n\",\n            \"I0601 05:21:25.326709 140062335764352 create_pascal_tfrecord.py:287] On image 1700 of 5717\\n\",\n            \"I0601 05:21:25.466770 140062335764352 create_pascal_tfrecord.py:287] On image 1800 of 5717\\n\",\n            \"I0601 05:21:25.591415 140062335764352 create_pascal_tfrecord.py:287] On image 1900 of 5717\\n\",\n            \"I0601 05:21:25.720433 140062335764352 create_pascal_tfrecord.py:287] On image 2000 of 5717\\n\",\n            \"I0601 05:21:25.838430 140062335764352 create_pascal_tfrecord.py:287] On image 2100 of 5717\\n\",\n            \"I0601 05:21:25.957128 140062335764352 create_pascal_tfrecord.py:287] On image 2200 of 5717\\n\",\n            \"I0601 05:21:26.078567 140062335764352 create_pascal_tfrecord.py:287] On image 2300 of 5717\\n\",\n            \"I0601 05:21:26.262683 140062335764352 create_pascal_tfrecord.py:287] On image 2400 of 5717\\n\",\n            \"I0601 05:21:26.406095 140062335764352 create_pascal_tfrecord.py:287] On image 2500 of 5717\\n\",\n            \"I0601 05:21:26.548389 140062335764352 create_pascal_tfrecord.py:287] On image 2600 of 5717\\n\",\n            \"I0601 05:21:26.699499 140062335764352 create_pascal_tfrecord.py:287] On image 2700 of 5717\\n\",\n            \"I0601 05:21:26.829630 140062335764352 create_pascal_tfrecord.py:287] On image 2800 of 5717\\n\",\n            \"I0601 05:21:26.967325 140062335764352 create_pascal_tfrecord.py:287] On image 2900 of 5717\\n\",\n            \"I0601 05:21:27.102216 140062335764352 create_pascal_tfrecord.py:287] On image 3000 of 5717\\n\",\n            \"I0601 05:21:27.228751 140062335764352 create_pascal_tfrecord.py:287] On image 3100 of 5717\\n\",\n            \"I0601 05:21:27.352252 140062335764352 create_pascal_tfrecord.py:287] On image 3200 of 5717\\n\",\n            \"I0601 05:21:27.477318 140062335764352 create_pascal_tfrecord.py:287] On image 3300 of 5717\\n\",\n            \"I0601 05:21:27.692891 140062335764352 create_pascal_tfrecord.py:287] On image 3400 of 5717\\n\",\n            \"I0601 05:21:27.826326 140062335764352 create_pascal_tfrecord.py:287] On image 3500 of 5717\\n\",\n            \"I0601 05:21:27.950285 140062335764352 create_pascal_tfrecord.py:287] On image 3600 of 5717\\n\",\n            \"I0601 05:21:28.072462 140062335764352 create_pascal_tfrecord.py:287] On image 3700 of 5717\\n\",\n            \"I0601 05:21:28.199001 140062335764352 create_pascal_tfrecord.py:287] On image 3800 of 5717\\n\",\n            \"I0601 05:21:28.321695 140062335764352 create_pascal_tfrecord.py:287] On image 3900 of 5717\\n\",\n            \"I0601 05:21:28.457443 140062335764352 create_pascal_tfrecord.py:287] On image 4000 of 5717\\n\",\n            \"I0601 05:21:28.582388 140062335764352 create_pascal_tfrecord.py:287] On image 4100 of 5717\\n\",\n            \"I0601 05:21:28.714639 140062335764352 create_pascal_tfrecord.py:287] On image 4200 of 5717\\n\",\n            \"I0601 05:21:28.838026 140062335764352 create_pascal_tfrecord.py:287] On image 4300 of 5717\\n\",\n            \"I0601 05:21:28.977360 140062335764352 create_pascal_tfrecord.py:287] On image 4400 of 5717\\n\",\n            \"I0601 05:21:29.144394 140062335764352 create_pascal_tfrecord.py:287] On image 4500 of 5717\\n\",\n            \"I0601 05:21:29.278243 140062335764352 create_pascal_tfrecord.py:287] On image 4600 of 5717\\n\",\n            \"I0601 05:21:29.412878 140062335764352 create_pascal_tfrecord.py:287] On image 4700 of 5717\\n\",\n            \"I0601 05:21:29.556532 140062335764352 create_pascal_tfrecord.py:287] On image 4800 of 5717\\n\",\n            \"I0601 05:21:29.694362 140062335764352 create_pascal_tfrecord.py:287] On image 4900 of 5717\\n\",\n            \"I0601 05:21:29.831758 140062335764352 create_pascal_tfrecord.py:287] On image 5000 of 5717\\n\",\n            \"I0601 05:21:30.044910 140062335764352 create_pascal_tfrecord.py:287] On image 5100 of 5717\\n\",\n            \"I0601 05:21:30.176852 140062335764352 create_pascal_tfrecord.py:287] On image 5200 of 5717\\n\",\n            \"I0601 05:21:30.303508 140062335764352 create_pascal_tfrecord.py:287] On image 5300 of 5717\\n\",\n            \"I0601 05:21:30.431598 140062335764352 create_pascal_tfrecord.py:287] On image 5400 of 5717\\n\",\n            \"I0601 05:21:30.558156 140062335764352 create_pascal_tfrecord.py:287] On image 5500 of 5717\\n\",\n            \"I0601 05:21:30.683752 140062335764352 create_pascal_tfrecord.py:287] On image 5600 of 5717\\n\",\n            \"I0601 05:21:30.810799 140062335764352 create_pascal_tfrecord.py:287] On image 5700 of 5717\\n\",\n            \"images_per_epoch = 56\\n\"\n          ],\n          \"name\": \"stdout\"\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ZcxDDCCW0ndv\",\n        \"colab_type\": \"text\"\n      },\n      \"source\": [\n        \"## 4.2 Train Pascal VOC 2012 from ImageNet checkpoint for Backbone.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"metadata\": {\n        \"id\": \"SHPgm9Q13X-l\",\n        \"colab_type\": \"code\",\n        \"outputId\": \"58a022a3-701a-42ca-cec7-cffb8f3cba85\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 1000\n        }\n      },\n      \"source\": [\n        \"# Train efficientdet from scratch with backbone checkpoint.\\n\",\n        \"backbone_name = {\\n\",\n        \"    'efficientdet-d0': 'efficientnet-b0',\\n\",\n        \"    'efficientdet-d1': 'efficientnet-b1',\\n\",\n        \"    'efficientdet-d2': 'efficientnet-b2',\\n\",\n        \"    'efficientdet-d3': 'efficientnet-b3',\\n\",\n        \"    'efficientdet-d4': 'efficientnet-b4',\\n\",\n        \"    'efficientdet-d5': 'efficientnet-b5',\\n\",\n        \"    'efficientdet-d6': 'efficientnet-b6',\\n\",\n        \"    'efficientdet-d7': 'efficientnet-b6',\\n\",\n        \"}[MODEL]\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"# generating train tfrecord is large, so we skip the execution here.\\n\",\n        \"import os\\n\",\n        \"if backbone_name not in os.listdir():\\n\",\n        \"  !wget https://storage.googleapis.com/cloud-tpu-checkpoints/efficientnet/ckptsaug/{backbone_name}.tar.gz\\n\",\n        \"  !tar xf {backbone_name}.tar.gz\\n\",\n        \"\\n\",\n        \"!mkdir /tmp/model_dir\\n\",\n        \"# key option: use --backbone_ckpt rather than --ckpt.\\n\",\n        \"# Don't use ema since we only train a few steps.\\n\",\n        \"!python main.py --mode=train_and_eval \\\\\\n\",\n        \"    --train_file_pattern=tfrecord/{file_pattern} \\\\\\n\",\n        \"    --val_file_pattern=tfrecord/{file_pattern} \\\\\\n\",\n        \"    --model_name={MODEL} \\\\\\n\",\n        \"    --model_dir=/tmp/model_dir/{MODEL}-scratch  \\\\\\n\",\n        \"    --backbone_ckpt={backbone_name} \\\\\\n\",\n        \"    --train_batch_size=4 \\\\\\n\",\n        \"    --eval_batch_size=4 --eval_samples={images_per_epoch}  \\\\\\n\",\n        \"    --num_examples_per_epoch={images_per_epoch}  --num_epochs=1  \\\\\\n\",\n        \"    --hparams=\\\"num_classes=20,moving_average_decay=0,mixed_precision=true\\\"\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"--2020-06-01 05:36:57--  https://storage.googleapis.com/cloud-tpu-checkpoints/efficientnet/ckptsaug/efficientnet-b0.tar.gz\\n\",\n            \"Resolving storage.googleapis.com (storage.googleapis.com)... 173.194.216.128, 2607:f8b0:400c:c13::80\\n\",\n            \"Connecting to storage.googleapis.com (storage.googleapis.com)|173.194.216.128|:443... connected.\\n\",\n            \"HTTP request sent, awaiting response... 200 OK\\n\",\n            \"Length: 39302973 (37M) [application/gzip]\\n\",\n            \"Saving to: ‘efficientnet-b0.tar.gz’\\n\",\n            \"\\n\",\n            \"\\refficientnet-b0.tar   0%[                    ]       0  --.-KB/s               \\refficientnet-b0.tar  85%[================>   ]  32.01M   148MB/s               \\refficientnet-b0.tar 100%[===================>]  37.48M   162MB/s    in 0.2s    \\n\",\n            \"\\n\",\n            \"2020-06-01 05:36:58 (162 MB/s) - ‘efficientnet-b0.tar.gz’ saved [39302973/39302973]\\n\",\n            \"\\n\",\n            \"2020-06-01 05:37:00.448456: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"WARNING:tensorflow:From main.py:234: The name tf.estimator.tpu.TPUConfig is deprecated. Please use tf.compat.v1.estimator.tpu.TPUConfig instead.\\n\",\n            \"\\n\",\n            \"W0601 05:37:02.781105 140460941842304 module_wrapper.py:138] From main.py:234: The name tf.estimator.tpu.TPUConfig is deprecated. Please use tf.compat.v1.estimator.tpu.TPUConfig instead.\\n\",\n            \"\\n\",\n            \"WARNING:tensorflow:From main.py:239: The name tf.estimator.tpu.InputPipelineConfig is deprecated. Please use tf.compat.v1.estimator.tpu.InputPipelineConfig instead.\\n\",\n            \"\\n\",\n            \"W0601 05:37:02.781442 140460941842304 module_wrapper.py:138] From main.py:239: The name tf.estimator.tpu.InputPipelineConfig is deprecated. Please use tf.compat.v1.estimator.tpu.InputPipelineConfig instead.\\n\",\n            \"\\n\",\n            \"WARNING:tensorflow:From main.py:247: The name tf.estimator.tpu.RunConfig is deprecated. Please use tf.compat.v1.estimator.tpu.RunConfig instead.\\n\",\n            \"\\n\",\n            \"W0601 05:37:02.781647 140460941842304 module_wrapper.py:138] From main.py:247: The name tf.estimator.tpu.RunConfig is deprecated. Please use tf.compat.v1.estimator.tpu.RunConfig instead.\\n\",\n            \"\\n\",\n            \"I0601 05:37:02.781888 140460941842304 main.py:262] {'name': 'efficientdet-d0', 'act_type': 'swish', 'image_size': (512, 512), 'target_size': None, 'input_rand_hflip': True, 'train_scale_min': 0.1, 'train_scale_max': 2.0, 'autoaugment_policy': None, 'use_augmix': False, 'augmix_params': (3, -1, 1), 'num_classes': 20, 'skip_crowd_during_training': True, 'label_id_mapping': None, 'max_instances_per_image': 100, 'min_level': 3, 'max_level': 7, 'num_scales': 3, 'aspect_ratios': [(1.0, 1.0), (1.4, 0.7), (0.7, 1.4)], 'anchor_scale': 4.0, 'is_training_bn': True, 'momentum': 0.9, 'optimizer': 'sgd', 'learning_rate': 0.08, 'lr_warmup_init': 0.008, 'lr_warmup_epoch': 1.0, 'first_lr_drop_epoch': 200.0, 'second_lr_drop_epoch': 250.0, 'poly_lr_power': 0.9, 'clip_gradients_norm': 10.0, 'num_epochs': 1, 'data_format': 'channels_last', 'alpha': 0.25, 'gamma': 1.5, 'delta': 0.1, 'box_loss_weight': 50.0, 'iou_loss_type': None, 'iou_loss_weight': 1.0, 'weight_decay': 4e-05, 'strategy': None, 'precision': 'mixed_float16', 'box_class_repeats': 3, 'fpn_cell_repeats': 3, 'fpn_num_filters': 64, 'separable_conv': True, 'apply_bn_for_resampling': True, 'conv_after_downsample': False, 'conv_bn_act_pattern': False, 'use_native_resize_op': True, 'pooling_type': None, 'fpn_name': None, 'fpn_weight_method': None, 'fpn_config': None, 'survival_prob': None, 'img_summary_steps': None, 'lr_decay_method': 'cosine', 'moving_average_decay': 0, 'ckpt_var_scope': None, 'var_exclude_expr': '.*/class-predict/.*', 'backbone_name': 'efficientnet-b0', 'backbone_config': None, 'var_freeze_expr': None, 'resnet_depth': 50, 'model_name': 'efficientdet-d0', 'iterations_per_loop': 100, 'model_dir': '/tmp/model_dir/efficientdet-d0-scratch', 'num_shards': 8, 'num_examples_per_epoch': 56, 'backbone_ckpt': 'efficientnet-b0', 'ckpt': None, 'val_json_file': None, 'testdev_dir': None, 'mode': 'train_and_eval'}\\n\",\n            \"I0601 05:37:02.782036 140460941842304 main.py:373] Starting training cycle, epoch: 0.\\n\",\n            \"WARNING:tensorflow:From main.py:374: The name tf.estimator.tpu.TPUEstimator is deprecated. Please use tf.compat.v1.estimator.tpu.TPUEstimator instead.\\n\",\n            \"\\n\",\n            \"W0601 05:37:02.782747 140460941842304 module_wrapper.py:138] From main.py:374: The name tf.estimator.tpu.TPUEstimator is deprecated. Please use tf.compat.v1.estimator.tpu.TPUEstimator instead.\\n\",\n            \"\\n\",\n            \"INFO:tensorflow:Using config: {'_model_dir': '/tmp/model_dir/efficientdet-d0-scratch', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true\\n\",\n            \", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': None, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1, '_tpu_config': TPUConfig(iterations_per_loop=100, num_shards=8, num_cores_per_replica=None, per_host_input_for_training=3, tpu_job_name=None, initial_infeed_sleep_secs=None, input_partition_dims=None, eval_training_input_configuration=2, experimental_host_call_every_n_steps=1), '_cluster': None}\\n\",\n            \"I0601 05:37:02.783222 140460941842304 estimator.py:191] Using config: {'_model_dir': '/tmp/model_dir/efficientdet-d0-scratch', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true\\n\",\n            \", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': None, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1, '_tpu_config': TPUConfig(iterations_per_loop=100, num_shards=8, num_cores_per_replica=None, per_host_input_for_training=3, tpu_job_name=None, initial_infeed_sleep_secs=None, input_partition_dims=None, eval_training_input_configuration=2, experimental_host_call_every_n_steps=1), '_cluster': None}\\n\",\n            \"INFO:tensorflow:_TPUContext: eval_on_tpu True\\n\",\n            \"I0601 05:37:02.783910 140460941842304 tpu_context.py:216] _TPUContext: eval_on_tpu True\\n\",\n            \"WARNING:tensorflow:eval_on_tpu ignored because use_tpu is False.\\n\",\n            \"W0601 05:37:02.784342 140460941842304 tpu_context.py:218] eval_on_tpu ignored because use_tpu is False.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:1666: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"If using Keras pass *_constraint arguments to layers.\\n\",\n            \"W0601 05:37:02.789717 140460941842304 deprecation.py:506] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:1666: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"If using Keras pass *_constraint arguments to layers.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/training/training_util.py:236: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.\\n\",\n            \"W0601 05:37:02.790105 140460941842304 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/training/training_util.py:236: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.\\n\",\n            \"2020-06-01 05:37:02.800769: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcuda.so.1\\n\",\n            \"2020-06-01 05:37:02.836188: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:37:02.836775: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1561] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla T4 computeCapability: 7.5\\n\",\n            \"coreClock: 1.59GHz coreCount: 40 deviceMemorySize: 14.73GiB deviceMemoryBandwidth: 298.08GiB/s\\n\",\n            \"2020-06-01 05:37:02.836815: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:37:02.839147: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"2020-06-01 05:37:02.848785: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2020-06-01 05:37:02.849571: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2020-06-01 05:37:02.857847: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2020-06-01 05:37:02.858945: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\\n\",\n            \"2020-06-01 05:37:02.871841: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 05:37:02.872030: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:37:02.872713: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:37:02.873299: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1703] Adding visible gpu devices: 0\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/dataloader.py:363: parallel_interleave (from tensorflow.python.data.experimental.ops.interleave_ops) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.data.Dataset.interleave(map_func, cycle_length, block_length, num_parallel_calls=tf.data.experimental.AUTOTUNE)` instead. If sloppy execution is desired, use `tf.data.Options.experimental_deterministic`.\\n\",\n            \"W0601 05:37:02.904621 140460941842304 deprecation.py:323] From /content/automl/efficientdet/dataloader.py:363: parallel_interleave (from tensorflow.python.data.experimental.ops.interleave_ops) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.data.Dataset.interleave(map_func, cycle_length, block_length, num_parallel_calls=tf.data.experimental.AUTOTUNE)` instead. If sloppy execution is desired, use `tf.data.Options.experimental_deterministic`.\\n\",\n            \"I0601 05:37:03.094781 140460941842304 dataloader.py:83] target_size = (512, 512), output_size = (512, 512)\\n\",\n            \"INFO:tensorflow:Calling model_fn.\\n\",\n            \"I0601 05:37:03.714015 140460941842304 estimator.py:1169] Calling model_fn.\\n\",\n            \"INFO:tensorflow:Running train on CPU/GPU\\n\",\n            \"I0601 05:37:03.714310 140460941842304 tpu_estimator.py:3171] Running train on CPU/GPU\\n\",\n            \"I0601 05:37:03.714726 140460941842304 utils.py:595] use mixed precision policy name mixed_float16\\n\",\n            \"I0601 05:37:03.719992 140460941842304 efficientdet_arch.py:720] act_type: swish\\n\",\n            \"alpha: 0.25\\n\",\n            \"anchor_scale: 4.0\\n\",\n            \"apply_bn_for_resampling: true\\n\",\n            \"aspect_ratios:\\n\",\n            \"- !!python/tuple\\n\",\n            \"    - 1.0\\n\",\n            \"    - 1.0\\n\",\n            \"- !!python/tuple\\n\",\n            \"    - 1.4\\n\",\n            \"    - 0.7\\n\",\n            \"- !!python/tuple\\n\",\n            \"    - 0.7\\n\",\n            \"    - 1.4\\n\",\n            \"augmix_params: !!python/tuple\\n\",\n            \"- 3\\n\",\n            \"- -1\\n\",\n            \"- 1\\n\",\n            \"autoaugment_policy: null\\n\",\n            \"backbone_ckpt: efficientnet-b0\\n\",\n            \"backbone_config: null\\n\",\n            \"backbone_name: efficientnet-b0\\n\",\n            \"batch_size: 4\\n\",\n            \"box_class_repeats: 3\\n\",\n            \"box_loss_weight: 50.0\\n\",\n            \"ckpt: null\\n\",\n            \"ckpt_var_scope: null\\n\",\n            \"clip_gradients_norm: 10.0\\n\",\n            \"conv_after_downsample: false\\n\",\n            \"conv_bn_act_pattern: false\\n\",\n            \"data_format: channels_last\\n\",\n            \"delta: 0.1\\n\",\n            \"first_lr_drop_epoch: 200.0\\n\",\n            \"fpn_cell_repeats: 3\\n\",\n            \"fpn_config: null\\n\",\n            \"fpn_name: null\\n\",\n            \"fpn_num_filters: 64\\n\",\n            \"fpn_weight_method: null\\n\",\n            \"gamma: 1.5\\n\",\n            \"image_size: !!python/tuple\\n\",\n            \"- 512\\n\",\n            \"- 512\\n\",\n            \"img_summary_steps: null\\n\",\n            \"input_rand_hflip: true\\n\",\n            \"iou_loss_type: null\\n\",\n            \"iou_loss_weight: 1.0\\n\",\n            \"is_training_bn: true\\n\",\n            \"iterations_per_loop: 100\\n\",\n            \"label_id_mapping: null\\n\",\n            \"learning_rate: 0.08\\n\",\n            \"lr_decay_method: cosine\\n\",\n            \"lr_warmup_epoch: 1.0\\n\",\n            \"lr_warmup_init: 0.008\\n\",\n            \"max_instances_per_image: 100\\n\",\n            \"max_level: 7\\n\",\n            \"min_level: 3\\n\",\n            \"mode: train_and_eval\\n\",\n            \"model_dir: /tmp/model_dir/efficientdet-d0-scratch\\n\",\n            \"model_name: efficientdet-d0\\n\",\n            \"momentum: 0.9\\n\",\n            \"moving_average_decay: 0\\n\",\n            \"name: efficientdet-d0\\n\",\n            \"num_classes: 20\\n\",\n            \"num_epochs: 1\\n\",\n            \"num_examples_per_epoch: 56\\n\",\n            \"num_scales: 3\\n\",\n            \"num_shards: 8\\n\",\n            \"optimizer: sgd\\n\",\n            \"poly_lr_power: 0.9\\n\",\n            \"pooling_type: null\\n\",\n            \"precision: mixed_float16\\n\",\n            \"resnet_depth: 50\\n\",\n            \"second_lr_drop_epoch: 250.0\\n\",\n            \"separable_conv: true\\n\",\n            \"skip_crowd_during_training: true\\n\",\n            \"strategy: null\\n\",\n            \"survival_prob: null\\n\",\n            \"target_size: null\\n\",\n            \"testdev_dir: null\\n\",\n            \"train_scale_max: 2.0\\n\",\n            \"train_scale_min: 0.1\\n\",\n            \"use_augmix: false\\n\",\n            \"use_native_resize_op: true\\n\",\n            \"use_tpu: false\\n\",\n            \"val_json_file: null\\n\",\n            \"var_exclude_expr: .*/class-predict/.*\\n\",\n            \"var_freeze_expr: null\\n\",\n            \"weight_decay: 4.0e-05\\n\",\n            \"\\n\",\n            \"I0601 05:37:03.724804 140460941842304 efficientnet_builder.py:224] global_params= GlobalParams(batch_norm_momentum=0.99, batch_norm_epsilon=0.001, dropout_rate=0.2, data_format='channels_last', num_classes=1000, width_coefficient=1.0, depth_coefficient=1.0, depth_divisor=8, min_depth=None, survival_prob=0.0, relu_fn=functools.partial(<function activation_fn at 0x7fbf45bc8d90>, act_type='swish'), batch_norm=<class 'utils.BatchNormalization'>, use_se=True, local_pooling=None, condconv_num_experts=None, clip_projection_output=False, blocks_args=['r1_k3_s11_e1_i32_o16_se0.25', 'r2_k3_s22_e6_i16_o24_se0.25', 'r2_k5_s22_e6_i24_o40_se0.25', 'r3_k3_s22_e6_i40_o80_se0.25', 'r3_k5_s11_e6_i80_o112_se0.25', 'r4_k5_s22_e6_i112_o192_se0.25', 'r1_k3_s11_e6_i192_o320_se0.25'], fix_head_stem=None)\\n\",\n            \"I0601 05:37:06.724712 140460941842304 api.py:587] Built stem layers with output shape: (4, 256, 256, 32)\\n\",\n            \"I0601 05:37:09.435570 140460941842304 api.py:587] Block mb_conv_block  input shape: (4, 256, 256, 32)\\n\",\n            \"I0601 05:37:09.477118 140460941842304 api.py:587] DWConv shape: (4, 256, 256, 32)\\n\",\n            \"I0601 05:37:09.638726 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 32)\\n\",\n            \"I0601 05:37:09.676554 140460941842304 api.py:587] Project shape: (4, 256, 256, 16)\\n\",\n            \"I0601 05:37:09.729519 140460941842304 api.py:587] Block mb_conv_block_1  input shape: (4, 256, 256, 16)\\n\",\n            \"I0601 05:37:09.769014 140460941842304 api.py:587] Expand shape: (4, 256, 256, 96)\\n\",\n            \"I0601 05:37:09.808395 140460941842304 api.py:587] DWConv shape: (4, 128, 128, 96)\\n\",\n            \"I0601 05:37:09.844948 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 96)\\n\",\n            \"I0601 05:37:09.884656 140460941842304 api.py:587] Project shape: (4, 128, 128, 24)\\n\",\n            \"I0601 05:37:09.894854 140460941842304 api.py:587] Block mb_conv_block_2  input shape: (4, 128, 128, 24)\\n\",\n            \"I0601 05:37:09.938410 140460941842304 api.py:587] Expand shape: (4, 128, 128, 144)\\n\",\n            \"I0601 05:37:09.976691 140460941842304 api.py:587] DWConv shape: (4, 128, 128, 144)\\n\",\n            \"I0601 05:37:10.013263 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 144)\\n\",\n            \"I0601 05:37:10.061878 140460941842304 api.py:587] Project shape: (4, 128, 128, 24)\\n\",\n            \"I0601 05:37:10.071763 140460941842304 api.py:587] Block mb_conv_block_3  input shape: (4, 128, 128, 24)\\n\",\n            \"I0601 05:37:10.116038 140460941842304 api.py:587] Expand shape: (4, 128, 128, 144)\\n\",\n            \"I0601 05:37:10.158890 140460941842304 api.py:587] DWConv shape: (4, 64, 64, 144)\\n\",\n            \"I0601 05:37:10.195446 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 144)\\n\",\n            \"I0601 05:37:10.236959 140460941842304 api.py:587] Project shape: (4, 64, 64, 40)\\n\",\n            \"I0601 05:37:10.246315 140460941842304 api.py:587] Block mb_conv_block_4  input shape: (4, 64, 64, 40)\\n\",\n            \"I0601 05:37:10.285769 140460941842304 api.py:587] Expand shape: (4, 64, 64, 240)\\n\",\n            \"I0601 05:37:10.331516 140460941842304 api.py:587] DWConv shape: (4, 64, 64, 240)\\n\",\n            \"I0601 05:37:10.366286 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 240)\\n\",\n            \"I0601 05:37:10.403537 140460941842304 api.py:587] Project shape: (4, 64, 64, 40)\\n\",\n            \"I0601 05:37:10.418687 140460941842304 api.py:587] Block mb_conv_block_5  input shape: (4, 64, 64, 40)\\n\",\n            \"I0601 05:37:10.457679 140460941842304 api.py:587] Expand shape: (4, 64, 64, 240)\\n\",\n            \"I0601 05:37:10.497891 140460941842304 api.py:587] DWConv shape: (4, 32, 32, 240)\\n\",\n            \"I0601 05:37:10.532069 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 240)\\n\",\n            \"I0601 05:37:10.571993 140460941842304 api.py:587] Project shape: (4, 32, 32, 80)\\n\",\n            \"I0601 05:37:10.582987 140460941842304 api.py:587] Block mb_conv_block_6  input shape: (4, 32, 32, 80)\\n\",\n            \"I0601 05:37:10.630499 140460941842304 api.py:587] Expand shape: (4, 32, 32, 480)\\n\",\n            \"I0601 05:37:10.675540 140460941842304 api.py:587] DWConv shape: (4, 32, 32, 480)\\n\",\n            \"I0601 05:37:10.711717 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 480)\\n\",\n            \"I0601 05:37:10.753072 140460941842304 api.py:587] Project shape: (4, 32, 32, 80)\\n\",\n            \"I0601 05:37:10.762947 140460941842304 api.py:587] Block mb_conv_block_7  input shape: (4, 32, 32, 80)\\n\",\n            \"I0601 05:37:10.804822 140460941842304 api.py:587] Expand shape: (4, 32, 32, 480)\\n\",\n            \"I0601 05:37:10.850904 140460941842304 api.py:587] DWConv shape: (4, 32, 32, 480)\\n\",\n            \"I0601 05:37:10.886982 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 480)\\n\",\n            \"I0601 05:37:10.926297 140460941842304 api.py:587] Project shape: (4, 32, 32, 80)\\n\",\n            \"I0601 05:37:10.936717 140460941842304 api.py:587] Block mb_conv_block_8  input shape: (4, 32, 32, 80)\\n\",\n            \"I0601 05:37:10.979605 140460941842304 api.py:587] Expand shape: (4, 32, 32, 480)\\n\",\n            \"I0601 05:37:11.026784 140460941842304 api.py:587] DWConv shape: (4, 32, 32, 480)\\n\",\n            \"I0601 05:37:11.067464 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 480)\\n\",\n            \"I0601 05:37:11.106633 140460941842304 api.py:587] Project shape: (4, 32, 32, 112)\\n\",\n            \"I0601 05:37:11.116317 140460941842304 api.py:587] Block mb_conv_block_9  input shape: (4, 32, 32, 112)\\n\",\n            \"I0601 05:37:11.158664 140460941842304 api.py:587] Expand shape: (4, 32, 32, 672)\\n\",\n            \"I0601 05:37:11.203032 140460941842304 api.py:587] DWConv shape: (4, 32, 32, 672)\\n\",\n            \"I0601 05:37:11.242812 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 672)\\n\",\n            \"I0601 05:37:11.280435 140460941842304 api.py:587] Project shape: (4, 32, 32, 112)\\n\",\n            \"I0601 05:37:11.289857 140460941842304 api.py:587] Block mb_conv_block_10  input shape: (4, 32, 32, 112)\\n\",\n            \"I0601 05:37:11.334518 140460941842304 api.py:587] Expand shape: (4, 32, 32, 672)\\n\",\n            \"I0601 05:37:11.375933 140460941842304 api.py:587] DWConv shape: (4, 32, 32, 672)\\n\",\n            \"I0601 05:37:11.413398 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 672)\\n\",\n            \"I0601 05:37:11.456761 140460941842304 api.py:587] Project shape: (4, 32, 32, 112)\\n\",\n            \"I0601 05:37:11.466616 140460941842304 api.py:587] Block mb_conv_block_11  input shape: (4, 32, 32, 112)\\n\",\n            \"I0601 05:37:11.506339 140460941842304 api.py:587] Expand shape: (4, 32, 32, 672)\\n\",\n            \"I0601 05:37:11.547044 140460941842304 api.py:587] DWConv shape: (4, 16, 16, 672)\\n\",\n            \"I0601 05:37:11.586595 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 672)\\n\",\n            \"I0601 05:37:11.626645 140460941842304 api.py:587] Project shape: (4, 16, 16, 192)\\n\",\n            \"I0601 05:37:11.640136 140460941842304 api.py:587] Block mb_conv_block_12  input shape: (4, 16, 16, 192)\\n\",\n            \"I0601 05:37:11.687014 140460941842304 api.py:587] Expand shape: (4, 16, 16, 1152)\\n\",\n            \"I0601 05:37:11.730680 140460941842304 api.py:587] DWConv shape: (4, 16, 16, 1152)\\n\",\n            \"I0601 05:37:11.768292 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 1152)\\n\",\n            \"I0601 05:37:11.808110 140460941842304 api.py:587] Project shape: (4, 16, 16, 192)\\n\",\n            \"I0601 05:37:11.817761 140460941842304 api.py:587] Block mb_conv_block_13  input shape: (4, 16, 16, 192)\\n\",\n            \"I0601 05:37:11.865955 140460941842304 api.py:587] Expand shape: (4, 16, 16, 1152)\\n\",\n            \"I0601 05:37:11.910137 140460941842304 api.py:587] DWConv shape: (4, 16, 16, 1152)\\n\",\n            \"I0601 05:37:11.947402 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 1152)\\n\",\n            \"I0601 05:37:11.991628 140460941842304 api.py:587] Project shape: (4, 16, 16, 192)\\n\",\n            \"I0601 05:37:12.002006 140460941842304 api.py:587] Block mb_conv_block_14  input shape: (4, 16, 16, 192)\\n\",\n            \"I0601 05:37:12.057711 140460941842304 api.py:587] Expand shape: (4, 16, 16, 1152)\\n\",\n            \"I0601 05:37:12.116494 140460941842304 api.py:587] DWConv shape: (4, 16, 16, 1152)\\n\",\n            \"I0601 05:37:12.158459 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 1152)\\n\",\n            \"I0601 05:37:12.203221 140460941842304 api.py:587] Project shape: (4, 16, 16, 192)\\n\",\n            \"I0601 05:37:12.212300 140460941842304 api.py:587] Block mb_conv_block_15  input shape: (4, 16, 16, 192)\\n\",\n            \"I0601 05:37:12.260587 140460941842304 api.py:587] Expand shape: (4, 16, 16, 1152)\\n\",\n            \"I0601 05:37:12.304630 140460941842304 api.py:587] DWConv shape: (4, 16, 16, 1152)\\n\",\n            \"I0601 05:37:12.346603 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 1152)\\n\",\n            \"I0601 05:37:12.389416 140460941842304 api.py:587] Project shape: (4, 16, 16, 320)\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/profiler/internal/flops_registry.py:142: tensor_shape_from_node_def_name (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.tensor_shape_from_node_def_name`\\n\",\n            \"W0601 05:37:12.399743 140460941842304 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/profiler/internal/flops_registry.py:142: tensor_shape_from_node_def_name (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.tensor_shape_from_node_def_name`\\n\",\n            \"Parsing Inputs...\\n\",\n            \"I0601 05:37:12.734671 140460941842304 efficientdet_arch.py:725] backbone params/flops = 3.595388M, 7.723650081B\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:138: conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.Conv2D` instead.\\n\",\n            \"W0601 05:37:12.735137 140460941842304 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:138: conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.Conv2D` instead.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/layers/convolutional.py:424: Layer.apply (from tensorflow.python.keras.engine.base_layer_v1) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Please use `layer.__call__` method instead.\\n\",\n            \"W0601 05:37:12.738550 140460941842304 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/layers/convolutional.py:424: Layer.apply (from tensorflow.python.keras.engine.base_layer_v1) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Please use `layer.__call__` method instead.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:164: max_pooling2d (from tensorflow.python.layers.pooling) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use keras.layers.MaxPooling2D instead.\\n\",\n            \"W0601 05:37:12.796389 140460941842304 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:164: max_pooling2d (from tensorflow.python.layers.pooling) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use keras.layers.MaxPooling2D instead.\\n\",\n            \"I0601 05:37:12.802982 140460941842304 efficientdet_arch.py:471] building cell 0\\n\",\n            \"I0601 05:37:12.803340 140460941842304 efficientdet_arch.py:648] fnode 0 : {'feat_level': 6, 'inputs_offsets': [3, 4]}\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:685: separable_conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.SeparableConv2D` instead.\\n\",\n            \"W0601 05:37:12.816923 140460941842304 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:685: separable_conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.SeparableConv2D` instead.\\n\",\n            \"I0601 05:37:12.879780 140460941842304 efficientdet_arch.py:648] fnode 1 : {'feat_level': 5, 'inputs_offsets': [2, 5]}\\n\",\n            \"I0601 05:37:13.014620 140460941842304 efficientdet_arch.py:648] fnode 2 : {'feat_level': 4, 'inputs_offsets': [1, 6]}\\n\",\n            \"I0601 05:37:13.156741 140460941842304 efficientdet_arch.py:648] fnode 3 : {'feat_level': 3, 'inputs_offsets': [0, 7]}\\n\",\n            \"I0601 05:37:13.296745 140460941842304 efficientdet_arch.py:648] fnode 4 : {'feat_level': 4, 'inputs_offsets': [1, 7, 8]}\\n\",\n            \"I0601 05:37:13.445665 140460941842304 efficientdet_arch.py:648] fnode 5 : {'feat_level': 5, 'inputs_offsets': [2, 6, 9]}\\n\",\n            \"I0601 05:37:13.594432 140460941842304 efficientdet_arch.py:648] fnode 6 : {'feat_level': 6, 'inputs_offsets': [3, 5, 10]}\\n\",\n            \"I0601 05:37:13.688446 140460941842304 efficientdet_arch.py:648] fnode 7 : {'feat_level': 7, 'inputs_offsets': [4, 11]}\\n\",\n            \"I0601 05:37:13.871573 140460941842304 efficientdet_arch.py:471] building cell 1\\n\",\n            \"I0601 05:37:13.871955 140460941842304 efficientdet_arch.py:648] fnode 0 : {'feat_level': 6, 'inputs_offsets': [3, 4]}\\n\",\n            \"I0601 05:37:13.965792 140460941842304 efficientdet_arch.py:648] fnode 1 : {'feat_level': 5, 'inputs_offsets': [2, 5]}\\n\",\n            \"I0601 05:37:14.063542 140460941842304 efficientdet_arch.py:648] fnode 2 : {'feat_level': 4, 'inputs_offsets': [1, 6]}\\n\",\n            \"I0601 05:37:14.167066 140460941842304 efficientdet_arch.py:648] fnode 3 : {'feat_level': 3, 'inputs_offsets': [0, 7]}\\n\",\n            \"I0601 05:37:14.261668 140460941842304 efficientdet_arch.py:648] fnode 4 : {'feat_level': 4, 'inputs_offsets': [1, 7, 8]}\\n\",\n            \"I0601 05:37:14.359102 140460941842304 efficientdet_arch.py:648] fnode 5 : {'feat_level': 5, 'inputs_offsets': [2, 6, 9]}\\n\",\n            \"I0601 05:37:14.460067 140460941842304 efficientdet_arch.py:648] fnode 6 : {'feat_level': 6, 'inputs_offsets': [3, 5, 10]}\\n\",\n            \"I0601 05:37:14.556290 140460941842304 efficientdet_arch.py:648] fnode 7 : {'feat_level': 7, 'inputs_offsets': [4, 11]}\\n\",\n            \"I0601 05:37:14.650365 140460941842304 efficientdet_arch.py:471] building cell 2\\n\",\n            \"I0601 05:37:14.650751 140460941842304 efficientdet_arch.py:648] fnode 0 : {'feat_level': 6, 'inputs_offsets': [3, 4]}\\n\",\n            \"I0601 05:37:14.746739 140460941842304 efficientdet_arch.py:648] fnode 1 : {'feat_level': 5, 'inputs_offsets': [2, 5]}\\n\",\n            \"I0601 05:37:14.839383 140460941842304 efficientdet_arch.py:648] fnode 2 : {'feat_level': 4, 'inputs_offsets': [1, 6]}\\n\",\n            \"I0601 05:37:14.936065 140460941842304 efficientdet_arch.py:648] fnode 3 : {'feat_level': 3, 'inputs_offsets': [0, 7]}\\n\",\n            \"I0601 05:37:15.035170 140460941842304 efficientdet_arch.py:648] fnode 4 : {'feat_level': 4, 'inputs_offsets': [1, 7, 8]}\\n\",\n            \"I0601 05:37:15.149712 140460941842304 efficientdet_arch.py:648] fnode 5 : {'feat_level': 5, 'inputs_offsets': [2, 6, 9]}\\n\",\n            \"I0601 05:37:15.257548 140460941842304 efficientdet_arch.py:648] fnode 6 : {'feat_level': 6, 'inputs_offsets': [3, 5, 10]}\\n\",\n            \"I0601 05:37:15.366530 140460941842304 efficientdet_arch.py:648] fnode 7 : {'feat_level': 7, 'inputs_offsets': [4, 11]}\\n\",\n            \"Parsing Inputs...\\n\",\n            \"I0601 05:37:16.045335 140460941842304 efficientdet_arch.py:730] backbone+fpn params/flops = 3.791669M, 8.294161259B\\n\",\n            \"Parsing Inputs...\\n\",\n            \"I0601 05:37:19.741914 140460941842304 efficientdet_arch.py:735] backbone+fpn+box params/flops = 3.839117M, 9.243552137B\\n\",\n            \"I0601 05:37:19.742230 140460941842304 utils.py:595] use mixed precision policy name float32\\n\",\n            \"I0601 05:37:19.746633 140460941842304 det_model_fn.py:97] LR schedule method: cosine\\n\",\n            \"I0601 05:37:19.968963 140460941842304 utils.py:394] Adding scale summary ('lrn_rate', <tf.Tensor 'Select:0' shape=() dtype=float32>)\\n\",\n            \"I0601 05:37:19.969849 140460941842304 utils.py:394] Adding scale summary ('trainloss/cls_loss', <tf.Tensor 'AddN:0' shape=() dtype=float32>)\\n\",\n            \"I0601 05:37:19.970671 140460941842304 utils.py:394] Adding scale summary ('trainloss/box_loss', <tf.Tensor 'AddN_1:0' shape=() dtype=float32>)\\n\",\n            \"I0601 05:37:19.971507 140460941842304 utils.py:394] Adding scale summary ('trainloss/det_loss', <tf.Tensor 'add_4:0' shape=() dtype=float32>)\\n\",\n            \"I0601 05:37:19.972293 140460941842304 utils.py:394] Adding scale summary ('trainloss/reg_l2_loss', <tf.Tensor 'mul_14:0' shape=() dtype=float32>)\\n\",\n            \"I0601 05:37:19.973071 140460941842304 utils.py:394] Adding scale summary ('trainloss/loss', <tf.Tensor 'add_5:0' shape=() dtype=float32>)\\n\",\n            \"I0601 05:37:19.973890 140460941842304 det_model_fn.py:537] clip gradients norm by 10.000000\\n\",\n            \"I0601 05:37:24.866811 140460941842304 utils.py:394] Adding scale summary ('gnorm', <tf.Tensor 'clip/global_norm/global_norm:0' shape=() dtype=float32>)\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/det_model_fn.py:659: The name tf.estimator.tpu.TPUEstimatorSpec is deprecated. Please use tf.compat.v1.estimator.tpu.TPUEstimatorSpec instead.\\n\",\n            \"\\n\",\n            \"W0601 05:37:27.070672 140460941842304 module_wrapper.py:138] From /content/automl/efficientdet/det_model_fn.py:659: The name tf.estimator.tpu.TPUEstimatorSpec is deprecated. Please use tf.compat.v1.estimator.tpu.TPUEstimatorSpec instead.\\n\",\n            \"\\n\",\n            \"I0601 05:37:27.116415 140460941842304 det_model_fn.py:638] restore variables from efficientnet-b0\\n\",\n            \"I0601 05:37:27.116562 140460941842304 utils.py:73] Init model from checkpoint efficientnet-b0\\n\",\n            \"I0601 05:37:27.121120 140460941842304 utils.py:108] Init efficientnet-b0/stem/conv2d/kernel from ckpt var efficientnet-b0/stem/conv2d/kernel\\n\",\n            \"I0601 05:37:27.121282 140460941842304 utils.py:108] Init efficientnet-b0/stem/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/stem/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:37:27.121384 140460941842304 utils.py:108] Init efficientnet-b0/stem/tpu_batch_normalization/beta from ckpt var efficientnet-b0/stem/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:37:27.121477 140460941842304 utils.py:108] Init efficientnet-b0/stem/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/stem/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:37:27.121554 140460941842304 utils.py:108] Init efficientnet-b0/stem/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/stem/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:37:27.121651 140460941842304 utils.py:108] Init efficientnet-b0/blocks_0/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_0/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:37:27.121726 140460941842304 utils.py:108] Init efficientnet-b0/blocks_0/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_0/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:37:27.121800 140460941842304 utils.py:108] Init efficientnet-b0/blocks_0/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_0/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:37:27.121912 140460941842304 utils.py:108] Init efficientnet-b0/blocks_0/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_0/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:37:27.122007 140460941842304 utils.py:108] Init efficientnet-b0/blocks_0/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_0/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:37:27.122094 140460941842304 utils.py:108] Init efficientnet-b0/blocks_0/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_0/se/conv2d/kernel\\n\",\n            \"I0601 05:37:27.122166 140460941842304 utils.py:108] Init efficientnet-b0/blocks_0/se/conv2d/bias from ckpt var efficientnet-b0/blocks_0/se/conv2d/bias\\n\",\n            \"I0601 05:37:27.122240 140460941842304 utils.py:108] Init efficientnet-b0/blocks_0/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_0/se/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.122339 140460941842304 utils.py:108] Init efficientnet-b0/blocks_0/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_0/se/conv2d_1/bias\\n\",\n            \"I0601 05:37:27.122414 140460941842304 utils.py:108] Init efficientnet-b0/blocks_0/conv2d/kernel from ckpt var efficientnet-b0/blocks_0/conv2d/kernel\\n\",\n            \"I0601 05:37:27.122487 140460941842304 utils.py:108] Init efficientnet-b0/blocks_0/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_0/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:37:27.122561 140460941842304 utils.py:108] Init efficientnet-b0/blocks_0/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_0/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:37:27.122643 140460941842304 utils.py:108] Init efficientnet-b0/blocks_0/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_0/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:37:27.122731 140460941842304 utils.py:108] Init efficientnet-b0/blocks_0/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_0/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:37:27.122803 140460941842304 utils.py:108] Init efficientnet-b0/blocks_1/conv2d/kernel from ckpt var efficientnet-b0/blocks_1/conv2d/kernel\\n\",\n            \"I0601 05:37:27.122874 140460941842304 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:37:27.122954 140460941842304 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:37:27.123035 140460941842304 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:37:27.123108 140460941842304 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:37:27.123179 140460941842304 utils.py:108] Init efficientnet-b0/blocks_1/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_1/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:37:27.123252 140460941842304 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:37:27.123349 140460941842304 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:37:27.123430 140460941842304 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:37:27.123515 140460941842304 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:37:27.123599 140460941842304 utils.py:108] Init efficientnet-b0/blocks_1/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_1/se/conv2d/kernel\\n\",\n            \"I0601 05:37:27.123682 140460941842304 utils.py:108] Init efficientnet-b0/blocks_1/se/conv2d/bias from ckpt var efficientnet-b0/blocks_1/se/conv2d/bias\\n\",\n            \"I0601 05:37:27.123755 140460941842304 utils.py:108] Init efficientnet-b0/blocks_1/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_1/se/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.123831 140460941842304 utils.py:108] Init efficientnet-b0/blocks_1/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_1/se/conv2d_1/bias\\n\",\n            \"I0601 05:37:27.123944 140460941842304 utils.py:108] Init efficientnet-b0/blocks_1/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_1/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.124024 140460941842304 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:37:27.124098 140460941842304 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:37:27.124171 140460941842304 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:37:27.124250 140460941842304 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:37:27.124345 140460941842304 utils.py:108] Init efficientnet-b0/blocks_2/conv2d/kernel from ckpt var efficientnet-b0/blocks_2/conv2d/kernel\\n\",\n            \"I0601 05:37:27.124422 140460941842304 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:37:27.124494 140460941842304 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:37:27.124572 140460941842304 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:37:27.124654 140460941842304 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:37:27.124723 140460941842304 utils.py:108] Init efficientnet-b0/blocks_2/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_2/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:37:27.124789 140460941842304 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:37:27.124865 140460941842304 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:37:27.124931 140460941842304 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:37:27.125029 140460941842304 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:37:27.125109 140460941842304 utils.py:108] Init efficientnet-b0/blocks_2/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_2/se/conv2d/kernel\\n\",\n            \"I0601 05:37:27.125190 140460941842304 utils.py:108] Init efficientnet-b0/blocks_2/se/conv2d/bias from ckpt var efficientnet-b0/blocks_2/se/conv2d/bias\\n\",\n            \"I0601 05:37:27.125257 140460941842304 utils.py:108] Init efficientnet-b0/blocks_2/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_2/se/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.125338 140460941842304 utils.py:108] Init efficientnet-b0/blocks_2/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_2/se/conv2d_1/bias\\n\",\n            \"I0601 05:37:27.125397 140460941842304 utils.py:108] Init efficientnet-b0/blocks_2/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_2/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.125455 140460941842304 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:37:27.125514 140460941842304 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:37:27.125575 140460941842304 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:37:27.125635 140460941842304 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:37:27.125697 140460941842304 utils.py:108] Init efficientnet-b0/blocks_3/conv2d/kernel from ckpt var efficientnet-b0/blocks_3/conv2d/kernel\\n\",\n            \"I0601 05:37:27.125760 140460941842304 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:37:27.125820 140460941842304 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:37:27.125884 140460941842304 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:37:27.125946 140460941842304 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:37:27.126019 140460941842304 utils.py:108] Init efficientnet-b0/blocks_3/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_3/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:37:27.126083 140460941842304 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:37:27.126143 140460941842304 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:37:27.126204 140460941842304 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:37:27.126283 140460941842304 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:37:27.126359 140460941842304 utils.py:108] Init efficientnet-b0/blocks_3/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_3/se/conv2d/kernel\\n\",\n            \"I0601 05:37:27.126425 140460941842304 utils.py:108] Init efficientnet-b0/blocks_3/se/conv2d/bias from ckpt var efficientnet-b0/blocks_3/se/conv2d/bias\\n\",\n            \"I0601 05:37:27.126497 140460941842304 utils.py:108] Init efficientnet-b0/blocks_3/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_3/se/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.126557 140460941842304 utils.py:108] Init efficientnet-b0/blocks_3/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_3/se/conv2d_1/bias\\n\",\n            \"I0601 05:37:27.126614 140460941842304 utils.py:108] Init efficientnet-b0/blocks_3/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_3/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.126672 140460941842304 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:37:27.126732 140460941842304 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:37:27.126789 140460941842304 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:37:27.126847 140460941842304 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:37:27.126905 140460941842304 utils.py:108] Init efficientnet-b0/blocks_4/conv2d/kernel from ckpt var efficientnet-b0/blocks_4/conv2d/kernel\\n\",\n            \"I0601 05:37:27.126962 140460941842304 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:37:27.127028 140460941842304 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:37:27.127087 140460941842304 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:37:27.127145 140460941842304 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:37:27.127201 140460941842304 utils.py:108] Init efficientnet-b0/blocks_4/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_4/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:37:27.127257 140460941842304 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:37:27.127334 140460941842304 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:37:27.127394 140460941842304 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:37:27.127453 140460941842304 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:37:27.127517 140460941842304 utils.py:108] Init efficientnet-b0/blocks_4/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_4/se/conv2d/kernel\\n\",\n            \"I0601 05:37:27.127576 140460941842304 utils.py:108] Init efficientnet-b0/blocks_4/se/conv2d/bias from ckpt var efficientnet-b0/blocks_4/se/conv2d/bias\\n\",\n            \"I0601 05:37:27.127633 140460941842304 utils.py:108] Init efficientnet-b0/blocks_4/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_4/se/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.127741 140460941842304 utils.py:108] Init efficientnet-b0/blocks_4/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_4/se/conv2d_1/bias\\n\",\n            \"I0601 05:37:27.127798 140460941842304 utils.py:108] Init efficientnet-b0/blocks_4/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_4/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.127856 140460941842304 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:37:27.127914 140460941842304 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:37:27.127989 140460941842304 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:37:27.128060 140460941842304 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:37:27.128121 140460941842304 utils.py:108] Init efficientnet-b0/blocks_5/conv2d/kernel from ckpt var efficientnet-b0/blocks_5/conv2d/kernel\\n\",\n            \"I0601 05:37:27.128182 140460941842304 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:37:27.207009 140460941842304 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:37:27.207161 140460941842304 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:37:27.207300 140460941842304 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:37:27.207408 140460941842304 utils.py:108] Init efficientnet-b0/blocks_5/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_5/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:37:27.207518 140460941842304 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:37:27.207614 140460941842304 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:37:27.207706 140460941842304 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:37:27.207796 140460941842304 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:37:27.207886 140460941842304 utils.py:108] Init efficientnet-b0/blocks_5/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_5/se/conv2d/kernel\\n\",\n            \"I0601 05:37:27.207974 140460941842304 utils.py:108] Init efficientnet-b0/blocks_5/se/conv2d/bias from ckpt var efficientnet-b0/blocks_5/se/conv2d/bias\\n\",\n            \"I0601 05:37:27.208061 140460941842304 utils.py:108] Init efficientnet-b0/blocks_5/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_5/se/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.208147 140460941842304 utils.py:108] Init efficientnet-b0/blocks_5/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_5/se/conv2d_1/bias\\n\",\n            \"I0601 05:37:27.208252 140460941842304 utils.py:108] Init efficientnet-b0/blocks_5/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_5/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.208363 140460941842304 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:37:27.208455 140460941842304 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:37:27.208564 140460941842304 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:37:27.208651 140460941842304 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:37:27.208736 140460941842304 utils.py:108] Init efficientnet-b0/blocks_6/conv2d/kernel from ckpt var efficientnet-b0/blocks_6/conv2d/kernel\\n\",\n            \"I0601 05:37:27.208820 140460941842304 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:37:27.208904 140460941842304 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:37:27.208990 140460941842304 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:37:27.209077 140460941842304 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:37:27.209163 140460941842304 utils.py:108] Init efficientnet-b0/blocks_6/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_6/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:37:27.209248 140460941842304 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:37:27.209354 140460941842304 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:37:27.209444 140460941842304 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:37:27.209539 140460941842304 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:37:27.209629 140460941842304 utils.py:108] Init efficientnet-b0/blocks_6/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_6/se/conv2d/kernel\\n\",\n            \"I0601 05:37:27.209716 140460941842304 utils.py:108] Init efficientnet-b0/blocks_6/se/conv2d/bias from ckpt var efficientnet-b0/blocks_6/se/conv2d/bias\\n\",\n            \"I0601 05:37:27.209800 140460941842304 utils.py:108] Init efficientnet-b0/blocks_6/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_6/se/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.209878 140460941842304 utils.py:108] Init efficientnet-b0/blocks_6/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_6/se/conv2d_1/bias\\n\",\n            \"I0601 05:37:27.209957 140460941842304 utils.py:108] Init efficientnet-b0/blocks_6/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_6/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.210039 140460941842304 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:37:27.210120 140460941842304 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:37:27.210199 140460941842304 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:37:27.210291 140460941842304 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:37:27.210397 140460941842304 utils.py:108] Init efficientnet-b0/blocks_7/conv2d/kernel from ckpt var efficientnet-b0/blocks_7/conv2d/kernel\\n\",\n            \"I0601 05:37:27.210498 140460941842304 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:37:27.210580 140460941842304 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:37:27.210665 140460941842304 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:37:27.210749 140460941842304 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:37:27.210835 140460941842304 utils.py:108] Init efficientnet-b0/blocks_7/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_7/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:37:27.210939 140460941842304 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:37:27.211031 140460941842304 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:37:27.211124 140460941842304 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:37:27.211214 140460941842304 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:37:27.211322 140460941842304 utils.py:108] Init efficientnet-b0/blocks_7/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_7/se/conv2d/kernel\\n\",\n            \"I0601 05:37:27.211417 140460941842304 utils.py:108] Init efficientnet-b0/blocks_7/se/conv2d/bias from ckpt var efficientnet-b0/blocks_7/se/conv2d/bias\\n\",\n            \"I0601 05:37:27.211518 140460941842304 utils.py:108] Init efficientnet-b0/blocks_7/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_7/se/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.211609 140460941842304 utils.py:108] Init efficientnet-b0/blocks_7/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_7/se/conv2d_1/bias\\n\",\n            \"I0601 05:37:27.211694 140460941842304 utils.py:108] Init efficientnet-b0/blocks_7/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_7/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.211786 140460941842304 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:37:27.211865 140460941842304 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:37:27.211946 140460941842304 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:37:27.212031 140460941842304 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:37:27.212116 140460941842304 utils.py:108] Init efficientnet-b0/blocks_8/conv2d/kernel from ckpt var efficientnet-b0/blocks_8/conv2d/kernel\\n\",\n            \"I0601 05:37:27.212202 140460941842304 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:37:27.212303 140460941842304 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:37:27.212392 140460941842304 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:37:27.212473 140460941842304 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:37:27.212568 140460941842304 utils.py:108] Init efficientnet-b0/blocks_8/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_8/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:37:27.212655 140460941842304 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:37:27.212738 140460941842304 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:37:27.212823 140460941842304 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:37:27.212906 140460941842304 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:37:27.212990 140460941842304 utils.py:108] Init efficientnet-b0/blocks_8/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_8/se/conv2d/kernel\\n\",\n            \"I0601 05:37:27.213074 140460941842304 utils.py:108] Init efficientnet-b0/blocks_8/se/conv2d/bias from ckpt var efficientnet-b0/blocks_8/se/conv2d/bias\\n\",\n            \"I0601 05:37:27.213157 140460941842304 utils.py:108] Init efficientnet-b0/blocks_8/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_8/se/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.213241 140460941842304 utils.py:108] Init efficientnet-b0/blocks_8/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_8/se/conv2d_1/bias\\n\",\n            \"I0601 05:37:27.213342 140460941842304 utils.py:108] Init efficientnet-b0/blocks_8/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_8/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.213423 140460941842304 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:37:27.213515 140460941842304 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:37:27.213601 140460941842304 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:37:27.213684 140460941842304 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:37:27.213773 140460941842304 utils.py:108] Init efficientnet-b0/blocks_9/conv2d/kernel from ckpt var efficientnet-b0/blocks_9/conv2d/kernel\\n\",\n            \"I0601 05:37:27.213858 140460941842304 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:37:27.213962 140460941842304 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:37:27.214052 140460941842304 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:37:27.214140 140460941842304 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:37:27.214229 140460941842304 utils.py:108] Init efficientnet-b0/blocks_9/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_9/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:37:27.214336 140460941842304 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:37:27.214425 140460941842304 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:37:27.214522 140460941842304 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:37:27.214612 140460941842304 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:37:27.214701 140460941842304 utils.py:108] Init efficientnet-b0/blocks_9/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_9/se/conv2d/kernel\\n\",\n            \"I0601 05:37:27.214841 140460941842304 utils.py:108] Init efficientnet-b0/blocks_9/se/conv2d/bias from ckpt var efficientnet-b0/blocks_9/se/conv2d/bias\\n\",\n            \"I0601 05:37:27.214942 140460941842304 utils.py:108] Init efficientnet-b0/blocks_9/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_9/se/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.215043 140460941842304 utils.py:108] Init efficientnet-b0/blocks_9/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_9/se/conv2d_1/bias\\n\",\n            \"I0601 05:37:27.215140 140460941842304 utils.py:108] Init efficientnet-b0/blocks_9/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_9/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.215238 140460941842304 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:37:27.215354 140460941842304 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:37:27.215455 140460941842304 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:37:27.215564 140460941842304 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:37:27.215664 140460941842304 utils.py:108] Init efficientnet-b0/blocks_10/conv2d/kernel from ckpt var efficientnet-b0/blocks_10/conv2d/kernel\\n\",\n            \"I0601 05:37:27.215763 140460941842304 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:37:27.215863 140460941842304 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:37:27.215981 140460941842304 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:37:27.216070 140460941842304 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:37:27.216159 140460941842304 utils.py:108] Init efficientnet-b0/blocks_10/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_10/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:37:27.216246 140460941842304 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:37:27.216351 140460941842304 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:37:27.216440 140460941842304 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:37:27.216543 140460941842304 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:37:27.216635 140460941842304 utils.py:108] Init efficientnet-b0/blocks_10/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_10/se/conv2d/kernel\\n\",\n            \"I0601 05:37:27.216723 140460941842304 utils.py:108] Init efficientnet-b0/blocks_10/se/conv2d/bias from ckpt var efficientnet-b0/blocks_10/se/conv2d/bias\\n\",\n            \"I0601 05:37:27.216810 140460941842304 utils.py:108] Init efficientnet-b0/blocks_10/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_10/se/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.216899 140460941842304 utils.py:108] Init efficientnet-b0/blocks_10/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_10/se/conv2d_1/bias\\n\",\n            \"I0601 05:37:27.216988 140460941842304 utils.py:108] Init efficientnet-b0/blocks_10/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_10/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.217077 140460941842304 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:37:27.217164 140460941842304 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:37:27.217254 140460941842304 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:37:27.217362 140460941842304 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:37:27.217463 140460941842304 utils.py:108] Init efficientnet-b0/blocks_11/conv2d/kernel from ckpt var efficientnet-b0/blocks_11/conv2d/kernel\\n\",\n            \"I0601 05:37:27.217554 140460941842304 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:37:27.217638 140460941842304 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:37:27.217722 140460941842304 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:37:27.217807 140460941842304 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:37:27.217891 140460941842304 utils.py:108] Init efficientnet-b0/blocks_11/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_11/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:37:27.217975 140460941842304 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:37:27.218059 140460941842304 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:37:27.218143 140460941842304 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:37:27.218247 140460941842304 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:37:27.218357 140460941842304 utils.py:108] Init efficientnet-b0/blocks_11/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_11/se/conv2d/kernel\\n\",\n            \"I0601 05:37:27.218445 140460941842304 utils.py:108] Init efficientnet-b0/blocks_11/se/conv2d/bias from ckpt var efficientnet-b0/blocks_11/se/conv2d/bias\\n\",\n            \"I0601 05:37:27.218544 140460941842304 utils.py:108] Init efficientnet-b0/blocks_11/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_11/se/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.218634 140460941842304 utils.py:108] Init efficientnet-b0/blocks_11/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_11/se/conv2d_1/bias\\n\",\n            \"I0601 05:37:27.218723 140460941842304 utils.py:108] Init efficientnet-b0/blocks_11/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_11/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.218819 140460941842304 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:37:27.218907 140460941842304 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:37:27.218994 140460941842304 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:37:27.219083 140460941842304 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:37:27.219170 140460941842304 utils.py:108] Init efficientnet-b0/blocks_12/conv2d/kernel from ckpt var efficientnet-b0/blocks_12/conv2d/kernel\\n\",\n            \"I0601 05:37:27.219257 140460941842304 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:37:27.219366 140460941842304 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:37:27.219455 140460941842304 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:37:27.219562 140460941842304 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:37:27.219646 140460941842304 utils.py:108] Init efficientnet-b0/blocks_12/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_12/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:37:27.219750 140460941842304 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:37:27.219845 140460941842304 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:37:27.219927 140460941842304 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:37:27.220011 140460941842304 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:37:27.220114 140460941842304 utils.py:108] Init efficientnet-b0/blocks_12/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_12/se/conv2d/kernel\\n\",\n            \"I0601 05:37:27.220211 140460941842304 utils.py:108] Init efficientnet-b0/blocks_12/se/conv2d/bias from ckpt var efficientnet-b0/blocks_12/se/conv2d/bias\\n\",\n            \"I0601 05:37:27.220309 140460941842304 utils.py:108] Init efficientnet-b0/blocks_12/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_12/se/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.220395 140460941842304 utils.py:108] Init efficientnet-b0/blocks_12/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_12/se/conv2d_1/bias\\n\",\n            \"I0601 05:37:27.220478 140460941842304 utils.py:108] Init efficientnet-b0/blocks_12/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_12/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.220593 140460941842304 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:37:27.220682 140460941842304 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:37:27.220764 140460941842304 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:37:27.220853 140460941842304 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:37:27.220942 140460941842304 utils.py:108] Init efficientnet-b0/blocks_13/conv2d/kernel from ckpt var efficientnet-b0/blocks_13/conv2d/kernel\\n\",\n            \"I0601 05:37:27.221038 140460941842304 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:37:27.221127 140460941842304 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:37:27.221215 140460941842304 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:37:27.221319 140460941842304 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:37:27.221410 140460941842304 utils.py:108] Init efficientnet-b0/blocks_13/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_13/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:37:27.221506 140460941842304 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:37:27.221597 140460941842304 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:37:27.221686 140460941842304 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:37:27.221774 140460941842304 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:37:27.221896 140460941842304 utils.py:108] Init efficientnet-b0/blocks_13/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_13/se/conv2d/kernel\\n\",\n            \"I0601 05:37:27.221987 140460941842304 utils.py:108] Init efficientnet-b0/blocks_13/se/conv2d/bias from ckpt var efficientnet-b0/blocks_13/se/conv2d/bias\\n\",\n            \"I0601 05:37:27.222090 140460941842304 utils.py:108] Init efficientnet-b0/blocks_13/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_13/se/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.222178 140460941842304 utils.py:108] Init efficientnet-b0/blocks_13/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_13/se/conv2d_1/bias\\n\",\n            \"I0601 05:37:27.222279 140460941842304 utils.py:108] Init efficientnet-b0/blocks_13/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_13/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.222373 140460941842304 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:37:27.222463 140460941842304 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:37:27.222568 140460941842304 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:37:27.222660 140460941842304 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:37:27.222749 140460941842304 utils.py:108] Init efficientnet-b0/blocks_14/conv2d/kernel from ckpt var efficientnet-b0/blocks_14/conv2d/kernel\\n\",\n            \"I0601 05:37:27.222837 140460941842304 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:37:27.222927 140460941842304 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:37:27.223024 140460941842304 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:37:27.223109 140460941842304 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:37:27.223192 140460941842304 utils.py:108] Init efficientnet-b0/blocks_14/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_14/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:37:27.223289 140460941842304 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:37:27.223378 140460941842304 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:37:27.223461 140460941842304 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:37:27.223575 140460941842304 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:37:27.223672 140460941842304 utils.py:108] Init efficientnet-b0/blocks_14/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_14/se/conv2d/kernel\\n\",\n            \"I0601 05:37:27.223776 140460941842304 utils.py:108] Init efficientnet-b0/blocks_14/se/conv2d/bias from ckpt var efficientnet-b0/blocks_14/se/conv2d/bias\\n\",\n            \"I0601 05:37:27.223863 140460941842304 utils.py:108] Init efficientnet-b0/blocks_14/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_14/se/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.223951 140460941842304 utils.py:108] Init efficientnet-b0/blocks_14/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_14/se/conv2d_1/bias\\n\",\n            \"I0601 05:37:27.224049 140460941842304 utils.py:108] Init efficientnet-b0/blocks_14/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_14/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.224131 140460941842304 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:37:27.224216 140460941842304 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:37:27.224336 140460941842304 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:37:27.224423 140460941842304 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:37:27.224520 140460941842304 utils.py:108] Init efficientnet-b0/blocks_15/conv2d/kernel from ckpt var efficientnet-b0/blocks_15/conv2d/kernel\\n\",\n            \"I0601 05:37:27.224611 140460941842304 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:37:27.224711 140460941842304 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:37:27.224797 140460941842304 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:37:27.224882 140460941842304 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:37:27.224988 140460941842304 utils.py:108] Init efficientnet-b0/blocks_15/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_15/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:37:27.225078 140460941842304 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:37:27.225168 140460941842304 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:37:27.225256 140460941842304 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:37:27.225366 140460941842304 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:37:27.225460 140460941842304 utils.py:108] Init efficientnet-b0/blocks_15/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_15/se/conv2d/kernel\\n\",\n            \"I0601 05:37:27.225552 140460941842304 utils.py:108] Init efficientnet-b0/blocks_15/se/conv2d/bias from ckpt var efficientnet-b0/blocks_15/se/conv2d/bias\\n\",\n            \"I0601 05:37:27.225637 140460941842304 utils.py:108] Init efficientnet-b0/blocks_15/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_15/se/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.225721 140460941842304 utils.py:108] Init efficientnet-b0/blocks_15/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_15/se/conv2d_1/bias\\n\",\n            \"I0601 05:37:27.225804 140460941842304 utils.py:108] Init efficientnet-b0/blocks_15/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_15/conv2d_1/kernel\\n\",\n            \"I0601 05:37:27.225886 140460941842304 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:37:27.225969 140460941842304 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:37:27.226053 140460941842304 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:37:27.226139 140460941842304 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization_2/moving_variance\\n\",\n            \"INFO:tensorflow:Done calling model_fn.\\n\",\n            \"I0601 05:37:27.906973 140460941842304 estimator.py:1171] Done calling model_fn.\\n\",\n            \"INFO:tensorflow:Create CheckpointSaverHook.\\n\",\n            \"I0601 05:37:27.908015 140460941842304 basic_session_run_hooks.py:546] Create CheckpointSaverHook.\\n\",\n            \"INFO:tensorflow:Graph was finalized.\\n\",\n            \"I0601 05:37:31.553620 140460941842304 monitored_session.py:246] Graph was finalized.\\n\",\n            \"2020-06-01 05:37:31.559414: I tensorflow/core/platform/profile_utils/cpu_utils.cc:102] CPU Frequency: 2300000000 Hz\\n\",\n            \"2020-06-01 05:37:31.559668: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x1b57100 initialized for platform Host (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-06-01 05:37:31.559704: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Host, Default Version\\n\",\n            \"2020-06-01 05:37:31.674881: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:37:31.675656: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x1b572c0 initialized for platform CUDA (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-06-01 05:37:31.675693: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Tesla T4, Compute Capability 7.5\\n\",\n            \"2020-06-01 05:37:31.675925: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:37:31.676551: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1561] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla T4 computeCapability: 7.5\\n\",\n            \"coreClock: 1.59GHz coreCount: 40 deviceMemorySize: 14.73GiB deviceMemoryBandwidth: 298.08GiB/s\\n\",\n            \"2020-06-01 05:37:31.676608: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:37:31.676661: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"2020-06-01 05:37:31.676689: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2020-06-01 05:37:31.676707: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2020-06-01 05:37:31.676728: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2020-06-01 05:37:31.676747: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\\n\",\n            \"2020-06-01 05:37:31.676767: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 05:37:31.676876: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:37:31.677540: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:37:31.678067: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1703] Adding visible gpu devices: 0\\n\",\n            \"2020-06-01 05:37:31.678122: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:37:32.222065: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1102] Device interconnect StreamExecutor with strength 1 edge matrix:\\n\",\n            \"2020-06-01 05:37:32.222124: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1108]      0 \\n\",\n            \"2020-06-01 05:37:32.222137: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1121] 0:   N \\n\",\n            \"2020-06-01 05:37:32.222377: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:37:32.223035: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:37:32.223671: W tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:39] Overriding allow_growth setting because the TF_FORCE_GPU_ALLOW_GROWTH environment variable is set. Original config value was 0.\\n\",\n            \"2020-06-01 05:37:32.223744: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1247] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 13970 MB memory) -> physical GPU (device: 0, name: Tesla T4, pci bus id: 0000:00:04.0, compute capability: 7.5)\\n\",\n            \"INFO:tensorflow:Running local_init_op.\\n\",\n            \"I0601 05:37:35.726311 140460941842304 session_manager.py:505] Running local_init_op.\\n\",\n            \"INFO:tensorflow:Done running local_init_op.\\n\",\n            \"I0601 05:37:35.975380 140460941842304 session_manager.py:508] Done running local_init_op.\\n\",\n            \"INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 0...\\n\",\n            \"I0601 05:37:45.047018 140460941842304 basic_session_run_hooks.py:614] Calling checkpoint listeners before saving checkpoint 0...\\n\",\n            \"INFO:tensorflow:Saving checkpoints for 0 into /tmp/model_dir/efficientdet-d0-scratch/model.ckpt.\\n\",\n            \"I0601 05:37:45.047802 140460941842304 basic_session_run_hooks.py:618] Saving checkpoints for 0 into /tmp/model_dir/efficientdet-d0-scratch/model.ckpt.\\n\",\n            \"INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 0...\\n\",\n            \"I0601 05:37:47.314194 140460941842304 basic_session_run_hooks.py:626] Calling checkpoint listeners after saving checkpoint 0...\\n\",\n            \"2020-06-01 05:37:56.700533: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 05:38:01.747040: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"INFO:tensorflow:global_step/sec: 0.140179\\n\",\n            \"I0601 05:38:10.825751 140460941842304 tpu_estimator.py:2350] global_step/sec: 0.140179\\n\",\n            \"INFO:tensorflow:examples/sec: 0.560716\\n\",\n            \"I0601 05:38:10.827028 140460941842304 tpu_estimator.py:2351] examples/sec: 0.560716\\n\",\n            \"INFO:tensorflow:global_step/sec: 5.58317\\n\",\n            \"I0601 05:38:11.004751 140460941842304 tpu_estimator.py:2350] global_step/sec: 5.58317\\n\",\n            \"INFO:tensorflow:examples/sec: 22.3327\\n\",\n            \"I0601 05:38:11.005145 140460941842304 tpu_estimator.py:2351] examples/sec: 22.3327\\n\",\n            \"INFO:tensorflow:global_step/sec: 5.53731\\n\",\n            \"I0601 05:38:11.185394 140460941842304 tpu_estimator.py:2350] global_step/sec: 5.53731\\n\",\n            \"INFO:tensorflow:examples/sec: 22.1492\\n\",\n            \"I0601 05:38:11.185792 140460941842304 tpu_estimator.py:2351] examples/sec: 22.1492\\n\",\n            \"INFO:tensorflow:global_step/sec: 5.00635\\n\",\n            \"I0601 05:38:11.385094 140460941842304 tpu_estimator.py:2350] global_step/sec: 5.00635\\n\",\n            \"INFO:tensorflow:examples/sec: 20.0254\\n\",\n            \"I0601 05:38:11.385536 140460941842304 tpu_estimator.py:2351] examples/sec: 20.0254\\n\",\n            \"INFO:tensorflow:global_step/sec: 5.12939\\n\",\n            \"I0601 05:38:11.580079 140460941842304 tpu_estimator.py:2350] global_step/sec: 5.12939\\n\",\n            \"INFO:tensorflow:examples/sec: 20.5176\\n\",\n            \"I0601 05:38:11.580518 140460941842304 tpu_estimator.py:2351] examples/sec: 20.5176\\n\",\n            \"INFO:tensorflow:global_step/sec: 4.99735\\n\",\n            \"I0601 05:38:11.780181 140460941842304 tpu_estimator.py:2350] global_step/sec: 4.99735\\n\",\n            \"INFO:tensorflow:examples/sec: 19.9894\\n\",\n            \"I0601 05:38:11.781100 140460941842304 tpu_estimator.py:2351] examples/sec: 19.9894\\n\",\n            \"INFO:tensorflow:global_step/sec: 5.44052\\n\",\n            \"I0601 05:38:11.963963 140460941842304 tpu_estimator.py:2350] global_step/sec: 5.44052\\n\",\n            \"INFO:tensorflow:examples/sec: 21.7621\\n\",\n            \"I0601 05:38:11.964417 140460941842304 tpu_estimator.py:2351] examples/sec: 21.7621\\n\",\n            \"INFO:tensorflow:global_step/sec: 5.10901\\n\",\n            \"I0601 05:38:12.159699 140460941842304 tpu_estimator.py:2350] global_step/sec: 5.10901\\n\",\n            \"INFO:tensorflow:examples/sec: 20.436\\n\",\n            \"I0601 05:38:12.160114 140460941842304 tpu_estimator.py:2351] examples/sec: 20.436\\n\",\n            \"INFO:tensorflow:global_step/sec: 5.72686\\n\",\n            \"I0601 05:38:12.334332 140460941842304 tpu_estimator.py:2350] global_step/sec: 5.72686\\n\",\n            \"INFO:tensorflow:examples/sec: 22.9074\\n\",\n            \"I0601 05:38:12.334732 140460941842304 tpu_estimator.py:2351] examples/sec: 22.9074\\n\",\n            \"INFO:tensorflow:global_step/sec: 5.79841\\n\",\n            \"I0601 05:38:12.506778 140460941842304 tpu_estimator.py:2350] global_step/sec: 5.79841\\n\",\n            \"INFO:tensorflow:examples/sec: 23.1936\\n\",\n            \"I0601 05:38:12.507008 140460941842304 tpu_estimator.py:2351] examples/sec: 23.1936\\n\",\n            \"INFO:tensorflow:global_step/sec: 5.25588\\n\",\n            \"I0601 05:38:12.697033 140460941842304 tpu_estimator.py:2350] global_step/sec: 5.25588\\n\",\n            \"INFO:tensorflow:examples/sec: 21.0235\\n\",\n            \"I0601 05:38:12.697489 140460941842304 tpu_estimator.py:2351] examples/sec: 21.0235\\n\",\n            \"INFO:tensorflow:global_step/sec: 5.23154\\n\",\n            \"I0601 05:38:12.888187 140460941842304 tpu_estimator.py:2350] global_step/sec: 5.23154\\n\",\n            \"INFO:tensorflow:examples/sec: 20.9262\\n\",\n            \"I0601 05:38:12.888642 140460941842304 tpu_estimator.py:2351] examples/sec: 20.9262\\n\",\n            \"INFO:tensorflow:global_step/sec: 6.56178\\n\",\n            \"I0601 05:38:13.040558 140460941842304 tpu_estimator.py:2350] global_step/sec: 6.56178\\n\",\n            \"INFO:tensorflow:examples/sec: 26.2471\\n\",\n            \"I0601 05:38:13.040964 140460941842304 tpu_estimator.py:2351] examples/sec: 26.2471\\n\",\n            \"INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 14...\\n\",\n            \"I0601 05:38:13.041552 140460941842304 basic_session_run_hooks.py:614] Calling checkpoint listeners before saving checkpoint 14...\\n\",\n            \"INFO:tensorflow:Saving checkpoints for 14 into /tmp/model_dir/efficientdet-d0-scratch/model.ckpt.\\n\",\n            \"I0601 05:38:13.041719 140460941842304 basic_session_run_hooks.py:618] Saving checkpoints for 14 into /tmp/model_dir/efficientdet-d0-scratch/model.ckpt.\\n\",\n            \"INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 14...\\n\",\n            \"I0601 05:38:14.448161 140460941842304 basic_session_run_hooks.py:626] Calling checkpoint listeners after saving checkpoint 14...\\n\",\n            \"INFO:tensorflow:Loss for final step: 2.4904294.\\n\",\n            \"I0601 05:38:15.084805 140460941842304 estimator.py:350] Loss for final step: 2.4904294.\\n\",\n            \"INFO:tensorflow:training_loop marked as finished\\n\",\n            \"I0601 05:38:15.085538 140460941842304 error_handling.py:115] training_loop marked as finished\\n\",\n            \"I0601 05:38:15.085692 140460941842304 main.py:388] Starting evaluation cycle, epoch: 0.\\n\",\n            \"INFO:tensorflow:Using config: {'_model_dir': '/tmp/model_dir/efficientdet-d0-scratch', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true\\n\",\n            \", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': None, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1, '_tpu_config': TPUConfig(iterations_per_loop=100, num_shards=8, num_cores_per_replica=None, per_host_input_for_training=3, tpu_job_name=None, initial_infeed_sleep_secs=None, input_partition_dims=None, eval_training_input_configuration=2, experimental_host_call_every_n_steps=1), '_cluster': None}\\n\",\n            \"I0601 05:38:15.086570 140460941842304 estimator.py:191] Using config: {'_model_dir': '/tmp/model_dir/efficientdet-d0-scratch', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true\\n\",\n            \", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': None, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1, '_tpu_config': TPUConfig(iterations_per_loop=100, num_shards=8, num_cores_per_replica=None, per_host_input_for_training=3, tpu_job_name=None, initial_infeed_sleep_secs=None, input_partition_dims=None, eval_training_input_configuration=2, experimental_host_call_every_n_steps=1), '_cluster': None}\\n\",\n            \"INFO:tensorflow:_TPUContext: eval_on_tpu True\\n\",\n            \"I0601 05:38:15.087005 140460941842304 tpu_context.py:216] _TPUContext: eval_on_tpu True\\n\",\n            \"WARNING:tensorflow:eval_on_tpu ignored because use_tpu is False.\\n\",\n            \"W0601 05:38:15.087114 140460941842304 tpu_context.py:218] eval_on_tpu ignored because use_tpu is False.\\n\",\n            \"INFO:tensorflow:Calling model_fn.\\n\",\n            \"I0601 05:38:15.609617 140460941842304 estimator.py:1169] Calling model_fn.\\n\",\n            \"INFO:tensorflow:Running eval on CPU/GPU\\n\",\n            \"I0601 05:38:15.609894 140460941842304 tpu_estimator.py:3171] Running eval on CPU/GPU\\n\",\n            \"I0601 05:38:15.610420 140460941842304 utils.py:595] use mixed precision policy name mixed_float16\\n\",\n            \"I0601 05:38:15.611307 140460941842304 efficientdet_arch.py:720] act_type: swish\\n\",\n            \"alpha: 0.25\\n\",\n            \"anchor_scale: 4.0\\n\",\n            \"apply_bn_for_resampling: true\\n\",\n            \"aspect_ratios:\\n\",\n            \"- !!python/tuple\\n\",\n            \"    - 1.0\\n\",\n            \"    - 1.0\\n\",\n            \"- !!python/tuple\\n\",\n            \"    - 1.4\\n\",\n            \"    - 0.7\\n\",\n            \"- !!python/tuple\\n\",\n            \"    - 0.7\\n\",\n            \"    - 1.4\\n\",\n            \"augmix_params: !!python/tuple\\n\",\n            \"- 3\\n\",\n            \"- -1\\n\",\n            \"- 1\\n\",\n            \"autoaugment_policy: null\\n\",\n            \"backbone_ckpt: efficientnet-b0\\n\",\n            \"backbone_config: null\\n\",\n            \"backbone_name: efficientnet-b0\\n\",\n            \"batch_size: 4\\n\",\n            \"box_class_repeats: 3\\n\",\n            \"box_loss_weight: 50.0\\n\",\n            \"ckpt: null\\n\",\n            \"ckpt_var_scope: null\\n\",\n            \"clip_gradients_norm: 10.0\\n\",\n            \"conv_after_downsample: false\\n\",\n            \"conv_bn_act_pattern: false\\n\",\n            \"data_format: channels_last\\n\",\n            \"delta: 0.1\\n\",\n            \"first_lr_drop_epoch: 200.0\\n\",\n            \"fpn_cell_repeats: 3\\n\",\n            \"fpn_config: null\\n\",\n            \"fpn_name: null\\n\",\n            \"fpn_num_filters: 64\\n\",\n            \"fpn_weight_method: null\\n\",\n            \"gamma: 1.5\\n\",\n            \"image_size: !!python/tuple\\n\",\n            \"- 512\\n\",\n            \"- 512\\n\",\n            \"img_summary_steps: null\\n\",\n            \"input_rand_hflip: false\\n\",\n            \"iou_loss_type: null\\n\",\n            \"iou_loss_weight: 1.0\\n\",\n            \"is_training_bn: false\\n\",\n            \"iterations_per_loop: 100\\n\",\n            \"label_id_mapping: null\\n\",\n            \"learning_rate: 0.08\\n\",\n            \"lr_decay_method: cosine\\n\",\n            \"lr_warmup_epoch: 1.0\\n\",\n            \"lr_warmup_init: 0.008\\n\",\n            \"max_instances_per_image: 100\\n\",\n            \"max_level: 7\\n\",\n            \"min_level: 3\\n\",\n            \"mode: train_and_eval\\n\",\n            \"model_dir: /tmp/model_dir/efficientdet-d0-scratch\\n\",\n            \"model_name: efficientdet-d0\\n\",\n            \"momentum: 0.9\\n\",\n            \"moving_average_decay: 0\\n\",\n            \"name: efficientdet-d0\\n\",\n            \"num_classes: 20\\n\",\n            \"num_epochs: 1\\n\",\n            \"num_examples_per_epoch: 56\\n\",\n            \"num_scales: 3\\n\",\n            \"num_shards: 8\\n\",\n            \"optimizer: sgd\\n\",\n            \"poly_lr_power: 0.9\\n\",\n            \"pooling_type: null\\n\",\n            \"precision: mixed_float16\\n\",\n            \"resnet_depth: 50\\n\",\n            \"second_lr_drop_epoch: 250.0\\n\",\n            \"separable_conv: true\\n\",\n            \"skip_crowd_during_training: true\\n\",\n            \"strategy: null\\n\",\n            \"survival_prob: null\\n\",\n            \"target_size: null\\n\",\n            \"testdev_dir: null\\n\",\n            \"train_scale_max: 2.0\\n\",\n            \"train_scale_min: 0.1\\n\",\n            \"use_augmix: false\\n\",\n            \"use_native_resize_op: true\\n\",\n            \"use_tpu: false\\n\",\n            \"val_json_file: null\\n\",\n            \"var_exclude_expr: .*/class-predict/.*\\n\",\n            \"var_freeze_expr: null\\n\",\n            \"weight_decay: 4.0e-05\\n\",\n            \"\\n\",\n            \"I0601 05:38:15.615985 140460941842304 efficientnet_builder.py:224] global_params= GlobalParams(batch_norm_momentum=0.99, batch_norm_epsilon=0.001, dropout_rate=0.2, data_format='channels_last', num_classes=1000, width_coefficient=1.0, depth_coefficient=1.0, depth_divisor=8, min_depth=None, survival_prob=0.0, relu_fn=functools.partial(<function activation_fn at 0x7fbf45bc8d90>, act_type='swish'), batch_norm=<class 'utils.BatchNormalization'>, use_se=True, local_pooling=None, condconv_num_experts=None, clip_projection_output=False, blocks_args=['r1_k3_s11_e1_i32_o16_se0.25', 'r2_k3_s22_e6_i16_o24_se0.25', 'r2_k5_s22_e6_i24_o40_se0.25', 'r3_k3_s22_e6_i40_o80_se0.25', 'r3_k5_s11_e6_i80_o112_se0.25', 'r4_k5_s22_e6_i112_o192_se0.25', 'r1_k3_s11_e6_i192_o320_se0.25'], fix_head_stem=None)\\n\",\n            \"I0601 05:38:15.879783 140460941842304 api.py:587] Built stem layers with output shape: (4, 256, 256, 32)\\n\",\n            \"I0601 05:38:15.890318 140460941842304 api.py:587] Block mb_conv_block  input shape: (4, 256, 256, 32)\\n\",\n            \"I0601 05:38:15.922745 140460941842304 api.py:587] DWConv shape: (4, 256, 256, 32)\\n\",\n            \"I0601 05:38:15.959311 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 32)\\n\",\n            \"I0601 05:38:15.990694 140460941842304 api.py:587] Project shape: (4, 256, 256, 16)\\n\",\n            \"I0601 05:38:16.003817 140460941842304 api.py:587] Block mb_conv_block_1  input shape: (4, 256, 256, 16)\\n\",\n            \"I0601 05:38:16.038242 140460941842304 api.py:587] Expand shape: (4, 256, 256, 96)\\n\",\n            \"I0601 05:38:16.073603 140460941842304 api.py:587] DWConv shape: (4, 128, 128, 96)\\n\",\n            \"I0601 05:38:16.113133 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 96)\\n\",\n            \"I0601 05:38:16.147000 140460941842304 api.py:587] Project shape: (4, 128, 128, 24)\\n\",\n            \"I0601 05:38:16.157407 140460941842304 api.py:587] Block mb_conv_block_2  input shape: (4, 128, 128, 24)\\n\",\n            \"I0601 05:38:16.200918 140460941842304 api.py:587] Expand shape: (4, 128, 128, 144)\\n\",\n            \"I0601 05:38:16.233961 140460941842304 api.py:587] DWConv shape: (4, 128, 128, 144)\\n\",\n            \"I0601 05:38:16.269518 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 144)\\n\",\n            \"I0601 05:38:16.301350 140460941842304 api.py:587] Project shape: (4, 128, 128, 24)\\n\",\n            \"I0601 05:38:16.312747 140460941842304 api.py:587] Block mb_conv_block_3  input shape: (4, 128, 128, 24)\\n\",\n            \"I0601 05:38:16.346515 140460941842304 api.py:587] Expand shape: (4, 128, 128, 144)\\n\",\n            \"I0601 05:38:16.381837 140460941842304 api.py:587] DWConv shape: (4, 64, 64, 144)\\n\",\n            \"I0601 05:38:16.426858 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 144)\\n\",\n            \"I0601 05:38:16.459029 140460941842304 api.py:587] Project shape: (4, 64, 64, 40)\\n\",\n            \"I0601 05:38:16.468211 140460941842304 api.py:587] Block mb_conv_block_4  input shape: (4, 64, 64, 40)\\n\",\n            \"I0601 05:38:16.503680 140460941842304 api.py:587] Expand shape: (4, 64, 64, 240)\\n\",\n            \"I0601 05:38:16.538903 140460941842304 api.py:587] DWConv shape: (4, 64, 64, 240)\\n\",\n            \"I0601 05:38:16.573376 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 240)\\n\",\n            \"I0601 05:38:16.609440 140460941842304 api.py:587] Project shape: (4, 64, 64, 40)\\n\",\n            \"I0601 05:38:16.619002 140460941842304 api.py:587] Block mb_conv_block_5  input shape: (4, 64, 64, 40)\\n\",\n            \"I0601 05:38:16.652125 140460941842304 api.py:587] Expand shape: (4, 64, 64, 240)\\n\",\n            \"I0601 05:38:16.685711 140460941842304 api.py:587] DWConv shape: (4, 32, 32, 240)\\n\",\n            \"I0601 05:38:16.722809 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 240)\\n\",\n            \"I0601 05:38:16.753987 140460941842304 api.py:587] Project shape: (4, 32, 32, 80)\\n\",\n            \"I0601 05:38:16.763768 140460941842304 api.py:587] Block mb_conv_block_6  input shape: (4, 32, 32, 80)\\n\",\n            \"I0601 05:38:16.798378 140460941842304 api.py:587] Expand shape: (4, 32, 32, 480)\\n\",\n            \"I0601 05:38:16.831592 140460941842304 api.py:587] DWConv shape: (4, 32, 32, 480)\\n\",\n            \"I0601 05:38:16.866909 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 480)\\n\",\n            \"I0601 05:38:16.902912 140460941842304 api.py:587] Project shape: (4, 32, 32, 80)\\n\",\n            \"I0601 05:38:16.915614 140460941842304 api.py:587] Block mb_conv_block_7  input shape: (4, 32, 32, 80)\\n\",\n            \"I0601 05:38:16.951402 140460941842304 api.py:587] Expand shape: (4, 32, 32, 480)\\n\",\n            \"I0601 05:38:16.985633 140460941842304 api.py:587] DWConv shape: (4, 32, 32, 480)\\n\",\n            \"I0601 05:38:17.025779 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 480)\\n\",\n            \"I0601 05:38:17.058473 140460941842304 api.py:587] Project shape: (4, 32, 32, 80)\\n\",\n            \"I0601 05:38:17.068195 140460941842304 api.py:587] Block mb_conv_block_8  input shape: (4, 32, 32, 80)\\n\",\n            \"I0601 05:38:17.104321 140460941842304 api.py:587] Expand shape: (4, 32, 32, 480)\\n\",\n            \"I0601 05:38:17.139899 140460941842304 api.py:587] DWConv shape: (4, 32, 32, 480)\\n\",\n            \"I0601 05:38:17.178178 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 480)\\n\",\n            \"I0601 05:38:17.223506 140460941842304 api.py:587] Project shape: (4, 32, 32, 112)\\n\",\n            \"I0601 05:38:17.233667 140460941842304 api.py:587] Block mb_conv_block_9  input shape: (4, 32, 32, 112)\\n\",\n            \"I0601 05:38:17.267562 140460941842304 api.py:587] Expand shape: (4, 32, 32, 672)\\n\",\n            \"I0601 05:38:17.300494 140460941842304 api.py:587] DWConv shape: (4, 32, 32, 672)\\n\",\n            \"I0601 05:38:17.337823 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 672)\\n\",\n            \"I0601 05:38:17.370885 140460941842304 api.py:587] Project shape: (4, 32, 32, 112)\\n\",\n            \"I0601 05:38:17.381354 140460941842304 api.py:587] Block mb_conv_block_10  input shape: (4, 32, 32, 112)\\n\",\n            \"I0601 05:38:17.425960 140460941842304 api.py:587] Expand shape: (4, 32, 32, 672)\\n\",\n            \"I0601 05:38:17.465528 140460941842304 api.py:587] DWConv shape: (4, 32, 32, 672)\\n\",\n            \"I0601 05:38:17.501981 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 672)\\n\",\n            \"I0601 05:38:17.535618 140460941842304 api.py:587] Project shape: (4, 32, 32, 112)\\n\",\n            \"I0601 05:38:17.545045 140460941842304 api.py:587] Block mb_conv_block_11  input shape: (4, 32, 32, 112)\\n\",\n            \"I0601 05:38:17.577750 140460941842304 api.py:587] Expand shape: (4, 32, 32, 672)\\n\",\n            \"I0601 05:38:17.613845 140460941842304 api.py:587] DWConv shape: (4, 16, 16, 672)\\n\",\n            \"I0601 05:38:17.648429 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 672)\\n\",\n            \"I0601 05:38:17.679331 140460941842304 api.py:587] Project shape: (4, 16, 16, 192)\\n\",\n            \"I0601 05:38:17.691922 140460941842304 api.py:587] Block mb_conv_block_12  input shape: (4, 16, 16, 192)\\n\",\n            \"I0601 05:38:17.730681 140460941842304 api.py:587] Expand shape: (4, 16, 16, 1152)\\n\",\n            \"I0601 05:38:17.768310 140460941842304 api.py:587] DWConv shape: (4, 16, 16, 1152)\\n\",\n            \"I0601 05:38:17.805337 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 1152)\\n\",\n            \"I0601 05:38:17.839221 140460941842304 api.py:587] Project shape: (4, 16, 16, 192)\\n\",\n            \"I0601 05:38:17.848821 140460941842304 api.py:587] Block mb_conv_block_13  input shape: (4, 16, 16, 192)\\n\",\n            \"I0601 05:38:17.888498 140460941842304 api.py:587] Expand shape: (4, 16, 16, 1152)\\n\",\n            \"I0601 05:38:17.929714 140460941842304 api.py:587] DWConv shape: (4, 16, 16, 1152)\\n\",\n            \"I0601 05:38:17.967441 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 1152)\\n\",\n            \"I0601 05:38:18.000252 140460941842304 api.py:587] Project shape: (4, 16, 16, 192)\\n\",\n            \"I0601 05:38:18.010449 140460941842304 api.py:587] Block mb_conv_block_14  input shape: (4, 16, 16, 192)\\n\",\n            \"I0601 05:38:18.052438 140460941842304 api.py:587] Expand shape: (4, 16, 16, 1152)\\n\",\n            \"I0601 05:38:18.093971 140460941842304 api.py:587] DWConv shape: (4, 16, 16, 1152)\\n\",\n            \"I0601 05:38:18.132887 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 1152)\\n\",\n            \"I0601 05:38:18.166574 140460941842304 api.py:587] Project shape: (4, 16, 16, 192)\\n\",\n            \"I0601 05:38:18.176187 140460941842304 api.py:587] Block mb_conv_block_15  input shape: (4, 16, 16, 192)\\n\",\n            \"I0601 05:38:18.221516 140460941842304 api.py:587] Expand shape: (4, 16, 16, 1152)\\n\",\n            \"I0601 05:38:18.258391 140460941842304 api.py:587] DWConv shape: (4, 16, 16, 1152)\\n\",\n            \"I0601 05:38:18.294653 140460941842304 api.py:587] Built Squeeze and Excitation with tensor shape: (4, 1, 1, 1152)\\n\",\n            \"I0601 05:38:18.327821 140460941842304 api.py:587] Project shape: (4, 16, 16, 320)\\n\",\n            \"Parsing Inputs...\\n\",\n            \"I0601 05:38:18.653207 140460941842304 efficientdet_arch.py:725] backbone params/flops = 3.595388M, 7.723610577B\\n\",\n            \"I0601 05:38:18.711972 140460941842304 efficientdet_arch.py:471] building cell 0\\n\",\n            \"I0601 05:38:18.712393 140460941842304 efficientdet_arch.py:648] fnode 0 : {'feat_level': 6, 'inputs_offsets': [3, 4]}\\n\",\n            \"I0601 05:38:18.790723 140460941842304 efficientdet_arch.py:648] fnode 1 : {'feat_level': 5, 'inputs_offsets': [2, 5]}\\n\",\n            \"I0601 05:38:18.910952 140460941842304 efficientdet_arch.py:648] fnode 2 : {'feat_level': 4, 'inputs_offsets': [1, 6]}\\n\",\n            \"I0601 05:38:19.033912 140460941842304 efficientdet_arch.py:648] fnode 3 : {'feat_level': 3, 'inputs_offsets': [0, 7]}\\n\",\n            \"I0601 05:38:19.155185 140460941842304 efficientdet_arch.py:648] fnode 4 : {'feat_level': 4, 'inputs_offsets': [1, 7, 8]}\\n\",\n            \"I0601 05:38:19.300895 140460941842304 efficientdet_arch.py:648] fnode 5 : {'feat_level': 5, 'inputs_offsets': [2, 6, 9]}\\n\",\n            \"I0601 05:38:19.448367 140460941842304 efficientdet_arch.py:648] fnode 6 : {'feat_level': 6, 'inputs_offsets': [3, 5, 10]}\\n\",\n            \"I0601 05:38:19.545654 140460941842304 efficientdet_arch.py:648] fnode 7 : {'feat_level': 7, 'inputs_offsets': [4, 11]}\\n\",\n            \"I0601 05:38:19.632055 140460941842304 efficientdet_arch.py:471] building cell 1\\n\",\n            \"I0601 05:38:19.632450 140460941842304 efficientdet_arch.py:648] fnode 0 : {'feat_level': 6, 'inputs_offsets': [3, 4]}\\n\",\n            \"I0601 05:38:19.719202 140460941842304 efficientdet_arch.py:648] fnode 1 : {'feat_level': 5, 'inputs_offsets': [2, 5]}\\n\",\n            \"I0601 05:38:19.801726 140460941842304 efficientdet_arch.py:648] fnode 2 : {'feat_level': 4, 'inputs_offsets': [1, 6]}\\n\",\n            \"I0601 05:38:19.890650 140460941842304 efficientdet_arch.py:648] fnode 3 : {'feat_level': 3, 'inputs_offsets': [0, 7]}\\n\",\n            \"I0601 05:38:19.980336 140460941842304 efficientdet_arch.py:648] fnode 4 : {'feat_level': 4, 'inputs_offsets': [1, 7, 8]}\\n\",\n            \"I0601 05:38:20.076389 140460941842304 efficientdet_arch.py:648] fnode 5 : {'feat_level': 5, 'inputs_offsets': [2, 6, 9]}\\n\",\n            \"I0601 05:38:20.187259 140460941842304 efficientdet_arch.py:648] fnode 6 : {'feat_level': 6, 'inputs_offsets': [3, 5, 10]}\\n\",\n            \"I0601 05:38:20.289635 140460941842304 efficientdet_arch.py:648] fnode 7 : {'feat_level': 7, 'inputs_offsets': [4, 11]}\\n\",\n            \"I0601 05:38:20.381916 140460941842304 efficientdet_arch.py:471] building cell 2\\n\",\n            \"I0601 05:38:20.382333 140460941842304 efficientdet_arch.py:648] fnode 0 : {'feat_level': 6, 'inputs_offsets': [3, 4]}\\n\",\n            \"I0601 05:38:20.470864 140460941842304 efficientdet_arch.py:648] fnode 1 : {'feat_level': 5, 'inputs_offsets': [2, 5]}\\n\",\n            \"I0601 05:38:20.571690 140460941842304 efficientdet_arch.py:648] fnode 2 : {'feat_level': 4, 'inputs_offsets': [1, 6]}\\n\",\n            \"I0601 05:38:20.665080 140460941842304 efficientdet_arch.py:648] fnode 3 : {'feat_level': 3, 'inputs_offsets': [0, 7]}\\n\",\n            \"I0601 05:38:20.769322 140460941842304 efficientdet_arch.py:648] fnode 4 : {'feat_level': 4, 'inputs_offsets': [1, 7, 8]}\\n\",\n            \"I0601 05:38:20.869930 140460941842304 efficientdet_arch.py:648] fnode 5 : {'feat_level': 5, 'inputs_offsets': [2, 6, 9]}\\n\",\n            \"I0601 05:38:20.977256 140460941842304 efficientdet_arch.py:648] fnode 6 : {'feat_level': 6, 'inputs_offsets': [3, 5, 10]}\\n\",\n            \"I0601 05:38:21.077913 140460941842304 efficientdet_arch.py:648] fnode 7 : {'feat_level': 7, 'inputs_offsets': [4, 11]}\\n\",\n            \"Parsing Inputs...\\n\",\n            \"I0601 05:38:21.728706 140460941842304 efficientdet_arch.py:730] backbone+fpn params/flops = 3.791669M, 8.294117884B\\n\",\n            \"Parsing Inputs...\\n\",\n            \"I0601 05:38:25.041391 140460941842304 efficientdet_arch.py:735] backbone+fpn+box params/flops = 3.839117M, 9.243504892B\\n\",\n            \"I0601 05:38:25.041746 140460941842304 utils.py:595] use mixed precision policy name float32\\n\",\n            \"I0601 05:38:25.045943 140460941842304 det_model_fn.py:97] LR schedule method: cosine\\n\",\n            \"I0601 05:38:25.389793 140460941842304 det_model_fn.py:589] Eval val with groudtruths None.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/anchors.py:587: py_func (from tensorflow.python.ops.script_ops) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"tf.py_func is deprecated in TF V2. Instead, there are two\\n\",\n            \"    options available in V2.\\n\",\n            \"    - tf.py_function takes a python function which manipulates tf eager\\n\",\n            \"    tensors instead of numpy arrays. It's easy to convert a tf eager tensor to\\n\",\n            \"    an ndarray (just call tensor.numpy()) but having access to eager tensors\\n\",\n            \"    means `tf.py_function`s can use accelerators such as GPUs as well as\\n\",\n            \"    being differentiable using a gradient tape.\\n\",\n            \"    - tf.numpy_function maintains the semantics of the deprecated tf.py_func\\n\",\n            \"    (it is not differentiable, and manipulates numpy arrays). It drops the\\n\",\n            \"    stateful argument making all functions stateful.\\n\",\n            \"    \\n\",\n            \"W0601 05:38:25.398602 140460941842304 deprecation.py:323] From /content/automl/efficientdet/anchors.py:587: py_func (from tensorflow.python.ops.script_ops) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"tf.py_func is deprecated in TF V2. Instead, there are two\\n\",\n            \"    options available in V2.\\n\",\n            \"    - tf.py_function takes a python function which manipulates tf eager\\n\",\n            \"    tensors instead of numpy arrays. It's easy to convert a tf eager tensor to\\n\",\n            \"    an ndarray (just call tensor.numpy()) but having access to eager tensors\\n\",\n            \"    means `tf.py_function`s can use accelerators such as GPUs as well as\\n\",\n            \"    being differentiable using a gradient tape.\\n\",\n            \"    - tf.numpy_function maintains the semantics of the deprecated tf.py_func\\n\",\n            \"    (it is not differentiable, and manipulates numpy arrays). It drops the\\n\",\n            \"    stateful argument making all functions stateful.\\n\",\n            \"    \\n\",\n            \"INFO:tensorflow:Done calling model_fn.\\n\",\n            \"I0601 05:38:25.449812 140460941842304 estimator.py:1171] Done calling model_fn.\\n\",\n            \"INFO:tensorflow:Starting evaluation at 2020-06-01T05:38:25Z\\n\",\n            \"I0601 05:38:25.467760 140460941842304 evaluation.py:255] Starting evaluation at 2020-06-01T05:38:25Z\\n\",\n            \"INFO:tensorflow:Graph was finalized.\\n\",\n            \"I0601 05:38:26.550288 140460941842304 monitored_session.py:246] Graph was finalized.\\n\",\n            \"2020-06-01 05:38:26.550968: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:38:26.551510: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1561] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla T4 computeCapability: 7.5\\n\",\n            \"coreClock: 1.59GHz coreCount: 40 deviceMemorySize: 14.73GiB deviceMemoryBandwidth: 298.08GiB/s\\n\",\n            \"2020-06-01 05:38:26.551566: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:38:26.551653: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"2020-06-01 05:38:26.551682: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2020-06-01 05:38:26.551705: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2020-06-01 05:38:26.551735: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2020-06-01 05:38:26.551758: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\\n\",\n            \"2020-06-01 05:38:26.551779: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 05:38:26.551890: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:38:26.552491: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:38:26.552956: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1703] Adding visible gpu devices: 0\\n\",\n            \"2020-06-01 05:38:26.553001: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1102] Device interconnect StreamExecutor with strength 1 edge matrix:\\n\",\n            \"2020-06-01 05:38:26.553014: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1108]      0 \\n\",\n            \"2020-06-01 05:38:26.553024: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1121] 0:   N \\n\",\n            \"2020-06-01 05:38:26.553140: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:38:26.553665: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:38:26.554150: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1247] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 13970 MB memory) -> physical GPU (device: 0, name: Tesla T4, pci bus id: 0000:00:04.0, compute capability: 7.5)\\n\",\n            \"INFO:tensorflow:Restoring parameters from /tmp/model_dir/efficientdet-d0-scratch/model.ckpt-14\\n\",\n            \"I0601 05:38:26.555339 140460941842304 saver.py:1293] Restoring parameters from /tmp/model_dir/efficientdet-d0-scratch/model.ckpt-14\\n\",\n            \"INFO:tensorflow:Running local_init_op.\\n\",\n            \"I0601 05:38:27.867699 140460941842304 session_manager.py:505] Running local_init_op.\\n\",\n            \"INFO:tensorflow:Done running local_init_op.\\n\",\n            \"I0601 05:38:27.978962 140460941842304 session_manager.py:508] Done running local_init_op.\\n\",\n            \"INFO:tensorflow:Evaluation [1/14]\\n\",\n            \"I0601 05:38:31.654607 140460941842304 evaluation.py:167] Evaluation [1/14]\\n\",\n            \"INFO:tensorflow:Evaluation [2/14]\\n\",\n            \"I0601 05:38:32.161419 140460941842304 evaluation.py:167] Evaluation [2/14]\\n\",\n            \"INFO:tensorflow:Evaluation [3/14]\\n\",\n            \"I0601 05:38:32.635456 140460941842304 evaluation.py:167] Evaluation [3/14]\\n\",\n            \"INFO:tensorflow:Evaluation [4/14]\\n\",\n            \"I0601 05:38:33.120565 140460941842304 evaluation.py:167] Evaluation [4/14]\\n\",\n            \"INFO:tensorflow:Evaluation [5/14]\\n\",\n            \"I0601 05:38:33.597185 140460941842304 evaluation.py:167] Evaluation [5/14]\\n\",\n            \"INFO:tensorflow:Evaluation [6/14]\\n\",\n            \"I0601 05:38:34.080617 140460941842304 evaluation.py:167] Evaluation [6/14]\\n\",\n            \"INFO:tensorflow:Evaluation [7/14]\\n\",\n            \"I0601 05:38:34.650703 140460941842304 evaluation.py:167] Evaluation [7/14]\\n\",\n            \"INFO:tensorflow:Evaluation [8/14]\\n\",\n            \"I0601 05:38:35.131882 140460941842304 evaluation.py:167] Evaluation [8/14]\\n\",\n            \"INFO:tensorflow:Evaluation [9/14]\\n\",\n            \"I0601 05:38:35.616208 140460941842304 evaluation.py:167] Evaluation [9/14]\\n\",\n            \"INFO:tensorflow:Evaluation [10/14]\\n\",\n            \"I0601 05:38:36.107428 140460941842304 evaluation.py:167] Evaluation [10/14]\\n\",\n            \"INFO:tensorflow:Evaluation [11/14]\\n\",\n            \"I0601 05:38:36.596470 140460941842304 evaluation.py:167] Evaluation [11/14]\\n\",\n            \"INFO:tensorflow:Evaluation [12/14]\\n\",\n            \"I0601 05:38:37.073881 140460941842304 evaluation.py:167] Evaluation [12/14]\\n\",\n            \"INFO:tensorflow:Evaluation [13/14]\\n\",\n            \"I0601 05:38:37.664412 140460941842304 evaluation.py:167] Evaluation [13/14]\\n\",\n            \"INFO:tensorflow:Evaluation [14/14]\\n\",\n            \"I0601 05:38:38.225920 140460941842304 evaluation.py:167] Evaluation [14/14]\\n\",\n            \"creating index...\\n\",\n            \"index created!\\n\",\n            \"Loading and preparing results...\\n\",\n            \"Converting ndarray to lists...\\n\",\n            \"(5600, 7)\\n\",\n            \"0/5600\\n\",\n            \"DONE (t=0.02s)\\n\",\n            \"creating index...\\n\",\n            \"index created!\\n\",\n            \"Running per image evaluation...\\n\",\n            \"Evaluate annotation type *bbox*\\n\",\n            \"DONE (t=0.29s).\\n\",\n            \"Accumulating evaluation results...\\n\",\n            \"DONE (t=0.07s).\\n\",\n            \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.000\\n\",\n            \" Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=100 ] = 0.000\\n\",\n            \" Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=100 ] = 0.000\\n\",\n            \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.003\\n\",\n            \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = -1.000\\n\",\n            \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = -1.000\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=  1 ] = 0.000\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets= 10 ] = 0.000\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.002\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.002\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = -1.000\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = -1.000\\n\",\n            \"INFO:tensorflow:Inference Time : 13.39342s\\n\",\n            \"I0601 05:38:38.861362 140460941842304 evaluation.py:273] Inference Time : 13.39342s\\n\",\n            \"INFO:tensorflow:Finished evaluation at 2020-06-01-05:38:38\\n\",\n            \"I0601 05:38:38.861632 140460941842304 evaluation.py:276] Finished evaluation at 2020-06-01-05:38:38\\n\",\n            \"INFO:tensorflow:Saving dict for global step 14: AP = 1.4968081e-06, AP50 = 4.20262e-06, AP75 = 2.8654225e-07, APl = -1.0, APm = -1.0, APs = 0.0025247524, ARl = -1.0, ARm = -1.0, ARmax1 = 0.0, ARmax10 = 0.0, ARmax100 = 0.00225, ARs = 0.00225, box_loss = 0.012224401, cls_loss = 1.2661626, global_step = 14, loss = 2.4825947\\n\",\n            \"I0601 05:38:38.861842 140460941842304 estimator.py:2066] Saving dict for global step 14: AP = 1.4968081e-06, AP50 = 4.20262e-06, AP75 = 2.8654225e-07, APl = -1.0, APm = -1.0, APs = 0.0025247524, ARl = -1.0, ARm = -1.0, ARmax1 = 0.0, ARmax10 = 0.0, ARmax100 = 0.00225, ARs = 0.00225, box_loss = 0.012224401, cls_loss = 1.2661626, global_step = 14, loss = 2.4825947\\n\",\n            \"INFO:tensorflow:Saving 'checkpoint_path' summary for global step 14: /tmp/model_dir/efficientdet-d0-scratch/model.ckpt-14\\n\",\n            \"I0601 05:38:39.915983 140460941842304 estimator.py:2127] Saving 'checkpoint_path' summary for global step 14: /tmp/model_dir/efficientdet-d0-scratch/model.ckpt-14\\n\",\n            \"INFO:tensorflow:evaluation_loop marked as finished\\n\",\n            \"I0601 05:38:39.916708 140460941842304 error_handling.py:115] evaluation_loop marked as finished\\n\",\n            \"I0601 05:38:39.916910 140460941842304 main.py:411] Evaluation results: {'AP': 1.4968081e-06, 'AP50': 4.20262e-06, 'AP75': 2.8654225e-07, 'APl': -1.0, 'APm': -1.0, 'APs': 0.0025247524, 'ARl': -1.0, 'ARm': -1.0, 'ARmax1': 0.0, 'ARmax10': 0.0, 'ARmax100': 0.00225, 'ARs': 0.00225, 'box_loss': 0.012224401, 'cls_loss': 1.2661626, 'loss': 2.4825947, 'global_step': 14}\\n\",\n            \"INFO:tensorflow:/tmp/model_dir/efficientdet-d0-scratch/archive/model.ckpt-14 is not in all_model_checkpoint_paths. Manually adding it.\\n\",\n            \"I0601 05:38:39.959321 140460941842304 checkpoint_management.py:102] /tmp/model_dir/efficientdet-d0-scratch/archive/model.ckpt-14 is not in all_model_checkpoint_paths. Manually adding it.\\n\",\n            \"I0601 05:38:39.959901 140460941842304 utils.py:491] Copying checkpoint /tmp/model_dir/efficientdet-d0-scratch/model.ckpt-14 to /tmp/model_dir/efficientdet-d0-scratch/archive\\n\"\n          ],\n          \"name\": \"stdout\"\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"SKHu-3lBwTiM\",\n        \"colab_type\": \"text\"\n      },\n      \"source\": [\n        \"## 4.3 Train Pascal VOC 2012 from COCO checkpoint for the whole net.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"metadata\": {\n        \"id\": \"SD59rsZJc1WW\",\n        \"colab_type\": \"code\",\n        \"outputId\": \"fe9ea8fe-976b-49f1-8af5-917eb518ddaf\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 1000\n        }\n      },\n      \"source\": [\n        \"# generating train tfrecord is large, so we skip the execution here.\\n\",\n        \"import os\\n\",\n        \"if MODEL not in os.listdir():\\n\",\n        \"  !wget https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco/{MODEL}.tar.gz\\n\",\n        \"  !tar xf {MODEL}.tar.gz\\n\",\n        \"\\n\",\n        \"!mkdir /tmp/model_dir/\\n\",\n        \"# key option: use --ckpt rather than --backbone_ckpt.\\n\",\n        \"!python main.py --mode=train_and_eval \\\\\\n\",\n        \"    --train_file_pattern=tfrecord/{file_pattern} \\\\\\n\",\n        \"    --val_file_pattern=tfrecord/{file_pattern} \\\\\\n\",\n        \"    --model_name={MODEL} \\\\\\n\",\n        \"    --model_dir=/tmp/model_dir/{MODEL}-finetune \\\\\\n\",\n        \"    --ckpt={MODEL} \\\\\\n\",\n        \"    --train_batch_size=8 \\\\\\n\",\n        \"    --eval_batch_size=8 --eval_samples={images_per_epoch}  \\\\\\n\",\n        \"    --num_examples_per_epoch={images_per_epoch}  --num_epochs=1  \\\\\\n\",\n        \"    --hparams=\\\"num_classes=20,moving_average_decay=0,mixed_precision=true\\\"\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"text\": [\n            \"mkdir: cannot create directory ‘/tmp/model_dir/’: File exists\\n\",\n            \"2020-06-01 05:38:43.365099: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"WARNING:tensorflow:From main.py:234: The name tf.estimator.tpu.TPUConfig is deprecated. Please use tf.compat.v1.estimator.tpu.TPUConfig instead.\\n\",\n            \"\\n\",\n            \"W0601 05:38:45.587865 140233166403456 module_wrapper.py:138] From main.py:234: The name tf.estimator.tpu.TPUConfig is deprecated. Please use tf.compat.v1.estimator.tpu.TPUConfig instead.\\n\",\n            \"\\n\",\n            \"WARNING:tensorflow:From main.py:239: The name tf.estimator.tpu.InputPipelineConfig is deprecated. Please use tf.compat.v1.estimator.tpu.InputPipelineConfig instead.\\n\",\n            \"\\n\",\n            \"W0601 05:38:45.588135 140233166403456 module_wrapper.py:138] From main.py:239: The name tf.estimator.tpu.InputPipelineConfig is deprecated. Please use tf.compat.v1.estimator.tpu.InputPipelineConfig instead.\\n\",\n            \"\\n\",\n            \"WARNING:tensorflow:From main.py:247: The name tf.estimator.tpu.RunConfig is deprecated. Please use tf.compat.v1.estimator.tpu.RunConfig instead.\\n\",\n            \"\\n\",\n            \"W0601 05:38:45.588366 140233166403456 module_wrapper.py:138] From main.py:247: The name tf.estimator.tpu.RunConfig is deprecated. Please use tf.compat.v1.estimator.tpu.RunConfig instead.\\n\",\n            \"\\n\",\n            \"I0601 05:38:45.588604 140233166403456 main.py:262] {'name': 'efficientdet-d0', 'act_type': 'swish', 'image_size': (512, 512), 'target_size': None, 'input_rand_hflip': True, 'train_scale_min': 0.1, 'train_scale_max': 2.0, 'autoaugment_policy': None, 'use_augmix': False, 'augmix_params': (3, -1, 1), 'num_classes': 20, 'skip_crowd_during_training': True, 'label_id_mapping': None, 'max_instances_per_image': 100, 'min_level': 3, 'max_level': 7, 'num_scales': 3, 'aspect_ratios': [(1.0, 1.0), (1.4, 0.7), (0.7, 1.4)], 'anchor_scale': 4.0, 'is_training_bn': True, 'momentum': 0.9, 'optimizer': 'sgd', 'learning_rate': 0.08, 'lr_warmup_init': 0.008, 'lr_warmup_epoch': 1.0, 'first_lr_drop_epoch': 200.0, 'second_lr_drop_epoch': 250.0, 'poly_lr_power': 0.9, 'clip_gradients_norm': 10.0, 'num_epochs': 1, 'data_format': 'channels_last', 'alpha': 0.25, 'gamma': 1.5, 'delta': 0.1, 'box_loss_weight': 50.0, 'iou_loss_type': None, 'iou_loss_weight': 1.0, 'weight_decay': 4e-05, 'strategy': None, 'precision': 'mixed_float16', 'box_class_repeats': 3, 'fpn_cell_repeats': 3, 'fpn_num_filters': 64, 'separable_conv': True, 'apply_bn_for_resampling': True, 'conv_after_downsample': False, 'conv_bn_act_pattern': False, 'use_native_resize_op': True, 'pooling_type': None, 'fpn_name': None, 'fpn_weight_method': None, 'fpn_config': None, 'survival_prob': None, 'img_summary_steps': None, 'lr_decay_method': 'cosine', 'moving_average_decay': 0, 'ckpt_var_scope': None, 'var_exclude_expr': '.*/class-predict/.*', 'backbone_name': 'efficientnet-b0', 'backbone_config': None, 'var_freeze_expr': None, 'resnet_depth': 50, 'model_name': 'efficientdet-d0', 'iterations_per_loop': 100, 'model_dir': '/tmp/model_dir/efficientdet-d0-finetune', 'num_shards': 8, 'num_examples_per_epoch': 56, 'backbone_ckpt': '', 'ckpt': 'efficientdet-d0', 'val_json_file': None, 'testdev_dir': None, 'mode': 'train_and_eval'}\\n\",\n            \"I0601 05:38:45.588753 140233166403456 main.py:373] Starting training cycle, epoch: 0.\\n\",\n            \"WARNING:tensorflow:From main.py:374: The name tf.estimator.tpu.TPUEstimator is deprecated. Please use tf.compat.v1.estimator.tpu.TPUEstimator instead.\\n\",\n            \"\\n\",\n            \"W0601 05:38:45.589545 140233166403456 module_wrapper.py:138] From main.py:374: The name tf.estimator.tpu.TPUEstimator is deprecated. Please use tf.compat.v1.estimator.tpu.TPUEstimator instead.\\n\",\n            \"\\n\",\n            \"INFO:tensorflow:Using config: {'_model_dir': '/tmp/model_dir/efficientdet-d0-finetune', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true\\n\",\n            \", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': None, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1, '_tpu_config': TPUConfig(iterations_per_loop=100, num_shards=8, num_cores_per_replica=None, per_host_input_for_training=3, tpu_job_name=None, initial_infeed_sleep_secs=None, input_partition_dims=None, eval_training_input_configuration=2, experimental_host_call_every_n_steps=1), '_cluster': None}\\n\",\n            \"I0601 05:38:45.590010 140233166403456 estimator.py:191] Using config: {'_model_dir': '/tmp/model_dir/efficientdet-d0-finetune', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true\\n\",\n            \", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': None, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1, '_tpu_config': TPUConfig(iterations_per_loop=100, num_shards=8, num_cores_per_replica=None, per_host_input_for_training=3, tpu_job_name=None, initial_infeed_sleep_secs=None, input_partition_dims=None, eval_training_input_configuration=2, experimental_host_call_every_n_steps=1), '_cluster': None}\\n\",\n            \"INFO:tensorflow:_TPUContext: eval_on_tpu True\\n\",\n            \"I0601 05:38:45.590748 140233166403456 tpu_context.py:216] _TPUContext: eval_on_tpu True\\n\",\n            \"WARNING:tensorflow:eval_on_tpu ignored because use_tpu is False.\\n\",\n            \"W0601 05:38:45.591170 140233166403456 tpu_context.py:218] eval_on_tpu ignored because use_tpu is False.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:1666: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"If using Keras pass *_constraint arguments to layers.\\n\",\n            \"W0601 05:38:45.596488 140233166403456 deprecation.py:506] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:1666: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"If using Keras pass *_constraint arguments to layers.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/training/training_util.py:236: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.\\n\",\n            \"W0601 05:38:45.596863 140233166403456 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/training/training_util.py:236: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.\\n\",\n            \"2020-06-01 05:38:45.605005: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcuda.so.1\\n\",\n            \"2020-06-01 05:38:45.639140: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:38:45.639714: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1561] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla T4 computeCapability: 7.5\\n\",\n            \"coreClock: 1.59GHz coreCount: 40 deviceMemorySize: 14.73GiB deviceMemoryBandwidth: 298.08GiB/s\\n\",\n            \"2020-06-01 05:38:45.639756: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:38:45.641296: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"2020-06-01 05:38:45.642996: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2020-06-01 05:38:45.643349: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2020-06-01 05:38:45.645110: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2020-06-01 05:38:45.646026: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\\n\",\n            \"2020-06-01 05:38:45.649821: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 05:38:45.649959: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:38:45.650550: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:38:45.651030: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1703] Adding visible gpu devices: 0\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/dataloader.py:363: parallel_interleave (from tensorflow.python.data.experimental.ops.interleave_ops) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.data.Dataset.interleave(map_func, cycle_length, block_length, num_parallel_calls=tf.data.experimental.AUTOTUNE)` instead. If sloppy execution is desired, use `tf.data.Options.experimental_deterministic`.\\n\",\n            \"W0601 05:38:45.680050 140233166403456 deprecation.py:323] From /content/automl/efficientdet/dataloader.py:363: parallel_interleave (from tensorflow.python.data.experimental.ops.interleave_ops) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.data.Dataset.interleave(map_func, cycle_length, block_length, num_parallel_calls=tf.data.experimental.AUTOTUNE)` instead. If sloppy execution is desired, use `tf.data.Options.experimental_deterministic`.\\n\",\n            \"I0601 05:38:45.853356 140233166403456 dataloader.py:83] target_size = (512, 512), output_size = (512, 512)\\n\",\n            \"INFO:tensorflow:Calling model_fn.\\n\",\n            \"I0601 05:38:46.429213 140233166403456 estimator.py:1169] Calling model_fn.\\n\",\n            \"INFO:tensorflow:Running train on CPU/GPU\\n\",\n            \"I0601 05:38:46.429509 140233166403456 tpu_estimator.py:3171] Running train on CPU/GPU\\n\",\n            \"I0601 05:38:46.429973 140233166403456 utils.py:595] use mixed precision policy name mixed_float16\\n\",\n            \"I0601 05:38:46.435803 140233166403456 efficientdet_arch.py:720] act_type: swish\\n\",\n            \"alpha: 0.25\\n\",\n            \"anchor_scale: 4.0\\n\",\n            \"apply_bn_for_resampling: true\\n\",\n            \"aspect_ratios:\\n\",\n            \"- !!python/tuple\\n\",\n            \"    - 1.0\\n\",\n            \"    - 1.0\\n\",\n            \"- !!python/tuple\\n\",\n            \"    - 1.4\\n\",\n            \"    - 0.7\\n\",\n            \"- !!python/tuple\\n\",\n            \"    - 0.7\\n\",\n            \"    - 1.4\\n\",\n            \"augmix_params: !!python/tuple\\n\",\n            \"- 3\\n\",\n            \"- -1\\n\",\n            \"- 1\\n\",\n            \"autoaugment_policy: null\\n\",\n            \"backbone_ckpt: ''\\n\",\n            \"backbone_config: null\\n\",\n            \"backbone_name: efficientnet-b0\\n\",\n            \"batch_size: 8\\n\",\n            \"box_class_repeats: 3\\n\",\n            \"box_loss_weight: 50.0\\n\",\n            \"ckpt: efficientdet-d0\\n\",\n            \"ckpt_var_scope: null\\n\",\n            \"clip_gradients_norm: 10.0\\n\",\n            \"conv_after_downsample: false\\n\",\n            \"conv_bn_act_pattern: false\\n\",\n            \"data_format: channels_last\\n\",\n            \"delta: 0.1\\n\",\n            \"first_lr_drop_epoch: 200.0\\n\",\n            \"fpn_cell_repeats: 3\\n\",\n            \"fpn_config: null\\n\",\n            \"fpn_name: null\\n\",\n            \"fpn_num_filters: 64\\n\",\n            \"fpn_weight_method: null\\n\",\n            \"gamma: 1.5\\n\",\n            \"image_size: !!python/tuple\\n\",\n            \"- 512\\n\",\n            \"- 512\\n\",\n            \"img_summary_steps: null\\n\",\n            \"input_rand_hflip: true\\n\",\n            \"iou_loss_type: null\\n\",\n            \"iou_loss_weight: 1.0\\n\",\n            \"is_training_bn: true\\n\",\n            \"iterations_per_loop: 100\\n\",\n            \"label_id_mapping: null\\n\",\n            \"learning_rate: 0.08\\n\",\n            \"lr_decay_method: cosine\\n\",\n            \"lr_warmup_epoch: 1.0\\n\",\n            \"lr_warmup_init: 0.008\\n\",\n            \"max_instances_per_image: 100\\n\",\n            \"max_level: 7\\n\",\n            \"min_level: 3\\n\",\n            \"mode: train_and_eval\\n\",\n            \"model_dir: /tmp/model_dir/efficientdet-d0-finetune\\n\",\n            \"model_name: efficientdet-d0\\n\",\n            \"momentum: 0.9\\n\",\n            \"moving_average_decay: 0\\n\",\n            \"name: efficientdet-d0\\n\",\n            \"num_classes: 20\\n\",\n            \"num_epochs: 1\\n\",\n            \"num_examples_per_epoch: 56\\n\",\n            \"num_scales: 3\\n\",\n            \"num_shards: 8\\n\",\n            \"optimizer: sgd\\n\",\n            \"poly_lr_power: 0.9\\n\",\n            \"pooling_type: null\\n\",\n            \"precision: mixed_float16\\n\",\n            \"resnet_depth: 50\\n\",\n            \"second_lr_drop_epoch: 250.0\\n\",\n            \"separable_conv: true\\n\",\n            \"skip_crowd_during_training: true\\n\",\n            \"strategy: null\\n\",\n            \"survival_prob: null\\n\",\n            \"target_size: null\\n\",\n            \"testdev_dir: null\\n\",\n            \"train_scale_max: 2.0\\n\",\n            \"train_scale_min: 0.1\\n\",\n            \"use_augmix: false\\n\",\n            \"use_native_resize_op: true\\n\",\n            \"use_tpu: false\\n\",\n            \"val_json_file: null\\n\",\n            \"var_exclude_expr: .*/class-predict/.*\\n\",\n            \"var_freeze_expr: null\\n\",\n            \"weight_decay: 4.0e-05\\n\",\n            \"\\n\",\n            \"I0601 05:38:46.441061 140233166403456 efficientnet_builder.py:224] global_params= GlobalParams(batch_norm_momentum=0.99, batch_norm_epsilon=0.001, dropout_rate=0.2, data_format='channels_last', num_classes=1000, width_coefficient=1.0, depth_coefficient=1.0, depth_divisor=8, min_depth=None, survival_prob=0.0, relu_fn=functools.partial(<function activation_fn at 0x7f8a3d432d90>, act_type='swish'), batch_norm=<class 'utils.BatchNormalization'>, use_se=True, local_pooling=None, condconv_num_experts=None, clip_projection_output=False, blocks_args=['r1_k3_s11_e1_i32_o16_se0.25', 'r2_k3_s22_e6_i16_o24_se0.25', 'r2_k5_s22_e6_i24_o40_se0.25', 'r3_k3_s22_e6_i40_o80_se0.25', 'r3_k5_s11_e6_i80_o112_se0.25', 'r4_k5_s22_e6_i112_o192_se0.25', 'r1_k3_s11_e6_i192_o320_se0.25'], fix_head_stem=None)\\n\",\n            \"I0601 05:38:49.344394 140233166403456 api.py:587] Built stem layers with output shape: (8, 256, 256, 32)\\n\",\n            \"I0601 05:38:51.915598 140233166403456 api.py:587] Block mb_conv_block  input shape: (8, 256, 256, 32)\\n\",\n            \"I0601 05:38:51.955710 140233166403456 api.py:587] DWConv shape: (8, 256, 256, 32)\\n\",\n            \"I0601 05:38:52.124546 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 32)\\n\",\n            \"I0601 05:38:52.166485 140233166403456 api.py:587] Project shape: (8, 256, 256, 16)\\n\",\n            \"I0601 05:38:52.220312 140233166403456 api.py:587] Block mb_conv_block_1  input shape: (8, 256, 256, 16)\\n\",\n            \"I0601 05:38:52.259614 140233166403456 api.py:587] Expand shape: (8, 256, 256, 96)\\n\",\n            \"I0601 05:38:52.297866 140233166403456 api.py:587] DWConv shape: (8, 128, 128, 96)\\n\",\n            \"I0601 05:38:52.334243 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 96)\\n\",\n            \"I0601 05:38:52.371809 140233166403456 api.py:587] Project shape: (8, 128, 128, 24)\\n\",\n            \"I0601 05:38:52.381985 140233166403456 api.py:587] Block mb_conv_block_2  input shape: (8, 128, 128, 24)\\n\",\n            \"I0601 05:38:52.426074 140233166403456 api.py:587] Expand shape: (8, 128, 128, 144)\\n\",\n            \"I0601 05:38:52.464259 140233166403456 api.py:587] DWConv shape: (8, 128, 128, 144)\\n\",\n            \"I0601 05:38:52.498655 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 144)\\n\",\n            \"I0601 05:38:52.537778 140233166403456 api.py:587] Project shape: (8, 128, 128, 24)\\n\",\n            \"I0601 05:38:52.547262 140233166403456 api.py:587] Block mb_conv_block_3  input shape: (8, 128, 128, 24)\\n\",\n            \"I0601 05:38:52.587199 140233166403456 api.py:587] Expand shape: (8, 128, 128, 144)\\n\",\n            \"I0601 05:38:52.632583 140233166403456 api.py:587] DWConv shape: (8, 64, 64, 144)\\n\",\n            \"I0601 05:38:52.671356 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 144)\\n\",\n            \"I0601 05:38:52.708576 140233166403456 api.py:587] Project shape: (8, 64, 64, 40)\\n\",\n            \"I0601 05:38:52.720406 140233166403456 api.py:587] Block mb_conv_block_4  input shape: (8, 64, 64, 40)\\n\",\n            \"I0601 05:38:52.758789 140233166403456 api.py:587] Expand shape: (8, 64, 64, 240)\\n\",\n            \"I0601 05:38:52.800186 140233166403456 api.py:587] DWConv shape: (8, 64, 64, 240)\\n\",\n            \"I0601 05:38:52.834159 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 240)\\n\",\n            \"I0601 05:38:52.871949 140233166403456 api.py:587] Project shape: (8, 64, 64, 40)\\n\",\n            \"I0601 05:38:52.881962 140233166403456 api.py:587] Block mb_conv_block_5  input shape: (8, 64, 64, 40)\\n\",\n            \"I0601 05:38:52.925455 140233166403456 api.py:587] Expand shape: (8, 64, 64, 240)\\n\",\n            \"I0601 05:38:52.964854 140233166403456 api.py:587] DWConv shape: (8, 32, 32, 240)\\n\",\n            \"I0601 05:38:52.999113 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 240)\\n\",\n            \"I0601 05:38:53.038467 140233166403456 api.py:587] Project shape: (8, 32, 32, 80)\\n\",\n            \"I0601 05:38:53.048321 140233166403456 api.py:587] Block mb_conv_block_6  input shape: (8, 32, 32, 80)\\n\",\n            \"I0601 05:38:53.089564 140233166403456 api.py:587] Expand shape: (8, 32, 32, 480)\\n\",\n            \"I0601 05:38:53.134333 140233166403456 api.py:587] DWConv shape: (8, 32, 32, 480)\\n\",\n            \"I0601 05:38:53.170628 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 480)\\n\",\n            \"I0601 05:38:53.214241 140233166403456 api.py:587] Project shape: (8, 32, 32, 80)\\n\",\n            \"I0601 05:38:53.223829 140233166403456 api.py:587] Block mb_conv_block_7  input shape: (8, 32, 32, 80)\\n\",\n            \"I0601 05:38:53.263554 140233166403456 api.py:587] Expand shape: (8, 32, 32, 480)\\n\",\n            \"I0601 05:38:53.303238 140233166403456 api.py:587] DWConv shape: (8, 32, 32, 480)\\n\",\n            \"I0601 05:38:53.341757 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 480)\\n\",\n            \"I0601 05:38:53.385471 140233166403456 api.py:587] Project shape: (8, 32, 32, 80)\\n\",\n            \"I0601 05:38:53.397035 140233166403456 api.py:587] Block mb_conv_block_8  input shape: (8, 32, 32, 80)\\n\",\n            \"I0601 05:38:53.440428 140233166403456 api.py:587] Expand shape: (8, 32, 32, 480)\\n\",\n            \"I0601 05:38:53.479074 140233166403456 api.py:587] DWConv shape: (8, 32, 32, 480)\\n\",\n            \"I0601 05:38:53.514025 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 480)\\n\",\n            \"I0601 05:38:53.553477 140233166403456 api.py:587] Project shape: (8, 32, 32, 112)\\n\",\n            \"I0601 05:38:53.563084 140233166403456 api.py:587] Block mb_conv_block_9  input shape: (8, 32, 32, 112)\\n\",\n            \"I0601 05:38:53.608322 140233166403456 api.py:587] Expand shape: (8, 32, 32, 672)\\n\",\n            \"I0601 05:38:53.649888 140233166403456 api.py:587] DWConv shape: (8, 32, 32, 672)\\n\",\n            \"I0601 05:38:53.687101 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 672)\\n\",\n            \"I0601 05:38:53.727589 140233166403456 api.py:587] Project shape: (8, 32, 32, 112)\\n\",\n            \"I0601 05:38:53.737505 140233166403456 api.py:587] Block mb_conv_block_10  input shape: (8, 32, 32, 112)\\n\",\n            \"I0601 05:38:53.776249 140233166403456 api.py:587] Expand shape: (8, 32, 32, 672)\\n\",\n            \"I0601 05:38:53.821624 140233166403456 api.py:587] DWConv shape: (8, 32, 32, 672)\\n\",\n            \"I0601 05:38:53.856718 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 672)\\n\",\n            \"I0601 05:38:53.894424 140233166403456 api.py:587] Project shape: (8, 32, 32, 112)\\n\",\n            \"I0601 05:38:53.904421 140233166403456 api.py:587] Block mb_conv_block_11  input shape: (8, 32, 32, 112)\\n\",\n            \"I0601 05:38:53.946480 140233166403456 api.py:587] Expand shape: (8, 32, 32, 672)\\n\",\n            \"I0601 05:38:53.987478 140233166403456 api.py:587] DWConv shape: (8, 16, 16, 672)\\n\",\n            \"I0601 05:38:54.027495 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 672)\\n\",\n            \"I0601 05:38:54.066024 140233166403456 api.py:587] Project shape: (8, 16, 16, 192)\\n\",\n            \"I0601 05:38:54.075679 140233166403456 api.py:587] Block mb_conv_block_12  input shape: (8, 16, 16, 192)\\n\",\n            \"I0601 05:38:54.123987 140233166403456 api.py:587] Expand shape: (8, 16, 16, 1152)\\n\",\n            \"I0601 05:38:54.170768 140233166403456 api.py:587] DWConv shape: (8, 16, 16, 1152)\\n\",\n            \"I0601 05:38:54.212948 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 1152)\\n\",\n            \"I0601 05:38:54.251599 140233166403456 api.py:587] Project shape: (8, 16, 16, 192)\\n\",\n            \"I0601 05:38:54.261630 140233166403456 api.py:587] Block mb_conv_block_13  input shape: (8, 16, 16, 192)\\n\",\n            \"I0601 05:38:54.305206 140233166403456 api.py:587] Expand shape: (8, 16, 16, 1152)\\n\",\n            \"I0601 05:38:54.350567 140233166403456 api.py:587] DWConv shape: (8, 16, 16, 1152)\\n\",\n            \"I0601 05:38:54.388493 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 1152)\\n\",\n            \"I0601 05:38:54.431553 140233166403456 api.py:587] Project shape: (8, 16, 16, 192)\\n\",\n            \"I0601 05:38:54.441506 140233166403456 api.py:587] Block mb_conv_block_14  input shape: (8, 16, 16, 192)\\n\",\n            \"I0601 05:38:54.485316 140233166403456 api.py:587] Expand shape: (8, 16, 16, 1152)\\n\",\n            \"I0601 05:38:54.528457 140233166403456 api.py:587] DWConv shape: (8, 16, 16, 1152)\\n\",\n            \"I0601 05:38:54.567310 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 1152)\\n\",\n            \"I0601 05:38:54.609861 140233166403456 api.py:587] Project shape: (8, 16, 16, 192)\\n\",\n            \"I0601 05:38:54.619987 140233166403456 api.py:587] Block mb_conv_block_15  input shape: (8, 16, 16, 192)\\n\",\n            \"I0601 05:38:54.669234 140233166403456 api.py:587] Expand shape: (8, 16, 16, 1152)\\n\",\n            \"I0601 05:38:54.713435 140233166403456 api.py:587] DWConv shape: (8, 16, 16, 1152)\\n\",\n            \"I0601 05:38:54.753134 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 1152)\\n\",\n            \"I0601 05:38:54.793503 140233166403456 api.py:587] Project shape: (8, 16, 16, 320)\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/profiler/internal/flops_registry.py:142: tensor_shape_from_node_def_name (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.tensor_shape_from_node_def_name`\\n\",\n            \"W0601 05:38:54.803869 140233166403456 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/profiler/internal/flops_registry.py:142: tensor_shape_from_node_def_name (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.compat.v1.graph_util.tensor_shape_from_node_def_name`\\n\",\n            \"Parsing Inputs...\\n\",\n            \"I0601 05:38:55.171925 140233166403456 efficientdet_arch.py:725] backbone params/flops = 3.595388M, 15.443714065B\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:138: conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.Conv2D` instead.\\n\",\n            \"W0601 05:38:55.172413 140233166403456 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:138: conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.Conv2D` instead.\\n\",\n            \"WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/layers/convolutional.py:424: Layer.apply (from tensorflow.python.keras.engine.base_layer_v1) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Please use `layer.__call__` method instead.\\n\",\n            \"W0601 05:38:55.175734 140233166403456 deprecation.py:323] From /usr/local/lib/python3.6/dist-packages/tensorflow/python/layers/convolutional.py:424: Layer.apply (from tensorflow.python.keras.engine.base_layer_v1) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Please use `layer.__call__` method instead.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:164: max_pooling2d (from tensorflow.python.layers.pooling) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use keras.layers.MaxPooling2D instead.\\n\",\n            \"W0601 05:38:55.236947 140233166403456 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:164: max_pooling2d (from tensorflow.python.layers.pooling) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use keras.layers.MaxPooling2D instead.\\n\",\n            \"I0601 05:38:55.243872 140233166403456 efficientdet_arch.py:471] building cell 0\\n\",\n            \"I0601 05:38:55.244316 140233166403456 efficientdet_arch.py:648] fnode 0 : {'feat_level': 6, 'inputs_offsets': [3, 4]}\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/efficientdet_arch.py:685: separable_conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.SeparableConv2D` instead.\\n\",\n            \"W0601 05:38:55.258609 140233166403456 deprecation.py:323] From /content/automl/efficientdet/efficientdet_arch.py:685: separable_conv2d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"Use `tf.keras.layers.SeparableConv2D` instead.\\n\",\n            \"I0601 05:38:55.329093 140233166403456 efficientdet_arch.py:648] fnode 1 : {'feat_level': 5, 'inputs_offsets': [2, 5]}\\n\",\n            \"I0601 05:38:55.460398 140233166403456 efficientdet_arch.py:648] fnode 2 : {'feat_level': 4, 'inputs_offsets': [1, 6]}\\n\",\n            \"I0601 05:38:55.598648 140233166403456 efficientdet_arch.py:648] fnode 3 : {'feat_level': 3, 'inputs_offsets': [0, 7]}\\n\",\n            \"I0601 05:38:55.742610 140233166403456 efficientdet_arch.py:648] fnode 4 : {'feat_level': 4, 'inputs_offsets': [1, 7, 8]}\\n\",\n            \"I0601 05:38:55.887705 140233166403456 efficientdet_arch.py:648] fnode 5 : {'feat_level': 5, 'inputs_offsets': [2, 6, 9]}\\n\",\n            \"I0601 05:38:56.035905 140233166403456 efficientdet_arch.py:648] fnode 6 : {'feat_level': 6, 'inputs_offsets': [3, 5, 10]}\\n\",\n            \"I0601 05:38:56.134672 140233166403456 efficientdet_arch.py:648] fnode 7 : {'feat_level': 7, 'inputs_offsets': [4, 11]}\\n\",\n            \"I0601 05:38:56.335880 140233166403456 efficientdet_arch.py:471] building cell 1\\n\",\n            \"I0601 05:38:56.336333 140233166403456 efficientdet_arch.py:648] fnode 0 : {'feat_level': 6, 'inputs_offsets': [3, 4]}\\n\",\n            \"I0601 05:38:56.428193 140233166403456 efficientdet_arch.py:648] fnode 1 : {'feat_level': 5, 'inputs_offsets': [2, 5]}\\n\",\n            \"I0601 05:38:56.522873 140233166403456 efficientdet_arch.py:648] fnode 2 : {'feat_level': 4, 'inputs_offsets': [1, 6]}\\n\",\n            \"I0601 05:38:56.612578 140233166403456 efficientdet_arch.py:648] fnode 3 : {'feat_level': 3, 'inputs_offsets': [0, 7]}\\n\",\n            \"I0601 05:38:56.722730 140233166403456 efficientdet_arch.py:648] fnode 4 : {'feat_level': 4, 'inputs_offsets': [1, 7, 8]}\\n\",\n            \"I0601 05:38:56.823106 140233166403456 efficientdet_arch.py:648] fnode 5 : {'feat_level': 5, 'inputs_offsets': [2, 6, 9]}\\n\",\n            \"I0601 05:38:56.918842 140233166403456 efficientdet_arch.py:648] fnode 6 : {'feat_level': 6, 'inputs_offsets': [3, 5, 10]}\\n\",\n            \"I0601 05:38:57.021353 140233166403456 efficientdet_arch.py:648] fnode 7 : {'feat_level': 7, 'inputs_offsets': [4, 11]}\\n\",\n            \"I0601 05:38:57.114783 140233166403456 efficientdet_arch.py:471] building cell 2\\n\",\n            \"I0601 05:38:57.115130 140233166403456 efficientdet_arch.py:648] fnode 0 : {'feat_level': 6, 'inputs_offsets': [3, 4]}\\n\",\n            \"I0601 05:38:57.213524 140233166403456 efficientdet_arch.py:648] fnode 1 : {'feat_level': 5, 'inputs_offsets': [2, 5]}\\n\",\n            \"I0601 05:38:57.308053 140233166403456 efficientdet_arch.py:648] fnode 2 : {'feat_level': 4, 'inputs_offsets': [1, 6]}\\n\",\n            \"I0601 05:38:57.408208 140233166403456 efficientdet_arch.py:648] fnode 3 : {'feat_level': 3, 'inputs_offsets': [0, 7]}\\n\",\n            \"I0601 05:38:57.504937 140233166403456 efficientdet_arch.py:648] fnode 4 : {'feat_level': 4, 'inputs_offsets': [1, 7, 8]}\\n\",\n            \"I0601 05:38:57.617605 140233166403456 efficientdet_arch.py:648] fnode 5 : {'feat_level': 5, 'inputs_offsets': [2, 6, 9]}\\n\",\n            \"I0601 05:38:57.734111 140233166403456 efficientdet_arch.py:648] fnode 6 : {'feat_level': 6, 'inputs_offsets': [3, 5, 10]}\\n\",\n            \"I0601 05:38:57.842484 140233166403456 efficientdet_arch.py:648] fnode 7 : {'feat_level': 7, 'inputs_offsets': [4, 11]}\\n\",\n            \"Parsing Inputs...\\n\",\n            \"I0601 05:38:58.557643 140233166403456 efficientdet_arch.py:730] backbone+fpn params/flops = 3.791669M, 16.584542043B\\n\",\n            \"Parsing Inputs...\\n\",\n            \"I0601 05:39:02.250997 140233166403456 efficientdet_arch.py:735] backbone+fpn+box params/flops = 3.839117M, 18.483276921B\\n\",\n            \"I0601 05:39:02.251317 140233166403456 utils.py:595] use mixed precision policy name float32\\n\",\n            \"I0601 05:39:02.255373 140233166403456 det_model_fn.py:97] LR schedule method: cosine\\n\",\n            \"I0601 05:39:02.480796 140233166403456 utils.py:394] Adding scale summary ('lrn_rate', <tf.Tensor 'Select:0' shape=() dtype=float32>)\\n\",\n            \"I0601 05:39:02.481896 140233166403456 utils.py:394] Adding scale summary ('trainloss/cls_loss', <tf.Tensor 'AddN:0' shape=() dtype=float32>)\\n\",\n            \"I0601 05:39:02.482783 140233166403456 utils.py:394] Adding scale summary ('trainloss/box_loss', <tf.Tensor 'AddN_1:0' shape=() dtype=float32>)\\n\",\n            \"I0601 05:39:02.483683 140233166403456 utils.py:394] Adding scale summary ('trainloss/det_loss', <tf.Tensor 'add_4:0' shape=() dtype=float32>)\\n\",\n            \"I0601 05:39:02.484507 140233166403456 utils.py:394] Adding scale summary ('trainloss/reg_l2_loss', <tf.Tensor 'mul_14:0' shape=() dtype=float32>)\\n\",\n            \"I0601 05:39:02.485368 140233166403456 utils.py:394] Adding scale summary ('trainloss/loss', <tf.Tensor 'add_5:0' shape=() dtype=float32>)\\n\",\n            \"I0601 05:39:02.486211 140233166403456 det_model_fn.py:537] clip gradients norm by 10.000000\\n\",\n            \"I0601 05:39:07.175653 140233166403456 utils.py:394] Adding scale summary ('gnorm', <tf.Tensor 'clip/global_norm/global_norm:0' shape=() dtype=float32>)\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/det_model_fn.py:659: The name tf.estimator.tpu.TPUEstimatorSpec is deprecated. Please use tf.compat.v1.estimator.tpu.TPUEstimatorSpec instead.\\n\",\n            \"\\n\",\n            \"W0601 05:39:09.331756 140233166403456 module_wrapper.py:138] From /content/automl/efficientdet/det_model_fn.py:659: The name tf.estimator.tpu.TPUEstimatorSpec is deprecated. Please use tf.compat.v1.estimator.tpu.TPUEstimatorSpec instead.\\n\",\n            \"\\n\",\n            \"I0601 05:39:09.375637 140233166403456 det_model_fn.py:638] restore variables from efficientdet-d0\\n\",\n            \"I0601 05:39:09.375814 140233166403456 utils.py:73] Init model from checkpoint efficientdet-d0\\n\",\n            \"I0601 05:39:09.394657 140233166403456 utils.py:108] Init global_step from ckpt var global_step\\n\",\n            \"I0601 05:39:09.394810 140233166403456 utils.py:105] skip current_loss_scale (current_loss_scale) -- not in ckpt\\n\",\n            \"I0601 05:39:09.394902 140233166403456 utils.py:105] skip good_steps (good_steps) -- not in ckpt\\n\",\n            \"I0601 05:39:09.394985 140233166403456 utils.py:108] Init efficientnet-b0/stem/conv2d/kernel from ckpt var efficientnet-b0/stem/conv2d/kernel\\n\",\n            \"I0601 05:39:09.395078 140233166403456 utils.py:108] Init efficientnet-b0/stem/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/stem/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:39:09.395157 140233166403456 utils.py:108] Init efficientnet-b0/stem/tpu_batch_normalization/beta from ckpt var efficientnet-b0/stem/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:39:09.395245 140233166403456 utils.py:108] Init efficientnet-b0/stem/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/stem/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:39:09.395364 140233166403456 utils.py:108] Init efficientnet-b0/stem/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/stem/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:39:09.395446 140233166403456 utils.py:108] Init efficientnet-b0/blocks_0/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_0/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:39:09.395530 140233166403456 utils.py:108] Init efficientnet-b0/blocks_0/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_0/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:39:09.395612 140233166403456 utils.py:108] Init efficientnet-b0/blocks_0/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_0/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:39:09.395702 140233166403456 utils.py:108] Init efficientnet-b0/blocks_0/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_0/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:39:09.395785 140233166403456 utils.py:108] Init efficientnet-b0/blocks_0/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_0/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:39:09.395869 140233166403456 utils.py:108] Init efficientnet-b0/blocks_0/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_0/se/conv2d/kernel\\n\",\n            \"I0601 05:39:09.395951 140233166403456 utils.py:108] Init efficientnet-b0/blocks_0/se/conv2d/bias from ckpt var efficientnet-b0/blocks_0/se/conv2d/bias\\n\",\n            \"I0601 05:39:09.396034 140233166403456 utils.py:108] Init efficientnet-b0/blocks_0/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_0/se/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.396115 140233166403456 utils.py:108] Init efficientnet-b0/blocks_0/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_0/se/conv2d_1/bias\\n\",\n            \"I0601 05:39:09.396197 140233166403456 utils.py:108] Init efficientnet-b0/blocks_0/conv2d/kernel from ckpt var efficientnet-b0/blocks_0/conv2d/kernel\\n\",\n            \"I0601 05:39:09.396296 140233166403456 utils.py:108] Init efficientnet-b0/blocks_0/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_0/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:39:09.396387 140233166403456 utils.py:108] Init efficientnet-b0/blocks_0/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_0/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:39:09.396468 140233166403456 utils.py:108] Init efficientnet-b0/blocks_0/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_0/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:39:09.396551 140233166403456 utils.py:108] Init efficientnet-b0/blocks_0/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_0/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:39:09.396643 140233166403456 utils.py:108] Init efficientnet-b0/blocks_1/conv2d/kernel from ckpt var efficientnet-b0/blocks_1/conv2d/kernel\\n\",\n            \"I0601 05:39:09.396720 140233166403456 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:39:09.396797 140233166403456 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:39:09.396875 140233166403456 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:39:09.396951 140233166403456 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:39:09.397027 140233166403456 utils.py:108] Init efficientnet-b0/blocks_1/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_1/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:39:09.397111 140233166403456 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:39:09.397184 140233166403456 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:39:09.397257 140233166403456 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:39:09.397348 140233166403456 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:39:09.397421 140233166403456 utils.py:108] Init efficientnet-b0/blocks_1/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_1/se/conv2d/kernel\\n\",\n            \"I0601 05:39:09.397493 140233166403456 utils.py:108] Init efficientnet-b0/blocks_1/se/conv2d/bias from ckpt var efficientnet-b0/blocks_1/se/conv2d/bias\\n\",\n            \"I0601 05:39:09.397565 140233166403456 utils.py:108] Init efficientnet-b0/blocks_1/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_1/se/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.397644 140233166403456 utils.py:108] Init efficientnet-b0/blocks_1/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_1/se/conv2d_1/bias\\n\",\n            \"I0601 05:39:09.397715 140233166403456 utils.py:108] Init efficientnet-b0/blocks_1/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_1/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.397787 140233166403456 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:39:09.397858 140233166403456 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:39:09.397932 140233166403456 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:39:09.398004 140233166403456 utils.py:108] Init efficientnet-b0/blocks_1/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_1/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:39:09.398075 140233166403456 utils.py:108] Init efficientnet-b0/blocks_2/conv2d/kernel from ckpt var efficientnet-b0/blocks_2/conv2d/kernel\\n\",\n            \"I0601 05:39:09.398149 140233166403456 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:39:09.398220 140233166403456 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:39:09.398308 140233166403456 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:39:09.398386 140233166403456 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:39:09.398461 140233166403456 utils.py:108] Init efficientnet-b0/blocks_2/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_2/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:39:09.398534 140233166403456 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:39:09.398606 140233166403456 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:39:09.398686 140233166403456 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:39:09.398759 140233166403456 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:39:09.398832 140233166403456 utils.py:108] Init efficientnet-b0/blocks_2/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_2/se/conv2d/kernel\\n\",\n            \"I0601 05:39:09.398904 140233166403456 utils.py:108] Init efficientnet-b0/blocks_2/se/conv2d/bias from ckpt var efficientnet-b0/blocks_2/se/conv2d/bias\\n\",\n            \"I0601 05:39:09.398976 140233166403456 utils.py:108] Init efficientnet-b0/blocks_2/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_2/se/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.399054 140233166403456 utils.py:108] Init efficientnet-b0/blocks_2/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_2/se/conv2d_1/bias\\n\",\n            \"I0601 05:39:09.399127 140233166403456 utils.py:108] Init efficientnet-b0/blocks_2/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_2/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.399202 140233166403456 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:39:09.399291 140233166403456 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:39:09.399364 140233166403456 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:39:09.399437 140233166403456 utils.py:108] Init efficientnet-b0/blocks_2/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_2/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:39:09.399510 140233166403456 utils.py:108] Init efficientnet-b0/blocks_3/conv2d/kernel from ckpt var efficientnet-b0/blocks_3/conv2d/kernel\\n\",\n            \"I0601 05:39:09.399582 140233166403456 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:39:09.399658 140233166403456 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:39:09.399731 140233166403456 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:39:09.399805 140233166403456 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:39:09.399877 140233166403456 utils.py:108] Init efficientnet-b0/blocks_3/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_3/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:39:09.399950 140233166403456 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:39:09.400024 140233166403456 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:39:09.400097 140233166403456 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:39:09.400170 140233166403456 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:39:09.400242 140233166403456 utils.py:108] Init efficientnet-b0/blocks_3/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_3/se/conv2d/kernel\\n\",\n            \"I0601 05:39:09.400327 140233166403456 utils.py:108] Init efficientnet-b0/blocks_3/se/conv2d/bias from ckpt var efficientnet-b0/blocks_3/se/conv2d/bias\\n\",\n            \"I0601 05:39:09.400401 140233166403456 utils.py:108] Init efficientnet-b0/blocks_3/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_3/se/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.400473 140233166403456 utils.py:108] Init efficientnet-b0/blocks_3/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_3/se/conv2d_1/bias\\n\",\n            \"I0601 05:39:09.400545 140233166403456 utils.py:108] Init efficientnet-b0/blocks_3/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_3/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.400617 140233166403456 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:39:09.400696 140233166403456 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:39:09.400772 140233166403456 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:39:09.400850 140233166403456 utils.py:108] Init efficientnet-b0/blocks_3/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_3/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:39:09.400922 140233166403456 utils.py:108] Init efficientnet-b0/blocks_4/conv2d/kernel from ckpt var efficientnet-b0/blocks_4/conv2d/kernel\\n\",\n            \"I0601 05:39:09.400992 140233166403456 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:39:09.401064 140233166403456 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:39:09.401135 140233166403456 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:39:09.401207 140233166403456 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:39:09.401290 140233166403456 utils.py:108] Init efficientnet-b0/blocks_4/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_4/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:39:09.401365 140233166403456 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:39:09.401436 140233166403456 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:39:09.401507 140233166403456 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:39:09.401582 140233166403456 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:39:09.401659 140233166403456 utils.py:108] Init efficientnet-b0/blocks_4/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_4/se/conv2d/kernel\\n\",\n            \"I0601 05:39:09.401734 140233166403456 utils.py:108] Init efficientnet-b0/blocks_4/se/conv2d/bias from ckpt var efficientnet-b0/blocks_4/se/conv2d/bias\\n\",\n            \"I0601 05:39:09.401811 140233166403456 utils.py:108] Init efficientnet-b0/blocks_4/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_4/se/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.401884 140233166403456 utils.py:108] Init efficientnet-b0/blocks_4/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_4/se/conv2d_1/bias\\n\",\n            \"I0601 05:39:09.401956 140233166403456 utils.py:108] Init efficientnet-b0/blocks_4/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_4/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.402033 140233166403456 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:39:09.402105 140233166403456 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:39:09.402178 140233166403456 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:39:09.402253 140233166403456 utils.py:108] Init efficientnet-b0/blocks_4/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_4/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:39:09.478443 140233166403456 utils.py:108] Init efficientnet-b0/blocks_5/conv2d/kernel from ckpt var efficientnet-b0/blocks_5/conv2d/kernel\\n\",\n            \"I0601 05:39:09.478590 140233166403456 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:39:09.478709 140233166403456 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:39:09.478804 140233166403456 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:39:09.478899 140233166403456 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:39:09.478995 140233166403456 utils.py:108] Init efficientnet-b0/blocks_5/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_5/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:39:09.479170 140233166403456 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:39:09.479353 140233166403456 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:39:09.479465 140233166403456 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:39:09.479566 140233166403456 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:39:09.479683 140233166403456 utils.py:108] Init efficientnet-b0/blocks_5/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_5/se/conv2d/kernel\\n\",\n            \"I0601 05:39:09.479765 140233166403456 utils.py:108] Init efficientnet-b0/blocks_5/se/conv2d/bias from ckpt var efficientnet-b0/blocks_5/se/conv2d/bias\\n\",\n            \"I0601 05:39:09.479843 140233166403456 utils.py:108] Init efficientnet-b0/blocks_5/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_5/se/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.479918 140233166403456 utils.py:108] Init efficientnet-b0/blocks_5/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_5/se/conv2d_1/bias\\n\",\n            \"I0601 05:39:09.479994 140233166403456 utils.py:108] Init efficientnet-b0/blocks_5/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_5/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.480067 140233166403456 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:39:09.480148 140233166403456 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:39:09.480223 140233166403456 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:39:09.480322 140233166403456 utils.py:108] Init efficientnet-b0/blocks_5/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_5/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:39:09.480395 140233166403456 utils.py:108] Init efficientnet-b0/blocks_6/conv2d/kernel from ckpt var efficientnet-b0/blocks_6/conv2d/kernel\\n\",\n            \"I0601 05:39:09.480469 140233166403456 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:39:09.480541 140233166403456 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:39:09.480614 140233166403456 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:39:09.480693 140233166403456 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:39:09.480766 140233166403456 utils.py:108] Init efficientnet-b0/blocks_6/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_6/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:39:09.480838 140233166403456 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:39:09.480909 140233166403456 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:39:09.480981 140233166403456 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:39:09.481052 140233166403456 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:39:09.481129 140233166403456 utils.py:108] Init efficientnet-b0/blocks_6/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_6/se/conv2d/kernel\\n\",\n            \"I0601 05:39:09.481203 140233166403456 utils.py:108] Init efficientnet-b0/blocks_6/se/conv2d/bias from ckpt var efficientnet-b0/blocks_6/se/conv2d/bias\\n\",\n            \"I0601 05:39:09.481291 140233166403456 utils.py:108] Init efficientnet-b0/blocks_6/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_6/se/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.481364 140233166403456 utils.py:108] Init efficientnet-b0/blocks_6/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_6/se/conv2d_1/bias\\n\",\n            \"I0601 05:39:09.481438 140233166403456 utils.py:108] Init efficientnet-b0/blocks_6/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_6/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.481511 140233166403456 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:39:09.481583 140233166403456 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:39:09.481656 140233166403456 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:39:09.481727 140233166403456 utils.py:108] Init efficientnet-b0/blocks_6/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_6/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:39:09.481799 140233166403456 utils.py:108] Init efficientnet-b0/blocks_7/conv2d/kernel from ckpt var efficientnet-b0/blocks_7/conv2d/kernel\\n\",\n            \"I0601 05:39:09.481874 140233166403456 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:39:09.481947 140233166403456 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:39:09.482020 140233166403456 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:39:09.482092 140233166403456 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:39:09.482172 140233166403456 utils.py:108] Init efficientnet-b0/blocks_7/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_7/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:39:09.482246 140233166403456 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:39:09.482330 140233166403456 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:39:09.482403 140233166403456 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:39:09.482478 140233166403456 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:39:09.482550 140233166403456 utils.py:108] Init efficientnet-b0/blocks_7/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_7/se/conv2d/kernel\\n\",\n            \"I0601 05:39:09.482622 140233166403456 utils.py:108] Init efficientnet-b0/blocks_7/se/conv2d/bias from ckpt var efficientnet-b0/blocks_7/se/conv2d/bias\\n\",\n            \"I0601 05:39:09.482696 140233166403456 utils.py:108] Init efficientnet-b0/blocks_7/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_7/se/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.482769 140233166403456 utils.py:108] Init efficientnet-b0/blocks_7/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_7/se/conv2d_1/bias\\n\",\n            \"I0601 05:39:09.482841 140233166403456 utils.py:108] Init efficientnet-b0/blocks_7/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_7/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.482913 140233166403456 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:39:09.482984 140233166403456 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:39:09.483054 140233166403456 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:39:09.483131 140233166403456 utils.py:108] Init efficientnet-b0/blocks_7/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_7/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:39:09.483201 140233166403456 utils.py:108] Init efficientnet-b0/blocks_8/conv2d/kernel from ckpt var efficientnet-b0/blocks_8/conv2d/kernel\\n\",\n            \"I0601 05:39:09.483285 140233166403456 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:39:09.483355 140233166403456 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:39:09.483428 140233166403456 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:39:09.483501 140233166403456 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:39:09.483573 140233166403456 utils.py:108] Init efficientnet-b0/blocks_8/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_8/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:39:09.483645 140233166403456 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:39:09.483716 140233166403456 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:39:09.483786 140233166403456 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:39:09.483860 140233166403456 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:39:09.483931 140233166403456 utils.py:108] Init efficientnet-b0/blocks_8/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_8/se/conv2d/kernel\\n\",\n            \"I0601 05:39:09.484006 140233166403456 utils.py:108] Init efficientnet-b0/blocks_8/se/conv2d/bias from ckpt var efficientnet-b0/blocks_8/se/conv2d/bias\\n\",\n            \"I0601 05:39:09.484081 140233166403456 utils.py:108] Init efficientnet-b0/blocks_8/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_8/se/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.484156 140233166403456 utils.py:108] Init efficientnet-b0/blocks_8/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_8/se/conv2d_1/bias\\n\",\n            \"I0601 05:39:09.484230 140233166403456 utils.py:108] Init efficientnet-b0/blocks_8/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_8/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.484314 140233166403456 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:39:09.484388 140233166403456 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:39:09.484469 140233166403456 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:39:09.484544 140233166403456 utils.py:108] Init efficientnet-b0/blocks_8/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_8/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:39:09.484617 140233166403456 utils.py:108] Init efficientnet-b0/blocks_9/conv2d/kernel from ckpt var efficientnet-b0/blocks_9/conv2d/kernel\\n\",\n            \"I0601 05:39:09.484691 140233166403456 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:39:09.484762 140233166403456 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:39:09.484836 140233166403456 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:39:09.484909 140233166403456 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:39:09.484979 140233166403456 utils.py:108] Init efficientnet-b0/blocks_9/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_9/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:39:09.485051 140233166403456 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:39:09.485147 140233166403456 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:39:09.485226 140233166403456 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:39:09.485315 140233166403456 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:39:09.485393 140233166403456 utils.py:108] Init efficientnet-b0/blocks_9/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_9/se/conv2d/kernel\\n\",\n            \"I0601 05:39:09.485471 140233166403456 utils.py:108] Init efficientnet-b0/blocks_9/se/conv2d/bias from ckpt var efficientnet-b0/blocks_9/se/conv2d/bias\\n\",\n            \"I0601 05:39:09.485548 140233166403456 utils.py:108] Init efficientnet-b0/blocks_9/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_9/se/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.485626 140233166403456 utils.py:108] Init efficientnet-b0/blocks_9/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_9/se/conv2d_1/bias\\n\",\n            \"I0601 05:39:09.485703 140233166403456 utils.py:108] Init efficientnet-b0/blocks_9/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_9/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.485780 140233166403456 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:39:09.485867 140233166403456 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:39:09.485958 140233166403456 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:39:09.486035 140233166403456 utils.py:108] Init efficientnet-b0/blocks_9/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_9/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:39:09.486113 140233166403456 utils.py:108] Init efficientnet-b0/blocks_10/conv2d/kernel from ckpt var efficientnet-b0/blocks_10/conv2d/kernel\\n\",\n            \"I0601 05:39:09.486196 140233166403456 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:39:09.486286 140233166403456 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:39:09.486371 140233166403456 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:39:09.486443 140233166403456 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:39:09.486515 140233166403456 utils.py:108] Init efficientnet-b0/blocks_10/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_10/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:39:09.486588 140233166403456 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:39:09.486662 140233166403456 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:39:09.579752 140233166403456 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:39:09.579918 140233166403456 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:39:09.580045 140233166403456 utils.py:108] Init efficientnet-b0/blocks_10/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_10/se/conv2d/kernel\\n\",\n            \"I0601 05:39:09.580178 140233166403456 utils.py:108] Init efficientnet-b0/blocks_10/se/conv2d/bias from ckpt var efficientnet-b0/blocks_10/se/conv2d/bias\\n\",\n            \"I0601 05:39:09.580321 140233166403456 utils.py:108] Init efficientnet-b0/blocks_10/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_10/se/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.580432 140233166403456 utils.py:108] Init efficientnet-b0/blocks_10/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_10/se/conv2d_1/bias\\n\",\n            \"I0601 05:39:09.580530 140233166403456 utils.py:108] Init efficientnet-b0/blocks_10/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_10/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.580643 140233166403456 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:39:09.580740 140233166403456 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:39:09.580813 140233166403456 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:39:09.580882 140233166403456 utils.py:108] Init efficientnet-b0/blocks_10/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_10/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:39:09.580953 140233166403456 utils.py:108] Init efficientnet-b0/blocks_11/conv2d/kernel from ckpt var efficientnet-b0/blocks_11/conv2d/kernel\\n\",\n            \"I0601 05:39:09.581029 140233166403456 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:39:09.581105 140233166403456 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:39:09.581208 140233166403456 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:39:09.581321 140233166403456 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:39:09.581416 140233166403456 utils.py:108] Init efficientnet-b0/blocks_11/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_11/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:39:09.581508 140233166403456 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:39:09.581592 140233166403456 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:39:09.581684 140233166403456 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:39:09.581779 140233166403456 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:39:09.581890 140233166403456 utils.py:108] Init efficientnet-b0/blocks_11/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_11/se/conv2d/kernel\\n\",\n            \"I0601 05:39:09.581973 140233166403456 utils.py:108] Init efficientnet-b0/blocks_11/se/conv2d/bias from ckpt var efficientnet-b0/blocks_11/se/conv2d/bias\\n\",\n            \"I0601 05:39:09.582057 140233166403456 utils.py:108] Init efficientnet-b0/blocks_11/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_11/se/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.582149 140233166403456 utils.py:108] Init efficientnet-b0/blocks_11/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_11/se/conv2d_1/bias\\n\",\n            \"I0601 05:39:09.582243 140233166403456 utils.py:108] Init efficientnet-b0/blocks_11/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_11/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.582370 140233166403456 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:39:09.582453 140233166403456 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:39:09.582535 140233166403456 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:39:09.582617 140233166403456 utils.py:108] Init efficientnet-b0/blocks_11/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_11/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:39:09.582708 140233166403456 utils.py:108] Init efficientnet-b0/blocks_12/conv2d/kernel from ckpt var efficientnet-b0/blocks_12/conv2d/kernel\\n\",\n            \"I0601 05:39:09.582797 140233166403456 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:39:09.582885 140233166403456 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:39:09.582967 140233166403456 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:39:09.583068 140233166403456 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:39:09.583152 140233166403456 utils.py:108] Init efficientnet-b0/blocks_12/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_12/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:39:09.583225 140233166403456 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:39:09.583319 140233166403456 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:39:09.583429 140233166403456 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:39:09.583520 140233166403456 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:39:09.583604 140233166403456 utils.py:108] Init efficientnet-b0/blocks_12/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_12/se/conv2d/kernel\\n\",\n            \"I0601 05:39:09.583690 140233166403456 utils.py:108] Init efficientnet-b0/blocks_12/se/conv2d/bias from ckpt var efficientnet-b0/blocks_12/se/conv2d/bias\\n\",\n            \"I0601 05:39:09.583773 140233166403456 utils.py:108] Init efficientnet-b0/blocks_12/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_12/se/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.583862 140233166403456 utils.py:108] Init efficientnet-b0/blocks_12/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_12/se/conv2d_1/bias\\n\",\n            \"I0601 05:39:09.583948 140233166403456 utils.py:108] Init efficientnet-b0/blocks_12/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_12/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.584049 140233166403456 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:39:09.584135 140233166403456 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:39:09.584223 140233166403456 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:39:09.585918 140233166403456 utils.py:108] Init efficientnet-b0/blocks_12/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_12/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:39:09.586033 140233166403456 utils.py:108] Init efficientnet-b0/blocks_13/conv2d/kernel from ckpt var efficientnet-b0/blocks_13/conv2d/kernel\\n\",\n            \"I0601 05:39:09.586118 140233166403456 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:39:09.586199 140233166403456 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:39:09.586302 140233166403456 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:39:09.586386 140233166403456 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:39:09.586467 140233166403456 utils.py:108] Init efficientnet-b0/blocks_13/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_13/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:39:09.586549 140233166403456 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:39:09.586637 140233166403456 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:39:09.586729 140233166403456 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:39:09.586802 140233166403456 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:39:09.586876 140233166403456 utils.py:108] Init efficientnet-b0/blocks_13/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_13/se/conv2d/kernel\\n\",\n            \"I0601 05:39:09.586953 140233166403456 utils.py:108] Init efficientnet-b0/blocks_13/se/conv2d/bias from ckpt var efficientnet-b0/blocks_13/se/conv2d/bias\\n\",\n            \"I0601 05:39:09.587029 140233166403456 utils.py:108] Init efficientnet-b0/blocks_13/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_13/se/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.587103 140233166403456 utils.py:108] Init efficientnet-b0/blocks_13/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_13/se/conv2d_1/bias\\n\",\n            \"I0601 05:39:09.587177 140233166403456 utils.py:108] Init efficientnet-b0/blocks_13/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_13/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.587250 140233166403456 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:39:09.587341 140233166403456 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:39:09.587430 140233166403456 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:39:09.587511 140233166403456 utils.py:108] Init efficientnet-b0/blocks_13/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_13/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:39:09.587595 140233166403456 utils.py:108] Init efficientnet-b0/blocks_14/conv2d/kernel from ckpt var efficientnet-b0/blocks_14/conv2d/kernel\\n\",\n            \"I0601 05:39:09.587682 140233166403456 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:39:09.587758 140233166403456 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:39:09.587831 140233166403456 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:39:09.587917 140233166403456 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:39:09.588001 140233166403456 utils.py:108] Init efficientnet-b0/blocks_14/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_14/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:39:09.588090 140233166403456 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:39:09.588164 140233166403456 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:39:09.588239 140233166403456 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:39:09.588338 140233166403456 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:39:09.588409 140233166403456 utils.py:108] Init efficientnet-b0/blocks_14/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_14/se/conv2d/kernel\\n\",\n            \"I0601 05:39:09.588497 140233166403456 utils.py:108] Init efficientnet-b0/blocks_14/se/conv2d/bias from ckpt var efficientnet-b0/blocks_14/se/conv2d/bias\\n\",\n            \"I0601 05:39:09.588569 140233166403456 utils.py:108] Init efficientnet-b0/blocks_14/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_14/se/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.588649 140233166403456 utils.py:108] Init efficientnet-b0/blocks_14/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_14/se/conv2d_1/bias\\n\",\n            \"I0601 05:39:09.588721 140233166403456 utils.py:108] Init efficientnet-b0/blocks_14/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_14/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.588803 140233166403456 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:39:09.588892 140233166403456 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:39:09.588983 140233166403456 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:39:09.589067 140233166403456 utils.py:108] Init efficientnet-b0/blocks_14/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_14/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:39:09.589147 140233166403456 utils.py:108] Init efficientnet-b0/blocks_15/conv2d/kernel from ckpt var efficientnet-b0/blocks_15/conv2d/kernel\\n\",\n            \"I0601 05:39:09.589230 140233166403456 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization/gamma from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization/gamma\\n\",\n            \"I0601 05:39:09.589327 140233166403456 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization/beta from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization/beta\\n\",\n            \"I0601 05:39:09.589406 140233166403456 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization/moving_mean from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization/moving_mean\\n\",\n            \"I0601 05:39:09.589492 140233166403456 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization/moving_variance from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization/moving_variance\\n\",\n            \"I0601 05:39:09.680724 140233166403456 utils.py:108] Init efficientnet-b0/blocks_15/depthwise_conv2d/depthwise_kernel from ckpt var efficientnet-b0/blocks_15/depthwise_conv2d/depthwise_kernel\\n\",\n            \"I0601 05:39:09.680886 140233166403456 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization_1/gamma from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization_1/gamma\\n\",\n            \"I0601 05:39:09.681005 140233166403456 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization_1/beta from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization_1/beta\\n\",\n            \"I0601 05:39:09.681105 140233166403456 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization_1/moving_mean from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization_1/moving_mean\\n\",\n            \"I0601 05:39:09.681201 140233166403456 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization_1/moving_variance from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization_1/moving_variance\\n\",\n            \"I0601 05:39:09.681341 140233166403456 utils.py:108] Init efficientnet-b0/blocks_15/se/conv2d/kernel from ckpt var efficientnet-b0/blocks_15/se/conv2d/kernel\\n\",\n            \"I0601 05:39:09.681472 140233166403456 utils.py:108] Init efficientnet-b0/blocks_15/se/conv2d/bias from ckpt var efficientnet-b0/blocks_15/se/conv2d/bias\\n\",\n            \"I0601 05:39:09.681543 140233166403456 utils.py:108] Init efficientnet-b0/blocks_15/se/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_15/se/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.681612 140233166403456 utils.py:108] Init efficientnet-b0/blocks_15/se/conv2d_1/bias from ckpt var efficientnet-b0/blocks_15/se/conv2d_1/bias\\n\",\n            \"I0601 05:39:09.681701 140233166403456 utils.py:108] Init efficientnet-b0/blocks_15/conv2d_1/kernel from ckpt var efficientnet-b0/blocks_15/conv2d_1/kernel\\n\",\n            \"I0601 05:39:09.681772 140233166403456 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization_2/gamma from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization_2/gamma\\n\",\n            \"I0601 05:39:09.681840 140233166403456 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization_2/beta from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization_2/beta\\n\",\n            \"I0601 05:39:09.681908 140233166403456 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization_2/moving_mean from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization_2/moving_mean\\n\",\n            \"I0601 05:39:09.681974 140233166403456 utils.py:108] Init efficientnet-b0/blocks_15/tpu_batch_normalization_2/moving_variance from ckpt var efficientnet-b0/blocks_15/tpu_batch_normalization_2/moving_variance\\n\",\n            \"I0601 05:39:09.682042 140233166403456 utils.py:108] Init resample_p6/conv2d/kernel from ckpt var resample_p6/conv2d/kernel\\n\",\n            \"I0601 05:39:09.682110 140233166403456 utils.py:108] Init resample_p6/conv2d/bias from ckpt var resample_p6/conv2d/bias\\n\",\n            \"I0601 05:39:09.682184 140233166403456 utils.py:108] Init resample_p6/bn/gamma from ckpt var resample_p6/bn/gamma\\n\",\n            \"I0601 05:39:09.682249 140233166403456 utils.py:108] Init resample_p6/bn/beta from ckpt var resample_p6/bn/beta\\n\",\n            \"I0601 05:39:09.682337 140233166403456 utils.py:108] Init resample_p6/bn/moving_mean from ckpt var resample_p6/bn/moving_mean\\n\",\n            \"I0601 05:39:09.682409 140233166403456 utils.py:108] Init resample_p6/bn/moving_variance from ckpt var resample_p6/bn/moving_variance\\n\",\n            \"I0601 05:39:09.682477 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode0/WSM from ckpt var fpn_cells/cell_0/fnode0/WSM\\n\",\n            \"I0601 05:39:09.682542 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode0/WSM_1 from ckpt var fpn_cells/cell_0/fnode0/WSM_1\\n\",\n            \"I0601 05:39:09.682608 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode0/op_after_combine5/conv/depthwise_kernel from ckpt var fpn_cells/cell_0/fnode0/op_after_combine5/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.682686 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode0/op_after_combine5/conv/pointwise_kernel from ckpt var fpn_cells/cell_0/fnode0/op_after_combine5/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.682752 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode0/op_after_combine5/conv/bias from ckpt var fpn_cells/cell_0/fnode0/op_after_combine5/conv/bias\\n\",\n            \"I0601 05:39:09.682821 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode0/op_after_combine5/bn/gamma from ckpt var fpn_cells/cell_0/fnode0/op_after_combine5/bn/gamma\\n\",\n            \"I0601 05:39:09.682886 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode0/op_after_combine5/bn/beta from ckpt var fpn_cells/cell_0/fnode0/op_after_combine5/bn/beta\\n\",\n            \"I0601 05:39:09.682978 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode0/op_after_combine5/bn/moving_mean from ckpt var fpn_cells/cell_0/fnode0/op_after_combine5/bn/moving_mean\\n\",\n            \"I0601 05:39:09.683043 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode0/op_after_combine5/bn/moving_variance from ckpt var fpn_cells/cell_0/fnode0/op_after_combine5/bn/moving_variance\\n\",\n            \"I0601 05:39:09.683118 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode1/resample_0_2_6/conv2d/kernel from ckpt var fpn_cells/cell_0/fnode1/resample_0_2_6/conv2d/kernel\\n\",\n            \"I0601 05:39:09.683178 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode1/resample_0_2_6/conv2d/bias from ckpt var fpn_cells/cell_0/fnode1/resample_0_2_6/conv2d/bias\\n\",\n            \"I0601 05:39:09.683238 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode1/resample_0_2_6/bn/gamma from ckpt var fpn_cells/cell_0/fnode1/resample_0_2_6/bn/gamma\\n\",\n            \"I0601 05:39:09.683312 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode1/resample_0_2_6/bn/beta from ckpt var fpn_cells/cell_0/fnode1/resample_0_2_6/bn/beta\\n\",\n            \"I0601 05:39:09.683375 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode1/resample_0_2_6/bn/moving_mean from ckpt var fpn_cells/cell_0/fnode1/resample_0_2_6/bn/moving_mean\\n\",\n            \"I0601 05:39:09.683435 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode1/resample_0_2_6/bn/moving_variance from ckpt var fpn_cells/cell_0/fnode1/resample_0_2_6/bn/moving_variance\\n\",\n            \"I0601 05:39:09.683495 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode1/WSM from ckpt var fpn_cells/cell_0/fnode1/WSM\\n\",\n            \"I0601 05:39:09.683554 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode1/WSM_1 from ckpt var fpn_cells/cell_0/fnode1/WSM_1\\n\",\n            \"I0601 05:39:09.683614 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode1/op_after_combine6/conv/depthwise_kernel from ckpt var fpn_cells/cell_0/fnode1/op_after_combine6/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.683681 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode1/op_after_combine6/conv/pointwise_kernel from ckpt var fpn_cells/cell_0/fnode1/op_after_combine6/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.683743 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode1/op_after_combine6/conv/bias from ckpt var fpn_cells/cell_0/fnode1/op_after_combine6/conv/bias\\n\",\n            \"I0601 05:39:09.683811 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode1/op_after_combine6/bn/gamma from ckpt var fpn_cells/cell_0/fnode1/op_after_combine6/bn/gamma\\n\",\n            \"I0601 05:39:09.683873 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode1/op_after_combine6/bn/beta from ckpt var fpn_cells/cell_0/fnode1/op_after_combine6/bn/beta\\n\",\n            \"I0601 05:39:09.683935 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode1/op_after_combine6/bn/moving_mean from ckpt var fpn_cells/cell_0/fnode1/op_after_combine6/bn/moving_mean\\n\",\n            \"I0601 05:39:09.684015 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode1/op_after_combine6/bn/moving_variance from ckpt var fpn_cells/cell_0/fnode1/op_after_combine6/bn/moving_variance\\n\",\n            \"I0601 05:39:09.684082 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode2/resample_0_1_7/conv2d/kernel from ckpt var fpn_cells/cell_0/fnode2/resample_0_1_7/conv2d/kernel\\n\",\n            \"I0601 05:39:09.684148 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode2/resample_0_1_7/conv2d/bias from ckpt var fpn_cells/cell_0/fnode2/resample_0_1_7/conv2d/bias\\n\",\n            \"I0601 05:39:09.684214 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode2/resample_0_1_7/bn/gamma from ckpt var fpn_cells/cell_0/fnode2/resample_0_1_7/bn/gamma\\n\",\n            \"I0601 05:39:09.684289 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode2/resample_0_1_7/bn/beta from ckpt var fpn_cells/cell_0/fnode2/resample_0_1_7/bn/beta\\n\",\n            \"I0601 05:39:09.684356 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode2/resample_0_1_7/bn/moving_mean from ckpt var fpn_cells/cell_0/fnode2/resample_0_1_7/bn/moving_mean\\n\",\n            \"I0601 05:39:09.684422 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode2/resample_0_1_7/bn/moving_variance from ckpt var fpn_cells/cell_0/fnode2/resample_0_1_7/bn/moving_variance\\n\",\n            \"I0601 05:39:09.684489 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode2/WSM from ckpt var fpn_cells/cell_0/fnode2/WSM\\n\",\n            \"I0601 05:39:09.684562 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode2/WSM_1 from ckpt var fpn_cells/cell_0/fnode2/WSM_1\\n\",\n            \"I0601 05:39:09.684633 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode2/op_after_combine7/conv/depthwise_kernel from ckpt var fpn_cells/cell_0/fnode2/op_after_combine7/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.684700 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode2/op_after_combine7/conv/pointwise_kernel from ckpt var fpn_cells/cell_0/fnode2/op_after_combine7/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.684767 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode2/op_after_combine7/conv/bias from ckpt var fpn_cells/cell_0/fnode2/op_after_combine7/conv/bias\\n\",\n            \"I0601 05:39:09.684833 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode2/op_after_combine7/bn/gamma from ckpt var fpn_cells/cell_0/fnode2/op_after_combine7/bn/gamma\\n\",\n            \"I0601 05:39:09.684899 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode2/op_after_combine7/bn/beta from ckpt var fpn_cells/cell_0/fnode2/op_after_combine7/bn/beta\\n\",\n            \"I0601 05:39:09.684964 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode2/op_after_combine7/bn/moving_mean from ckpt var fpn_cells/cell_0/fnode2/op_after_combine7/bn/moving_mean\\n\",\n            \"I0601 05:39:09.685030 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode2/op_after_combine7/bn/moving_variance from ckpt var fpn_cells/cell_0/fnode2/op_after_combine7/bn/moving_variance\\n\",\n            \"I0601 05:39:09.685096 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode3/resample_0_0_8/conv2d/kernel from ckpt var fpn_cells/cell_0/fnode3/resample_0_0_8/conv2d/kernel\\n\",\n            \"I0601 05:39:09.685162 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode3/resample_0_0_8/conv2d/bias from ckpt var fpn_cells/cell_0/fnode3/resample_0_0_8/conv2d/bias\\n\",\n            \"I0601 05:39:09.685228 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode3/resample_0_0_8/bn/gamma from ckpt var fpn_cells/cell_0/fnode3/resample_0_0_8/bn/gamma\\n\",\n            \"I0601 05:39:09.685305 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode3/resample_0_0_8/bn/beta from ckpt var fpn_cells/cell_0/fnode3/resample_0_0_8/bn/beta\\n\",\n            \"I0601 05:39:09.685372 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode3/resample_0_0_8/bn/moving_mean from ckpt var fpn_cells/cell_0/fnode3/resample_0_0_8/bn/moving_mean\\n\",\n            \"I0601 05:39:09.685436 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode3/resample_0_0_8/bn/moving_variance from ckpt var fpn_cells/cell_0/fnode3/resample_0_0_8/bn/moving_variance\\n\",\n            \"I0601 05:39:09.685500 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode3/WSM from ckpt var fpn_cells/cell_0/fnode3/WSM\\n\",\n            \"I0601 05:39:09.685568 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode3/WSM_1 from ckpt var fpn_cells/cell_0/fnode3/WSM_1\\n\",\n            \"I0601 05:39:09.685638 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode3/op_after_combine8/conv/depthwise_kernel from ckpt var fpn_cells/cell_0/fnode3/op_after_combine8/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.685704 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode3/op_after_combine8/conv/pointwise_kernel from ckpt var fpn_cells/cell_0/fnode3/op_after_combine8/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.685769 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode3/op_after_combine8/conv/bias from ckpt var fpn_cells/cell_0/fnode3/op_after_combine8/conv/bias\\n\",\n            \"I0601 05:39:09.685832 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode3/op_after_combine8/bn/gamma from ckpt var fpn_cells/cell_0/fnode3/op_after_combine8/bn/gamma\\n\",\n            \"I0601 05:39:09.685894 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode3/op_after_combine8/bn/beta from ckpt var fpn_cells/cell_0/fnode3/op_after_combine8/bn/beta\\n\",\n            \"I0601 05:39:09.685956 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode3/op_after_combine8/bn/moving_mean from ckpt var fpn_cells/cell_0/fnode3/op_after_combine8/bn/moving_mean\\n\",\n            \"I0601 05:39:09.686019 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode3/op_after_combine8/bn/moving_variance from ckpt var fpn_cells/cell_0/fnode3/op_after_combine8/bn/moving_variance\\n\",\n            \"I0601 05:39:09.686087 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode4/resample_0_1_9/conv2d/kernel from ckpt var fpn_cells/cell_0/fnode4/resample_0_1_9/conv2d/kernel\\n\",\n            \"I0601 05:39:09.686151 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode4/resample_0_1_9/conv2d/bias from ckpt var fpn_cells/cell_0/fnode4/resample_0_1_9/conv2d/bias\\n\",\n            \"I0601 05:39:09.686214 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode4/resample_0_1_9/bn/gamma from ckpt var fpn_cells/cell_0/fnode4/resample_0_1_9/bn/gamma\\n\",\n            \"I0601 05:39:09.686291 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode4/resample_0_1_9/bn/beta from ckpt var fpn_cells/cell_0/fnode4/resample_0_1_9/bn/beta\\n\",\n            \"I0601 05:39:09.686357 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode4/resample_0_1_9/bn/moving_mean from ckpt var fpn_cells/cell_0/fnode4/resample_0_1_9/bn/moving_mean\\n\",\n            \"I0601 05:39:09.686423 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode4/resample_0_1_9/bn/moving_variance from ckpt var fpn_cells/cell_0/fnode4/resample_0_1_9/bn/moving_variance\\n\",\n            \"I0601 05:39:09.686486 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode4/WSM from ckpt var fpn_cells/cell_0/fnode4/WSM\\n\",\n            \"I0601 05:39:09.686549 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode4/WSM_1 from ckpt var fpn_cells/cell_0/fnode4/WSM_1\\n\",\n            \"I0601 05:39:09.686622 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode4/WSM_2 from ckpt var fpn_cells/cell_0/fnode4/WSM_2\\n\",\n            \"I0601 05:39:09.686692 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode4/op_after_combine9/conv/depthwise_kernel from ckpt var fpn_cells/cell_0/fnode4/op_after_combine9/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.686753 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode4/op_after_combine9/conv/pointwise_kernel from ckpt var fpn_cells/cell_0/fnode4/op_after_combine9/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.686812 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode4/op_after_combine9/conv/bias from ckpt var fpn_cells/cell_0/fnode4/op_after_combine9/conv/bias\\n\",\n            \"I0601 05:39:09.686872 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode4/op_after_combine9/bn/gamma from ckpt var fpn_cells/cell_0/fnode4/op_after_combine9/bn/gamma\\n\",\n            \"I0601 05:39:09.686930 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode4/op_after_combine9/bn/beta from ckpt var fpn_cells/cell_0/fnode4/op_after_combine9/bn/beta\\n\",\n            \"I0601 05:39:09.686988 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode4/op_after_combine9/bn/moving_mean from ckpt var fpn_cells/cell_0/fnode4/op_after_combine9/bn/moving_mean\\n\",\n            \"I0601 05:39:09.687047 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode4/op_after_combine9/bn/moving_variance from ckpt var fpn_cells/cell_0/fnode4/op_after_combine9/bn/moving_variance\\n\",\n            \"I0601 05:39:09.687106 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode5/resample_0_2_10/conv2d/kernel from ckpt var fpn_cells/cell_0/fnode5/resample_0_2_10/conv2d/kernel\\n\",\n            \"I0601 05:39:09.687166 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode5/resample_0_2_10/conv2d/bias from ckpt var fpn_cells/cell_0/fnode5/resample_0_2_10/conv2d/bias\\n\",\n            \"I0601 05:39:09.687225 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode5/resample_0_2_10/bn/gamma from ckpt var fpn_cells/cell_0/fnode5/resample_0_2_10/bn/gamma\\n\",\n            \"I0601 05:39:09.687295 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode5/resample_0_2_10/bn/beta from ckpt var fpn_cells/cell_0/fnode5/resample_0_2_10/bn/beta\\n\",\n            \"I0601 05:39:09.687374 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode5/resample_0_2_10/bn/moving_mean from ckpt var fpn_cells/cell_0/fnode5/resample_0_2_10/bn/moving_mean\\n\",\n            \"I0601 05:39:09.687442 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode5/resample_0_2_10/bn/moving_variance from ckpt var fpn_cells/cell_0/fnode5/resample_0_2_10/bn/moving_variance\\n\",\n            \"I0601 05:39:09.687508 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode5/WSM from ckpt var fpn_cells/cell_0/fnode5/WSM\\n\",\n            \"I0601 05:39:09.687572 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode5/WSM_1 from ckpt var fpn_cells/cell_0/fnode5/WSM_1\\n\",\n            \"I0601 05:39:09.687639 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode5/WSM_2 from ckpt var fpn_cells/cell_0/fnode5/WSM_2\\n\",\n            \"I0601 05:39:09.687706 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode5/op_after_combine10/conv/depthwise_kernel from ckpt var fpn_cells/cell_0/fnode5/op_after_combine10/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.687773 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode5/op_after_combine10/conv/pointwise_kernel from ckpt var fpn_cells/cell_0/fnode5/op_after_combine10/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.687840 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode5/op_after_combine10/conv/bias from ckpt var fpn_cells/cell_0/fnode5/op_after_combine10/conv/bias\\n\",\n            \"I0601 05:39:09.781375 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode5/op_after_combine10/bn/gamma from ckpt var fpn_cells/cell_0/fnode5/op_after_combine10/bn/gamma\\n\",\n            \"I0601 05:39:09.781520 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode5/op_after_combine10/bn/beta from ckpt var fpn_cells/cell_0/fnode5/op_after_combine10/bn/beta\\n\",\n            \"I0601 05:39:09.781668 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode5/op_after_combine10/bn/moving_mean from ckpt var fpn_cells/cell_0/fnode5/op_after_combine10/bn/moving_mean\\n\",\n            \"I0601 05:39:09.781780 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode5/op_after_combine10/bn/moving_variance from ckpt var fpn_cells/cell_0/fnode5/op_after_combine10/bn/moving_variance\\n\",\n            \"I0601 05:39:09.781880 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode6/WSM from ckpt var fpn_cells/cell_0/fnode6/WSM\\n\",\n            \"I0601 05:39:09.781976 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode6/WSM_1 from ckpt var fpn_cells/cell_0/fnode6/WSM_1\\n\",\n            \"I0601 05:39:09.782070 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode6/WSM_2 from ckpt var fpn_cells/cell_0/fnode6/WSM_2\\n\",\n            \"I0601 05:39:09.782170 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode6/op_after_combine11/conv/depthwise_kernel from ckpt var fpn_cells/cell_0/fnode6/op_after_combine11/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.782253 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode6/op_after_combine11/conv/pointwise_kernel from ckpt var fpn_cells/cell_0/fnode6/op_after_combine11/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.782346 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode6/op_after_combine11/conv/bias from ckpt var fpn_cells/cell_0/fnode6/op_after_combine11/conv/bias\\n\",\n            \"I0601 05:39:09.782412 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode6/op_after_combine11/bn/gamma from ckpt var fpn_cells/cell_0/fnode6/op_after_combine11/bn/gamma\\n\",\n            \"I0601 05:39:09.782474 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode6/op_after_combine11/bn/beta from ckpt var fpn_cells/cell_0/fnode6/op_after_combine11/bn/beta\\n\",\n            \"I0601 05:39:09.782537 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode6/op_after_combine11/bn/moving_mean from ckpt var fpn_cells/cell_0/fnode6/op_after_combine11/bn/moving_mean\\n\",\n            \"I0601 05:39:09.782600 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode6/op_after_combine11/bn/moving_variance from ckpt var fpn_cells/cell_0/fnode6/op_after_combine11/bn/moving_variance\\n\",\n            \"I0601 05:39:09.782676 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode7/WSM from ckpt var fpn_cells/cell_0/fnode7/WSM\\n\",\n            \"I0601 05:39:09.782739 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode7/WSM_1 from ckpt var fpn_cells/cell_0/fnode7/WSM_1\\n\",\n            \"I0601 05:39:09.782803 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode7/op_after_combine12/conv/depthwise_kernel from ckpt var fpn_cells/cell_0/fnode7/op_after_combine12/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.782864 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode7/op_after_combine12/conv/pointwise_kernel from ckpt var fpn_cells/cell_0/fnode7/op_after_combine12/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.782925 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode7/op_after_combine12/conv/bias from ckpt var fpn_cells/cell_0/fnode7/op_after_combine12/conv/bias\\n\",\n            \"I0601 05:39:09.782986 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode7/op_after_combine12/bn/gamma from ckpt var fpn_cells/cell_0/fnode7/op_after_combine12/bn/gamma\\n\",\n            \"I0601 05:39:09.783047 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode7/op_after_combine12/bn/beta from ckpt var fpn_cells/cell_0/fnode7/op_after_combine12/bn/beta\\n\",\n            \"I0601 05:39:09.783108 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode7/op_after_combine12/bn/moving_mean from ckpt var fpn_cells/cell_0/fnode7/op_after_combine12/bn/moving_mean\\n\",\n            \"I0601 05:39:09.783169 140233166403456 utils.py:108] Init fpn_cells/cell_0/fnode7/op_after_combine12/bn/moving_variance from ckpt var fpn_cells/cell_0/fnode7/op_after_combine12/bn/moving_variance\\n\",\n            \"I0601 05:39:09.783231 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode0/WSM from ckpt var fpn_cells/cell_1/fnode0/WSM\\n\",\n            \"I0601 05:39:09.783308 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode0/WSM_1 from ckpt var fpn_cells/cell_1/fnode0/WSM_1\\n\",\n            \"I0601 05:39:09.783375 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode0/op_after_combine5/conv/depthwise_kernel from ckpt var fpn_cells/cell_1/fnode0/op_after_combine5/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.783438 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode0/op_after_combine5/conv/pointwise_kernel from ckpt var fpn_cells/cell_1/fnode0/op_after_combine5/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.783498 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode0/op_after_combine5/conv/bias from ckpt var fpn_cells/cell_1/fnode0/op_after_combine5/conv/bias\\n\",\n            \"I0601 05:39:09.783558 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode0/op_after_combine5/bn/gamma from ckpt var fpn_cells/cell_1/fnode0/op_after_combine5/bn/gamma\\n\",\n            \"I0601 05:39:09.783617 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode0/op_after_combine5/bn/beta from ckpt var fpn_cells/cell_1/fnode0/op_after_combine5/bn/beta\\n\",\n            \"I0601 05:39:09.783686 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode0/op_after_combine5/bn/moving_mean from ckpt var fpn_cells/cell_1/fnode0/op_after_combine5/bn/moving_mean\\n\",\n            \"I0601 05:39:09.783748 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode0/op_after_combine5/bn/moving_variance from ckpt var fpn_cells/cell_1/fnode0/op_after_combine5/bn/moving_variance\\n\",\n            \"I0601 05:39:09.783813 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode1/WSM from ckpt var fpn_cells/cell_1/fnode1/WSM\\n\",\n            \"I0601 05:39:09.783874 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode1/WSM_1 from ckpt var fpn_cells/cell_1/fnode1/WSM_1\\n\",\n            \"I0601 05:39:09.783934 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode1/op_after_combine6/conv/depthwise_kernel from ckpt var fpn_cells/cell_1/fnode1/op_after_combine6/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.783993 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode1/op_after_combine6/conv/pointwise_kernel from ckpt var fpn_cells/cell_1/fnode1/op_after_combine6/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.784075 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode1/op_after_combine6/conv/bias from ckpt var fpn_cells/cell_1/fnode1/op_after_combine6/conv/bias\\n\",\n            \"I0601 05:39:09.784140 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode1/op_after_combine6/bn/gamma from ckpt var fpn_cells/cell_1/fnode1/op_after_combine6/bn/gamma\\n\",\n            \"I0601 05:39:09.784207 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode1/op_after_combine6/bn/beta from ckpt var fpn_cells/cell_1/fnode1/op_after_combine6/bn/beta\\n\",\n            \"I0601 05:39:09.784284 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode1/op_after_combine6/bn/moving_mean from ckpt var fpn_cells/cell_1/fnode1/op_after_combine6/bn/moving_mean\\n\",\n            \"I0601 05:39:09.784351 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode1/op_after_combine6/bn/moving_variance from ckpt var fpn_cells/cell_1/fnode1/op_after_combine6/bn/moving_variance\\n\",\n            \"I0601 05:39:09.784428 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode2/WSM from ckpt var fpn_cells/cell_1/fnode2/WSM\\n\",\n            \"I0601 05:39:09.784506 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode2/WSM_1 from ckpt var fpn_cells/cell_1/fnode2/WSM_1\\n\",\n            \"I0601 05:39:09.784582 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode2/op_after_combine7/conv/depthwise_kernel from ckpt var fpn_cells/cell_1/fnode2/op_after_combine7/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.784648 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode2/op_after_combine7/conv/pointwise_kernel from ckpt var fpn_cells/cell_1/fnode2/op_after_combine7/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.784710 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode2/op_after_combine7/conv/bias from ckpt var fpn_cells/cell_1/fnode2/op_after_combine7/conv/bias\\n\",\n            \"I0601 05:39:09.784774 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode2/op_after_combine7/bn/gamma from ckpt var fpn_cells/cell_1/fnode2/op_after_combine7/bn/gamma\\n\",\n            \"I0601 05:39:09.784835 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode2/op_after_combine7/bn/beta from ckpt var fpn_cells/cell_1/fnode2/op_after_combine7/bn/beta\\n\",\n            \"I0601 05:39:09.784895 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode2/op_after_combine7/bn/moving_mean from ckpt var fpn_cells/cell_1/fnode2/op_after_combine7/bn/moving_mean\\n\",\n            \"I0601 05:39:09.784955 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode2/op_after_combine7/bn/moving_variance from ckpt var fpn_cells/cell_1/fnode2/op_after_combine7/bn/moving_variance\\n\",\n            \"I0601 05:39:09.785014 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode3/WSM from ckpt var fpn_cells/cell_1/fnode3/WSM\\n\",\n            \"I0601 05:39:09.785072 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode3/WSM_1 from ckpt var fpn_cells/cell_1/fnode3/WSM_1\\n\",\n            \"I0601 05:39:09.785131 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode3/op_after_combine8/conv/depthwise_kernel from ckpt var fpn_cells/cell_1/fnode3/op_after_combine8/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.785192 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode3/op_after_combine8/conv/pointwise_kernel from ckpt var fpn_cells/cell_1/fnode3/op_after_combine8/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.785251 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode3/op_after_combine8/conv/bias from ckpt var fpn_cells/cell_1/fnode3/op_after_combine8/conv/bias\\n\",\n            \"I0601 05:39:09.785324 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode3/op_after_combine8/bn/gamma from ckpt var fpn_cells/cell_1/fnode3/op_after_combine8/bn/gamma\\n\",\n            \"I0601 05:39:09.785387 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode3/op_after_combine8/bn/beta from ckpt var fpn_cells/cell_1/fnode3/op_after_combine8/bn/beta\\n\",\n            \"I0601 05:39:09.785446 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode3/op_after_combine8/bn/moving_mean from ckpt var fpn_cells/cell_1/fnode3/op_after_combine8/bn/moving_mean\\n\",\n            \"I0601 05:39:09.785504 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode3/op_after_combine8/bn/moving_variance from ckpt var fpn_cells/cell_1/fnode3/op_after_combine8/bn/moving_variance\\n\",\n            \"I0601 05:39:09.785562 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode4/WSM from ckpt var fpn_cells/cell_1/fnode4/WSM\\n\",\n            \"I0601 05:39:09.785621 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode4/WSM_1 from ckpt var fpn_cells/cell_1/fnode4/WSM_1\\n\",\n            \"I0601 05:39:09.785688 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode4/WSM_2 from ckpt var fpn_cells/cell_1/fnode4/WSM_2\\n\",\n            \"I0601 05:39:09.785750 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode4/op_after_combine9/conv/depthwise_kernel from ckpt var fpn_cells/cell_1/fnode4/op_after_combine9/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.785813 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode4/op_after_combine9/conv/pointwise_kernel from ckpt var fpn_cells/cell_1/fnode4/op_after_combine9/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.785874 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode4/op_after_combine9/conv/bias from ckpt var fpn_cells/cell_1/fnode4/op_after_combine9/conv/bias\\n\",\n            \"I0601 05:39:09.785933 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode4/op_after_combine9/bn/gamma from ckpt var fpn_cells/cell_1/fnode4/op_after_combine9/bn/gamma\\n\",\n            \"I0601 05:39:09.785992 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode4/op_after_combine9/bn/beta from ckpt var fpn_cells/cell_1/fnode4/op_after_combine9/bn/beta\\n\",\n            \"I0601 05:39:09.786051 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode4/op_after_combine9/bn/moving_mean from ckpt var fpn_cells/cell_1/fnode4/op_after_combine9/bn/moving_mean\\n\",\n            \"I0601 05:39:09.786109 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode4/op_after_combine9/bn/moving_variance from ckpt var fpn_cells/cell_1/fnode4/op_after_combine9/bn/moving_variance\\n\",\n            \"I0601 05:39:09.786168 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode5/WSM from ckpt var fpn_cells/cell_1/fnode5/WSM\\n\",\n            \"I0601 05:39:09.786227 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode5/WSM_1 from ckpt var fpn_cells/cell_1/fnode5/WSM_1\\n\",\n            \"I0601 05:39:09.786297 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode5/WSM_2 from ckpt var fpn_cells/cell_1/fnode5/WSM_2\\n\",\n            \"I0601 05:39:09.786358 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode5/op_after_combine10/conv/depthwise_kernel from ckpt var fpn_cells/cell_1/fnode5/op_after_combine10/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.786420 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode5/op_after_combine10/conv/pointwise_kernel from ckpt var fpn_cells/cell_1/fnode5/op_after_combine10/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.786479 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode5/op_after_combine10/conv/bias from ckpt var fpn_cells/cell_1/fnode5/op_after_combine10/conv/bias\\n\",\n            \"I0601 05:39:09.786538 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode5/op_after_combine10/bn/gamma from ckpt var fpn_cells/cell_1/fnode5/op_after_combine10/bn/gamma\\n\",\n            \"I0601 05:39:09.786597 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode5/op_after_combine10/bn/beta from ckpt var fpn_cells/cell_1/fnode5/op_after_combine10/bn/beta\\n\",\n            \"I0601 05:39:09.786663 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode5/op_after_combine10/bn/moving_mean from ckpt var fpn_cells/cell_1/fnode5/op_after_combine10/bn/moving_mean\\n\",\n            \"I0601 05:39:09.786725 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode5/op_after_combine10/bn/moving_variance from ckpt var fpn_cells/cell_1/fnode5/op_after_combine10/bn/moving_variance\\n\",\n            \"I0601 05:39:09.786785 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode6/WSM from ckpt var fpn_cells/cell_1/fnode6/WSM\\n\",\n            \"I0601 05:39:09.786845 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode6/WSM_1 from ckpt var fpn_cells/cell_1/fnode6/WSM_1\\n\",\n            \"I0601 05:39:09.786904 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode6/WSM_2 from ckpt var fpn_cells/cell_1/fnode6/WSM_2\\n\",\n            \"I0601 05:39:09.786964 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode6/op_after_combine11/conv/depthwise_kernel from ckpt var fpn_cells/cell_1/fnode6/op_after_combine11/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.787023 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode6/op_after_combine11/conv/pointwise_kernel from ckpt var fpn_cells/cell_1/fnode6/op_after_combine11/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.787081 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode6/op_after_combine11/conv/bias from ckpt var fpn_cells/cell_1/fnode6/op_after_combine11/conv/bias\\n\",\n            \"I0601 05:39:09.787141 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode6/op_after_combine11/bn/gamma from ckpt var fpn_cells/cell_1/fnode6/op_after_combine11/bn/gamma\\n\",\n            \"I0601 05:39:09.787205 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode6/op_after_combine11/bn/beta from ckpt var fpn_cells/cell_1/fnode6/op_after_combine11/bn/beta\\n\",\n            \"I0601 05:39:09.787276 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode6/op_after_combine11/bn/moving_mean from ckpt var fpn_cells/cell_1/fnode6/op_after_combine11/bn/moving_mean\\n\",\n            \"I0601 05:39:09.787339 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode6/op_after_combine11/bn/moving_variance from ckpt var fpn_cells/cell_1/fnode6/op_after_combine11/bn/moving_variance\\n\",\n            \"I0601 05:39:09.787401 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode7/WSM from ckpt var fpn_cells/cell_1/fnode7/WSM\\n\",\n            \"I0601 05:39:09.787460 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode7/WSM_1 from ckpt var fpn_cells/cell_1/fnode7/WSM_1\\n\",\n            \"I0601 05:39:09.787520 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode7/op_after_combine12/conv/depthwise_kernel from ckpt var fpn_cells/cell_1/fnode7/op_after_combine12/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.787579 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode7/op_after_combine12/conv/pointwise_kernel from ckpt var fpn_cells/cell_1/fnode7/op_after_combine12/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.787642 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode7/op_after_combine12/conv/bias from ckpt var fpn_cells/cell_1/fnode7/op_after_combine12/conv/bias\\n\",\n            \"I0601 05:39:09.787704 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode7/op_after_combine12/bn/gamma from ckpt var fpn_cells/cell_1/fnode7/op_after_combine12/bn/gamma\\n\",\n            \"I0601 05:39:09.787765 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode7/op_after_combine12/bn/beta from ckpt var fpn_cells/cell_1/fnode7/op_after_combine12/bn/beta\\n\",\n            \"I0601 05:39:09.787825 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode7/op_after_combine12/bn/moving_mean from ckpt var fpn_cells/cell_1/fnode7/op_after_combine12/bn/moving_mean\\n\",\n            \"I0601 05:39:09.787887 140233166403456 utils.py:108] Init fpn_cells/cell_1/fnode7/op_after_combine12/bn/moving_variance from ckpt var fpn_cells/cell_1/fnode7/op_after_combine12/bn/moving_variance\\n\",\n            \"I0601 05:39:09.787950 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode0/WSM from ckpt var fpn_cells/cell_2/fnode0/WSM\\n\",\n            \"I0601 05:39:09.788008 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode0/WSM_1 from ckpt var fpn_cells/cell_2/fnode0/WSM_1\\n\",\n            \"I0601 05:39:09.788068 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode0/op_after_combine5/conv/depthwise_kernel from ckpt var fpn_cells/cell_2/fnode0/op_after_combine5/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.883184 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode0/op_after_combine5/conv/pointwise_kernel from ckpt var fpn_cells/cell_2/fnode0/op_after_combine5/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.883306 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode0/op_after_combine5/conv/bias from ckpt var fpn_cells/cell_2/fnode0/op_after_combine5/conv/bias\\n\",\n            \"I0601 05:39:09.883397 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode0/op_after_combine5/bn/gamma from ckpt var fpn_cells/cell_2/fnode0/op_after_combine5/bn/gamma\\n\",\n            \"I0601 05:39:09.883474 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode0/op_after_combine5/bn/beta from ckpt var fpn_cells/cell_2/fnode0/op_after_combine5/bn/beta\\n\",\n            \"I0601 05:39:09.883541 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode0/op_after_combine5/bn/moving_mean from ckpt var fpn_cells/cell_2/fnode0/op_after_combine5/bn/moving_mean\\n\",\n            \"I0601 05:39:09.883614 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode0/op_after_combine5/bn/moving_variance from ckpt var fpn_cells/cell_2/fnode0/op_after_combine5/bn/moving_variance\\n\",\n            \"I0601 05:39:09.883682 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode1/WSM from ckpt var fpn_cells/cell_2/fnode1/WSM\\n\",\n            \"I0601 05:39:09.883745 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode1/WSM_1 from ckpt var fpn_cells/cell_2/fnode1/WSM_1\\n\",\n            \"I0601 05:39:09.883815 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode1/op_after_combine6/conv/depthwise_kernel from ckpt var fpn_cells/cell_2/fnode1/op_after_combine6/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.883885 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode1/op_after_combine6/conv/pointwise_kernel from ckpt var fpn_cells/cell_2/fnode1/op_after_combine6/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.883952 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode1/op_after_combine6/conv/bias from ckpt var fpn_cells/cell_2/fnode1/op_after_combine6/conv/bias\\n\",\n            \"I0601 05:39:09.884037 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode1/op_after_combine6/bn/gamma from ckpt var fpn_cells/cell_2/fnode1/op_after_combine6/bn/gamma\\n\",\n            \"I0601 05:39:09.884108 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode1/op_after_combine6/bn/beta from ckpt var fpn_cells/cell_2/fnode1/op_after_combine6/bn/beta\\n\",\n            \"I0601 05:39:09.884201 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode1/op_after_combine6/bn/moving_mean from ckpt var fpn_cells/cell_2/fnode1/op_after_combine6/bn/moving_mean\\n\",\n            \"I0601 05:39:09.884292 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode1/op_after_combine6/bn/moving_variance from ckpt var fpn_cells/cell_2/fnode1/op_after_combine6/bn/moving_variance\\n\",\n            \"I0601 05:39:09.884384 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode2/WSM from ckpt var fpn_cells/cell_2/fnode2/WSM\\n\",\n            \"I0601 05:39:09.884450 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode2/WSM_1 from ckpt var fpn_cells/cell_2/fnode2/WSM_1\\n\",\n            \"I0601 05:39:09.884507 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode2/op_after_combine7/conv/depthwise_kernel from ckpt var fpn_cells/cell_2/fnode2/op_after_combine7/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.884561 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode2/op_after_combine7/conv/pointwise_kernel from ckpt var fpn_cells/cell_2/fnode2/op_after_combine7/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.884613 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode2/op_after_combine7/conv/bias from ckpt var fpn_cells/cell_2/fnode2/op_after_combine7/conv/bias\\n\",\n            \"I0601 05:39:09.884668 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode2/op_after_combine7/bn/gamma from ckpt var fpn_cells/cell_2/fnode2/op_after_combine7/bn/gamma\\n\",\n            \"I0601 05:39:09.884725 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode2/op_after_combine7/bn/beta from ckpt var fpn_cells/cell_2/fnode2/op_after_combine7/bn/beta\\n\",\n            \"I0601 05:39:09.884793 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode2/op_after_combine7/bn/moving_mean from ckpt var fpn_cells/cell_2/fnode2/op_after_combine7/bn/moving_mean\\n\",\n            \"I0601 05:39:09.884858 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode2/op_after_combine7/bn/moving_variance from ckpt var fpn_cells/cell_2/fnode2/op_after_combine7/bn/moving_variance\\n\",\n            \"I0601 05:39:09.884924 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode3/WSM from ckpt var fpn_cells/cell_2/fnode3/WSM\\n\",\n            \"I0601 05:39:09.884983 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode3/WSM_1 from ckpt var fpn_cells/cell_2/fnode3/WSM_1\\n\",\n            \"I0601 05:39:09.885045 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode3/op_after_combine8/conv/depthwise_kernel from ckpt var fpn_cells/cell_2/fnode3/op_after_combine8/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.885109 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode3/op_after_combine8/conv/pointwise_kernel from ckpt var fpn_cells/cell_2/fnode3/op_after_combine8/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.885179 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode3/op_after_combine8/conv/bias from ckpt var fpn_cells/cell_2/fnode3/op_after_combine8/conv/bias\\n\",\n            \"I0601 05:39:09.885231 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode3/op_after_combine8/bn/gamma from ckpt var fpn_cells/cell_2/fnode3/op_after_combine8/bn/gamma\\n\",\n            \"I0601 05:39:09.885309 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode3/op_after_combine8/bn/beta from ckpt var fpn_cells/cell_2/fnode3/op_after_combine8/bn/beta\\n\",\n            \"I0601 05:39:09.885375 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode3/op_after_combine8/bn/moving_mean from ckpt var fpn_cells/cell_2/fnode3/op_after_combine8/bn/moving_mean\\n\",\n            \"I0601 05:39:09.885443 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode3/op_after_combine8/bn/moving_variance from ckpt var fpn_cells/cell_2/fnode3/op_after_combine8/bn/moving_variance\\n\",\n            \"I0601 05:39:09.885506 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode4/WSM from ckpt var fpn_cells/cell_2/fnode4/WSM\\n\",\n            \"I0601 05:39:09.885568 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode4/WSM_1 from ckpt var fpn_cells/cell_2/fnode4/WSM_1\\n\",\n            \"I0601 05:39:09.885658 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode4/WSM_2 from ckpt var fpn_cells/cell_2/fnode4/WSM_2\\n\",\n            \"I0601 05:39:09.885725 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode4/op_after_combine9/conv/depthwise_kernel from ckpt var fpn_cells/cell_2/fnode4/op_after_combine9/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.885786 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode4/op_after_combine9/conv/pointwise_kernel from ckpt var fpn_cells/cell_2/fnode4/op_after_combine9/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.885845 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode4/op_after_combine9/conv/bias from ckpt var fpn_cells/cell_2/fnode4/op_after_combine9/conv/bias\\n\",\n            \"I0601 05:39:09.885913 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode4/op_after_combine9/bn/gamma from ckpt var fpn_cells/cell_2/fnode4/op_after_combine9/bn/gamma\\n\",\n            \"I0601 05:39:09.885987 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode4/op_after_combine9/bn/beta from ckpt var fpn_cells/cell_2/fnode4/op_after_combine9/bn/beta\\n\",\n            \"I0601 05:39:09.886046 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode4/op_after_combine9/bn/moving_mean from ckpt var fpn_cells/cell_2/fnode4/op_after_combine9/bn/moving_mean\\n\",\n            \"I0601 05:39:09.886107 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode4/op_after_combine9/bn/moving_variance from ckpt var fpn_cells/cell_2/fnode4/op_after_combine9/bn/moving_variance\\n\",\n            \"I0601 05:39:09.886177 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode5/WSM from ckpt var fpn_cells/cell_2/fnode5/WSM\\n\",\n            \"I0601 05:39:09.886237 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode5/WSM_1 from ckpt var fpn_cells/cell_2/fnode5/WSM_1\\n\",\n            \"I0601 05:39:09.886321 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode5/WSM_2 from ckpt var fpn_cells/cell_2/fnode5/WSM_2\\n\",\n            \"I0601 05:39:09.886381 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode5/op_after_combine10/conv/depthwise_kernel from ckpt var fpn_cells/cell_2/fnode5/op_after_combine10/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.886441 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode5/op_after_combine10/conv/pointwise_kernel from ckpt var fpn_cells/cell_2/fnode5/op_after_combine10/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.886497 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode5/op_after_combine10/conv/bias from ckpt var fpn_cells/cell_2/fnode5/op_after_combine10/conv/bias\\n\",\n            \"I0601 05:39:09.886555 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode5/op_after_combine10/bn/gamma from ckpt var fpn_cells/cell_2/fnode5/op_after_combine10/bn/gamma\\n\",\n            \"I0601 05:39:09.886611 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode5/op_after_combine10/bn/beta from ckpt var fpn_cells/cell_2/fnode5/op_after_combine10/bn/beta\\n\",\n            \"I0601 05:39:09.886669 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode5/op_after_combine10/bn/moving_mean from ckpt var fpn_cells/cell_2/fnode5/op_after_combine10/bn/moving_mean\\n\",\n            \"I0601 05:39:09.886727 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode5/op_after_combine10/bn/moving_variance from ckpt var fpn_cells/cell_2/fnode5/op_after_combine10/bn/moving_variance\\n\",\n            \"I0601 05:39:09.886785 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode6/WSM from ckpt var fpn_cells/cell_2/fnode6/WSM\\n\",\n            \"I0601 05:39:09.886841 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode6/WSM_1 from ckpt var fpn_cells/cell_2/fnode6/WSM_1\\n\",\n            \"I0601 05:39:09.886898 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode6/WSM_2 from ckpt var fpn_cells/cell_2/fnode6/WSM_2\\n\",\n            \"I0601 05:39:09.886963 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode6/op_after_combine11/conv/depthwise_kernel from ckpt var fpn_cells/cell_2/fnode6/op_after_combine11/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.887020 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode6/op_after_combine11/conv/pointwise_kernel from ckpt var fpn_cells/cell_2/fnode6/op_after_combine11/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.887078 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode6/op_after_combine11/conv/bias from ckpt var fpn_cells/cell_2/fnode6/op_after_combine11/conv/bias\\n\",\n            \"I0601 05:39:09.887142 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode6/op_after_combine11/bn/gamma from ckpt var fpn_cells/cell_2/fnode6/op_after_combine11/bn/gamma\\n\",\n            \"I0601 05:39:09.887200 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode6/op_after_combine11/bn/beta from ckpt var fpn_cells/cell_2/fnode6/op_after_combine11/bn/beta\\n\",\n            \"I0601 05:39:09.887255 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode6/op_after_combine11/bn/moving_mean from ckpt var fpn_cells/cell_2/fnode6/op_after_combine11/bn/moving_mean\\n\",\n            \"I0601 05:39:09.887362 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode6/op_after_combine11/bn/moving_variance from ckpt var fpn_cells/cell_2/fnode6/op_after_combine11/bn/moving_variance\\n\",\n            \"I0601 05:39:09.887431 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode7/WSM from ckpt var fpn_cells/cell_2/fnode7/WSM\\n\",\n            \"I0601 05:39:09.887496 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode7/WSM_1 from ckpt var fpn_cells/cell_2/fnode7/WSM_1\\n\",\n            \"I0601 05:39:09.887563 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode7/op_after_combine12/conv/depthwise_kernel from ckpt var fpn_cells/cell_2/fnode7/op_after_combine12/conv/depthwise_kernel\\n\",\n            \"I0601 05:39:09.887628 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode7/op_after_combine12/conv/pointwise_kernel from ckpt var fpn_cells/cell_2/fnode7/op_after_combine12/conv/pointwise_kernel\\n\",\n            \"I0601 05:39:09.887694 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode7/op_after_combine12/conv/bias from ckpt var fpn_cells/cell_2/fnode7/op_after_combine12/conv/bias\\n\",\n            \"I0601 05:39:09.887764 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode7/op_after_combine12/bn/gamma from ckpt var fpn_cells/cell_2/fnode7/op_after_combine12/bn/gamma\\n\",\n            \"I0601 05:39:09.887830 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode7/op_after_combine12/bn/beta from ckpt var fpn_cells/cell_2/fnode7/op_after_combine12/bn/beta\\n\",\n            \"I0601 05:39:09.887897 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode7/op_after_combine12/bn/moving_mean from ckpt var fpn_cells/cell_2/fnode7/op_after_combine12/bn/moving_mean\\n\",\n            \"I0601 05:39:09.887961 140233166403456 utils.py:108] Init fpn_cells/cell_2/fnode7/op_after_combine12/bn/moving_variance from ckpt var fpn_cells/cell_2/fnode7/op_after_combine12/bn/moving_variance\\n\",\n            \"I0601 05:39:09.888028 140233166403456 utils.py:108] Init class_net/class-0/depthwise_kernel from ckpt var class_net/class-0/depthwise_kernel\\n\",\n            \"I0601 05:39:09.888116 140233166403456 utils.py:108] Init class_net/class-0/pointwise_kernel from ckpt var class_net/class-0/pointwise_kernel\\n\",\n            \"I0601 05:39:09.888230 140233166403456 utils.py:108] Init class_net/class-0/bias from ckpt var class_net/class-0/bias\\n\",\n            \"I0601 05:39:09.888341 140233166403456 utils.py:108] Init class_net/class-0-bn-3/gamma from ckpt var class_net/class-0-bn-3/gamma\\n\",\n            \"I0601 05:39:09.888427 140233166403456 utils.py:108] Init class_net/class-0-bn-3/beta from ckpt var class_net/class-0-bn-3/beta\\n\",\n            \"I0601 05:39:09.888499 140233166403456 utils.py:108] Init class_net/class-0-bn-3/moving_mean from ckpt var class_net/class-0-bn-3/moving_mean\\n\",\n            \"I0601 05:39:09.888574 140233166403456 utils.py:108] Init class_net/class-0-bn-3/moving_variance from ckpt var class_net/class-0-bn-3/moving_variance\\n\",\n            \"I0601 05:39:09.888659 140233166403456 utils.py:108] Init class_net/class-1/depthwise_kernel from ckpt var class_net/class-1/depthwise_kernel\\n\",\n            \"I0601 05:39:09.888747 140233166403456 utils.py:108] Init class_net/class-1/pointwise_kernel from ckpt var class_net/class-1/pointwise_kernel\\n\",\n            \"I0601 05:39:09.888813 140233166403456 utils.py:108] Init class_net/class-1/bias from ckpt var class_net/class-1/bias\\n\",\n            \"I0601 05:39:09.888877 140233166403456 utils.py:108] Init class_net/class-1-bn-3/gamma from ckpt var class_net/class-1-bn-3/gamma\\n\",\n            \"I0601 05:39:09.888990 140233166403456 utils.py:108] Init class_net/class-1-bn-3/beta from ckpt var class_net/class-1-bn-3/beta\\n\",\n            \"I0601 05:39:09.889225 140233166403456 utils.py:108] Init class_net/class-1-bn-3/moving_mean from ckpt var class_net/class-1-bn-3/moving_mean\\n\",\n            \"I0601 05:39:09.889338 140233166403456 utils.py:108] Init class_net/class-1-bn-3/moving_variance from ckpt var class_net/class-1-bn-3/moving_variance\\n\",\n            \"I0601 05:39:09.889415 140233166403456 utils.py:108] Init class_net/class-2/depthwise_kernel from ckpt var class_net/class-2/depthwise_kernel\\n\",\n            \"I0601 05:39:09.889478 140233166403456 utils.py:108] Init class_net/class-2/pointwise_kernel from ckpt var class_net/class-2/pointwise_kernel\\n\",\n            \"I0601 05:39:09.889540 140233166403456 utils.py:108] Init class_net/class-2/bias from ckpt var class_net/class-2/bias\\n\",\n            \"I0601 05:39:09.889602 140233166403456 utils.py:108] Init class_net/class-2-bn-3/gamma from ckpt var class_net/class-2-bn-3/gamma\\n\",\n            \"I0601 05:39:09.889662 140233166403456 utils.py:108] Init class_net/class-2-bn-3/beta from ckpt var class_net/class-2-bn-3/beta\\n\",\n            \"I0601 05:39:09.889726 140233166403456 utils.py:108] Init class_net/class-2-bn-3/moving_mean from ckpt var class_net/class-2-bn-3/moving_mean\\n\",\n            \"I0601 05:39:09.889790 140233166403456 utils.py:108] Init class_net/class-2-bn-3/moving_variance from ckpt var class_net/class-2-bn-3/moving_variance\\n\",\n            \"I0601 05:39:09.890433 140233166403456 utils.py:91] skip class_net/class-predict/depthwise_kernel -- excluded by .*/class-predict/.*\\n\",\n            \"I0601 05:39:09.890554 140233166403456 utils.py:91] skip class_net/class-predict/pointwise_kernel -- excluded by .*/class-predict/.*\\n\",\n            \"I0601 05:39:09.890639 140233166403456 utils.py:91] skip class_net/class-predict/bias -- excluded by .*/class-predict/.*\\n\",\n            \"I0601 05:39:09.890724 140233166403456 utils.py:108] Init class_net/class-0-bn-4/gamma from ckpt var class_net/class-0-bn-4/gamma\\n\",\n            \"I0601 05:39:09.890801 140233166403456 utils.py:108] Init class_net/class-0-bn-4/beta from ckpt var class_net/class-0-bn-4/beta\\n\",\n            \"I0601 05:39:09.890890 140233166403456 utils.py:108] Init class_net/class-0-bn-4/moving_mean from ckpt var class_net/class-0-bn-4/moving_mean\\n\",\n            \"I0601 05:39:09.890956 140233166403456 utils.py:108] Init class_net/class-0-bn-4/moving_variance from ckpt var class_net/class-0-bn-4/moving_variance\\n\",\n            \"I0601 05:39:09.891025 140233166403456 utils.py:108] Init class_net/class-1-bn-4/gamma from ckpt var class_net/class-1-bn-4/gamma\\n\",\n            \"I0601 05:39:09.891092 140233166403456 utils.py:108] Init class_net/class-1-bn-4/beta from ckpt var class_net/class-1-bn-4/beta\\n\",\n            \"I0601 05:39:09.891165 140233166403456 utils.py:108] Init class_net/class-1-bn-4/moving_mean from ckpt var class_net/class-1-bn-4/moving_mean\\n\",\n            \"I0601 05:39:09.891230 140233166403456 utils.py:108] Init class_net/class-1-bn-4/moving_variance from ckpt var class_net/class-1-bn-4/moving_variance\\n\",\n            \"I0601 05:39:09.983481 140233166403456 utils.py:108] Init class_net/class-2-bn-4/gamma from ckpt var class_net/class-2-bn-4/gamma\\n\",\n            \"I0601 05:39:09.983674 140233166403456 utils.py:108] Init class_net/class-2-bn-4/beta from ckpt var class_net/class-2-bn-4/beta\\n\",\n            \"I0601 05:39:09.983794 140233166403456 utils.py:108] Init class_net/class-2-bn-4/moving_mean from ckpt var class_net/class-2-bn-4/moving_mean\\n\",\n            \"I0601 05:39:09.983893 140233166403456 utils.py:108] Init class_net/class-2-bn-4/moving_variance from ckpt var class_net/class-2-bn-4/moving_variance\\n\",\n            \"I0601 05:39:09.983987 140233166403456 utils.py:108] Init class_net/class-0-bn-5/gamma from ckpt var class_net/class-0-bn-5/gamma\\n\",\n            \"I0601 05:39:09.984071 140233166403456 utils.py:108] Init class_net/class-0-bn-5/beta from ckpt var class_net/class-0-bn-5/beta\\n\",\n            \"I0601 05:39:09.984158 140233166403456 utils.py:108] Init class_net/class-0-bn-5/moving_mean from ckpt var class_net/class-0-bn-5/moving_mean\\n\",\n            \"I0601 05:39:09.984228 140233166403456 utils.py:108] Init class_net/class-0-bn-5/moving_variance from ckpt var class_net/class-0-bn-5/moving_variance\\n\",\n            \"I0601 05:39:09.984346 140233166403456 utils.py:108] Init class_net/class-1-bn-5/gamma from ckpt var class_net/class-1-bn-5/gamma\\n\",\n            \"I0601 05:39:09.984415 140233166403456 utils.py:108] Init class_net/class-1-bn-5/beta from ckpt var class_net/class-1-bn-5/beta\\n\",\n            \"I0601 05:39:09.984481 140233166403456 utils.py:108] Init class_net/class-1-bn-5/moving_mean from ckpt var class_net/class-1-bn-5/moving_mean\\n\",\n            \"I0601 05:39:09.984550 140233166403456 utils.py:108] Init class_net/class-1-bn-5/moving_variance from ckpt var class_net/class-1-bn-5/moving_variance\\n\",\n            \"I0601 05:39:09.984613 140233166403456 utils.py:108] Init class_net/class-2-bn-5/gamma from ckpt var class_net/class-2-bn-5/gamma\\n\",\n            \"I0601 05:39:09.984683 140233166403456 utils.py:108] Init class_net/class-2-bn-5/beta from ckpt var class_net/class-2-bn-5/beta\\n\",\n            \"I0601 05:39:09.984744 140233166403456 utils.py:108] Init class_net/class-2-bn-5/moving_mean from ckpt var class_net/class-2-bn-5/moving_mean\\n\",\n            \"I0601 05:39:09.984804 140233166403456 utils.py:108] Init class_net/class-2-bn-5/moving_variance from ckpt var class_net/class-2-bn-5/moving_variance\\n\",\n            \"I0601 05:39:09.984863 140233166403456 utils.py:108] Init class_net/class-0-bn-6/gamma from ckpt var class_net/class-0-bn-6/gamma\\n\",\n            \"I0601 05:39:09.984923 140233166403456 utils.py:108] Init class_net/class-0-bn-6/beta from ckpt var class_net/class-0-bn-6/beta\\n\",\n            \"I0601 05:39:09.984981 140233166403456 utils.py:108] Init class_net/class-0-bn-6/moving_mean from ckpt var class_net/class-0-bn-6/moving_mean\\n\",\n            \"I0601 05:39:09.985039 140233166403456 utils.py:108] Init class_net/class-0-bn-6/moving_variance from ckpt var class_net/class-0-bn-6/moving_variance\\n\",\n            \"I0601 05:39:09.985130 140233166403456 utils.py:108] Init class_net/class-1-bn-6/gamma from ckpt var class_net/class-1-bn-6/gamma\\n\",\n            \"I0601 05:39:09.985195 140233166403456 utils.py:108] Init class_net/class-1-bn-6/beta from ckpt var class_net/class-1-bn-6/beta\\n\",\n            \"I0601 05:39:09.985259 140233166403456 utils.py:108] Init class_net/class-1-bn-6/moving_mean from ckpt var class_net/class-1-bn-6/moving_mean\\n\",\n            \"I0601 05:39:09.985340 140233166403456 utils.py:108] Init class_net/class-1-bn-6/moving_variance from ckpt var class_net/class-1-bn-6/moving_variance\\n\",\n            \"I0601 05:39:09.985402 140233166403456 utils.py:108] Init class_net/class-2-bn-6/gamma from ckpt var class_net/class-2-bn-6/gamma\\n\",\n            \"I0601 05:39:09.985461 140233166403456 utils.py:108] Init class_net/class-2-bn-6/beta from ckpt var class_net/class-2-bn-6/beta\\n\",\n            \"I0601 05:39:09.985521 140233166403456 utils.py:108] Init class_net/class-2-bn-6/moving_mean from ckpt var class_net/class-2-bn-6/moving_mean\\n\",\n            \"I0601 05:39:09.985580 140233166403456 utils.py:108] Init class_net/class-2-bn-6/moving_variance from ckpt var class_net/class-2-bn-6/moving_variance\\n\",\n            \"I0601 05:39:09.985645 140233166403456 utils.py:108] Init class_net/class-0-bn-7/gamma from ckpt var class_net/class-0-bn-7/gamma\\n\",\n            \"I0601 05:39:09.985706 140233166403456 utils.py:108] Init class_net/class-0-bn-7/beta from ckpt var class_net/class-0-bn-7/beta\\n\",\n            \"I0601 05:39:09.985767 140233166403456 utils.py:108] Init class_net/class-0-bn-7/moving_mean from ckpt var class_net/class-0-bn-7/moving_mean\\n\",\n            \"I0601 05:39:09.985827 140233166403456 utils.py:108] Init class_net/class-0-bn-7/moving_variance from ckpt var class_net/class-0-bn-7/moving_variance\\n\",\n            \"I0601 05:39:09.985886 140233166403456 utils.py:108] Init class_net/class-1-bn-7/gamma from ckpt var class_net/class-1-bn-7/gamma\\n\",\n            \"I0601 05:39:09.985946 140233166403456 utils.py:108] Init class_net/class-1-bn-7/beta from ckpt var class_net/class-1-bn-7/beta\\n\",\n            \"I0601 05:39:09.986003 140233166403456 utils.py:108] Init class_net/class-1-bn-7/moving_mean from ckpt var class_net/class-1-bn-7/moving_mean\\n\",\n            \"I0601 05:39:09.986062 140233166403456 utils.py:108] Init class_net/class-1-bn-7/moving_variance from ckpt var class_net/class-1-bn-7/moving_variance\\n\",\n            \"I0601 05:39:09.986149 140233166403456 utils.py:108] Init class_net/class-2-bn-7/gamma from ckpt var class_net/class-2-bn-7/gamma\\n\",\n            \"I0601 05:39:09.986224 140233166403456 utils.py:108] Init class_net/class-2-bn-7/beta from ckpt var class_net/class-2-bn-7/beta\\n\",\n            \"I0601 05:39:09.986313 140233166403456 utils.py:108] Init class_net/class-2-bn-7/moving_mean from ckpt var class_net/class-2-bn-7/moving_mean\\n\",\n            \"I0601 05:39:09.986379 140233166403456 utils.py:108] Init class_net/class-2-bn-7/moving_variance from ckpt var class_net/class-2-bn-7/moving_variance\\n\",\n            \"I0601 05:39:09.986446 140233166403456 utils.py:108] Init box_net/box-0/depthwise_kernel from ckpt var box_net/box-0/depthwise_kernel\\n\",\n            \"I0601 05:39:09.986513 140233166403456 utils.py:108] Init box_net/box-0/pointwise_kernel from ckpt var box_net/box-0/pointwise_kernel\\n\",\n            \"I0601 05:39:09.986578 140233166403456 utils.py:108] Init box_net/box-0/bias from ckpt var box_net/box-0/bias\\n\",\n            \"I0601 05:39:09.986651 140233166403456 utils.py:108] Init box_net/box-0-bn-3/gamma from ckpt var box_net/box-0-bn-3/gamma\\n\",\n            \"I0601 05:39:09.986748 140233166403456 utils.py:108] Init box_net/box-0-bn-3/beta from ckpt var box_net/box-0-bn-3/beta\\n\",\n            \"I0601 05:39:09.986815 140233166403456 utils.py:108] Init box_net/box-0-bn-3/moving_mean from ckpt var box_net/box-0-bn-3/moving_mean\\n\",\n            \"I0601 05:39:09.986881 140233166403456 utils.py:108] Init box_net/box-0-bn-3/moving_variance from ckpt var box_net/box-0-bn-3/moving_variance\\n\",\n            \"I0601 05:39:09.986946 140233166403456 utils.py:108] Init box_net/box-1/depthwise_kernel from ckpt var box_net/box-1/depthwise_kernel\\n\",\n            \"I0601 05:39:09.987009 140233166403456 utils.py:108] Init box_net/box-1/pointwise_kernel from ckpt var box_net/box-1/pointwise_kernel\\n\",\n            \"I0601 05:39:09.987073 140233166403456 utils.py:108] Init box_net/box-1/bias from ckpt var box_net/box-1/bias\\n\",\n            \"I0601 05:39:09.987136 140233166403456 utils.py:108] Init box_net/box-1-bn-3/gamma from ckpt var box_net/box-1-bn-3/gamma\\n\",\n            \"I0601 05:39:09.987200 140233166403456 utils.py:108] Init box_net/box-1-bn-3/beta from ckpt var box_net/box-1-bn-3/beta\\n\",\n            \"I0601 05:39:09.987262 140233166403456 utils.py:108] Init box_net/box-1-bn-3/moving_mean from ckpt var box_net/box-1-bn-3/moving_mean\\n\",\n            \"I0601 05:39:09.987340 140233166403456 utils.py:108] Init box_net/box-1-bn-3/moving_variance from ckpt var box_net/box-1-bn-3/moving_variance\\n\",\n            \"I0601 05:39:09.987406 140233166403456 utils.py:108] Init box_net/box-2/depthwise_kernel from ckpt var box_net/box-2/depthwise_kernel\\n\",\n            \"I0601 05:39:09.987472 140233166403456 utils.py:108] Init box_net/box-2/pointwise_kernel from ckpt var box_net/box-2/pointwise_kernel\\n\",\n            \"I0601 05:39:09.987536 140233166403456 utils.py:108] Init box_net/box-2/bias from ckpt var box_net/box-2/bias\\n\",\n            \"I0601 05:39:09.987598 140233166403456 utils.py:108] Init box_net/box-2-bn-3/gamma from ckpt var box_net/box-2-bn-3/gamma\\n\",\n            \"I0601 05:39:09.987668 140233166403456 utils.py:108] Init box_net/box-2-bn-3/beta from ckpt var box_net/box-2-bn-3/beta\\n\",\n            \"I0601 05:39:09.987733 140233166403456 utils.py:108] Init box_net/box-2-bn-3/moving_mean from ckpt var box_net/box-2-bn-3/moving_mean\\n\",\n            \"I0601 05:39:09.987796 140233166403456 utils.py:108] Init box_net/box-2-bn-3/moving_variance from ckpt var box_net/box-2-bn-3/moving_variance\\n\",\n            \"I0601 05:39:09.987859 140233166403456 utils.py:108] Init box_net/box-predict/depthwise_kernel from ckpt var box_net/box-predict/depthwise_kernel\\n\",\n            \"I0601 05:39:09.987922 140233166403456 utils.py:108] Init box_net/box-predict/pointwise_kernel from ckpt var box_net/box-predict/pointwise_kernel\\n\",\n            \"I0601 05:39:09.987985 140233166403456 utils.py:108] Init box_net/box-predict/bias from ckpt var box_net/box-predict/bias\\n\",\n            \"I0601 05:39:09.988047 140233166403456 utils.py:108] Init box_net/box-0-bn-4/gamma from ckpt var box_net/box-0-bn-4/gamma\\n\",\n            \"I0601 05:39:09.988109 140233166403456 utils.py:108] Init box_net/box-0-bn-4/beta from ckpt var box_net/box-0-bn-4/beta\\n\",\n            \"I0601 05:39:09.988172 140233166403456 utils.py:108] Init box_net/box-0-bn-4/moving_mean from ckpt var box_net/box-0-bn-4/moving_mean\\n\",\n            \"I0601 05:39:09.988252 140233166403456 utils.py:108] Init box_net/box-0-bn-4/moving_variance from ckpt var box_net/box-0-bn-4/moving_variance\\n\",\n            \"I0601 05:39:09.988334 140233166403456 utils.py:108] Init box_net/box-1-bn-4/gamma from ckpt var box_net/box-1-bn-4/gamma\\n\",\n            \"I0601 05:39:09.988414 140233166403456 utils.py:108] Init box_net/box-1-bn-4/beta from ckpt var box_net/box-1-bn-4/beta\\n\",\n            \"I0601 05:39:09.988478 140233166403456 utils.py:108] Init box_net/box-1-bn-4/moving_mean from ckpt var box_net/box-1-bn-4/moving_mean\\n\",\n            \"I0601 05:39:09.988542 140233166403456 utils.py:108] Init box_net/box-1-bn-4/moving_variance from ckpt var box_net/box-1-bn-4/moving_variance\\n\",\n            \"I0601 05:39:09.988607 140233166403456 utils.py:108] Init box_net/box-2-bn-4/gamma from ckpt var box_net/box-2-bn-4/gamma\\n\",\n            \"I0601 05:39:09.988676 140233166403456 utils.py:108] Init box_net/box-2-bn-4/beta from ckpt var box_net/box-2-bn-4/beta\\n\",\n            \"I0601 05:39:09.988740 140233166403456 utils.py:108] Init box_net/box-2-bn-4/moving_mean from ckpt var box_net/box-2-bn-4/moving_mean\\n\",\n            \"I0601 05:39:09.988821 140233166403456 utils.py:108] Init box_net/box-2-bn-4/moving_variance from ckpt var box_net/box-2-bn-4/moving_variance\\n\",\n            \"I0601 05:39:09.988892 140233166403456 utils.py:108] Init box_net/box-0-bn-5/gamma from ckpt var box_net/box-0-bn-5/gamma\\n\",\n            \"I0601 05:39:09.988961 140233166403456 utils.py:108] Init box_net/box-0-bn-5/beta from ckpt var box_net/box-0-bn-5/beta\\n\",\n            \"I0601 05:39:09.989033 140233166403456 utils.py:108] Init box_net/box-0-bn-5/moving_mean from ckpt var box_net/box-0-bn-5/moving_mean\\n\",\n            \"I0601 05:39:09.989109 140233166403456 utils.py:108] Init box_net/box-0-bn-5/moving_variance from ckpt var box_net/box-0-bn-5/moving_variance\\n\",\n            \"I0601 05:39:09.989176 140233166403456 utils.py:108] Init box_net/box-1-bn-5/gamma from ckpt var box_net/box-1-bn-5/gamma\\n\",\n            \"I0601 05:39:09.989240 140233166403456 utils.py:108] Init box_net/box-1-bn-5/beta from ckpt var box_net/box-1-bn-5/beta\\n\",\n            \"I0601 05:39:09.989319 140233166403456 utils.py:108] Init box_net/box-1-bn-5/moving_mean from ckpt var box_net/box-1-bn-5/moving_mean\\n\",\n            \"I0601 05:39:09.989387 140233166403456 utils.py:108] Init box_net/box-1-bn-5/moving_variance from ckpt var box_net/box-1-bn-5/moving_variance\\n\",\n            \"I0601 05:39:09.989453 140233166403456 utils.py:108] Init box_net/box-2-bn-5/gamma from ckpt var box_net/box-2-bn-5/gamma\\n\",\n            \"I0601 05:39:09.989518 140233166403456 utils.py:108] Init box_net/box-2-bn-5/beta from ckpt var box_net/box-2-bn-5/beta\\n\",\n            \"I0601 05:39:09.989582 140233166403456 utils.py:108] Init box_net/box-2-bn-5/moving_mean from ckpt var box_net/box-2-bn-5/moving_mean\\n\",\n            \"I0601 05:39:09.989650 140233166403456 utils.py:108] Init box_net/box-2-bn-5/moving_variance from ckpt var box_net/box-2-bn-5/moving_variance\\n\",\n            \"I0601 05:39:09.989731 140233166403456 utils.py:108] Init box_net/box-0-bn-6/gamma from ckpt var box_net/box-0-bn-6/gamma\\n\",\n            \"I0601 05:39:09.989798 140233166403456 utils.py:108] Init box_net/box-0-bn-6/beta from ckpt var box_net/box-0-bn-6/beta\\n\",\n            \"I0601 05:39:09.989864 140233166403456 utils.py:108] Init box_net/box-0-bn-6/moving_mean from ckpt var box_net/box-0-bn-6/moving_mean\\n\",\n            \"I0601 05:39:09.989939 140233166403456 utils.py:108] Init box_net/box-0-bn-6/moving_variance from ckpt var box_net/box-0-bn-6/moving_variance\\n\",\n            \"I0601 05:39:09.990000 140233166403456 utils.py:108] Init box_net/box-1-bn-6/gamma from ckpt var box_net/box-1-bn-6/gamma\\n\",\n            \"I0601 05:39:09.990062 140233166403456 utils.py:108] Init box_net/box-1-bn-6/beta from ckpt var box_net/box-1-bn-6/beta\\n\",\n            \"I0601 05:39:09.990124 140233166403456 utils.py:108] Init box_net/box-1-bn-6/moving_mean from ckpt var box_net/box-1-bn-6/moving_mean\\n\",\n            \"I0601 05:39:09.990205 140233166403456 utils.py:108] Init box_net/box-1-bn-6/moving_variance from ckpt var box_net/box-1-bn-6/moving_variance\\n\",\n            \"I0601 05:39:09.990283 140233166403456 utils.py:108] Init box_net/box-2-bn-6/gamma from ckpt var box_net/box-2-bn-6/gamma\\n\",\n            \"I0601 05:39:09.990348 140233166403456 utils.py:108] Init box_net/box-2-bn-6/beta from ckpt var box_net/box-2-bn-6/beta\\n\",\n            \"I0601 05:39:09.990414 140233166403456 utils.py:108] Init box_net/box-2-bn-6/moving_mean from ckpt var box_net/box-2-bn-6/moving_mean\\n\",\n            \"I0601 05:39:09.990480 140233166403456 utils.py:108] Init box_net/box-2-bn-6/moving_variance from ckpt var box_net/box-2-bn-6/moving_variance\\n\",\n            \"I0601 05:39:09.990545 140233166403456 utils.py:108] Init box_net/box-0-bn-7/gamma from ckpt var box_net/box-0-bn-7/gamma\\n\",\n            \"I0601 05:39:09.990610 140233166403456 utils.py:108] Init box_net/box-0-bn-7/beta from ckpt var box_net/box-0-bn-7/beta\\n\",\n            \"I0601 05:39:09.990680 140233166403456 utils.py:108] Init box_net/box-0-bn-7/moving_mean from ckpt var box_net/box-0-bn-7/moving_mean\\n\",\n            \"I0601 05:39:09.990744 140233166403456 utils.py:108] Init box_net/box-0-bn-7/moving_variance from ckpt var box_net/box-0-bn-7/moving_variance\\n\",\n            \"I0601 05:39:09.990807 140233166403456 utils.py:108] Init box_net/box-1-bn-7/gamma from ckpt var box_net/box-1-bn-7/gamma\\n\",\n            \"I0601 05:39:09.990874 140233166403456 utils.py:108] Init box_net/box-1-bn-7/beta from ckpt var box_net/box-1-bn-7/beta\\n\",\n            \"I0601 05:39:09.990938 140233166403456 utils.py:108] Init box_net/box-1-bn-7/moving_mean from ckpt var box_net/box-1-bn-7/moving_mean\\n\",\n            \"I0601 05:39:09.991002 140233166403456 utils.py:108] Init box_net/box-1-bn-7/moving_variance from ckpt var box_net/box-1-bn-7/moving_variance\\n\",\n            \"I0601 05:39:09.991075 140233166403456 utils.py:108] Init box_net/box-2-bn-7/gamma from ckpt var box_net/box-2-bn-7/gamma\\n\",\n            \"I0601 05:39:09.991136 140233166403456 utils.py:108] Init box_net/box-2-bn-7/beta from ckpt var box_net/box-2-bn-7/beta\\n\",\n            \"I0601 05:39:09.991196 140233166403456 utils.py:108] Init box_net/box-2-bn-7/moving_mean from ckpt var box_net/box-2-bn-7/moving_mean\\n\",\n            \"I0601 05:39:09.991255 140233166403456 utils.py:108] Init box_net/box-2-bn-7/moving_variance from ckpt var box_net/box-2-bn-7/moving_variance\\n\",\n            \"I0601 05:39:09.993984 140233166403456 utils.py:91] skip class_net/class-predict/depthwise_kernel/Momentum -- excluded by .*/class-predict/.*\\n\",\n            \"I0601 05:39:09.994096 140233166403456 utils.py:91] skip class_net/class-predict/pointwise_kernel/Momentum -- excluded by .*/class-predict/.*\\n\",\n            \"I0601 05:39:09.994165 140233166403456 utils.py:91] skip class_net/class-predict/bias/Momentum -- excluded by .*/class-predict/.*\\n\",\n            \"INFO:tensorflow:Done calling model_fn.\\n\",\n            \"I0601 05:39:11.603600 140233166403456 estimator.py:1171] Done calling model_fn.\\n\",\n            \"INFO:tensorflow:Create CheckpointSaverHook.\\n\",\n            \"I0601 05:39:11.604779 140233166403456 basic_session_run_hooks.py:546] Create CheckpointSaverHook.\\n\",\n            \"INFO:tensorflow:Graph was finalized.\\n\",\n            \"I0601 05:39:15.392178 140233166403456 monitored_session.py:246] Graph was finalized.\\n\",\n            \"2020-06-01 05:39:15.397315: I tensorflow/core/platform/profile_utils/cpu_utils.cc:102] CPU Frequency: 2300000000 Hz\\n\",\n            \"2020-06-01 05:39:15.397531: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x313f100 initialized for platform Host (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-06-01 05:39:15.397568: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Host, Default Version\\n\",\n            \"2020-06-01 05:39:15.504852: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:39:15.505557: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x313f2c0 initialized for platform CUDA (this does not guarantee that XLA will be used). Devices:\\n\",\n            \"2020-06-01 05:39:15.505594: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Tesla T4, Compute Capability 7.5\\n\",\n            \"2020-06-01 05:39:15.505802: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:39:15.506529: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1561] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla T4 computeCapability: 7.5\\n\",\n            \"coreClock: 1.59GHz coreCount: 40 deviceMemorySize: 14.73GiB deviceMemoryBandwidth: 298.08GiB/s\\n\",\n            \"2020-06-01 05:39:15.506599: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:39:15.506655: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"2020-06-01 05:39:15.506677: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2020-06-01 05:39:15.506701: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2020-06-01 05:39:15.506720: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2020-06-01 05:39:15.506738: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\\n\",\n            \"2020-06-01 05:39:15.506757: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 05:39:15.506858: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:39:15.507460: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:39:15.507937: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1703] Adding visible gpu devices: 0\\n\",\n            \"2020-06-01 05:39:15.507984: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:39:16.020285: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1102] Device interconnect StreamExecutor with strength 1 edge matrix:\\n\",\n            \"2020-06-01 05:39:16.020341: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1108]      0 \\n\",\n            \"2020-06-01 05:39:16.020355: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1121] 0:   N \\n\",\n            \"2020-06-01 05:39:16.020572: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:39:16.021232: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:39:16.021838: W tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:39] Overriding allow_growth setting because the TF_FORCE_GPU_ALLOW_GROWTH environment variable is set. Original config value was 0.\\n\",\n            \"2020-06-01 05:39:16.021902: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1247] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 13970 MB memory) -> physical GPU (device: 0, name: Tesla T4, pci bus id: 0000:00:04.0, compute capability: 7.5)\\n\",\n            \"INFO:tensorflow:Running local_init_op.\\n\",\n            \"I0601 05:39:20.487684 140233166403456 session_manager.py:505] Running local_init_op.\\n\",\n            \"INFO:tensorflow:Done running local_init_op.\\n\",\n            \"I0601 05:39:20.766017 140233166403456 session_manager.py:508] Done running local_init_op.\\n\",\n            \"INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 0...\\n\",\n            \"I0601 05:39:30.224425 140233166403456 basic_session_run_hooks.py:614] Calling checkpoint listeners before saving checkpoint 0...\\n\",\n            \"INFO:tensorflow:Saving checkpoints for 0 into /tmp/model_dir/efficientdet-d0-finetune/model.ckpt.\\n\",\n            \"I0601 05:39:30.225207 140233166403456 basic_session_run_hooks.py:618] Saving checkpoints for 0 into /tmp/model_dir/efficientdet-d0-finetune/model.ckpt.\\n\",\n            \"INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 0...\\n\",\n            \"I0601 05:39:32.620833 140233166403456 basic_session_run_hooks.py:626] Calling checkpoint listeners after saving checkpoint 0...\\n\",\n            \"2020-06-01 05:39:42.789892: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 05:39:44.603517: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"INFO:tensorflow:global_step/sec: 0.127531\\n\",\n            \"I0601 05:39:53.424471 140233166403456 tpu_estimator.py:2350] global_step/sec: 0.127531\\n\",\n            \"INFO:tensorflow:examples/sec: 1.02024\\n\",\n            \"I0601 05:39:53.425666 140233166403456 tpu_estimator.py:2351] examples/sec: 1.02024\\n\",\n            \"INFO:tensorflow:global_step/sec: 2.87711\\n\",\n            \"I0601 05:39:53.771956 140233166403456 tpu_estimator.py:2350] global_step/sec: 2.87711\\n\",\n            \"INFO:tensorflow:examples/sec: 23.0169\\n\",\n            \"I0601 05:39:53.772370 140233166403456 tpu_estimator.py:2351] examples/sec: 23.0169\\n\",\n            \"INFO:tensorflow:global_step/sec: 2.94437\\n\",\n            \"I0601 05:39:54.111575 140233166403456 tpu_estimator.py:2350] global_step/sec: 2.94437\\n\",\n            \"INFO:tensorflow:examples/sec: 23.5549\\n\",\n            \"I0601 05:39:54.111993 140233166403456 tpu_estimator.py:2351] examples/sec: 23.5549\\n\",\n            \"INFO:tensorflow:global_step/sec: 3.28482\\n\",\n            \"I0601 05:39:54.415997 140233166403456 tpu_estimator.py:2350] global_step/sec: 3.28482\\n\",\n            \"INFO:tensorflow:examples/sec: 26.2785\\n\",\n            \"I0601 05:39:54.416417 140233166403456 tpu_estimator.py:2351] examples/sec: 26.2785\\n\",\n            \"INFO:tensorflow:global_step/sec: 3.26759\\n\",\n            \"I0601 05:39:54.722034 140233166403456 tpu_estimator.py:2350] global_step/sec: 3.26759\\n\",\n            \"INFO:tensorflow:examples/sec: 26.1407\\n\",\n            \"I0601 05:39:54.722304 140233166403456 tpu_estimator.py:2351] examples/sec: 26.1407\\n\",\n            \"INFO:tensorflow:global_step/sec: 3.4836\\n\",\n            \"I0601 05:39:55.009089 140233166403456 tpu_estimator.py:2350] global_step/sec: 3.4836\\n\",\n            \"INFO:tensorflow:examples/sec: 27.8688\\n\",\n            \"I0601 05:39:55.009364 140233166403456 tpu_estimator.py:2351] examples/sec: 27.8688\\n\",\n            \"INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 7...\\n\",\n            \"I0601 05:39:55.009915 140233166403456 basic_session_run_hooks.py:614] Calling checkpoint listeners before saving checkpoint 7...\\n\",\n            \"INFO:tensorflow:Saving checkpoints for 7 into /tmp/model_dir/efficientdet-d0-finetune/model.ckpt.\\n\",\n            \"I0601 05:39:55.010076 140233166403456 basic_session_run_hooks.py:618] Saving checkpoints for 7 into /tmp/model_dir/efficientdet-d0-finetune/model.ckpt.\\n\",\n            \"INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 7...\\n\",\n            \"I0601 05:39:56.579441 140233166403456 basic_session_run_hooks.py:626] Calling checkpoint listeners after saving checkpoint 7...\\n\",\n            \"INFO:tensorflow:Loss for final step: 1.5810888.\\n\",\n            \"I0601 05:39:57.246410 140233166403456 estimator.py:350] Loss for final step: 1.5810888.\\n\",\n            \"INFO:tensorflow:training_loop marked as finished\\n\",\n            \"I0601 05:39:57.247187 140233166403456 error_handling.py:115] training_loop marked as finished\\n\",\n            \"I0601 05:39:57.247375 140233166403456 main.py:388] Starting evaluation cycle, epoch: 0.\\n\",\n            \"INFO:tensorflow:Using config: {'_model_dir': '/tmp/model_dir/efficientdet-d0-finetune', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true\\n\",\n            \", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': None, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1, '_tpu_config': TPUConfig(iterations_per_loop=100, num_shards=8, num_cores_per_replica=None, per_host_input_for_training=3, tpu_job_name=None, initial_infeed_sleep_secs=None, input_partition_dims=None, eval_training_input_configuration=2, experimental_host_call_every_n_steps=1), '_cluster': None}\\n\",\n            \"I0601 05:39:57.248242 140233166403456 estimator.py:191] Using config: {'_model_dir': '/tmp/model_dir/efficientdet-d0-finetune', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true\\n\",\n            \", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': None, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1, '_tpu_config': TPUConfig(iterations_per_loop=100, num_shards=8, num_cores_per_replica=None, per_host_input_for_training=3, tpu_job_name=None, initial_infeed_sleep_secs=None, input_partition_dims=None, eval_training_input_configuration=2, experimental_host_call_every_n_steps=1), '_cluster': None}\\n\",\n            \"INFO:tensorflow:_TPUContext: eval_on_tpu True\\n\",\n            \"I0601 05:39:57.248666 140233166403456 tpu_context.py:216] _TPUContext: eval_on_tpu True\\n\",\n            \"WARNING:tensorflow:eval_on_tpu ignored because use_tpu is False.\\n\",\n            \"W0601 05:39:57.248775 140233166403456 tpu_context.py:218] eval_on_tpu ignored because use_tpu is False.\\n\",\n            \"INFO:tensorflow:Calling model_fn.\\n\",\n            \"I0601 05:39:57.717588 140233166403456 estimator.py:1169] Calling model_fn.\\n\",\n            \"INFO:tensorflow:Running eval on CPU/GPU\\n\",\n            \"I0601 05:39:57.717878 140233166403456 tpu_estimator.py:3171] Running eval on CPU/GPU\\n\",\n            \"I0601 05:39:57.718361 140233166403456 utils.py:595] use mixed precision policy name mixed_float16\\n\",\n            \"I0601 05:39:57.719170 140233166403456 efficientdet_arch.py:720] act_type: swish\\n\",\n            \"alpha: 0.25\\n\",\n            \"anchor_scale: 4.0\\n\",\n            \"apply_bn_for_resampling: true\\n\",\n            \"aspect_ratios:\\n\",\n            \"- !!python/tuple\\n\",\n            \"    - 1.0\\n\",\n            \"    - 1.0\\n\",\n            \"- !!python/tuple\\n\",\n            \"    - 1.4\\n\",\n            \"    - 0.7\\n\",\n            \"- !!python/tuple\\n\",\n            \"    - 0.7\\n\",\n            \"    - 1.4\\n\",\n            \"augmix_params: !!python/tuple\\n\",\n            \"- 3\\n\",\n            \"- -1\\n\",\n            \"- 1\\n\",\n            \"autoaugment_policy: null\\n\",\n            \"backbone_ckpt: ''\\n\",\n            \"backbone_config: null\\n\",\n            \"backbone_name: efficientnet-b0\\n\",\n            \"batch_size: 8\\n\",\n            \"box_class_repeats: 3\\n\",\n            \"box_loss_weight: 50.0\\n\",\n            \"ckpt: efficientdet-d0\\n\",\n            \"ckpt_var_scope: null\\n\",\n            \"clip_gradients_norm: 10.0\\n\",\n            \"conv_after_downsample: false\\n\",\n            \"conv_bn_act_pattern: false\\n\",\n            \"data_format: channels_last\\n\",\n            \"delta: 0.1\\n\",\n            \"first_lr_drop_epoch: 200.0\\n\",\n            \"fpn_cell_repeats: 3\\n\",\n            \"fpn_config: null\\n\",\n            \"fpn_name: null\\n\",\n            \"fpn_num_filters: 64\\n\",\n            \"fpn_weight_method: null\\n\",\n            \"gamma: 1.5\\n\",\n            \"image_size: !!python/tuple\\n\",\n            \"- 512\\n\",\n            \"- 512\\n\",\n            \"img_summary_steps: null\\n\",\n            \"input_rand_hflip: false\\n\",\n            \"iou_loss_type: null\\n\",\n            \"iou_loss_weight: 1.0\\n\",\n            \"is_training_bn: false\\n\",\n            \"iterations_per_loop: 100\\n\",\n            \"label_id_mapping: null\\n\",\n            \"learning_rate: 0.08\\n\",\n            \"lr_decay_method: cosine\\n\",\n            \"lr_warmup_epoch: 1.0\\n\",\n            \"lr_warmup_init: 0.008\\n\",\n            \"max_instances_per_image: 100\\n\",\n            \"max_level: 7\\n\",\n            \"min_level: 3\\n\",\n            \"mode: train_and_eval\\n\",\n            \"model_dir: /tmp/model_dir/efficientdet-d0-finetune\\n\",\n            \"model_name: efficientdet-d0\\n\",\n            \"momentum: 0.9\\n\",\n            \"moving_average_decay: 0\\n\",\n            \"name: efficientdet-d0\\n\",\n            \"num_classes: 20\\n\",\n            \"num_epochs: 1\\n\",\n            \"num_examples_per_epoch: 56\\n\",\n            \"num_scales: 3\\n\",\n            \"num_shards: 8\\n\",\n            \"optimizer: sgd\\n\",\n            \"poly_lr_power: 0.9\\n\",\n            \"pooling_type: null\\n\",\n            \"precision: mixed_float16\\n\",\n            \"resnet_depth: 50\\n\",\n            \"second_lr_drop_epoch: 250.0\\n\",\n            \"separable_conv: true\\n\",\n            \"skip_crowd_during_training: true\\n\",\n            \"strategy: null\\n\",\n            \"survival_prob: null\\n\",\n            \"target_size: null\\n\",\n            \"testdev_dir: null\\n\",\n            \"train_scale_max: 2.0\\n\",\n            \"train_scale_min: 0.1\\n\",\n            \"use_augmix: false\\n\",\n            \"use_native_resize_op: true\\n\",\n            \"use_tpu: false\\n\",\n            \"val_json_file: null\\n\",\n            \"var_exclude_expr: .*/class-predict/.*\\n\",\n            \"var_freeze_expr: null\\n\",\n            \"weight_decay: 4.0e-05\\n\",\n            \"\\n\",\n            \"I0601 05:39:57.724439 140233166403456 efficientnet_builder.py:224] global_params= GlobalParams(batch_norm_momentum=0.99, batch_norm_epsilon=0.001, dropout_rate=0.2, data_format='channels_last', num_classes=1000, width_coefficient=1.0, depth_coefficient=1.0, depth_divisor=8, min_depth=None, survival_prob=0.0, relu_fn=functools.partial(<function activation_fn at 0x7f8a3d432d90>, act_type='swish'), batch_norm=<class 'utils.BatchNormalization'>, use_se=True, local_pooling=None, condconv_num_experts=None, clip_projection_output=False, blocks_args=['r1_k3_s11_e1_i32_o16_se0.25', 'r2_k3_s22_e6_i16_o24_se0.25', 'r2_k5_s22_e6_i24_o40_se0.25', 'r3_k3_s22_e6_i40_o80_se0.25', 'r3_k5_s11_e6_i80_o112_se0.25', 'r4_k5_s22_e6_i112_o192_se0.25', 'r1_k3_s11_e6_i192_o320_se0.25'], fix_head_stem=None)\\n\",\n            \"I0601 05:39:57.994890 140233166403456 api.py:587] Built stem layers with output shape: (8, 256, 256, 32)\\n\",\n            \"I0601 05:39:58.004813 140233166403456 api.py:587] Block mb_conv_block  input shape: (8, 256, 256, 32)\\n\",\n            \"I0601 05:39:58.041763 140233166403456 api.py:587] DWConv shape: (8, 256, 256, 32)\\n\",\n            \"I0601 05:39:58.082441 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 32)\\n\",\n            \"I0601 05:39:58.118283 140233166403456 api.py:587] Project shape: (8, 256, 256, 16)\\n\",\n            \"I0601 05:39:58.129572 140233166403456 api.py:587] Block mb_conv_block_1  input shape: (8, 256, 256, 16)\\n\",\n            \"I0601 05:39:58.167157 140233166403456 api.py:587] Expand shape: (8, 256, 256, 96)\\n\",\n            \"I0601 05:39:58.200410 140233166403456 api.py:587] DWConv shape: (8, 128, 128, 96)\\n\",\n            \"I0601 05:39:58.240649 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 96)\\n\",\n            \"I0601 05:39:58.273849 140233166403456 api.py:587] Project shape: (8, 128, 128, 24)\\n\",\n            \"I0601 05:39:58.283733 140233166403456 api.py:587] Block mb_conv_block_2  input shape: (8, 128, 128, 24)\\n\",\n            \"I0601 05:39:58.316170 140233166403456 api.py:587] Expand shape: (8, 128, 128, 144)\\n\",\n            \"I0601 05:39:58.349213 140233166403456 api.py:587] DWConv shape: (8, 128, 128, 144)\\n\",\n            \"I0601 05:39:58.393920 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 144)\\n\",\n            \"I0601 05:39:58.425131 140233166403456 api.py:587] Project shape: (8, 128, 128, 24)\\n\",\n            \"I0601 05:39:58.439640 140233166403456 api.py:587] Block mb_conv_block_3  input shape: (8, 128, 128, 24)\\n\",\n            \"I0601 05:39:58.480211 140233166403456 api.py:587] Expand shape: (8, 128, 128, 144)\\n\",\n            \"I0601 05:39:58.512615 140233166403456 api.py:587] DWConv shape: (8, 64, 64, 144)\\n\",\n            \"I0601 05:39:58.547719 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 144)\\n\",\n            \"I0601 05:39:58.581837 140233166403456 api.py:587] Project shape: (8, 64, 64, 40)\\n\",\n            \"I0601 05:39:58.593011 140233166403456 api.py:587] Block mb_conv_block_4  input shape: (8, 64, 64, 40)\\n\",\n            \"I0601 05:39:58.625525 140233166403456 api.py:587] Expand shape: (8, 64, 64, 240)\\n\",\n            \"I0601 05:39:58.658681 140233166403456 api.py:587] DWConv shape: (8, 64, 64, 240)\\n\",\n            \"I0601 05:39:58.694889 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 240)\\n\",\n            \"I0601 05:39:58.726229 140233166403456 api.py:587] Project shape: (8, 64, 64, 40)\\n\",\n            \"I0601 05:39:58.739845 140233166403456 api.py:587] Block mb_conv_block_5  input shape: (8, 64, 64, 40)\\n\",\n            \"I0601 05:39:58.774328 140233166403456 api.py:587] Expand shape: (8, 64, 64, 240)\\n\",\n            \"I0601 05:39:58.809783 140233166403456 api.py:587] DWConv shape: (8, 32, 32, 240)\\n\",\n            \"I0601 05:39:58.845743 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 240)\\n\",\n            \"I0601 05:39:58.878186 140233166403456 api.py:587] Project shape: (8, 32, 32, 80)\\n\",\n            \"I0601 05:39:58.889413 140233166403456 api.py:587] Block mb_conv_block_6  input shape: (8, 32, 32, 80)\\n\",\n            \"I0601 05:39:58.924906 140233166403456 api.py:587] Expand shape: (8, 32, 32, 480)\\n\",\n            \"I0601 05:39:58.958735 140233166403456 api.py:587] DWConv shape: (8, 32, 32, 480)\\n\",\n            \"I0601 05:39:59.002022 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 480)\\n\",\n            \"I0601 05:39:59.038743 140233166403456 api.py:587] Project shape: (8, 32, 32, 80)\\n\",\n            \"I0601 05:39:59.051755 140233166403456 api.py:587] Block mb_conv_block_7  input shape: (8, 32, 32, 80)\\n\",\n            \"I0601 05:39:59.085218 140233166403456 api.py:587] Expand shape: (8, 32, 32, 480)\\n\",\n            \"I0601 05:39:59.118895 140233166403456 api.py:587] DWConv shape: (8, 32, 32, 480)\\n\",\n            \"I0601 05:39:59.154775 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 480)\\n\",\n            \"I0601 05:39:59.191923 140233166403456 api.py:587] Project shape: (8, 32, 32, 80)\\n\",\n            \"I0601 05:39:59.201802 140233166403456 api.py:587] Block mb_conv_block_8  input shape: (8, 32, 32, 80)\\n\",\n            \"I0601 05:39:59.236343 140233166403456 api.py:587] Expand shape: (8, 32, 32, 480)\\n\",\n            \"I0601 05:39:59.277680 140233166403456 api.py:587] DWConv shape: (8, 32, 32, 480)\\n\",\n            \"I0601 05:39:59.316390 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 480)\\n\",\n            \"I0601 05:39:59.352003 140233166403456 api.py:587] Project shape: (8, 32, 32, 112)\\n\",\n            \"I0601 05:39:59.361599 140233166403456 api.py:587] Block mb_conv_block_9  input shape: (8, 32, 32, 112)\\n\",\n            \"I0601 05:39:59.403441 140233166403456 api.py:587] Expand shape: (8, 32, 32, 672)\\n\",\n            \"I0601 05:39:59.437427 140233166403456 api.py:587] DWConv shape: (8, 32, 32, 672)\\n\",\n            \"I0601 05:39:59.482208 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 672)\\n\",\n            \"I0601 05:39:59.514467 140233166403456 api.py:587] Project shape: (8, 32, 32, 112)\\n\",\n            \"I0601 05:39:59.524105 140233166403456 api.py:587] Block mb_conv_block_10  input shape: (8, 32, 32, 112)\\n\",\n            \"I0601 05:39:59.558211 140233166403456 api.py:587] Expand shape: (8, 32, 32, 672)\\n\",\n            \"I0601 05:39:59.595371 140233166403456 api.py:587] DWConv shape: (8, 32, 32, 672)\\n\",\n            \"I0601 05:39:59.630897 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 672)\\n\",\n            \"I0601 05:39:59.666866 140233166403456 api.py:587] Project shape: (8, 32, 32, 112)\\n\",\n            \"I0601 05:39:59.676909 140233166403456 api.py:587] Block mb_conv_block_11  input shape: (8, 32, 32, 112)\\n\",\n            \"I0601 05:39:59.711543 140233166403456 api.py:587] Expand shape: (8, 32, 32, 672)\\n\",\n            \"I0601 05:39:59.745486 140233166403456 api.py:587] DWConv shape: (8, 16, 16, 672)\\n\",\n            \"I0601 05:39:59.784313 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 672)\\n\",\n            \"I0601 05:39:59.815086 140233166403456 api.py:587] Project shape: (8, 16, 16, 192)\\n\",\n            \"I0601 05:39:59.824952 140233166403456 api.py:587] Block mb_conv_block_12  input shape: (8, 16, 16, 192)\\n\",\n            \"I0601 05:39:59.865648 140233166403456 api.py:587] Expand shape: (8, 16, 16, 1152)\\n\",\n            \"I0601 05:39:59.903240 140233166403456 api.py:587] DWConv shape: (8, 16, 16, 1152)\\n\",\n            \"I0601 05:39:59.940747 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 1152)\\n\",\n            \"I0601 05:39:59.988296 140233166403456 api.py:587] Project shape: (8, 16, 16, 192)\\n\",\n            \"I0601 05:39:59.998693 140233166403456 api.py:587] Block mb_conv_block_13  input shape: (8, 16, 16, 192)\\n\",\n            \"I0601 05:40:00.038145 140233166403456 api.py:587] Expand shape: (8, 16, 16, 1152)\\n\",\n            \"I0601 05:40:00.081367 140233166403456 api.py:587] DWConv shape: (8, 16, 16, 1152)\\n\",\n            \"I0601 05:40:00.118943 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 1152)\\n\",\n            \"I0601 05:40:00.155311 140233166403456 api.py:587] Project shape: (8, 16, 16, 192)\\n\",\n            \"I0601 05:40:00.166164 140233166403456 api.py:587] Block mb_conv_block_14  input shape: (8, 16, 16, 192)\\n\",\n            \"I0601 05:40:00.204564 140233166403456 api.py:587] Expand shape: (8, 16, 16, 1152)\\n\",\n            \"I0601 05:40:00.248919 140233166403456 api.py:587] DWConv shape: (8, 16, 16, 1152)\\n\",\n            \"I0601 05:40:00.289414 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 1152)\\n\",\n            \"I0601 05:40:00.327728 140233166403456 api.py:587] Project shape: (8, 16, 16, 192)\\n\",\n            \"I0601 05:40:00.337415 140233166403456 api.py:587] Block mb_conv_block_15  input shape: (8, 16, 16, 192)\\n\",\n            \"I0601 05:40:00.375041 140233166403456 api.py:587] Expand shape: (8, 16, 16, 1152)\\n\",\n            \"I0601 05:40:00.420453 140233166403456 api.py:587] DWConv shape: (8, 16, 16, 1152)\\n\",\n            \"I0601 05:40:00.460810 140233166403456 api.py:587] Built Squeeze and Excitation with tensor shape: (8, 1, 1, 1152)\\n\",\n            \"I0601 05:40:00.500894 140233166403456 api.py:587] Project shape: (8, 16, 16, 320)\\n\",\n            \"Parsing Inputs...\\n\",\n            \"I0601 05:40:00.823563 140233166403456 efficientdet_arch.py:725] backbone params/flops = 3.595388M, 15.443674561B\\n\",\n            \"I0601 05:40:00.884665 140233166403456 efficientdet_arch.py:471] building cell 0\\n\",\n            \"I0601 05:40:00.885131 140233166403456 efficientdet_arch.py:648] fnode 0 : {'feat_level': 6, 'inputs_offsets': [3, 4]}\\n\",\n            \"I0601 05:40:00.957454 140233166403456 efficientdet_arch.py:648] fnode 1 : {'feat_level': 5, 'inputs_offsets': [2, 5]}\\n\",\n            \"I0601 05:40:01.079147 140233166403456 efficientdet_arch.py:648] fnode 2 : {'feat_level': 4, 'inputs_offsets': [1, 6]}\\n\",\n            \"I0601 05:40:01.204365 140233166403456 efficientdet_arch.py:648] fnode 3 : {'feat_level': 3, 'inputs_offsets': [0, 7]}\\n\",\n            \"I0601 05:40:01.331107 140233166403456 efficientdet_arch.py:648] fnode 4 : {'feat_level': 4, 'inputs_offsets': [1, 7, 8]}\\n\",\n            \"I0601 05:40:01.475595 140233166403456 efficientdet_arch.py:648] fnode 5 : {'feat_level': 5, 'inputs_offsets': [2, 6, 9]}\\n\",\n            \"I0601 05:40:01.613884 140233166403456 efficientdet_arch.py:648] fnode 6 : {'feat_level': 6, 'inputs_offsets': [3, 5, 10]}\\n\",\n            \"I0601 05:40:01.698622 140233166403456 efficientdet_arch.py:648] fnode 7 : {'feat_level': 7, 'inputs_offsets': [4, 11]}\\n\",\n            \"I0601 05:40:01.783592 140233166403456 efficientdet_arch.py:471] building cell 1\\n\",\n            \"I0601 05:40:01.783952 140233166403456 efficientdet_arch.py:648] fnode 0 : {'feat_level': 6, 'inputs_offsets': [3, 4]}\\n\",\n            \"I0601 05:40:01.864315 140233166403456 efficientdet_arch.py:648] fnode 1 : {'feat_level': 5, 'inputs_offsets': [2, 5]}\\n\",\n            \"I0601 05:40:01.946357 140233166403456 efficientdet_arch.py:648] fnode 2 : {'feat_level': 4, 'inputs_offsets': [1, 6]}\\n\",\n            \"I0601 05:40:02.038874 140233166403456 efficientdet_arch.py:648] fnode 3 : {'feat_level': 3, 'inputs_offsets': [0, 7]}\\n\",\n            \"I0601 05:40:02.128052 140233166403456 efficientdet_arch.py:648] fnode 4 : {'feat_level': 4, 'inputs_offsets': [1, 7, 8]}\\n\",\n            \"I0601 05:40:02.226508 140233166403456 efficientdet_arch.py:648] fnode 5 : {'feat_level': 5, 'inputs_offsets': [2, 6, 9]}\\n\",\n            \"I0601 05:40:02.316256 140233166403456 efficientdet_arch.py:648] fnode 6 : {'feat_level': 6, 'inputs_offsets': [3, 5, 10]}\\n\",\n            \"I0601 05:40:02.414864 140233166403456 efficientdet_arch.py:648] fnode 7 : {'feat_level': 7, 'inputs_offsets': [4, 11]}\\n\",\n            \"I0601 05:40:02.514123 140233166403456 efficientdet_arch.py:471] building cell 2\\n\",\n            \"I0601 05:40:02.514500 140233166403456 efficientdet_arch.py:648] fnode 0 : {'feat_level': 6, 'inputs_offsets': [3, 4]}\\n\",\n            \"I0601 05:40:02.607001 140233166403456 efficientdet_arch.py:648] fnode 1 : {'feat_level': 5, 'inputs_offsets': [2, 5]}\\n\",\n            \"I0601 05:40:02.694679 140233166403456 efficientdet_arch.py:648] fnode 2 : {'feat_level': 4, 'inputs_offsets': [1, 6]}\\n\",\n            \"I0601 05:40:02.784893 140233166403456 efficientdet_arch.py:648] fnode 3 : {'feat_level': 3, 'inputs_offsets': [0, 7]}\\n\",\n            \"I0601 05:40:02.871577 140233166403456 efficientdet_arch.py:648] fnode 4 : {'feat_level': 4, 'inputs_offsets': [1, 7, 8]}\\n\",\n            \"I0601 05:40:02.971745 140233166403456 efficientdet_arch.py:648] fnode 5 : {'feat_level': 5, 'inputs_offsets': [2, 6, 9]}\\n\",\n            \"I0601 05:40:03.074774 140233166403456 efficientdet_arch.py:648] fnode 6 : {'feat_level': 6, 'inputs_offsets': [3, 5, 10]}\\n\",\n            \"I0601 05:40:03.176123 140233166403456 efficientdet_arch.py:648] fnode 7 : {'feat_level': 7, 'inputs_offsets': [4, 11]}\\n\",\n            \"Parsing Inputs...\\n\",\n            \"I0601 05:40:03.882147 140233166403456 efficientdet_arch.py:730] backbone+fpn params/flops = 3.791669M, 16.584498668B\\n\",\n            \"Parsing Inputs...\\n\",\n            \"I0601 05:40:07.417416 140233166403456 efficientdet_arch.py:735] backbone+fpn+box params/flops = 3.839117M, 18.483229676B\\n\",\n            \"I0601 05:40:07.417771 140233166403456 utils.py:595] use mixed precision policy name float32\\n\",\n            \"I0601 05:40:07.421836 140233166403456 det_model_fn.py:97] LR schedule method: cosine\\n\",\n            \"I0601 05:40:07.972064 140233166403456 det_model_fn.py:589] Eval val with groudtruths None.\\n\",\n            \"WARNING:tensorflow:From /content/automl/efficientdet/anchors.py:587: py_func (from tensorflow.python.ops.script_ops) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"tf.py_func is deprecated in TF V2. Instead, there are two\\n\",\n            \"    options available in V2.\\n\",\n            \"    - tf.py_function takes a python function which manipulates tf eager\\n\",\n            \"    tensors instead of numpy arrays. It's easy to convert a tf eager tensor to\\n\",\n            \"    an ndarray (just call tensor.numpy()) but having access to eager tensors\\n\",\n            \"    means `tf.py_function`s can use accelerators such as GPUs as well as\\n\",\n            \"    being differentiable using a gradient tape.\\n\",\n            \"    - tf.numpy_function maintains the semantics of the deprecated tf.py_func\\n\",\n            \"    (it is not differentiable, and manipulates numpy arrays). It drops the\\n\",\n            \"    stateful argument making all functions stateful.\\n\",\n            \"    \\n\",\n            \"W0601 05:40:07.981205 140233166403456 deprecation.py:323] From /content/automl/efficientdet/anchors.py:587: py_func (from tensorflow.python.ops.script_ops) is deprecated and will be removed in a future version.\\n\",\n            \"Instructions for updating:\\n\",\n            \"tf.py_func is deprecated in TF V2. Instead, there are two\\n\",\n            \"    options available in V2.\\n\",\n            \"    - tf.py_function takes a python function which manipulates tf eager\\n\",\n            \"    tensors instead of numpy arrays. It's easy to convert a tf eager tensor to\\n\",\n            \"    an ndarray (just call tensor.numpy()) but having access to eager tensors\\n\",\n            \"    means `tf.py_function`s can use accelerators such as GPUs as well as\\n\",\n            \"    being differentiable using a gradient tape.\\n\",\n            \"    - tf.numpy_function maintains the semantics of the deprecated tf.py_func\\n\",\n            \"    (it is not differentiable, and manipulates numpy arrays). It drops the\\n\",\n            \"    stateful argument making all functions stateful.\\n\",\n            \"    \\n\",\n            \"INFO:tensorflow:Done calling model_fn.\\n\",\n            \"I0601 05:40:08.068996 140233166403456 estimator.py:1171] Done calling model_fn.\\n\",\n            \"INFO:tensorflow:Starting evaluation at 2020-06-01T05:40:08Z\\n\",\n            \"I0601 05:40:08.085507 140233166403456 evaluation.py:255] Starting evaluation at 2020-06-01T05:40:08Z\\n\",\n            \"INFO:tensorflow:Graph was finalized.\\n\",\n            \"I0601 05:40:08.946110 140233166403456 monitored_session.py:246] Graph was finalized.\\n\",\n            \"2020-06-01 05:40:08.946854: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:40:08.947376: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1561] Found device 0 with properties: \\n\",\n            \"pciBusID: 0000:00:04.0 name: Tesla T4 computeCapability: 7.5\\n\",\n            \"coreClock: 1.59GHz coreCount: 40 deviceMemorySize: 14.73GiB deviceMemoryBandwidth: 298.08GiB/s\\n\",\n            \"2020-06-01 05:40:08.947440: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1\\n\",\n            \"2020-06-01 05:40:08.947494: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10\\n\",\n            \"2020-06-01 05:40:08.947518: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10\\n\",\n            \"2020-06-01 05:40:08.947538: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10\\n\",\n            \"2020-06-01 05:40:08.947557: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10\\n\",\n            \"2020-06-01 05:40:08.947576: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10\\n\",\n            \"2020-06-01 05:40:08.947596: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7\\n\",\n            \"2020-06-01 05:40:08.947709: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:40:08.948242: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:40:08.948695: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1703] Adding visible gpu devices: 0\\n\",\n            \"2020-06-01 05:40:08.948743: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1102] Device interconnect StreamExecutor with strength 1 edge matrix:\\n\",\n            \"2020-06-01 05:40:08.948757: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1108]      0 \\n\",\n            \"2020-06-01 05:40:08.948767: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1121] 0:   N \\n\",\n            \"2020-06-01 05:40:08.948898: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:40:08.949417: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero\\n\",\n            \"2020-06-01 05:40:08.949866: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1247] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 13970 MB memory) -> physical GPU (device: 0, name: Tesla T4, pci bus id: 0000:00:04.0, compute capability: 7.5)\\n\",\n            \"INFO:tensorflow:Restoring parameters from /tmp/model_dir/efficientdet-d0-finetune/model.ckpt-7\\n\",\n            \"I0601 05:40:08.951066 140233166403456 saver.py:1293] Restoring parameters from /tmp/model_dir/efficientdet-d0-finetune/model.ckpt-7\\n\",\n            \"INFO:tensorflow:Running local_init_op.\\n\",\n            \"I0601 05:40:10.190663 140233166403456 session_manager.py:505] Running local_init_op.\\n\",\n            \"INFO:tensorflow:Done running local_init_op.\\n\",\n            \"I0601 05:40:10.302396 140233166403456 session_manager.py:508] Done running local_init_op.\\n\",\n            \"INFO:tensorflow:Evaluation [1/7]\\n\",\n            \"I0601 05:40:14.238775 140233166403456 evaluation.py:167] Evaluation [1/7]\\n\",\n            \"INFO:tensorflow:Evaluation [2/7]\\n\",\n            \"I0601 05:40:14.770026 140233166403456 evaluation.py:167] Evaluation [2/7]\\n\",\n            \"INFO:tensorflow:Evaluation [3/7]\\n\",\n            \"I0601 05:40:15.282811 140233166403456 evaluation.py:167] Evaluation [3/7]\\n\",\n            \"INFO:tensorflow:Evaluation [4/7]\\n\",\n            \"I0601 05:40:15.774128 140233166403456 evaluation.py:167] Evaluation [4/7]\\n\",\n            \"INFO:tensorflow:Evaluation [5/7]\\n\",\n            \"I0601 05:40:16.294507 140233166403456 evaluation.py:167] Evaluation [5/7]\\n\",\n            \"INFO:tensorflow:Evaluation [6/7]\\n\",\n            \"I0601 05:40:16.848151 140233166403456 evaluation.py:167] Evaluation [6/7]\\n\",\n            \"INFO:tensorflow:Evaluation [7/7]\\n\",\n            \"I0601 05:40:17.344349 140233166403456 evaluation.py:167] Evaluation [7/7]\\n\",\n            \"creating index...\\n\",\n            \"index created!\\n\",\n            \"Loading and preparing results...\\n\",\n            \"Converting ndarray to lists...\\n\",\n            \"(5600, 7)\\n\",\n            \"0/5600\\n\",\n            \"DONE (t=0.02s)\\n\",\n            \"creating index...\\n\",\n            \"index created!\\n\",\n            \"Running per image evaluation...\\n\",\n            \"Evaluate annotation type *bbox*\\n\",\n            \"DONE (t=0.36s).\\n\",\n            \"Accumulating evaluation results...\\n\",\n            \"DONE (t=0.13s).\\n\",\n            \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.021\\n\",\n            \" Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=100 ] = 0.030\\n\",\n            \" Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=100 ] = 0.021\\n\",\n            \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.172\\n\",\n            \" Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = -1.000\\n\",\n            \" Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = -1.000\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=  1 ] = 0.146\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets= 10 ] = 0.270\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.270\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.270\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = -1.000\\n\",\n            \" Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = -1.000\\n\",\n            \"INFO:tensorflow:Inference Time : 10.03779s\\n\",\n            \"I0601 05:40:18.123470 140233166403456 evaluation.py:273] Inference Time : 10.03779s\\n\",\n            \"INFO:tensorflow:Finished evaluation at 2020-06-01-05:40:18\\n\",\n            \"I0601 05:40:18.123685 140233166403456 evaluation.py:276] Finished evaluation at 2020-06-01-05:40:18\\n\",\n            \"INFO:tensorflow:Saving dict for global step 7: AP = 0.021243384, AP50 = 0.02968482, AP75 = 0.020640317, APl = -1.0, APm = -1.0, APs = 0.17150821, ARl = -1.0, ARm = -1.0, ARmax1 = 0.14580555, ARmax10 = 0.26992804, ARmax100 = 0.26992804, ARs = 0.26992804, box_loss = 0.0024008455, cls_loss = 1.2510093, global_step = 7, loss = 1.4653944\\n\",\n            \"I0601 05:40:18.123841 140233166403456 estimator.py:2066] Saving dict for global step 7: AP = 0.021243384, AP50 = 0.02968482, AP75 = 0.020640317, APl = -1.0, APm = -1.0, APs = 0.17150821, ARl = -1.0, ARm = -1.0, ARmax1 = 0.14580555, ARmax10 = 0.26992804, ARmax100 = 0.26992804, ARs = 0.26992804, box_loss = 0.0024008455, cls_loss = 1.2510093, global_step = 7, loss = 1.4653944\\n\",\n            \"INFO:tensorflow:Saving 'checkpoint_path' summary for global step 7: /tmp/model_dir/efficientdet-d0-finetune/model.ckpt-7\\n\",\n            \"I0601 05:40:19.170074 140233166403456 estimator.py:2127] Saving 'checkpoint_path' summary for global step 7: /tmp/model_dir/efficientdet-d0-finetune/model.ckpt-7\\n\",\n            \"INFO:tensorflow:evaluation_loop marked as finished\\n\",\n            \"I0601 05:40:19.170791 140233166403456 error_handling.py:115] evaluation_loop marked as finished\\n\",\n            \"I0601 05:40:19.170985 140233166403456 main.py:411] Evaluation results: {'AP': 0.021243384, 'AP50': 0.02968482, 'AP75': 0.020640317, 'APl': -1.0, 'APm': -1.0, 'APs': 0.17150821, 'ARl': -1.0, 'ARm': -1.0, 'ARmax1': 0.14580555, 'ARmax10': 0.26992804, 'ARmax100': 0.26992804, 'ARs': 0.26992804, 'box_loss': 0.0024008455, 'cls_loss': 1.2510093, 'loss': 1.4653944, 'global_step': 7}\\n\",\n            \"INFO:tensorflow:/tmp/model_dir/efficientdet-d0-finetune/archive/model.ckpt-7 is not in all_model_checkpoint_paths. Manually adding it.\\n\",\n            \"I0601 05:40:19.210081 140233166403456 checkpoint_management.py:102] /tmp/model_dir/efficientdet-d0-finetune/archive/model.ckpt-7 is not in all_model_checkpoint_paths. Manually adding it.\\n\",\n            \"I0601 05:40:19.210609 140233166403456 utils.py:491] Copying checkpoint /tmp/model_dir/efficientdet-d0-finetune/model.ckpt-7 to /tmp/model_dir/efficientdet-d0-finetune/archive\\n\"\n          ],\n          \"name\": \"stdout\"\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"QcBGPMCXRC8q\",\n        \"colab_type\": \"text\"\n      },\n      \"source\": [\n        \"## 4.4 View tensorboard for loss and accuracy.\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"metadata\": {\n        \"id\": \"Vrkty06SRD0k\",\n        \"colab_type\": \"code\",\n        \"colab\": {}\n      },\n      \"source\": [\n        \"%load_ext tensorboard\\n\",\n        \"%tensorboard --logdir /tmp/model_dir/\\n\",\n        \"# Notably, this is just a demo with almost zero accuracy due to very limited\\n\",\n        \"# training steps, but we can see finetuning has smaller loss than training\\n\",\n        \"# from scratch at the begining.\"\n      ],\n      \"execution_count\": 0,\n      \"outputs\": []\n    }\n  ],\n  \"metadata\": {\n    \"accelerator\": \"GPU\",\n    \"colab\": {\n      \"name\": \"tutorial.ipynb\",\n      \"provenance\": [],\n      \"collapsed_sections\": []\n    },\n    \"kernelspec\": {\n      \"name\": \"python3\",\n      \"display_name\": \"Python 3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/utils.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Common utils.\"\"\"\nimport collections\nimport contextlib\nimport os\nfrom typing import Text, Tuple, Union\nfrom absl import logging\nimport gin\nimport numpy as np\nimport tensorflow.compat.v1 as tf\nimport tensorflow.compat.v2 as tf2\n\n# pylint: disable=g-import-not-at-top\ntry:\n  from tensorflow.python.eager import record  # pylint:disable=g-direct-tensorflow-import\nexcept ImportError:\n  from tensorflow.python.eager import tape as record  # pylint:disable=g-direct-tensorflow-import\n\nfrom tensorflow.python.tpu import tpu_function  # pylint:disable=g-direct-tensorflow-import\n# pylint: disable=logging-format-interpolation\n\n\ndef srelu_fn(x):\n  \"\"\"Smooth relu: a smooth version of relu.\"\"\"\n  with tf.name_scope('srelu'):\n    beta = tf.Variable(20.0, name='srelu_beta', dtype=tf.float32)**2\n    beta = tf.cast(beta**2, x.dtype)\n    safe_log = tf.math.log(tf.where(x > 0., beta * x + 1., tf.ones_like(x)))\n    return tf.where((x > 0.), x - (1. / beta) * safe_log, tf.zeros_like(x))\n\n\ndef activation_fn(features: tf.Tensor, act_type: Text):\n  \"\"\"Customized non-linear activation type.\"\"\"\n  if act_type in ('silu', 'swish'):\n    return tf.nn.swish(features)\n  elif act_type == 'swish_native':\n    return features * tf.sigmoid(features)\n  elif act_type == 'hswish':\n    return features * tf.nn.relu6(features + 3) / 6\n  elif act_type == 'relu':\n    return tf.nn.relu(features)\n  elif act_type == 'relu6':\n    return tf.nn.relu6(features)\n  elif act_type == 'mish':\n    return features * tf.math.tanh(tf.math.softplus(features))\n  elif act_type == 'srelu':\n    return srelu_fn(features)\n  else:\n    raise ValueError('Unsupported act_type {}'.format(act_type))\n\n\ndef cross_replica_mean(t, num_shards_per_group=None):\n  \"\"\"Calculates the average value of input tensor across TPU replicas.\"\"\"\n  num_shards = tpu_function.get_tpu_context().number_of_shards\n  if not num_shards:\n    return t\n\n  if not num_shards_per_group:\n    return tf.tpu.cross_replica_sum(t) / tf.cast(num_shards, t.dtype)\n\n  group_assignment = None\n  if num_shards_per_group > 1:\n    if num_shards % num_shards_per_group != 0:\n      raise ValueError('num_shards: %d mod shards_per_group: %d, should be 0' %\n                       (num_shards, num_shards_per_group))\n    num_groups = num_shards // num_shards_per_group\n    group_assignment = [[\n        x for x in range(num_shards) if x // num_shards_per_group == y\n    ] for y in range(num_groups)]\n  return tf.tpu.cross_replica_sum(t, group_assignment) / tf.cast(\n      num_shards_per_group, t.dtype)\n\n\ndef get_ema_vars():\n  \"\"\"Get all exponential moving average (ema) variables.\"\"\"\n  ema_vars = (\n      tf.trainable_variables() +\n      tf.get_collection(tf.GraphKeys.MOVING_AVERAGE_VARIABLES))\n  for v in tf.global_variables():\n    # We maintain mva for batch norm moving mean and variance as well.\n    if 'moving_mean' in v.name or 'moving_variance' in v.name:\n      ema_vars.append(v)\n  return list(set(ema_vars))\n\n\ndef get_ckpt_var_map(ckpt_path, ckpt_scope, var_scope, skip_mismatch=None):\n  \"\"\"Get a var map for restoring from pretrained checkpoints.\n\n  Args:\n    ckpt_path: string. A pretrained checkpoint path.\n    ckpt_scope: string. Scope name for checkpoint variables.\n    var_scope: string. Scope name for model variables.\n    skip_mismatch: skip variables if shape mismatch.\n\n  Returns:\n    var_map: a dictionary from checkpoint name to model variables.\n  \"\"\"\n  logging.info('Init model from checkpoint {}'.format(ckpt_path))\n  if not ckpt_scope.endswith('/') or not var_scope.endswith('/'):\n    raise ValueError('Please specific scope name ending with /')\n  if ckpt_scope.startswith('/'):\n    ckpt_scope = ckpt_scope[1:]\n  if var_scope.startswith('/'):\n    var_scope = var_scope[1:]\n\n  var_map = {}\n  # Get the list of vars to restore.\n  model_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope=var_scope)\n  reader = tf.train.load_checkpoint(ckpt_path)\n  ckpt_var_name_to_shape = reader.get_variable_to_shape_map()\n  ckpt_var_names = set(reader.get_variable_to_shape_map().keys())\n\n  if tf.distribute.get_replica_context():\n    replica_id = tf.get_static_value(\n        tf.distribute.get_replica_context().replica_id_in_sync_group)\n  else:\n    replica_id = 0\n\n  for i, v in enumerate(model_vars):\n    var_op_name = v.op.name\n\n    if replica_id >= 1:\n      var_op_name = ''.join(var_op_name.rsplit(f'/replica_{replica_id}', 1))\n\n    if not var_op_name.startswith(var_scope):\n      logging.info('skip {} -- does not match scope {}'.format(\n          var_op_name, var_scope))\n    ckpt_var = ckpt_scope + var_op_name[len(var_scope):]\n    if 'global_step' in ckpt_var:\n      continue\n\n    if (ckpt_var not in ckpt_var_names and\n        var_op_name.endswith('/ExponentialMovingAverage')):\n      ckpt_var = ckpt_scope + var_op_name[:-len('/ExponentialMovingAverage')]\n\n    if ckpt_var not in ckpt_var_names:\n      if 'Momentum' in ckpt_var or 'RMSProp' in ckpt_var:\n        # Skip optimizer variables.\n        continue\n      if skip_mismatch:\n        logging.info('skip {} ({}) -- not in ckpt'.format(\n            var_op_name, ckpt_var))\n        continue\n      raise ValueError('{} is not in ckpt {}'.format(v.op, ckpt_path))\n\n    if v.shape != ckpt_var_name_to_shape[ckpt_var]:\n      if skip_mismatch:\n        logging.info('skip {} ({} vs {}) -- shape mismatch'.format(\n            var_op_name, v.shape, ckpt_var_name_to_shape[ckpt_var]))\n        continue\n      raise ValueError('shape mismatch {} ({} vs {})'.format(\n          var_op_name, v.shape, ckpt_var_name_to_shape[ckpt_var]))\n\n    if i < 5:\n      # Log the first few elements for sanity check.\n      logging.info('Init {} from ckpt var {}'.format(var_op_name, ckpt_var))\n    var_map[ckpt_var] = v\n\n  return var_map\n\n\nclass TpuBatchNormalization(tf.keras.layers.BatchNormalization):\n  \"\"\"Cross replica batch normalization.\"\"\"\n\n  def __init__(self, fused=False, **kwargs):\n    if not kwargs.get('name', None):\n      kwargs['name'] = 'tpu_batch_normalization'\n    if fused in (True, None):\n      raise ValueError('TpuBatchNormalization does not support fused=True.')\n    super().__init__(fused=fused, **kwargs)\n\n  def _moments(self, inputs, reduction_axes, keep_dims, mask=None):\n    \"\"\"Compute the mean and variance: it overrides the original _moments.\"\"\"\n    shard_mean, shard_variance = super()._moments(\n        inputs, reduction_axes, keep_dims=keep_dims, mask=mask)\n\n    num_shards = tpu_function.get_tpu_context().number_of_shards or 1\n    num_shards_per_group = min(32, num_shards)  # aggregate up to 32 cores.\n    logging.info('TpuBatchNormalization with num_shards_per_group {}'.format(\n        num_shards_per_group))\n    if num_shards_per_group > 1:\n      # Compute variance using: Var[X]= E[X^2] - E[X]^2.\n      shard_square_of_mean = tf.math.square(shard_mean)\n      shard_mean_of_square = shard_variance + shard_square_of_mean\n      group_mean = cross_replica_mean(shard_mean, num_shards_per_group)\n      group_mean_of_square = cross_replica_mean(shard_mean_of_square,\n                                                num_shards_per_group)\n      group_variance = group_mean_of_square - tf.math.square(group_mean)\n      return (group_mean, group_variance)\n    else:\n      return (shard_mean, shard_variance)\n\n  def call(self, inputs, mask=None, training=None):\n    outputs = super().call(inputs, mask=mask, training=training)\n    # A temporary hack for tf1 compatibility with keras batch norm.\n    for u in self.updates:\n      tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, u)\n    return outputs\n\n\nclass SyncBatchNormalization(tf.keras.layers.BatchNormalization):\n  \"\"\"Cross replica batch normalization.\"\"\"\n\n  def __init__(self, fused=False, **kwargs):\n    if not kwargs.get('name', None):\n      kwargs['name'] = 'tpu_batch_normalization'\n    if fused in (True, None):\n      raise ValueError('SyncBatchNormalization does not support fused=True.')\n    super().__init__(fused=fused, **kwargs)\n\n  def _moments(self, inputs, reduction_axes, keep_dims, mask=None):\n    \"\"\"Compute the mean and variance: it overrides the original _moments.\"\"\"\n    shard_mean, shard_variance = super()._moments(\n        inputs, reduction_axes, keep_dims=keep_dims, mask=mask)\n\n    replica_context = tf.distribute.get_replica_context()\n    num_shards = replica_context.num_replicas_in_sync or 1\n\n    if num_shards > 1:\n      # Compute variance using: Var[X]= E[X^2] - E[X]^2.\n      shard_square_of_mean = tf.math.square(shard_mean)\n      shard_mean_of_square = shard_variance + shard_square_of_mean\n      group_mean = replica_context.all_reduce(\n          tf.distribute.ReduceOp.MEAN, shard_mean)\n      group_mean_of_square = replica_context.all_reduce(\n          tf.distribute.ReduceOp.MEAN, shard_mean_of_square)\n      group_variance = group_mean_of_square - tf.math.square(group_mean)\n      return (group_mean, group_variance)\n    else:\n      return (shard_mean, shard_variance)\n\n  def call(self, inputs, mask=None, training=None):\n    outputs = super().call(inputs, mask=mask, training=training)\n    # A temporary hack for tf1 compatibility with keras batch norm.\n    for u in self.updates:\n      tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, u)\n    return outputs\n\n\nclass BatchNormalization(tf.keras.layers.BatchNormalization):\n  \"\"\"Fixed default name of BatchNormalization to match TpuBatchNormalization.\"\"\"\n\n  def __init__(self, **kwargs):\n    if not kwargs.get('name', None):\n      kwargs['name'] = 'tpu_batch_normalization'\n    super().__init__(**kwargs)\n\n  def call(self, inputs, mask=None, training=None):\n    outputs = super().call(inputs, mask=mask, training=training)\n    # A temporary hack for tf1 compatibility with keras batch norm.\n    for u in self.updates:\n      tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, u)\n    return outputs\n\n\ndef batch_norm_class(is_training, strategy=None):\n  if is_training and strategy == 'tpu':\n    return TpuBatchNormalization\n  elif is_training and strategy == 'gpus':\n    return SyncBatchNormalization\n  else:\n    return BatchNormalization\n\n# A cache of variable scope to BatchNorm layer.\n_BN_LAYER_CACHE = collections.defaultdict(dict)\n\n\n@gin.configurable\ndef batch_normalization(inputs,\n                        training=False,\n                        strategy=None,\n                        reuse_scope: bool = False,\n                        **kwargs):\n  \"\"\"A wrapper for TpuBatchNormalization.\n\n  Keras layers are incompatible with automatic tf scope reuse.\n\n  Supports reuse of the exiting variable scope when a model is called multiple\n  times. Otherwise, checkpoint weights would not be restored correctly.\n\n  Args:\n    inputs: Input to BatchNorm layer.\n    training: Argument of Keras BatchNorm layer.\n    strategy: Argument of Keras BatchNorm layer.\n    reuse_scope: Whether to reuse existing layer in same scope.\n    **kwargs: Arguments passed to Keras BatchNorm layer.\n\n  Returns:\n    Result of BatchNorm applied to inputs.\n  \"\"\"\n  if reuse_scope:\n    scope_name = tf.get_variable_scope().name\n    bn_layer_cache = _BN_LAYER_CACHE[hash(inputs.graph)]\n    if scope_name in bn_layer_cache:\n      bn_layer = bn_layer_cache[scope_name]\n      logging.info('Reusing variable scope %s', scope_name)\n    else:\n      bn_layer = batch_norm_class(training, strategy)(**kwargs)\n      bn_layer_cache[scope_name] = bn_layer\n  else:\n    bn_layer = batch_norm_class(training, strategy)(**kwargs)\n  return bn_layer(inputs, training=training)\n\n\ndef batch_norm_act(inputs,\n                   is_training_bn: bool,\n                   act_type: Union[Text, None],\n                   init_zero: bool = False,\n                   data_format: Text = 'channels_last',\n                   momentum: float = 0.99,\n                   epsilon: float = 1e-3,\n                   strategy: Text = None,\n                   name: Text = None,\n                   batch_norm_trainable: bool = True):\n  \"\"\"Performs a batch normalization followed by a non-linear activation.\n\n  Args:\n    inputs: `Tensor` of shape `[batch, channels, ...]`.\n    is_training_bn: `bool` for whether the model is training.\n    act_type: non-linear relu function type. If None, omits the relu operation.\n    init_zero: `bool` if True, initializes scale parameter of batch\n      normalization with 0 instead of 1 (default).\n    data_format: `str` either \"channels_first\" for `[batch, channels, height,\n      width]` or \"channels_last for `[batch, height, width, channels]`.\n    momentum: `float`, momentume of batch norm.\n    epsilon: `float`, small value for numerical stability.\n    strategy: string to specify training strategy for TPU/GPU/CPU.\n    name: the name of the batch normalization layer\n    batch_norm_trainable: 'bool' if False, the batch statistics will not be\n      updated.\n\n  Returns:\n    A normalized `Tensor` with the same `data_format`.\n  \"\"\"\n  if init_zero:\n    gamma_initializer = tf.zeros_initializer()\n  else:\n    gamma_initializer = tf.ones_initializer()\n\n  if data_format == 'channels_first':\n    axis = 1\n  else:\n    axis = 3\n\n  inputs = batch_normalization(\n      inputs=inputs,\n      axis=axis,\n      momentum=momentum,\n      epsilon=epsilon,\n      center=True,\n      scale=True,\n      trainable=batch_norm_trainable,\n      training=is_training_bn,\n      strategy=strategy,\n      gamma_initializer=gamma_initializer,\n      name=name)\n\n  if act_type:\n    inputs = activation_fn(inputs, act_type)\n  return inputs\n\n\ndef drop_connect(inputs, is_training, survival_prob):\n  \"\"\"Drop the entire conv with given survival probability.\"\"\"\n  # \"Deep Networks with Stochastic Depth\", https://arxiv.org/pdf/1603.09382.pdf\n  if not is_training:\n    return inputs\n\n  # Compute tensor.\n  batch_size = tf.shape(inputs)[0]\n  random_tensor = survival_prob\n  random_tensor += tf.random.uniform([batch_size, 1, 1, 1], dtype=inputs.dtype)\n  binary_tensor = tf.floor(random_tensor)\n  # Unlike conventional way that multiply survival_prob at test time, here we\n  # divide survival_prob at training time, such that no addition compute is\n  # needed at test time.\n  output = inputs / survival_prob * binary_tensor\n  return output\n\n\ndef num_params_flops(readable_format=True):\n  \"\"\"Return number of parameters and flops.\"\"\"\n  nparams = np.sum(\n      [np.prod(v.get_shape().as_list()) for v in tf.trainable_variables()])\n  options = tf.profiler.ProfileOptionBuilder.float_operation()\n  options['output'] = 'none'\n  flops = tf.profiler.profile(\n      tf.get_default_graph(), options=options).total_float_ops\n  # We use flops to denote multiply-adds, which is counted as 2 ops in tfprof.\n  flops = flops // 2\n  if readable_format:\n    nparams = float(nparams) * 1e-6\n    flops = float(flops) * 1e-9\n  return nparams, flops\n\n\nconv_kernel_initializer = tf.initializers.variance_scaling()\ndense_kernel_initializer = tf.initializers.variance_scaling()\n\n\nclass Pair(tuple):\n\n  def __new__(cls, name, value):\n    return super().__new__(cls, (name, value))\n\n  def __init__(self, name, _):  # pylint: disable=super-init-not-called\n    self.name = name\n\n\ndef scalar(name, tensor, is_tpu=True):\n  \"\"\"Stores a (name, Tensor) tuple in a custom collection.\"\"\"\n  logging.info('Adding scale summary {}'.format(Pair(name, tensor)))\n  if is_tpu:\n    tf.add_to_collection('scalar_summaries', Pair(name, tf.reduce_mean(tensor)))\n  else:\n    tf.summary.scalar(name, tf.reduce_mean(tensor))\n\n\ndef image(name, tensor, is_tpu=True):\n  logging.info('Adding image summary {}'.format(Pair(name, tensor)))\n  if is_tpu:\n    tf.add_to_collection('image_summaries', Pair(name, tensor))\n  else:\n    tf.summary.image(name, tensor)\n\n\ndef get_tpu_host_call(global_step, params):\n  \"\"\"Get TPU host call for summaries.\"\"\"\n  scalar_summaries = tf.get_collection('scalar_summaries')\n  if params['img_summary_steps']:\n    image_summaries = tf.get_collection('image_summaries')\n  else:\n    image_summaries = []\n  if not scalar_summaries and not image_summaries:\n    return None  # No summaries to write.\n\n  model_dir = params['model_dir']\n  iterations_per_loop = params.get('iterations_per_loop', 100)\n  img_steps = params['img_summary_steps']\n\n  def host_call_fn(global_step, *args):\n    \"\"\"Training host call. Creates summaries for training metrics.\"\"\"\n    gs = global_step[0]\n    with tf2.summary.create_file_writer(\n        model_dir, max_queue=iterations_per_loop).as_default():\n      with tf2.summary.record_if(True):\n        for i, _ in enumerate(scalar_summaries):\n          name = scalar_summaries[i][0]\n          tensor = args[i][0]\n          tf2.summary.scalar(name, tensor, step=gs)\n\n      if img_steps:\n        with tf2.summary.record_if(lambda: tf.math.equal(gs % img_steps, 0)):\n          # Log images every 1k steps.\n          for i, _ in enumerate(image_summaries):\n            name = image_summaries[i][0]\n            tensor = args[i + len(scalar_summaries)]\n            tf2.summary.image(name, tensor, step=gs)\n\n      return tf.summary.all_v2_summary_ops()\n\n  reshaped_tensors = [tf.reshape(t, [1]) for _, t in scalar_summaries]\n  reshaped_tensors += [t for _, t in image_summaries]\n  global_step_t = tf.reshape(global_step, [1])\n  return host_call_fn, [global_step_t] + reshaped_tensors\n\n\ndef archive_ckpt(ckpt_eval, ckpt_objective, ckpt_path):\n  \"\"\"Archive a checkpoint if the metric is better.\"\"\"\n  ckpt_dir, ckpt_name = os.path.split(ckpt_path)\n\n  saved_objective_path = os.path.join(ckpt_dir, 'best_objective.txt')\n  saved_objective = float('-inf')\n  if tf.io.gfile.exists(saved_objective_path):\n    with tf.io.gfile.GFile(saved_objective_path, 'r') as f:\n      saved_objective = float(f.read())\n  if saved_objective > ckpt_objective:\n    logging.info('Ckpt {} is worse than {}'.format(ckpt_objective,\n                                                   saved_objective))\n    return False\n\n  filenames = tf.io.gfile.glob(ckpt_path + '.*')\n  if filenames is None:\n    logging.info('No files to copy for checkpoint {}'.format(ckpt_path))\n    return False\n\n  # clear up the backup folder.\n  backup_dir = os.path.join(ckpt_dir, 'backup')\n  if tf.io.gfile.exists(backup_dir):\n    tf.io.gfile.rmtree(backup_dir)\n\n  # rename the old checkpoints to backup folder.\n  dst_dir = os.path.join(ckpt_dir, 'archive')\n  if tf.io.gfile.exists(dst_dir):\n    logging.info('mv {} to {}'.format(dst_dir, backup_dir))\n    tf.io.gfile.rename(dst_dir, backup_dir)\n\n  # Write checkpoints.\n  tf.io.gfile.makedirs(dst_dir)\n  for f in filenames:\n    dest = os.path.join(dst_dir, os.path.basename(f))\n    tf.io.gfile.copy(f, dest, overwrite=True)\n  ckpt_state = tf.train.generate_checkpoint_state_proto(\n      dst_dir, model_checkpoint_path=os.path.join(dst_dir, ckpt_name))\n  with tf.io.gfile.GFile(os.path.join(dst_dir, 'checkpoint'), 'w') as f:\n    f.write(str(ckpt_state))\n  with tf.io.gfile.GFile(os.path.join(dst_dir, 'best_eval.txt'), 'w') as f:\n    f.write('%s' % ckpt_eval)\n\n  # Update the best objective.\n  with tf.io.gfile.GFile(saved_objective_path, 'w') as f:\n    f.write('%f' % ckpt_objective)\n\n  logging.info('Copying checkpoint {} to {}'.format(ckpt_path, dst_dir))\n  return True\n\n\ndef parse_image_size(image_size: Union[Text, int, Tuple[int, int]]):\n  \"\"\"Parse the image size and return (height, width).\n\n  Args:\n    image_size: A integer, a tuple (H, W), or a string with HxW format.\n\n  Returns:\n    A tuple of integer (height, width).\n  \"\"\"\n  if isinstance(image_size, int):\n    # image_size is integer, with the same width and height.\n    return (image_size, image_size)\n\n  if isinstance(image_size, str):\n    # image_size is a string with format WxH\n    width, height = image_size.lower().split('x')\n    return (int(height), int(width))\n\n  if isinstance(image_size, tuple):\n    return image_size\n\n  raise ValueError('image_size must be an int, WxH string, or (height, width)'\n                   'tuple. Was %r' % image_size)\n\n\ndef get_feat_sizes(image_size: Union[Text, int, Tuple[int, int]],\n                   max_level: int):\n  \"\"\"Get feat widths and heights for all levels.\n\n  Args:\n    image_size: A integer, a tuple (H, W), or a string with HxW format.\n    max_level: maximum feature level.\n\n  Returns:\n    feat_sizes: a list of tuples (height, width) for each level.\n  \"\"\"\n  image_size = parse_image_size(image_size)\n  feat_sizes = [{'height': image_size[0], 'width': image_size[1]}]\n  feat_size = image_size\n  for _ in range(1, max_level + 1):\n    feat_size = ((feat_size[0] - 1) // 2 + 1, (feat_size[1] - 1) // 2 + 1)\n    feat_sizes.append({'height': feat_size[0], 'width': feat_size[1]})\n  return feat_sizes\n\n\ndef verify_feats_size(feats,\n                      feat_sizes,\n                      min_level,\n                      max_level,\n                      data_format='channels_last'):\n  \"\"\"Verify the feature map sizes.\"\"\"\n  expected_output_size = feat_sizes[min_level:max_level + 1]\n  for cnt, size in enumerate(expected_output_size):\n    h_id, w_id = (2, 3) if data_format == 'channels_first' else (1, 2)\n    if feats[cnt].shape[h_id] != size['height']:\n      raise ValueError(\n          'feats[{}] has shape {} but its height should be {}.'\n          '(input_height: {}, min_level: {}, max_level: {}.)'.format(\n              cnt, feats[cnt].shape, size['height'], feat_sizes[0]['height'],\n              min_level, max_level))\n    if feats[cnt].shape[w_id] != size['width']:\n      raise ValueError(\n          'feats[{}] has shape {} but its width should be {}.'\n          '(input_width: {}, min_level: {}, max_level: {}.)'.format(\n              cnt, feats[cnt].shape, size['width'], feat_sizes[0]['width'],\n              min_level, max_level))\n\n\ndef get_precision(strategy: str, mixed_precision: bool = False):\n  \"\"\"Get the precision policy for a given strategy.\"\"\"\n  if mixed_precision:\n    if strategy == 'tpu':\n      return 'mixed_bfloat16'\n\n    if tf.config.list_physical_devices('GPU'):\n      return 'mixed_float16'\n\n    # TODO(fsx950223): Fix CPU float16 inference\n    # https://github.com/google/automl/issues/504\n    logging.warning('float16 is not supported for CPU, use float32 instead')\n    return 'float32'\n\n  return 'float32'\n\n\n@contextlib.contextmanager\ndef float16_scope():\n  \"\"\"Scope class for float16.\"\"\"\n\n  def _custom_getter(getter, *args, **kwargs):\n    \"\"\"Returns a custom getter that methods must be called under.\"\"\"\n    cast_to_float16 = False\n    requested_dtype = kwargs['dtype']\n    if requested_dtype == tf.float16:\n      kwargs['dtype'] = tf.float32\n      cast_to_float16 = True\n    var = getter(*args, **kwargs)\n    if cast_to_float16:\n      var = tf.cast(var, tf.float16)\n    return var\n\n  with tf.variable_scope('', custom_getter=_custom_getter) as varscope:\n    yield varscope\n\n\ndef set_precision_policy(policy_name: Text = None):\n  \"\"\"Set precision policy according to the name.\n\n  Args:\n    policy_name: precision policy name, one of 'float32', 'mixed_float16',\n      'mixed_bfloat16', or None.\n  \"\"\"\n  if not policy_name:\n    return\n\n  assert policy_name in ('mixed_float16', 'mixed_bfloat16', 'float32')\n  logging.info('use mixed precision policy name %s', policy_name)\n  tf.compat.v1.keras.layers.enable_v2_dtype_behavior()\n  # mixed_float16 training is not supported for now, so disable loss_scale.\n  # float32 and mixed_bfloat16 do not need loss scale for training.\n  policy = tf2.keras.mixed_precision.Policy(policy_name)\n  tf2.keras.mixed_precision.set_global_policy(policy)\n\n\ndef build_model_with_precision(pp, mm, ii, *args, **kwargs):\n  \"\"\"Build model with its inputs/params for a specified precision context.\n\n  This is highly specific to this codebase, and not intended to be general API.\n  Advanced users only. DO NOT use it if you don't know what it does.\n  NOTE: short argument names are intended to avoid conficts with kwargs.\n\n  Args:\n    pp: A string, precision policy name, such as \"mixed_float16\".\n    mm: A function, for rmodel builder.\n    ii: A tensor, for model inputs.\n    *args: A list of model arguments.\n    **kwargs: A dict, extra model parameters.\n\n  Returns:\n    the output of mm model.\n  \"\"\"\n  if pp == 'mixed_bfloat16':\n    set_precision_policy(pp)\n    inputs = tf.cast(ii, tf.bfloat16)\n    with tf.tpu.bfloat16_scope():\n      outputs = mm(inputs, *args, **kwargs)\n  elif pp == 'mixed_float16':\n    set_precision_policy(pp)\n    inputs = tf.cast(ii, tf.float16)\n    with float16_scope():\n      outputs = mm(inputs, *args, **kwargs)\n  elif not pp or pp == 'float32':\n    set_precision_policy(pp)\n    outputs = mm(ii, *args, **kwargs)\n  else:\n    raise ValueError('Unknow precision name {}'.format(pp))\n\n  # Users are responsible to convert the dtype of all outputs.\n  return outputs\n\n\ndef _recompute_grad(f):\n  \"\"\"An eager-compatible version of recompute_grad.\n\n  For f(*args, **kwargs), this supports gradients with respect to args or\n  kwargs, but kwargs are currently only supported in eager-mode.\n  Note that for keras layer and model objects, this is handled automatically.\n\n  Warning: If `f` was originally a tf.keras Model or Layer object, `g` will not\n  be able to access the member variables of that object, because `g` returns\n  through the wrapper function `inner`.  When recomputing gradients through\n  objects that inherit from keras, we suggest keeping a reference to the\n  underlying object around for the purpose of accessing these variables.\n\n  Args:\n    f: function `f(*x)` that returns a `Tensor` or sequence of `Tensor` outputs.\n\n  Returns:\n   A function `g` that wraps `f`, but which recomputes `f` on the backwards\n   pass of a gradient call.\n  \"\"\"\n\n  @tf.custom_gradient\n  def inner(*args, **kwargs):\n    \"\"\"Inner function closure for calculating gradients.\"\"\"\n    current_var_scope = tf.get_variable_scope()\n    with record.stop_recording():\n      result = f(*args, **kwargs)\n\n    def grad_wrapper(*wrapper_args, **grad_kwargs):\n      \"\"\"Wrapper function to accomodate lack of kwargs in graph mode decorator.\"\"\"\n\n      @tf.custom_gradient\n      def inner_recompute_grad(*dresult):\n        \"\"\"Nested custom gradient function for computing grads in reverse and forward mode autodiff.\"\"\"\n        # Gradient calculation for reverse mode autodiff.\n        variables = grad_kwargs.get('variables')\n        with tf.GradientTape() as t:\n          id_args = tf.nest.map_structure(tf.identity, args)\n          t.watch(id_args)\n          if variables is not None:\n            t.watch(variables)\n          with tf.control_dependencies(dresult):\n            with tf.variable_scope(current_var_scope):\n              result = f(*id_args, **kwargs)\n        kw_vars = []\n        if variables is not None:\n          kw_vars = list(variables)\n        grads = t.gradient(\n            result,\n            list(id_args) + kw_vars,\n            output_gradients=dresult,\n            unconnected_gradients=tf.UnconnectedGradients.ZERO)\n\n        def transpose(*t_args, **t_kwargs):\n          \"\"\"Gradient function calculation for forward mode autodiff.\"\"\"\n          # Just throw an error since gradients / activations are not stored on\n          # tape for recompute.\n          raise NotImplementedError(\n              'recompute_grad tried to transpose grad of {}. '\n              'Consider not using recompute_grad in forward mode'\n              'autodiff'.format(f.__name__))\n\n        return (grads[:len(id_args)], grads[len(id_args):]), transpose\n\n      return inner_recompute_grad(*wrapper_args)\n\n    return result, grad_wrapper\n\n  return inner\n\n\ndef recompute_grad(recompute=False):\n  \"\"\"Decorator determine whether use gradient checkpoint.\"\"\"\n\n  def _wrapper(f):\n    if recompute:\n      return _recompute_grad(f)\n    return f\n\n  return _wrapper\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/visualize/__init__.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n# Visualization library is mostly based on TensorFlow object detection API:\n# https://github.com/tensorflow/models/tree/master/research/object_detection\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/visualize/shape_utils.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Utils used to manipulate tensor shapes.\"\"\"\nfrom six.moves import zip\nimport tensorflow.compat.v1 as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.visualize import static_shape\n\n\ndef _is_tensor(t):\n  \"\"\"Returns a boolean indicating whether the input is a tensor.\n\n  Args:\n    t: the input to be tested.\n\n  Returns:\n    a boolean that indicates whether t is a tensor.\n  \"\"\"\n  return isinstance(t, (tf.Tensor, tf.SparseTensor, tf.Variable))\n\n\ndef _set_dim_0(t, d0):\n  \"\"\"Sets the 0-th dimension of the input tensor.\n\n  Args:\n    t: the input tensor, assuming the rank is at least 1.\n    d0: an integer indicating the 0-th dimension of the input tensor.\n\n  Returns:\n    the tensor t with the 0-th dimension set.\n  \"\"\"\n  t_shape = t.get_shape().as_list()\n  t_shape[0] = d0\n  t.set_shape(t_shape)\n  return t\n\n\ndef pad_tensor(t, length):\n  \"\"\"Pads the input tensor with 0s along the first dimension up to the length.\n\n  Args:\n    t: the input tensor, assuming the rank is at least 1.\n    length: a tensor of shape [1]  or an integer, indicating the first dimension\n      of the input tensor t after padding, assuming length <= t.shape[0].\n\n  Returns:\n    padded_t: the padded tensor, whose first dimension is length. If the length\n      is an integer, the first dimension of padded_t is set to length\n      statically.\n  \"\"\"\n  t_rank = tf.rank(t)\n  t_shape = tf.shape(t)\n  t_d0 = t_shape[0]\n  pad_d0 = tf.expand_dims(length - t_d0, 0)\n  pad_shape = tf.cond(\n      tf.greater(t_rank, 1), lambda: tf.concat([pad_d0, t_shape[1:]], 0),\n      lambda: tf.expand_dims(length - t_d0, 0))\n  padded_t = tf.concat([t, tf.zeros(pad_shape, dtype=t.dtype)], 0)\n  if not _is_tensor(length):\n    padded_t = _set_dim_0(padded_t, length)\n  return padded_t\n\n\ndef clip_tensor(t, length):\n  \"\"\"Clips the input tensor along the first dimension up to the length.\n\n  Args:\n    t: the input tensor, assuming the rank is at least 1.\n    length: a tensor of shape [1]  or an integer, indicating the first dimension\n      of the input tensor t after clipping, assuming length <= t.shape[0].\n\n  Returns:\n    clipped_t: the clipped tensor, whose first dimension is length. If the\n      length is an integer, the first dimension of clipped_t is set to length\n      statically.\n  \"\"\"\n  clipped_t = tf.gather(t, tf.range(length))\n  if not _is_tensor(length):\n    clipped_t = _set_dim_0(clipped_t, length)\n  return clipped_t\n\n\ndef pad_or_clip_tensor(t, length):\n  \"\"\"Pad or clip the input tensor along the first dimension.\n\n  Args:\n    t: the input tensor, assuming the rank is at least 1.\n    length: a tensor of shape [1]  or an integer, indicating the first dimension\n      of the input tensor t after processing.\n\n  Returns:\n    processed_t: the processed tensor, whose first dimension is length. If the\n      length is an integer, the first dimension of the processed tensor is set\n      to length statically.\n  \"\"\"\n  return pad_or_clip_nd(t, [length] + t.shape.as_list()[1:])\n\n\ndef pad_or_clip_nd(tensor, output_shape):\n  \"\"\"Pad or Clip given tensor to the output shape.\n\n  Args:\n    tensor: Input tensor to pad or clip.\n    output_shape: A list of integers / scalar tensors (or None for dynamic dim)\n      representing the size to pad or clip each dimension of the input tensor.\n\n  Returns:\n    Input tensor padded and clipped to the output shape.\n  \"\"\"\n  tensor_shape = tf.shape(tensor)\n  clip_size = [\n      tf.where(tensor_shape[i] - shape > 0, shape, -1)\n      if shape is not None else -1 for i, shape in enumerate(output_shape)\n  ]\n  clipped_tensor = tf.slice(\n      tensor,\n      begin=tf.zeros(len(clip_size), dtype=tf.int32),\n      size=clip_size)\n\n  # Pad tensor if the shape of clipped tensor is smaller than the expected\n  # shape.\n  clipped_tensor_shape = tf.shape(clipped_tensor)\n  trailing_paddings = [\n      shape - clipped_tensor_shape[i] if shape is not None else 0\n      for i, shape in enumerate(output_shape)\n  ]\n  paddings = tf.stack(\n      [\n          tf.zeros(len(trailing_paddings), dtype=tf.int32),\n          trailing_paddings\n      ],\n      axis=1)\n  padded_tensor = tf.pad(clipped_tensor, paddings=paddings)\n  output_static_shape = [\n      dim if not isinstance(dim, tf.Tensor) else None for dim in output_shape\n  ]\n  padded_tensor.set_shape(output_static_shape)\n  return padded_tensor\n\n\ndef combined_static_and_dynamic_shape(tensor):\n  \"\"\"Returns a list containing static and dynamic values for the dimensions.\n\n  Returns a list of static and dynamic values for shape dimensions. This is\n  useful to preserve static shapes when available in reshape operation.\n\n  Args:\n    tensor: A tensor of any type.\n\n  Returns:\n    A list of size tensor.shape.ndims containing integers or a scalar tensor.\n  \"\"\"\n  static_tensor_shape = tensor.shape.as_list()\n  dynamic_tensor_shape = tf.shape(tensor)\n  combined_shape = []\n  for index, dim in enumerate(static_tensor_shape):\n    if dim is not None:\n      combined_shape.append(dim)\n    else:\n      combined_shape.append(dynamic_tensor_shape[index])\n  return combined_shape\n\n\ndef static_or_dynamic_map_fn(fn, elems, dtype=None,\n                             parallel_iterations=32, back_prop=True):\n  \"\"\"Runs map_fn as a (static) for loop when possible.\n\n  This function rewrites the map_fn as an explicit unstack input -> for loop\n  over function calls -> stack result combination.  This allows our graphs to\n  be acyclic when the batch size is static.\n  For comparison, see https://www.tensorflow.org/api_docs/python/tf/map_fn.\n\n  Note that `static_or_dynamic_map_fn` currently is not *fully* interchangeable\n  with the default tf.map_fn function as it does not accept nested inputs (only\n  Tensors or lists of Tensors).  Likewise, the output of `fn` can only be a\n  Tensor or list of Tensors.\n\n  TODO(jonathanhuang): make this function fully interchangeable with tf.map_fn.\n\n  Args:\n    fn: The callable to be performed. It accepts one argument, which will have\n      the same structure as elems. Its output must have the\n      same structure as elems.\n    elems: A tensor or list of tensors, each of which will\n      be unpacked along their first dimension. The sequence of the\n      resulting slices will be applied to fn.\n    dtype:  (optional) The output type(s) of fn. If fn returns a structure of\n      Tensors differing from the structure of elems, then dtype is not optional\n      and must have the same structure as the output of fn.\n    parallel_iterations: (optional) number of batch items to process in\n      parallel.  This flag is only used if the native tf.map_fn is used\n      and defaults to 32 instead of 10 (unlike the standard tf.map_fn default).\n    back_prop: (optional) True enables support for back propagation.\n      This flag is only used if the native tf.map_fn is used.\n\n  Returns:\n    A tensor or sequence of tensors. Each tensor packs the\n    results of applying fn to tensors unpacked from elems along the first\n    dimension, from first to last.\n  Raises:\n    ValueError: if `elems` a Tensor or a list of Tensors.\n    ValueError: if `fn` does not return a Tensor or list of Tensors\n  \"\"\"\n  if isinstance(elems, list):\n    for elem in elems:\n      if not isinstance(elem, tf.Tensor):\n        raise ValueError('`elems` must be a Tensor or list of Tensors.')\n\n    elem_shapes = [elem.shape.as_list() for elem in elems]\n    # Fall back on tf.map_fn if shapes of each entry of `elems` are None or fail\n    # to all be the same size along the batch dimension.\n    for elem_shape in elem_shapes:\n      if (not elem_shape or not elem_shape[0]\n          or elem_shape[0] != elem_shapes[0][0]):\n        return tf.map_fn(fn, elems, dtype, parallel_iterations, back_prop)\n    arg_tuples = zip(*[tf.unstack(elem) for elem in elems])\n    outputs = [fn(arg_tuple) for arg_tuple in arg_tuples]\n  else:\n    if not isinstance(elems, tf.Tensor):\n      raise ValueError('`elems` must be a Tensor or list of Tensors.')\n    elems_shape = elems.shape.as_list()\n    if not elems_shape or not elems_shape[0]:\n      return tf.map_fn(fn, elems, dtype, parallel_iterations, back_prop)\n    outputs = [fn(arg) for arg in tf.unstack(elems)]\n  # Stack `outputs`, which is a list of Tensors or list of lists of Tensors\n  if all([isinstance(output, tf.Tensor) for output in outputs]):\n    return tf.stack(outputs)\n  else:\n    if all([isinstance(output, list) for output in outputs]):\n      if all([all(\n          [isinstance(entry, tf.Tensor) for entry in output_list])\n              for output_list in outputs]):\n        return [tf.stack(output_tuple) for output_tuple in zip(*outputs)]\n  raise ValueError('`fn` should return a Tensor or a list of Tensors.')\n\n\ndef check_min_image_dim(min_dim, image_tensor):\n  \"\"\"Checks that the image width/height are greater than some number.\n\n  This function is used to check that the width and height of an image are above\n  a certain value. If the image shape is static, this function will perform the\n  check at graph construction time. Otherwise, if the image shape varies, an\n  Assertion control dependency will be added to the graph.\n\n  Args:\n    min_dim: The minimum number of pixels along the width and height of the\n             image.\n    image_tensor: The image tensor to check size for.\n\n  Returns:\n    If `image_tensor` has dynamic size, return `image_tensor` with a Assert\n    control dependency. Otherwise returns image_tensor.\n\n  Raises:\n    ValueError: if `image_tensor`'s' width or height is smaller than `min_dim`.\n  \"\"\"\n  image_shape = image_tensor.get_shape()\n  image_height = static_shape.get_height(image_shape)\n  image_width = static_shape.get_width(image_shape)\n  if image_height is None or image_width is None:\n    shape_assert = tf.Assert(\n        tf.logical_and(tf.greater_equal(tf.shape(image_tensor)[1], min_dim),\n                       tf.greater_equal(tf.shape(image_tensor)[2], min_dim)),\n        ['image size must be >= {} in both height and width.'.format(min_dim)])\n    with tf.control_dependencies([shape_assert]):\n      return tf.identity(image_tensor)\n\n  if image_height < min_dim or image_width < min_dim:\n    raise ValueError(\n        'image size must be >= %d in both height and width; image dim = %d,%d' %\n        (min_dim, image_height, image_width))\n\n  return image_tensor\n\n\ndef assert_shape_equal(shape_a, shape_b):\n  \"\"\"Asserts that shape_a and shape_b are equal.\n\n  If the shapes are static, raises a ValueError when the shapes\n  mismatch.\n\n  If the shapes are dynamic, raises a tf InvalidArgumentError when the shapes\n  mismatch.\n\n  Args:\n    shape_a: a list containing shape of the first tensor.\n    shape_b: a list containing shape of the second tensor.\n\n  Returns:\n    Either a tf.no_op() when shapes are all static and a tf.assert_equal() op\n    when the shapes are dynamic.\n\n  Raises:\n    ValueError: When shapes are both static and unequal.\n  \"\"\"\n  if (all(isinstance(dim, int) for dim in shape_a) and\n      all(isinstance(dim, int) for dim in shape_b)):\n    if shape_a != shape_b:\n      raise ValueError('Unequal shapes {}, {}'.format(shape_a, shape_b))\n    else: return tf.no_op()\n  else:\n    return tf.assert_equal(shape_a, shape_b)\n\n\ndef assert_shape_equal_along_first_dimension(shape_a, shape_b):\n  \"\"\"Asserts that shape_a and shape_b are the same along the 0th-dimension.\n\n  If the shapes are static, raises a ValueError when the shapes\n  mismatch.\n\n  If the shapes are dynamic, raises a tf InvalidArgumentError when the shapes\n  mismatch.\n\n  Args:\n    shape_a: a list containing shape of the first tensor.\n    shape_b: a list containing shape of the second tensor.\n\n  Returns:\n    Either a tf.no_op() when shapes are all static and a tf.assert_equal() op\n    when the shapes are dynamic.\n\n  Raises:\n    ValueError: When shapes are both static and unequal.\n  \"\"\"\n  if isinstance(shape_a[0], int) and isinstance(shape_b[0], int):\n    if shape_a[0] != shape_b[0]:\n      raise ValueError('Unequal first dimension {}, {}'.format(\n          shape_a[0], shape_b[0]))\n    else: return tf.no_op()\n  else:\n    return tf.assert_equal(shape_a[0], shape_b[0])\n\n\ndef assert_box_normalized(boxes, maximum_normalized_coordinate=1.1):\n  \"\"\"Asserts the input box tensor is normalized.\n\n  Args:\n    boxes: a tensor of shape [N, 4] where N is the number of boxes.\n    maximum_normalized_coordinate: Maximum coordinate value to be considered\n      as normalized, default to 1.1.\n\n  Returns:\n    a tf.Assert op which fails when the input box tensor is not normalized.\n\n  Raises:\n    ValueError: When the input box tensor is not normalized.\n  \"\"\"\n  box_minimum = tf.reduce_min(boxes)\n  box_maximum = tf.reduce_max(boxes)\n  return tf.Assert(\n      tf.logical_and(\n          tf.less_equal(box_maximum, maximum_normalized_coordinate),\n          tf.greater_equal(box_minimum, 0)),\n      [boxes])\n\n\ndef flatten_dimensions(inputs, first, last):\n  \"\"\"Flattens `K-d` tensor along [first, last) dimensions.\n\n  Converts `inputs` with shape [D0, D1, ..., D(K-1)] into a tensor of shape\n  [D0, D1, ..., D(first) * D(first+1) * ... * D(last-1), D(last), ..., D(K-1)].\n\n  Example:\n  `inputs` is a tensor with initial shape [10, 5, 20, 20, 3].\n  new_tensor = flatten_dimensions(inputs, first=1, last=3)\n  new_tensor.shape -> [10, 100, 20, 3].\n\n  Args:\n    inputs: a tensor with shape [D0, D1, ..., D(K-1)].\n    first: first value for the range of dimensions to flatten.\n    last: last value for the range of dimensions to flatten. Note that the last\n      dimension itself is excluded.\n\n  Returns:\n    a tensor with shape\n    [D0, D1, ..., D(first) * D(first + 1) * ... * D(last - 1), D(last), ...,\n     D(K-1)].\n\n  Raises:\n    ValueError: if first and last arguments are incorrect.\n  \"\"\"\n  if first >= inputs.shape.ndims or last > inputs.shape.ndims:\n    raise ValueError('`first` and `last` must be less than inputs.shape.ndims. '\n                     'found {} and {} respectively while ndims is {}'.format(\n                         first, last, inputs.shape.ndims))\n  shape = combined_static_and_dynamic_shape(inputs)\n  flattened_dim_prod = tf.reduce_prod(shape[first:last],\n                                      keepdims=True)\n  new_shape = tf.concat([shape[:first], flattened_dim_prod,\n                         shape[last:]], axis=0)\n  return tf.reshape(inputs, new_shape)\n\n\ndef flatten_first_n_dimensions(inputs, n):\n  \"\"\"Flattens `K-d` tensor along first n dimension to be a `(K-n+1)-d` tensor.\n\n  Converts `inputs` with shape [D0, D1, ..., D(K-1)] into a tensor of shape\n  [D0 * D1 * ... * D(n-1), D(n), ... D(K-1)].\n\n  Example:\n  `inputs` is a tensor with initial shape [10, 5, 20, 20, 3].\n  new_tensor = flatten_first_n_dimensions(inputs, 2)\n  new_tensor.shape -> [50, 20, 20, 3].\n\n  Args:\n    inputs: a tensor with shape [D0, D1, ..., D(K-1)].\n    n: The number of dimensions to flatten.\n\n  Returns:\n    a tensor with shape [D0 * D1 * ... * D(n-1), D(n), ... D(K-1)].\n  \"\"\"\n  return flatten_dimensions(inputs, first=0, last=n)\n\n\ndef expand_first_dimension(inputs, dims):\n  \"\"\"Expands `K-d` tensor along first dimension to be a `(K+n-1)-d` tensor.\n\n  Converts `inputs` with shape [D0, D1, ..., D(K-1)] into a tensor of shape\n  [dims[0], dims[1], ..., dims[-1], D1, ..., D(k-1)].\n\n  Example:\n  `inputs` is a tensor with shape [50, 20, 20, 3].\n  new_tensor = expand_first_dimension(inputs, [10, 5]).\n  new_tensor.shape -> [10, 5, 20, 20, 3].\n\n  Args:\n    inputs: a tensor with shape [D0, D1, ..., D(K-1)].\n    dims: List with new dimensions to expand first axis into. The length of\n      `dims` is typically 2 or larger.\n\n  Returns:\n    a tensor with shape [dims[0], dims[1], ..., dims[-1], D1, ..., D(k-1)].\n  \"\"\"\n  inputs_shape = combined_static_and_dynamic_shape(inputs)\n  expanded_shape = tf.stack(dims + inputs_shape[1:])\n\n  # Verify that it is possible to expand the first axis of inputs.\n  assert_op = tf.assert_equal(\n      inputs_shape[0], tf.reduce_prod(tf.stack(dims)),\n      message=('First dimension of `inputs` cannot be expanded into provided '\n               '`dims`'))\n\n  with tf.control_dependencies([assert_op]):\n    inputs_reshaped = tf.reshape(inputs, expanded_shape)\n\n  return inputs_reshaped\n\n\ndef resize_images_and_return_shapes(inputs, image_resizer_fn):\n  \"\"\"Resizes images using the given function and returns their true shapes.\n\n  Args:\n    inputs: a float32 Tensor representing a batch of inputs of shape\n      [batch_size, height, width, channels].\n    image_resizer_fn: a function which takes in a single image and outputs\n      a resized image and its original shape.\n\n  Returns:\n    resized_inputs: The inputs resized according to image_resizer_fn.\n    true_image_shapes: A integer tensor of shape [batch_size, 3]\n      representing the height, width and number of channels in inputs.\n  \"\"\"\n\n  if inputs.dtype is not tf.float32:\n    raise ValueError('`resize_images_and_return_shapes` expects a'\n                     ' tf.float32 tensor')\n\n  # TODO(jonathanhuang): revisit whether to always use batch size as\n  # the number of parallel iterations vs allow for dynamic batching.\n  outputs = static_or_dynamic_map_fn(\n      image_resizer_fn,\n      elems=inputs,\n      dtype=[tf.float32, tf.int32])\n  resized_inputs = outputs[0]\n  true_image_shapes = outputs[1]\n\n  return resized_inputs, true_image_shapes\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/visualize/standard_fields.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\n\"\"\"Contains classes specifying naming conventions used for object detection.\n\n\nSpecifies:\n  InputDataFields: standard fields used by reader/preprocessor/batcher.\n  DetectionResultFields: standard fields returned by object detector.\n  BoxListFields: standard field used by BoxList\n  TfExampleFields: standard fields for tf-example data format (go/tf-example).\n\"\"\"\n\n\nclass InputDataFields(object):\n  \"\"\"Names for the input tensors.\n\n  Holds the standard data field names to use for identifying input tensors. This\n  should be used by the decoder to identify keys for the returned tensor_dict\n  containing input tensors. And it should be used by the model to identify the\n  tensors it needs.\n\n  Attributes:\n    image: image.\n    image_additional_channels: additional channels.\n    original_image: image in the original input size.\n    original_image_spatial_shape: image in the original input size.\n    key: unique key corresponding to image.\n    source_id: source of the original image.\n    filename: original filename of the dataset (without common path).\n    groundtruth_image_classes: image-level class labels.\n    groundtruth_image_confidences: image-level class confidences.\n    groundtruth_boxes: coordinates of the ground truth boxes in the image.\n    groundtruth_classes: box-level class labels.\n    groundtruth_confidences: box-level class confidences. The shape should be\n      the same as the shape of groundtruth_classes.\n    groundtruth_label_types: box-level label types (e.g. explicit negative).\n    groundtruth_is_crowd: [DEPRECATED, use groundtruth_group_of instead]\n      is the groundtruth a single object or a crowd.\n    groundtruth_area: area of a groundtruth segment.\n    groundtruth_difficult: is a `difficult` object\n    groundtruth_group_of: is a `group_of` objects, e.g. multiple objects of the\n      same class, forming a connected group, where instances are heavily\n      occluding each other.\n    proposal_boxes: coordinates of object proposal boxes.\n    proposal_objectness: objectness score of each proposal.\n    groundtruth_instance_masks: ground truth instance masks.\n    groundtruth_instance_boundaries: ground truth instance boundaries.\n    groundtruth_instance_classes: instance mask-level class labels.\n    groundtruth_keypoints: ground truth keypoints.\n    groundtruth_keypoint_visibilities: ground truth keypoint visibilities.\n    groundtruth_keypoint_weights: groundtruth weight factor for keypoints.\n    groundtruth_label_weights: groundtruth label weights.\n    groundtruth_weights: groundtruth weight factor for bounding boxes.\n    num_groundtruth_boxes: number of groundtruth boxes.\n    is_annotated: whether an image has been labeled or not.\n    true_image_shapes: true shapes of images in the resized images, as resized\n      images can be padded with zeros.\n    multiclass_scores: the label score per class for each box.\n    context_features: a flattened list of contextual features.\n    context_feature_length: the fixed length of each feature in\n      context_features, used for reshaping.\n    valid_context_size: the valid context size, used in filtering the padded\n      context features.\n  \"\"\"\n  image = 'image'\n  image_additional_channels = 'image_additional_channels'\n  original_image = 'original_image'\n  original_image_spatial_shape = 'original_image_spatial_shape'\n  key = 'key'\n  source_id = 'source_id'\n  filename = 'filename'\n  groundtruth_image_classes = 'groundtruth_image_classes'\n  groundtruth_image_confidences = 'groundtruth_image_confidences'\n  groundtruth_boxes = 'groundtruth_boxes'\n  groundtruth_classes = 'groundtruth_classes'\n  groundtruth_confidences = 'groundtruth_confidences'\n  groundtruth_label_types = 'groundtruth_label_types'\n  groundtruth_is_crowd = 'groundtruth_is_crowd'\n  groundtruth_area = 'groundtruth_area'\n  groundtruth_difficult = 'groundtruth_difficult'\n  groundtruth_group_of = 'groundtruth_group_of'\n  proposal_boxes = 'proposal_boxes'\n  proposal_objectness = 'proposal_objectness'\n  groundtruth_instance_masks = 'groundtruth_instance_masks'\n  groundtruth_instance_boundaries = 'groundtruth_instance_boundaries'\n  groundtruth_instance_classes = 'groundtruth_instance_classes'\n  groundtruth_keypoints = 'groundtruth_keypoints'\n  groundtruth_keypoint_visibilities = 'groundtruth_keypoint_visibilities'\n  groundtruth_keypoint_weights = 'groundtruth_keypoint_weights'\n  groundtruth_label_weights = 'groundtruth_label_weights'\n  groundtruth_weights = 'groundtruth_weights'\n  num_groundtruth_boxes = 'num_groundtruth_boxes'\n  is_annotated = 'is_annotated'\n  true_image_shape = 'true_image_shape'\n  multiclass_scores = 'multiclass_scores'\n  context_features = 'context_features'\n  context_feature_length = 'context_feature_length'\n  valid_context_size = 'valid_context_size'\n\n\nclass DetectionResultFields(object):\n  \"\"\"Naming conventions for storing the output of the detector.\n\n  Attributes:\n    source_id: source of the original image.\n    key: unique key corresponding to image.\n    detection_boxes: coordinates of the detection boxes in the image.\n    detection_scores: detection scores for the detection boxes in the image.\n    detection_multiclass_scores: class score distribution (including background)\n      for detection boxes in the image including background class.\n    detection_classes: detection-level class labels.\n    detection_masks: contains a segmentation mask for each detection box.\n    detection_boundaries: contains an object boundary for each detection box.\n    detection_keypoints: contains detection keypoints for each detection box.\n    detection_keypoint_scores: contains detection keypoint scores.\n    num_detections: number of detections in the batch.\n    raw_detection_boxes: contains decoded detection boxes without Non-Max\n      suppression.\n    raw_detection_scores: contains class score logits for raw detection boxes.\n    detection_anchor_indices: The anchor indices of the detections after NMS.\n    detection_features: contains extracted features for each detected box\n      after NMS.\n  \"\"\"\n\n  source_id = 'source_id'\n  key = 'key'\n  detection_boxes = 'detection_boxes'\n  detection_scores = 'detection_scores'\n  detection_multiclass_scores = 'detection_multiclass_scores'\n  detection_features = 'detection_features'\n  detection_classes = 'detection_classes'\n  detection_masks = 'detection_masks'\n  detection_boundaries = 'detection_boundaries'\n  detection_keypoints = 'detection_keypoints'\n  detection_keypoint_scores = 'detection_keypoint_scores'\n  num_detections = 'num_detections'\n  raw_detection_boxes = 'raw_detection_boxes'\n  raw_detection_scores = 'raw_detection_scores'\n  detection_anchor_indices = 'detection_anchor_indices'\n\n\nclass BoxListFields(object):\n  \"\"\"Naming conventions for BoxLists.\n\n  Attributes:\n    boxes: bounding box coordinates.\n    classes: classes per bounding box.\n    scores: scores per bounding box.\n    weights: sample weights per bounding box.\n    objectness: objectness score per bounding box.\n    masks: masks per bounding box.\n    boundaries: boundaries per bounding box.\n    keypoints: keypoints per bounding box.\n    keypoint_heatmaps: keypoint heatmaps per bounding box.\n    is_crowd: is_crowd annotation per bounding box.\n  \"\"\"\n  boxes = 'boxes'\n  classes = 'classes'\n  scores = 'scores'\n  weights = 'weights'\n  confidences = 'confidences'\n  objectness = 'objectness'\n  masks = 'masks'\n  boundaries = 'boundaries'\n  keypoints = 'keypoints'\n  keypoint_heatmaps = 'keypoint_heatmaps'\n  is_crowd = 'is_crowd'\n\n\nclass PredictionFields(object):\n  \"\"\"Naming conventions for standardized prediction outputs.\n\n  Attributes:\n    feature_maps: List of feature maps for prediction.\n    anchors: Generated anchors.\n    raw_detection_boxes: Decoded detection boxes without NMS.\n    raw_detection_feature_map_indices: Feature map indices from which each raw\n      detection box was produced.\n  \"\"\"\n  feature_maps = 'feature_maps'\n  anchors = 'anchors'\n  raw_detection_boxes = 'raw_detection_boxes'\n  raw_detection_feature_map_indices = 'raw_detection_feature_map_indices'\n\n\nclass TfExampleFields(object):\n  \"\"\"TF-example proto feature names for object detection.\n\n  Holds the standard feature names to load from an Example proto for object\n  detection.\n\n  Attributes:\n    image_encoded: JPEG encoded string\n    image_format: image format, e.g. \"JPEG\"\n    filename: filename\n    channels: number of channels of image\n    colorspace: colorspace, e.g. \"RGB\"\n    height: height of image in pixels, e.g. 462\n    width: width of image in pixels, e.g. 581\n    source_id: original source of the image\n    image_class_text: image-level label in text format\n    image_class_label: image-level label in numerical format\n    object_class_text: labels in text format, e.g. [\"person\", \"cat\"]\n    object_class_label: labels in numbers, e.g. [16, 8]\n    object_bbox_xmin: xmin coordinates of groundtruth box, e.g. 10, 30\n    object_bbox_xmax: xmax coordinates of groundtruth box, e.g. 50, 40\n    object_bbox_ymin: ymin coordinates of groundtruth box, e.g. 40, 50\n    object_bbox_ymax: ymax coordinates of groundtruth box, e.g. 80, 70\n    object_view: viewpoint of object, e.g. [\"frontal\", \"left\"]\n    object_truncated: is object truncated, e.g. [true, false]\n    object_occluded: is object occluded, e.g. [true, false]\n    object_difficult: is object difficult, e.g. [true, false]\n    object_group_of: is object a single object or a group of objects\n    object_depiction: is object a depiction\n    object_is_crowd: [DEPRECATED, use object_group_of instead]\n      is the object a single object or a crowd\n    object_segment_area: the area of the segment.\n    object_weight: a weight factor for the object's bounding box.\n    instance_masks: instance segmentation masks.\n    instance_boundaries: instance boundaries.\n    instance_classes: Classes for each instance segmentation mask.\n    detection_class_label: class label in numbers.\n    detection_bbox_ymin: ymin coordinates of a detection box.\n    detection_bbox_xmin: xmin coordinates of a detection box.\n    detection_bbox_ymax: ymax coordinates of a detection box.\n    detection_bbox_xmax: xmax coordinates of a detection box.\n    detection_score: detection score for the class label and box.\n  \"\"\"\n  image_encoded = 'image/encoded'\n  image_format = 'image/format'  # format is reserved keyword\n  filename = 'image/filename'\n  channels = 'image/channels'\n  colorspace = 'image/colorspace'\n  height = 'image/height'\n  width = 'image/width'\n  source_id = 'image/source_id'\n  image_class_text = 'image/class/text'\n  image_class_label = 'image/class/label'\n  object_class_text = 'image/object/class/text'\n  object_class_label = 'image/object/class/label'\n  object_bbox_ymin = 'image/object/bbox/ymin'\n  object_bbox_xmin = 'image/object/bbox/xmin'\n  object_bbox_ymax = 'image/object/bbox/ymax'\n  object_bbox_xmax = 'image/object/bbox/xmax'\n  object_view = 'image/object/view'\n  object_truncated = 'image/object/truncated'\n  object_occluded = 'image/object/occluded'\n  object_difficult = 'image/object/difficult'\n  object_group_of = 'image/object/group_of'\n  object_depiction = 'image/object/depiction'\n  object_is_crowd = 'image/object/is_crowd'\n  object_segment_area = 'image/object/segment/area'\n  object_weight = 'image/object/weight'\n  instance_masks = 'image/segmentation/object'\n  instance_boundaries = 'image/boundaries/object'\n  instance_classes = 'image/segmentation/object/class'\n  detection_class_label = 'image/detection/label'\n  detection_bbox_ymin = 'image/detection/bbox/ymin'\n  detection_bbox_xmin = 'image/detection/bbox/xmin'\n  detection_bbox_ymax = 'image/detection/bbox/ymax'\n  detection_bbox_xmax = 'image/detection/bbox/xmax'\n  detection_score = 'image/detection/score'\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/visualize/static_shape.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Helper functions to access TensorShape values.\n\nThe rank 4 tensor_shape must be of the form [batch_size, height, width, depth].\n\"\"\"\n\n\ndef get_dim_as_int(dim):\n  \"\"\"Utility to get v1 or v2 TensorShape dim as an int.\n\n  Args:\n    dim: The TensorShape dimension to get as an int\n\n  Returns:\n    None or an int.\n  \"\"\"\n  try:\n    return dim.value\n  except AttributeError:\n    return dim\n\n\ndef get_batch_size(tensor_shape):\n  \"\"\"Returns batch size from the tensor shape.\n\n  Args:\n    tensor_shape: A rank 4 TensorShape.\n\n  Returns:\n    An integer representing the batch size of the tensor.\n  \"\"\"\n  tensor_shape.assert_has_rank(rank=4)\n  return get_dim_as_int(tensor_shape[0])\n\n\ndef get_height(tensor_shape):\n  \"\"\"Returns height from the tensor shape.\n\n  Args:\n    tensor_shape: A rank 4 TensorShape.\n\n  Returns:\n    An integer representing the height of the tensor.\n  \"\"\"\n  tensor_shape.assert_has_rank(rank=4)\n  return get_dim_as_int(tensor_shape[1])\n\n\ndef get_width(tensor_shape):\n  \"\"\"Returns width from the tensor shape.\n\n  Args:\n    tensor_shape: A rank 4 TensorShape.\n\n  Returns:\n    An integer representing the width of the tensor.\n  \"\"\"\n  tensor_shape.assert_has_rank(rank=4)\n  return get_dim_as_int(tensor_shape[2])\n\n\ndef get_depth(tensor_shape):\n  \"\"\"Returns depth from the tensor shape.\n\n  Args:\n    tensor_shape: A rank 4 TensorShape.\n\n  Returns:\n    An integer representing the depth of the tensor.\n  \"\"\"\n  tensor_shape.assert_has_rank(rank=4)\n  return get_dim_as_int(tensor_shape[3])\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/efficientdet/visualize/vis_utils.py",
    "content": "# Copyright 2020 Google Research. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"A set of functions that are used for visualization.\n\nThese functions often receive an image, perform some visualization on the image.\nThe functions do not return a value, instead they modify the image itself.\n\"\"\"\nimport abc\nimport collections\nimport matplotlib\nimport matplotlib.pyplot as plt  # pylint: disable=g-import-not-at-top\nimport numpy as np\nimport PIL.Image as Image\nimport PIL.ImageColor as ImageColor\nimport PIL.ImageDraw as ImageDraw\nimport PIL.ImageFont as ImageFont\nimport six\nfrom six.moves import range\nfrom six.moves import zip\nimport tensorflow.compat.v1 as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.visualize import shape_utils\nfrom tensorflow_examples.lite.model_maker.third_party.efficientdet.visualize import standard_fields as fields\n\n_TITLE_LEFT_MARGIN = 10\n_TITLE_TOP_MARGIN = 10\nSTANDARD_COLORS = [\n    'AliceBlue', 'Chartreuse', 'Aqua', 'Aquamarine', 'Azure', 'Beige', 'Bisque',\n    'BlanchedAlmond', 'BlueViolet', 'BurlyWood', 'CadetBlue', 'AntiqueWhite',\n    'Chocolate', 'Coral', 'CornflowerBlue', 'Cornsilk', 'Crimson', 'Cyan',\n    'DarkCyan', 'DarkGoldenRod', 'DarkGrey', 'DarkKhaki', 'DarkOrange',\n    'DarkOrchid', 'DarkSalmon', 'DarkSeaGreen', 'DarkTurquoise', 'DarkViolet',\n    'DeepPink', 'DeepSkyBlue', 'DodgerBlue', 'FireBrick', 'FloralWhite',\n    'ForestGreen', 'Fuchsia', 'Gainsboro', 'GhostWhite', 'Gold', 'GoldenRod',\n    'Salmon', 'Tan', 'HoneyDew', 'HotPink', 'IndianRed', 'Ivory', 'Khaki',\n    'Lavender', 'LavenderBlush', 'LawnGreen', 'LemonChiffon', 'LightBlue',\n    'LightCoral', 'LightCyan', 'LightGoldenRodYellow', 'LightGray', 'LightGrey',\n    'LightGreen', 'LightPink', 'LightSalmon', 'LightSeaGreen', 'LightSkyBlue',\n    'LightSlateGray', 'LightSlateGrey', 'LightSteelBlue', 'LightYellow', 'Lime',\n    'LimeGreen', 'Linen', 'Magenta', 'MediumAquaMarine', 'MediumOrchid',\n    'MediumPurple', 'MediumSeaGreen', 'MediumSlateBlue', 'MediumSpringGreen',\n    'MediumTurquoise', 'MediumVioletRed', 'MintCream', 'MistyRose', 'Moccasin',\n    'NavajoWhite', 'OldLace', 'Olive', 'OliveDrab', 'Orange', 'OrangeRed',\n    'Orchid', 'PaleGoldenRod', 'PaleGreen', 'PaleTurquoise', 'PaleVioletRed',\n    'PapayaWhip', 'PeachPuff', 'Peru', 'Pink', 'Plum', 'PowderBlue', 'Purple',\n    'Red', 'RosyBrown', 'RoyalBlue', 'SaddleBrown', 'Green', 'SandyBrown',\n    'SeaGreen', 'SeaShell', 'Sienna', 'Silver', 'SkyBlue', 'SlateBlue',\n    'SlateGray', 'SlateGrey', 'Snow', 'SpringGreen', 'SteelBlue', 'GreenYellow',\n    'Teal', 'Thistle', 'Tomato', 'Turquoise', 'Violet', 'Wheat', 'White',\n    'WhiteSmoke', 'Yellow', 'YellowGreen'\n]\n\n\ndef _force_matplotlib_backend():\n  \"\"\"force the backend of matplotlib to Agg.\"\"\"\n  if matplotlib.get_backend().lower() != 'agg':  # case-insensitive\n    matplotlib.use('Agg')  # Set headless-friendly backend.\n\n\ndef _get_multiplier_for_color_randomness():\n  \"\"\"Returns a multiplier to get semi-random colors from successive indices.\n\n  This function computes a prime number, p, in the range [2, 17] that:\n  - is closest to len(STANDARD_COLORS) / 10\n  - does not divide len(STANDARD_COLORS)\n\n  If no prime numbers in that range satisfy the constraints, p is returned as 1.\n\n  Once p is established, it can be used as a multiplier to select\n  non-consecutive colors from STANDARD_COLORS:\n  colors = [(p * i) % len(STANDARD_COLORS) for i in range(20)]\n  \"\"\"\n  num_colors = len(STANDARD_COLORS)\n  prime_candidates = [5, 7, 11, 13, 17]\n\n  # Remove all prime candidates that divide the number of colors.\n  prime_candidates = [p for p in prime_candidates if num_colors % p]\n  if not prime_candidates:\n    return 1\n\n  # Return the closest prime number to num_colors / 10.\n  abs_distance = [np.abs(num_colors / 10. - p) for p in prime_candidates]\n  num_candidates = len(abs_distance)\n  inds = [i for _, i in sorted(zip(abs_distance, range(num_candidates)))]\n  return prime_candidates[inds[0]]\n\n\ndef save_image_array_as_png(image, output_path):\n  \"\"\"Saves an image (represented as a numpy array) to PNG.\n\n  Args:\n    image: a numpy array with shape [height, width, 3].\n    output_path: path to which image should be written.\n  \"\"\"\n  image_pil = Image.fromarray(np.uint8(image)).convert('RGB')\n  with tf.gfile.Open(output_path, 'w') as fid:\n    image_pil.save(fid, 'PNG')\n\n\ndef encode_image_array_as_png_str(image):\n  \"\"\"Encodes a numpy array into a PNG string.\n\n  Args:\n    image: a numpy array with shape [height, width, 3].\n\n  Returns:\n    PNG encoded image string.\n  \"\"\"\n  image_pil = Image.fromarray(np.uint8(image))\n  output = six.BytesIO()\n  image_pil.save(output, format='PNG')\n  png_string = output.getvalue()\n  output.close()\n  return png_string\n\n\ndef draw_bounding_box_on_image_array(image,\n                                     ymin,\n                                     xmin,\n                                     ymax,\n                                     xmax,\n                                     color='red',\n                                     thickness=4,\n                                     display_str_list=(),\n                                     use_normalized_coordinates=True):\n  \"\"\"Adds a bounding box to an image (numpy array).\n\n  Bounding box coordinates can be specified in either absolute (pixel) or\n  normalized coordinates by setting the use_normalized_coordinates argument.\n\n  Args:\n    image: a numpy array with shape [height, width, 3].\n    ymin: ymin of bounding box.\n    xmin: xmin of bounding box.\n    ymax: ymax of bounding box.\n    xmax: xmax of bounding box.\n    color: color to draw bounding box. Default is red.\n    thickness: line thickness. Default value is 4.\n    display_str_list: list of strings to display in box (each to be shown on its\n      own line).\n    use_normalized_coordinates: If True (default), treat coordinates ymin, xmin,\n      ymax, xmax as relative to the image.  Otherwise treat coordinates as\n      absolute.\n  \"\"\"\n  image_pil = Image.fromarray(np.uint8(image)).convert('RGB')\n  draw_bounding_box_on_image(image_pil, ymin, xmin, ymax, xmax, color,\n                             thickness, display_str_list,\n                             use_normalized_coordinates)\n  np.copyto(image, np.array(image_pil))\n\n\ndef draw_bounding_box_on_image(image,\n                               ymin,\n                               xmin,\n                               ymax,\n                               xmax,\n                               color='red',\n                               thickness=4,\n                               display_str_list=(),\n                               use_normalized_coordinates=True):\n  \"\"\"Adds a bounding box to an image.\n\n  Bounding box coordinates can be specified in either absolute (pixel) or\n  normalized coordinates by setting the use_normalized_coordinates argument.\n\n  Each string in display_str_list is displayed on a separate line above the\n  bounding box in black text on a rectangle filled with the input 'color'.\n  If the top of the bounding box extends to the edge of the image, the strings\n  are displayed below the bounding box.\n\n  Args:\n    image: a PIL.Image object.\n    ymin: ymin of bounding box.\n    xmin: xmin of bounding box.\n    ymax: ymax of bounding box.\n    xmax: xmax of bounding box.\n    color: color to draw bounding box. Default is red.\n    thickness: line thickness. Default value is 4.\n    display_str_list: list of strings to display in box (each to be shown on its\n      own line).\n    use_normalized_coordinates: If True (default), treat coordinates ymin, xmin,\n      ymax, xmax as relative to the image.  Otherwise treat coordinates as\n      absolute.\n  \"\"\"\n  draw = ImageDraw.Draw(image)\n  im_width, im_height = image.size\n  if use_normalized_coordinates:\n    (left, right, top, bottom) = (xmin * im_width, xmax * im_width,\n                                  ymin * im_height, ymax * im_height)\n  else:\n    (left, right, top, bottom) = (xmin, xmax, ymin, ymax)\n  if thickness > 0:\n    draw.line([(left, top), (left, bottom), (right, bottom), (right, top),\n               (left, top)],\n              width=thickness,\n              fill=color)\n  try:\n    font = ImageFont.truetype('arial.ttf', 24)\n  except IOError:\n    font = ImageFont.load_default()\n\n  # If the total height of the display strings added to the top of the bounding\n  # box exceeds the top of the image, stack the strings below the bounding box\n  # instead of above.\n  display_str_heights = [font.getbbox(ds)[3] for ds in display_str_list]\n  # Each display_str has a top and bottom margin of 0.05x.\n  total_display_str_height = (1 + 2 * 0.05) * sum(display_str_heights)\n\n  if top > total_display_str_height:\n    text_bottom = top\n  else:\n    text_bottom = bottom + total_display_str_height\n  # Reverse list and print from bottom to top.\n  for display_str in display_str_list[::-1]:\n    _, _, text_width, text_height = font.getbbox(display_str)\n    margin = np.ceil(0.05 * text_height)\n    draw.rectangle([(left, text_bottom - text_height - 2 * margin),\n                    (left + text_width, text_bottom)],\n                   fill=color)\n    draw.text((left + margin, text_bottom - text_height - margin),\n              display_str,\n              fill='black',\n              font=font)\n    text_bottom -= text_height - 2 * margin\n\n\ndef draw_bounding_boxes_on_image_array(image,\n                                       boxes,\n                                       color='red',\n                                       thickness=4,\n                                       display_str_list_list=()):\n  \"\"\"Draws bounding boxes on image (numpy array).\n\n  Args:\n    image: a numpy array object.\n    boxes: a 2 dimensional numpy array of [N, 4]: (ymin, xmin, ymax, xmax). The\n      coordinates are in normalized format between [0, 1].\n    color: color to draw bounding box. Default is red.\n    thickness: line thickness. Default value is 4.\n    display_str_list_list: list of list of strings. a list of strings for each\n      bounding box. The reason to pass a list of strings for a bounding box is\n      that it might contain multiple labels.\n\n  Raises:\n    ValueError: if boxes is not a [N, 4] array\n  \"\"\"\n  image_pil = Image.fromarray(image)\n  draw_bounding_boxes_on_image(image_pil, boxes, color, thickness,\n                               display_str_list_list)\n  np.copyto(image, np.array(image_pil))\n\n\ndef draw_bounding_boxes_on_image(image,\n                                 boxes,\n                                 color='red',\n                                 thickness=4,\n                                 display_str_list_list=()):\n  \"\"\"Draws bounding boxes on image.\n\n  Args:\n    image: a PIL.Image object.\n    boxes: a 2 dimensional numpy array of [N, 4]: (ymin, xmin, ymax, xmax). The\n      coordinates are in normalized format between [0, 1].\n    color: color to draw bounding box. Default is red.\n    thickness: line thickness. Default value is 4.\n    display_str_list_list: list of list of strings. a list of strings for each\n      bounding box. The reason to pass a list of strings for a bounding box is\n      that it might contain multiple labels.\n\n  Raises:\n    ValueError: if boxes is not a [N, 4] array\n  \"\"\"\n  boxes_shape = boxes.shape\n  if not boxes_shape:\n    return\n  if len(boxes_shape) != 2 or boxes_shape[1] != 4:\n    raise ValueError('Input must be of size [N, 4]')\n  for i in range(boxes_shape[0]):\n    display_str_list = ()\n    if display_str_list_list:\n      display_str_list = display_str_list_list[i]\n    draw_bounding_box_on_image(image, boxes[i, 0], boxes[i, 1], boxes[i, 2],\n                               boxes[i, 3], color, thickness, display_str_list)\n\n\ndef create_visualization_fn(category_index,\n                            include_masks=False,\n                            include_keypoints=False,\n                            include_track_ids=False,\n                            **kwargs):\n  \"\"\"Constructs a visualization function that can be wrapped in a py_func.\n\n  py_funcs only accept positional arguments. This function returns a suitable\n  function with the correct positional argument mapping. The positional\n  arguments in order are:\n  0: image\n  1: boxes\n  2: classes\n  3: scores\n  [4-6]: masks (optional)\n  [4-6]: keypoints (optional)\n  [4-6]: track_ids (optional)\n\n  -- Example 1 --\n  vis_only_masks_fn = create_visualization_fn(category_index,\n    include_masks=True, include_keypoints=False, include_track_ids=False,\n    **kwargs)\n  image = tf.py_func(vis_only_masks_fn,\n                     inp=[image, boxes, classes, scores, masks],\n                     Tout=tf.uint8)\n\n  -- Example 2 --\n  vis_masks_and_track_ids_fn = create_visualization_fn(category_index,\n    include_masks=True, include_keypoints=False, include_track_ids=True,\n    **kwargs)\n  image = tf.py_func(vis_masks_and_track_ids_fn,\n                     inp=[image, boxes, classes, scores, masks, track_ids],\n                     Tout=tf.uint8)\n\n  Args:\n    category_index: a dict that maps integer ids to category dicts. e.g.\n      {1: {1: 'dog'}, 2: {2: 'cat'}, ...}\n    include_masks: Whether masks should be expected as a positional argument in\n      the returned function.\n    include_keypoints: Whether keypoints should be expected as a positional\n      argument in the returned function.\n    include_track_ids: Whether track ids should be expected as a positional\n      argument in the returned function.\n    **kwargs: Additional kwargs that will be passed to\n      visualize_boxes_and_labels_on_image_array.\n\n  Returns:\n    Returns a function that only takes tensors as positional arguments.\n  \"\"\"\n\n  def visualization_py_func_fn(*args):\n    \"\"\"Visualization function that can be wrapped in a tf.py_func.\n\n    Args:\n      *args: First 4 positional arguments must be: image - uint8 numpy array\n        with shape (img_height, img_width, 3). boxes - a numpy array of shape\n        [N, 4]. classes - a numpy array of shape [N]. scores - a numpy array of\n        shape [N] or None. -- Optional positional arguments -- instance_masks -\n        a numpy array of shape [N, image_height, image_width]. keypoints - a\n        numpy array of shape [N, num_keypoints, 2]. track_ids - a numpy array of\n        shape [N] with unique track ids.\n\n    Returns:\n      uint8 numpy array with shape (img_height, img_width, 3) with overlaid\n      boxes.\n    \"\"\"\n    image = args[0]\n    boxes = args[1]\n    classes = args[2]\n    scores = args[3]\n    masks = keypoints = track_ids = None\n    pos_arg_ptr = 4  # Positional argument for first optional tensor (masks).\n    if include_masks:\n      masks = args[pos_arg_ptr]\n      pos_arg_ptr += 1\n    if include_keypoints:\n      keypoints = args[pos_arg_ptr]\n      pos_arg_ptr += 1\n    if include_track_ids:\n      track_ids = args[pos_arg_ptr]\n\n    return visualize_boxes_and_labels_on_image_array(\n        image,\n        boxes,\n        classes,\n        scores,\n        category_index=category_index,\n        instance_masks=masks,\n        keypoints=keypoints,\n        track_ids=track_ids,\n        **kwargs)\n\n  return visualization_py_func_fn\n\n\ndef _resize_original_image(image, image_shape):\n  image = tf.expand_dims(image, 0)\n  image = tf.image.resize_images(\n      image,\n      image_shape,\n      method=tf.image.ResizeMethod.NEAREST_NEIGHBOR,\n      align_corners=True)\n  return tf.cast(tf.squeeze(image, 0), tf.uint8)\n\n\ndef draw_bounding_boxes_on_image_tensors(images,\n                                         boxes,\n                                         classes,\n                                         scores,\n                                         category_index,\n                                         original_image_spatial_shape=None,\n                                         true_image_shape=None,\n                                         instance_masks=None,\n                                         keypoints=None,\n                                         keypoint_edges=None,\n                                         track_ids=None,\n                                         max_boxes_to_draw=20,\n                                         min_score_thresh=0.2,\n                                         use_normalized_coordinates=True):\n  \"\"\"Draws bounding boxes, masks, and keypoints on batch of image tensors.\n\n  Args:\n    images: A 4D uint8 image tensor of shape [N, H, W, C]. If C > 3, additional\n      channels will be ignored. If C = 1, then we convert the images to RGB\n      images.\n    boxes: [N, max_detections, 4] float32 tensor of detection boxes.\n    classes: [N, max_detections] int tensor of detection classes. Note that\n      classes are 1-indexed.\n    scores: [N, max_detections] float32 tensor of detection scores.\n    category_index: a dict that maps integer ids to category dicts. e.g.\n      {1: {1: 'dog'}, 2: {2: 'cat'}, ...}\n    original_image_spatial_shape: [N, 2] tensor containing the spatial size of\n      the original image.\n    true_image_shape: [N, 3] tensor containing the spatial size of unpadded\n      original_image.\n    instance_masks: A 4D uint8 tensor of shape [N, max_detection, H, W] with\n      instance masks.\n    keypoints: A 4D float32 tensor of shape [N, max_detection, num_keypoints, 2]\n      with keypoints.\n    keypoint_edges: A list of tuples with keypoint indices that specify which\n      keypoints should be connected by an edge, e.g. [(0, 1), (2, 4)] draws\n      edges from keypoint 0 to 1 and from keypoint 2 to 4.\n    track_ids: [N, max_detections] int32 tensor of unique tracks ids (i.e.\n      instance ids for each object). If provided, the color-coding of boxes is\n      dictated by these ids, and not classes.\n    max_boxes_to_draw: Maximum number of boxes to draw on an image. Default 20.\n    min_score_thresh: Minimum score threshold for visualization. Default 0.2.\n    use_normalized_coordinates: Whether to assume boxes and kepoints are in\n      normalized coordinates (as opposed to absolute coordiantes). Default is\n      True.\n\n  Returns:\n    4D image tensor of type uint8, with boxes drawn on top.\n  \"\"\"\n  # Additional channels are being ignored.\n  if images.shape[3] > 3:\n    images = images[:, :, :, 0:3]\n  elif images.shape[3] == 1:\n    images = tf.image.grayscale_to_rgb(images)\n  visualization_keyword_args = {\n      'use_normalized_coordinates': use_normalized_coordinates,\n      'max_boxes_to_draw': max_boxes_to_draw,\n      'min_score_thresh': min_score_thresh,\n      'agnostic_mode': False,\n      'line_thickness': 4,\n      'keypoint_edges': keypoint_edges\n  }\n  if true_image_shape is None:\n    true_shapes = tf.constant(-1, shape=[images.shape.as_list()[0], 3])\n  else:\n    true_shapes = true_image_shape\n  if original_image_spatial_shape is None:\n    original_shapes = tf.constant(-1, shape=[images.shape.as_list()[0], 2])\n  else:\n    original_shapes = original_image_spatial_shape\n\n  visualize_boxes_fn = create_visualization_fn(\n      category_index,\n      include_masks=instance_masks is not None,\n      include_keypoints=keypoints is not None,\n      include_track_ids=track_ids is not None,\n      **visualization_keyword_args)\n\n  elems = [true_shapes, original_shapes, images, boxes, classes, scores]\n  if instance_masks is not None:\n    elems.append(instance_masks)\n  if keypoints is not None:\n    elems.append(keypoints)\n  if track_ids is not None:\n    elems.append(track_ids)\n\n  def draw_boxes(image_and_detections):\n    \"\"\"Draws boxes on image.\"\"\"\n    true_shape = image_and_detections[0]\n    original_shape = image_and_detections[1]\n    if true_image_shape is not None:\n      image = shape_utils.pad_or_clip_nd(image_and_detections[2],\n                                         [true_shape[0], true_shape[1], 3])\n    if original_image_spatial_shape is not None:\n      image_and_detections[2] = _resize_original_image(image, original_shape)\n\n    image_with_boxes = tf.py_func(visualize_boxes_fn, image_and_detections[2:],\n                                  tf.uint8)\n    return image_with_boxes\n\n  images = tf.map_fn(draw_boxes, elems, dtype=tf.uint8, back_prop=False)\n  return images\n\n\ndef draw_side_by_side_evaluation_image(eval_dict,\n                                       category_index,\n                                       max_boxes_to_draw=20,\n                                       min_score_thresh=0.2,\n                                       use_normalized_coordinates=True,\n                                       keypoint_edges=None):\n  \"\"\"Creates a side-by-side image with detections and groundtruth.\n\n  Bounding boxes (and instance masks, if available) are visualized on both\n  subimages.\n\n  Args:\n    eval_dict: The evaluation dictionary returned by\n      eval_util.result_dict_for_batched_example() or\n      eval_util.result_dict_for_single_example().\n    category_index: A category index (dictionary) produced from a labelmap.\n    max_boxes_to_draw: The maximum number of boxes to draw for detections.\n    min_score_thresh: The minimum score threshold for showing detections.\n    use_normalized_coordinates: Whether to assume boxes and keypoints are in\n      normalized coordinates (as opposed to absolute coordinates). Default is\n      True.\n    keypoint_edges: A list of tuples with keypoint indices that specify which\n      keypoints should be connected by an edge, e.g. [(0, 1), (2, 4)] draws\n      edges from keypoint 0 to 1 and from keypoint 2 to 4.\n\n  Returns:\n    A list of [1, H, 2 * W, C] uint8 tensor. The subimage on the left\n      corresponds to detections, while the subimage on the right corresponds to\n      groundtruth.\n  \"\"\"\n  detection_fields = fields.DetectionResultFields()\n  input_data_fields = fields.InputDataFields()\n\n  images_with_detections_list = []\n\n  # Add the batch dimension if the eval_dict is for single example.\n  if len(eval_dict[detection_fields.detection_classes].shape) == 1:\n    for key in eval_dict:\n      if key != input_data_fields.original_image and key != input_data_fields.image_additional_channels:\n        eval_dict[key] = tf.expand_dims(eval_dict[key], 0)\n\n  for indx in range(eval_dict[input_data_fields.original_image].shape[0]):\n    instance_masks = None\n    if detection_fields.detection_masks in eval_dict:\n      instance_masks = tf.cast(\n          tf.expand_dims(\n              eval_dict[detection_fields.detection_masks][indx], axis=0),\n          tf.uint8)\n    keypoints = None\n    if detection_fields.detection_keypoints in eval_dict:\n      keypoints = tf.expand_dims(\n          eval_dict[detection_fields.detection_keypoints][indx], axis=0)\n    groundtruth_instance_masks = None\n    if input_data_fields.groundtruth_instance_masks in eval_dict:\n      groundtruth_instance_masks = tf.cast(\n          tf.expand_dims(\n              eval_dict[input_data_fields.groundtruth_instance_masks][indx],\n              axis=0), tf.uint8)\n\n    images_with_detections = draw_bounding_boxes_on_image_tensors(\n        tf.expand_dims(\n            eval_dict[input_data_fields.original_image][indx], axis=0),\n        tf.expand_dims(\n            eval_dict[detection_fields.detection_boxes][indx], axis=0),\n        tf.expand_dims(\n            eval_dict[detection_fields.detection_classes][indx], axis=0),\n        tf.expand_dims(\n            eval_dict[detection_fields.detection_scores][indx], axis=0),\n        category_index,\n        original_image_spatial_shape=tf.expand_dims(\n            eval_dict[input_data_fields.original_image_spatial_shape][indx],\n            axis=0),\n        true_image_shape=tf.expand_dims(\n            eval_dict[input_data_fields.true_image_shape][indx], axis=0),\n        instance_masks=instance_masks,\n        keypoints=keypoints,\n        keypoint_edges=keypoint_edges,\n        max_boxes_to_draw=max_boxes_to_draw,\n        min_score_thresh=min_score_thresh,\n        use_normalized_coordinates=use_normalized_coordinates)\n    images_with_groundtruth = draw_bounding_boxes_on_image_tensors(\n        tf.expand_dims(\n            eval_dict[input_data_fields.original_image][indx], axis=0),\n        tf.expand_dims(\n            eval_dict[input_data_fields.groundtruth_boxes][indx], axis=0),\n        tf.expand_dims(\n            eval_dict[input_data_fields.groundtruth_classes][indx], axis=0),\n        tf.expand_dims(\n            tf.ones_like(\n                eval_dict[input_data_fields.groundtruth_classes][indx],\n                dtype=tf.float32),\n            axis=0),\n        category_index,\n        original_image_spatial_shape=tf.expand_dims(\n            eval_dict[input_data_fields.original_image_spatial_shape][indx],\n            axis=0),\n        true_image_shape=tf.expand_dims(\n            eval_dict[input_data_fields.true_image_shape][indx], axis=0),\n        instance_masks=groundtruth_instance_masks,\n        keypoints=None,\n        keypoint_edges=None,\n        max_boxes_to_draw=None,\n        min_score_thresh=0.0,\n        use_normalized_coordinates=use_normalized_coordinates)\n    images_to_visualize = tf.concat(\n        [images_with_detections, images_with_groundtruth], axis=2)\n\n    if input_data_fields.image_additional_channels in eval_dict:\n      images_with_additional_channels_groundtruth = (\n          draw_bounding_boxes_on_image_tensors(\n              tf.expand_dims(\n                  eval_dict[input_data_fields.image_additional_channels][indx],\n                  axis=0),\n              tf.expand_dims(\n                  eval_dict[input_data_fields.groundtruth_boxes][indx], axis=0),\n              tf.expand_dims(\n                  eval_dict[input_data_fields.groundtruth_classes][indx],\n                  axis=0),\n              tf.expand_dims(\n                  tf.ones_like(\n                      eval_dict[input_data_fields.groundtruth_classes][indx],\n                      dtype=tf.float32),\n                  axis=0),\n              category_index,\n              original_image_spatial_shape=tf.expand_dims(\n                  eval_dict[input_data_fields.original_image_spatial_shape]\n                  [indx],\n                  axis=0),\n              true_image_shape=tf.expand_dims(\n                  eval_dict[input_data_fields.true_image_shape][indx], axis=0),\n              instance_masks=groundtruth_instance_masks,\n              keypoints=None,\n              keypoint_edges=None,\n              max_boxes_to_draw=None,\n              min_score_thresh=0.0,\n              use_normalized_coordinates=use_normalized_coordinates))\n      images_to_visualize = tf.concat(\n          [images_to_visualize, images_with_additional_channels_groundtruth],\n          axis=2)\n    images_with_detections_list.append(images_to_visualize)\n\n  return images_with_detections_list\n\n\ndef draw_keypoints_on_image_array(image,\n                                  keypoints,\n                                  color='red',\n                                  radius=2,\n                                  use_normalized_coordinates=True,\n                                  keypoint_edges=None,\n                                  keypoint_edge_color='green',\n                                  keypoint_edge_width=2):\n  \"\"\"Draws keypoints on an image (numpy array).\n\n  Args:\n    image: a numpy array with shape [height, width, 3].\n    keypoints: a numpy array with shape [num_keypoints, 2].\n    color: color to draw the keypoints with. Default is red.\n    radius: keypoint radius. Default value is 2.\n    use_normalized_coordinates: if True (default), treat keypoint values as\n      relative to the image.  Otherwise treat them as absolute.\n    keypoint_edges: A list of tuples with keypoint indices that specify which\n      keypoints should be connected by an edge, e.g. [(0, 1), (2, 4)] draws\n      edges from keypoint 0 to 1 and from keypoint 2 to 4.\n    keypoint_edge_color: color to draw the keypoint edges with. Default is red.\n    keypoint_edge_width: width of the edges drawn between keypoints. Default\n      value is 2.\n  \"\"\"\n  image_pil = Image.fromarray(np.uint8(image)).convert('RGB')\n  draw_keypoints_on_image(image_pil, keypoints, color, radius,\n                          use_normalized_coordinates, keypoint_edges,\n                          keypoint_edge_color, keypoint_edge_width)\n  np.copyto(image, np.array(image_pil))\n\n\ndef draw_keypoints_on_image(image,\n                            keypoints,\n                            color='red',\n                            radius=2,\n                            use_normalized_coordinates=True,\n                            keypoint_edges=None,\n                            keypoint_edge_color='green',\n                            keypoint_edge_width=2):\n  \"\"\"Draws keypoints on an image.\n\n  Args:\n    image: a PIL.Image object.\n    keypoints: a numpy array with shape [num_keypoints, 2].\n    color: color to draw the keypoints with. Default is red.\n    radius: keypoint radius. Default value is 2.\n    use_normalized_coordinates: if True (default), treat keypoint values as\n      relative to the image.  Otherwise treat them as absolute.\n    keypoint_edges: A list of tuples with keypoint indices that specify which\n      keypoints should be connected by an edge, e.g. [(0, 1), (2, 4)] draws\n      edges from keypoint 0 to 1 and from keypoint 2 to 4.\n    keypoint_edge_color: color to draw the keypoint edges with. Default is red.\n    keypoint_edge_width: width of the edges drawn between keypoints. Default\n      value is 2.\n  \"\"\"\n  draw = ImageDraw.Draw(image)\n  im_width, im_height = image.size\n  keypoints_x = [k[1] for k in keypoints]\n  keypoints_y = [k[0] for k in keypoints]\n  if use_normalized_coordinates:\n    keypoints_x = tuple([im_width * x for x in keypoints_x])\n    keypoints_y = tuple([im_height * y for y in keypoints_y])\n  for keypoint_x, keypoint_y in zip(keypoints_x, keypoints_y):\n    draw.ellipse([(keypoint_x - radius, keypoint_y - radius),\n                  (keypoint_x + radius, keypoint_y + radius)],\n                 outline=color,\n                 fill=color)\n  if keypoint_edges is not None:\n    for keypoint_start, keypoint_end in keypoint_edges:\n      if (keypoint_start < 0 or keypoint_start >= len(keypoints) or\n          keypoint_end < 0 or keypoint_end >= len(keypoints)):\n        continue\n      edge_coordinates = [\n          keypoints_x[keypoint_start], keypoints_y[keypoint_start],\n          keypoints_x[keypoint_end], keypoints_y[keypoint_end]\n      ]\n      draw.line(\n          edge_coordinates, fill=keypoint_edge_color, width=keypoint_edge_width)\n\n\ndef draw_mask_on_image_array(image, mask, color='red', alpha=0.4):\n  \"\"\"Draws mask on an image.\n\n  Args:\n    image: uint8 numpy array with shape (img_height, img_height, 3)\n    mask: a uint8 numpy array of shape (img_height, img_height) with values\n      between either 0 or 1.\n    color: color to draw the keypoints with. Default is red.\n    alpha: transparency value between 0 and 1. (default: 0.4)\n\n  Raises:\n    ValueError: On incorrect data type for image or masks.\n  \"\"\"\n  if image.dtype != np.uint8:\n    raise ValueError('`image` not of type np.uint8')\n  if mask.dtype != np.uint8:\n    raise ValueError('`mask` not of type np.uint8')\n  if np.any(np.logical_and(mask != 1, mask != 0)):\n    raise ValueError('`mask` elements should be in [0, 1]')\n  if image.shape[:2] != mask.shape:\n    raise ValueError('The image has spatial dimensions %s but the mask has '\n                     'dimensions %s' % (image.shape[:2], mask.shape))\n  rgb = ImageColor.getrgb(color)\n  pil_image = Image.fromarray(image)\n\n  solid_color = np.expand_dims(\n      np.ones_like(mask), axis=2) * np.reshape(list(rgb), [1, 1, 3])\n  pil_solid_color = Image.fromarray(np.uint8(solid_color)).convert('RGBA')\n  pil_mask = Image.fromarray(np.uint8(255.0 * alpha * mask)).convert('L')\n  pil_image = Image.composite(pil_solid_color, pil_image, pil_mask)\n  np.copyto(image, np.array(pil_image.convert('RGB')))\n\n\ndef visualize_boxes_and_labels_on_image_array(\n    image,\n    boxes,\n    classes,\n    scores,\n    category_index,\n    instance_masks=None,\n    instance_boundaries=None,\n    keypoints=None,\n    keypoint_edges=None,\n    track_ids=None,\n    use_normalized_coordinates=False,\n    max_boxes_to_draw=20,\n    min_score_thresh=.5,\n    agnostic_mode=False,\n    line_thickness=4,\n    groundtruth_box_visualization_color='black',\n    skip_boxes=False,\n    skip_scores=False,\n    skip_labels=False,\n    skip_track_ids=False):\n  \"\"\"Overlay labeled boxes on an image with formatted scores and label names.\n\n  This function groups boxes that correspond to the same location\n  and creates a display string for each detection and overlays these\n  on the image. Note that this function modifies the image in place, and returns\n  that same image.\n\n  Args:\n    image: uint8 numpy array with shape (img_height, img_width, 3)\n    boxes: a numpy array of shape [N, 4]\n    classes: a numpy array of shape [N]. Note that class indices are 1-based,\n      and match the keys in the label map.\n    scores: a numpy array of shape [N] or None.  If scores=None, then this\n      function assumes that the boxes to be plotted are groundtruth boxes and\n      plot all boxes as black with no classes or scores.\n    category_index: a dict containing category dictionaries (each holding\n      category index `id` and category name `name`) keyed by category indices.\n    instance_masks: a numpy array of shape [N, image_height, image_width] with\n      values ranging between 0 and 1, can be None.\n    instance_boundaries: a numpy array of shape [N, image_height, image_width]\n      with values ranging between 0 and 1, can be None.\n    keypoints: a numpy array of shape [N, num_keypoints, 2], can be None\n    keypoint_edges: A list of tuples with keypoint indices that specify which\n      keypoints should be connected by an edge, e.g. [(0, 1), (2, 4)] draws\n      edges from keypoint 0 to 1 and from keypoint 2 to 4.\n    track_ids: a numpy array of shape [N] with unique track ids. If provided,\n      color-coding of boxes will be determined by these ids, and not the class\n      indices.\n    use_normalized_coordinates: whether boxes is to be interpreted as normalized\n      coordinates or not.\n    max_boxes_to_draw: maximum number of boxes to visualize.  If None, draw all\n      boxes.\n    min_score_thresh: minimum score threshold for a box to be visualized\n    agnostic_mode: boolean (default: False) controlling whether to evaluate in\n      class-agnostic mode or not.  This mode will display scores but ignore\n      classes.\n    line_thickness: integer (default: 4) controlling line width of the boxes.\n    groundtruth_box_visualization_color: box color for visualizing groundtruth\n      boxes\n    skip_boxes: whether to skip the drawing of bounding boxes.\n    skip_scores: whether to skip score when drawing a single detection\n    skip_labels: whether to skip label when drawing a single detection\n    skip_track_ids: whether to skip track id when drawing a single detection\n\n  Returns:\n    uint8 numpy array with shape (img_height, img_width, 3) with overlaid boxes.\n  \"\"\"\n  # Create a display string (and color) for every box location, group any boxes\n  # that correspond to the same location.\n  box_to_display_str_map = collections.defaultdict(list)\n  box_to_color_map = collections.defaultdict(str)\n  box_to_instance_masks_map = {}\n  box_to_instance_boundaries_map = {}\n  box_to_keypoints_map = collections.defaultdict(list)\n  box_to_track_ids_map = {}\n  if not max_boxes_to_draw:\n    max_boxes_to_draw = boxes.shape[0]\n  for i in range(boxes.shape[0]):\n    if max_boxes_to_draw == len(box_to_color_map):\n      break\n    if scores is None or scores[i] > min_score_thresh:\n      box = tuple(boxes[i].tolist())\n      if instance_masks is not None:\n        box_to_instance_masks_map[box] = instance_masks[i]\n      if instance_boundaries is not None:\n        box_to_instance_boundaries_map[box] = instance_boundaries[i]\n      if keypoints is not None:\n        box_to_keypoints_map[box].extend(keypoints[i])\n      if track_ids is not None:\n        box_to_track_ids_map[box] = track_ids[i]\n      if scores is None:\n        box_to_color_map[box] = groundtruth_box_visualization_color\n      else:\n        display_str = ''\n        if not skip_labels:\n          if not agnostic_mode:\n            if classes[i] in six.viewkeys(category_index):\n              class_name = category_index[classes[i]]['name']\n            else:\n              class_name = 'N/A'\n            display_str = str(class_name)\n        if not skip_scores:\n          if not display_str:\n            display_str = '{}%'.format(int(100 * scores[i]))\n          else:\n            display_str = '{}: {}%'.format(display_str, int(100 * scores[i]))\n        if not skip_track_ids and track_ids is not None:\n          if not display_str:\n            display_str = 'ID {}'.format(track_ids[i])\n          else:\n            display_str = '{}: ID {}'.format(display_str, track_ids[i])\n        box_to_display_str_map[box].append(display_str)\n        if agnostic_mode:\n          box_to_color_map[box] = 'DarkOrange'\n        elif track_ids is not None:\n          prime_multipler = _get_multiplier_for_color_randomness()\n          box_to_color_map[box] = STANDARD_COLORS[(prime_multipler *\n                                                   track_ids[i]) %\n                                                  len(STANDARD_COLORS)]\n        else:\n          box_to_color_map[box] = STANDARD_COLORS[classes[i] %\n                                                  len(STANDARD_COLORS)]\n\n  # Draw all boxes onto image.\n  for box, color in box_to_color_map.items():\n    ymin, xmin, ymax, xmax = box\n    if instance_masks is not None:\n      draw_mask_on_image_array(\n          image, box_to_instance_masks_map[box], color=color)\n    if instance_boundaries is not None:\n      draw_mask_on_image_array(\n          image, box_to_instance_boundaries_map[box], color='red', alpha=1.0)\n    draw_bounding_box_on_image_array(\n        image,\n        ymin,\n        xmin,\n        ymax,\n        xmax,\n        color=color,\n        thickness=0 if skip_boxes else line_thickness,\n        display_str_list=box_to_display_str_map[box],\n        use_normalized_coordinates=use_normalized_coordinates)\n    if keypoints is not None:\n      draw_keypoints_on_image_array(\n          image,\n          box_to_keypoints_map[box],\n          color=color,\n          radius=line_thickness / 2,\n          use_normalized_coordinates=use_normalized_coordinates,\n          keypoint_edges=keypoint_edges,\n          keypoint_edge_color=color,\n          keypoint_edge_width=line_thickness // 2)\n\n  return image\n\n\ndef add_cdf_image_summary(values, name):\n  \"\"\"Adds a tf.summary.image for a CDF plot of the values.\n\n  Normalizes `values` such that they sum to 1, plots the cumulative distribution\n  function and creates a tf image summary.\n\n  Args:\n    values: a 1-D float32 tensor containing the values.\n    name: name for the image summary.\n  \"\"\"\n  _force_matplotlib_backend()\n\n  def cdf_plot(values):\n    \"\"\"Numpy function to plot CDF.\"\"\"\n    normalized_values = values / np.sum(values)\n    sorted_values = np.sort(normalized_values)\n    cumulative_values = np.cumsum(sorted_values)\n    fraction_of_examples = (\n        np.arange(cumulative_values.size, dtype=np.float32) /\n        cumulative_values.size)\n    fig = plt.figure(frameon=False)\n    ax = fig.add_subplot(1, 1, 1)\n    ax.plot(fraction_of_examples, cumulative_values)\n    ax.set_ylabel('cumulative normalized values')\n    ax.set_xlabel('fraction of examples')\n    fig.canvas.draw()\n    width, height = fig.get_size_inches() * fig.get_dpi()\n    image = np.frombuffer(\n        fig.canvas.tostring_rgb(),\n        dtype='uint8').reshape(1, int(height), int(width), 3)\n    return image\n\n  cdf_plot = tf.py_func(cdf_plot, [values], tf.uint8)\n  tf.summary.image(name, cdf_plot)\n\n\ndef add_hist_image_summary(values, bins, name):\n  \"\"\"Adds a tf.summary.image for a histogram plot of the values.\n\n  Plots the histogram of values and creates a tf image summary.\n\n  Args:\n    values: a 1-D float32 tensor containing the values.\n    bins: bin edges which will be directly passed to np.histogram.\n    name: name for the image summary.\n  \"\"\"\n  _force_matplotlib_backend()\n\n  def hist_plot(values, bins):\n    \"\"\"Numpy function to plot hist.\"\"\"\n    fig = plt.figure(frameon=False)\n    ax = fig.add_subplot(1, 1, 1)\n    y, x = np.histogram(values, bins=bins)\n    ax.plot(x[:-1], y)\n    ax.set_ylabel('count')\n    ax.set_xlabel('value')\n    fig.canvas.draw()\n    width, height = fig.get_size_inches() * fig.get_dpi()\n    image = np.frombuffer(\n        fig.canvas.tostring_rgb(),\n        dtype='uint8').reshape(1, int(height), int(width), 3)\n    return image\n\n  hist_plot = tf.py_func(hist_plot, [values, bins], tf.uint8)\n  tf.summary.image(name, hist_plot)\n\n\nclass EvalMetricOpsVisualization(six.with_metaclass(abc.ABCMeta, object)):\n  \"\"\"Abstract base class responsible for visualizations during evaluation.\n\n  Currently, summary images are not run during evaluation. One way to produce\n  evaluation images in Tensorboard is to provide tf.summary.image strings as\n  `value_ops` in tf.estimator.EstimatorSpec's `eval_metric_ops`. This class is\n  responsible for accruing images (with overlaid detections and groundtruth)\n  and returning a dictionary that can be passed to `eval_metric_ops`.\n  \"\"\"\n\n  def __init__(self,\n               category_index,\n               max_examples_to_draw=5,\n               max_boxes_to_draw=20,\n               min_score_thresh=0.2,\n               use_normalized_coordinates=True,\n               summary_name_prefix='evaluation_image',\n               keypoint_edges=None):\n    \"\"\"Creates an EvalMetricOpsVisualization.\n\n    Args:\n      category_index: A category index (dictionary) produced from a labelmap.\n      max_examples_to_draw: The maximum number of example summaries to produce.\n      max_boxes_to_draw: The maximum number of boxes to draw for detections.\n      min_score_thresh: The minimum score threshold for showing detections.\n      use_normalized_coordinates: Whether to assume boxes and keypoints are in\n        normalized coordinates (as opposed to absolute coordinates). Default is\n        True.\n      summary_name_prefix: A string prefix for each image summary.\n      keypoint_edges: A list of tuples with keypoint indices that specify which\n        keypoints should be connected by an edge, e.g. [(0, 1), (2, 4)] draws\n        edges from keypoint 0 to 1 and from keypoint 2 to 4.\n    \"\"\"\n\n    self._category_index = category_index\n    self._max_examples_to_draw = max_examples_to_draw\n    self._max_boxes_to_draw = max_boxes_to_draw\n    self._min_score_thresh = min_score_thresh\n    self._use_normalized_coordinates = use_normalized_coordinates\n    self._summary_name_prefix = summary_name_prefix\n    self._keypoint_edges = keypoint_edges\n    self._images = []\n\n  def clear(self):\n    self._images = []\n\n  def add_images(self, images):\n    \"\"\"Store a list of images, each with shape [1, H, W, C].\"\"\"\n    if len(self._images) >= self._max_examples_to_draw:\n      return\n\n    # Store images and clip list if necessary.\n    self._images.extend(images)\n    if len(self._images) > self._max_examples_to_draw:\n      self._images[self._max_examples_to_draw:] = []\n\n  def get_estimator_eval_metric_ops(self, eval_dict):\n    # pyformat: disable\n    \"\"\"Returns metric ops for use in tf.estimator.EstimatorSpec.\n\n    Args:\n      eval_dict: A dictionary that holds an image, groundtruth, and detections\n        for a batched example. Note that, we use only the first example for\n        visualization. See eval_util.result_dict_for_batched_example() for a\n        convenient method for constructing such a dictionary. The dictionary\n        contains\n        fields.InputDataFields.original_image: [batch_size, H, W, 3] image.\n        fields.InputDataFields.original_image_spatial_shape: [batch_size, 2]\n          tensor containing the size of the original image.\n        fields.InputDataFields.true_image_shape: [batch_size, 3]\n          tensor containing the spatial size of the upadded original image.\n        fields.InputDataFields.groundtruth_boxes - [batch_size, num_boxes, 4]\n          float32 tensor with groundtruth boxes in range [0.0, 1.0].\n        fields.InputDataFields.groundtruth_classes - [batch_size, num_boxes]\n          int64 tensor with 1-indexed groundtruth classes.\n        fields.InputDataFields.groundtruth_instance_masks - (optional)\n          [batch_size, num_boxes, H, W] int64 tensor with instance masks.\n        fields.DetectionResultFields.detection_boxes - [batch_size,\n          max_num_boxes, 4] float32 tensor with detection boxes in range [0.0,\n          1.0].\n        fields.DetectionResultFields.detection_classes - [batch_size,\n          max_num_boxes] int64 tensor with 1-indexed detection classes.\n        fields.DetectionResultFields.detection_scores - [batch_size,\n          max_num_boxes] float32 tensor with detection scores.\n        fields.DetectionResultFields.detection_masks - (optional) [batch_size,\n          max_num_boxes, H, W] float32 tensor of binarized masks.\n        fields.DetectionResultFields.detection_keypoints - (optional)\n          [batch_size, max_num_boxes, num_keypoints, 2] float32 tensor with\n          keypoints.\n\n    Returns:\n      A dictionary of image summary names to tuple of (value_op, update_op). The\n      `update_op` is the same for all items in the dictionary, and is\n      responsible for saving a single side-by-side image with detections and\n      groundtruth. Each `value_op` holds the tf.summary.image string for a given\n      image.\n    \"\"\"\n    # pyformat: enable\n    if self._max_examples_to_draw == 0:\n      return {}\n    images = self.images_from_evaluation_dict(eval_dict)\n\n    def get_images():\n      \"\"\"Returns a list of images, padded to self._max_images_to_draw.\"\"\"\n      images = self._images\n      while len(images) < self._max_examples_to_draw:\n        images.append(np.array(0, dtype=np.uint8))\n      self.clear()\n      return images\n\n    def image_summary_or_default_string(summary_name, image):\n      \"\"\"Returns image summaries for non-padded elements.\"\"\"\n      return tf.cond(\n          tf.equal(tf.size(tf.shape(image)), 4),  # pyformat: disable\n          lambda: tf.summary.image(summary_name, image),\n          lambda: tf.constant(''))\n\n    if tf.executing_eagerly():\n      update_op = self.add_images([[images[0]]])  # pylint: disable=assignment-from-none\n      image_tensors = get_images()\n    else:\n      update_op = tf.py_func(self.add_images, [[images[0]]], [])\n      image_tensors = tf.py_func(get_images, [],\n                                 [tf.uint8] * self._max_examples_to_draw)\n    eval_metric_ops = {}\n    for i, image in enumerate(image_tensors):\n      summary_name = self._summary_name_prefix + '/' + str(i)\n      value_op = image_summary_or_default_string(summary_name, image)\n      eval_metric_ops[summary_name] = (value_op, update_op)\n    return eval_metric_ops\n\n  @abc.abstractmethod\n  def images_from_evaluation_dict(self, eval_dict):\n    \"\"\"Converts evaluation dictionary into a list of image tensors.\n\n    To be overridden by implementations.\n\n    Args:\n      eval_dict: A dictionary with all the necessary information for producing\n        visualizations.\n\n    Returns:\n      A list of [1, H, W, C] uint8 tensors.\n    \"\"\"\n    raise NotImplementedError\n\n\nclass VisualizeSingleFrameDetections(EvalMetricOpsVisualization):\n  \"\"\"Class responsible for single-frame object detection visualizations.\"\"\"\n\n  def __init__(self,\n               category_index,\n               max_examples_to_draw=5,\n               max_boxes_to_draw=20,\n               min_score_thresh=0.2,\n               use_normalized_coordinates=True,\n               summary_name_prefix='Detections_Left_Groundtruth_Right',\n               keypoint_edges=None):\n    super(VisualizeSingleFrameDetections, self).__init__(\n        category_index=category_index,\n        max_examples_to_draw=max_examples_to_draw,\n        max_boxes_to_draw=max_boxes_to_draw,\n        min_score_thresh=min_score_thresh,\n        use_normalized_coordinates=use_normalized_coordinates,\n        summary_name_prefix=summary_name_prefix,\n        keypoint_edges=keypoint_edges)\n\n  def images_from_evaluation_dict(self, eval_dict):\n    return draw_side_by_side_evaluation_image(eval_dict, self._category_index,\n                                              self._max_boxes_to_draw,\n                                              self._min_score_thresh,\n                                              self._use_normalized_coordinates,\n                                              self._keypoint_edges)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/configs/__init__.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/configs/input-config.proto",
    "content": "// For re-generating `input_config_generated_pb2.py`, please refer to\n// https://developers.google.com/protocol-buffers/docs/overview#generating.\nsyntax = \"proto3\";\n\npackage tensorflow_examples.lite.examples.recommendation.ml_v2.configs;\n\n\n// Different input feature types.\nenum FeatureType {\n  STRING = 0;\n  INT = 1;\n  FLOAT = 2;\n}\n\n// Supported context encoder types.\nenum EncoderType {\n  BOW = 0;\n  CNN = 1;\n  LSTM = 2;\n}\n\n// Input feature.\nmessage Feature {\n  // Name of the feature.\n  optional string feature_name = 1;\n\n  // Type of the feature.\n  optional FeatureType feature_type = 2;\n\n  // Name of the vocabulary (vocab file name).\n  // For INT type features, if the vocab_name is unset, the vocab of the feature\n  // will be considered to be [0, vocab_size). If this is not desired, the\n  // vocab_name should be set for INT type features.\n  // For STRING type features, the vocab_name field should be set.\n  // For FLOAT type feature, vocab_name is not needed.\n  optional string vocab_name = 3;\n\n  // Size of the vocabulary, if feature_type is STRING or INT.\n  optional int64 vocab_size = 4;\n\n  // Embedding dimension for STRING or INT feature.\n  optional int64 embedding_dim = 5;\n\n  // Sequence length of the feature.\n  optional int64 feature_length = 6;\n}\n\n// A Group of features that should be concatenated and encoded together.\nmessage FeatureGroup {\n  // Features in this feature group. STRING or INT features will be represented\n  // with embeddings. And FLOAT features will be directly used and concatenated\n  // with embeddings.\n  repeated Feature features = 1;\n\n  // Type of the encoder to generate encoding for this group. The supported\n  // encoder types are: 'bow', 'cnn', 'rnn'. Parameters for the selected encoder\n  // will be set in training config.\n  optional EncoderType encoder_type = 2;\n}\n\n// Config for input data.\nmessage InputConfig {\n  // Global feature groups, such as groups of user-level features\n  // (e.g. gender, age etc).\n  repeated FeatureGroup global_feature_groups = 1;\n\n  // Group of activity-level sequence features, that are mostly associated with\n  // each activity.\n  repeated FeatureGroup activity_feature_groups = 2;\n\n  // Label feature.\n  // Currently only support id-based label encoding, hence label feature expects\n  // to have FeatureType.INT.\n  optional Feature label_feature = 3;\n}\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/configs/input_config_pb2.py",
    "content": "# THIS IS A GENERATED FILE. DO NOT EDIT!\n# pylint: skip-file\n# pyformat: disable\n# Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n# -*- coding: utf-8 -*-\n# Generated by the protocol buffer compiler.  DO NOT EDIT!\n# source: input-config.proto\n\nfrom google.protobuf.internal import enum_type_wrapper\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import message as _message\nfrom google.protobuf import reflection as _reflection\nfrom google.protobuf import symbol_database as _symbol_database\n# @@protoc_insertion_point(imports)\n\n_sym_db = _symbol_database.Default()\n\n\n\n\nDESCRIPTOR = _descriptor.FileDescriptor(\n  name='input-config.proto',\n  package='tensorflow_examples.lite.examples.recommendation.ml_v2.configs',\n  syntax='proto2',\n  serialized_options=None,\n  serialized_pb=b'\\n\\x12input-config.proto\\x12>tensorflow_examples.lite.examples.recommendation.ml_v2.configs\\\"\\xd9\\x01\\n\\x07\\x46\\x65\\x61ture\\x12\\x14\\n\\x0c\\x66\\x65\\x61ture_name\\x18\\x01 \\x01(\\t\\x12\\x61\\n\\x0c\\x66\\x65\\x61ture_type\\x18\\x02 \\x01(\\x0e\\x32K.tensorflow_examples.lite.examples.recommendation.ml_v2.configs.FeatureType\\x12\\x12\\n\\nvocab_name\\x18\\x03 \\x01(\\t\\x12\\x12\\n\\nvocab_size\\x18\\x04 \\x01(\\x03\\x12\\x15\\n\\rembedding_dim\\x18\\x05 \\x01(\\x03\\x12\\x16\\n\\x0e\\x66\\x65\\x61ture_length\\x18\\x06 \\x01(\\x03\\\"\\xcc\\x01\\n\\x0c\\x46\\x65\\x61tureGroup\\x12Y\\n\\x08\\x66\\x65\\x61tures\\x18\\x01 \\x03(\\x0b\\x32G.tensorflow_examples.lite.examples.recommendation.ml_v2.configs.Feature\\x12\\x61\\n\\x0c\\x65ncoder_type\\x18\\x02 \\x01(\\x0e\\x32K.tensorflow_examples.lite.examples.recommendation.ml_v2.configs.EncoderType\\\"\\xc9\\x02\\n\\x0bInputConfig\\x12k\\n\\x15global_feature_groups\\x18\\x01 \\x03(\\x0b\\x32L.tensorflow_examples.lite.examples.recommendation.ml_v2.configs.FeatureGroup\\x12m\\n\\x17\\x61\\x63tivity_feature_groups\\x18\\x02 \\x03(\\x0b\\x32L.tensorflow_examples.lite.examples.recommendation.ml_v2.configs.FeatureGroup\\x12^\\n\\rlabel_feature\\x18\\x03 \\x01(\\x0b\\x32G.tensorflow_examples.lite.examples.recommendation.ml_v2.configs.Feature*-\\n\\x0b\\x46\\x65\\x61tureType\\x12\\n\\n\\x06STRING\\x10\\x00\\x12\\x07\\n\\x03INT\\x10\\x01\\x12\\t\\n\\x05\\x46LOAT\\x10\\x02*)\\n\\x0b\\x45ncoderType\\x12\\x07\\n\\x03\\x42OW\\x10\\x00\\x12\\x07\\n\\x03\\x43NN\\x10\\x01\\x12\\x08\\n\\x04LSTM\\x10\\x02'\n)\n\n_FEATURETYPE = _descriptor.EnumDescriptor(\n  name='FeatureType',\n  full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.FeatureType',\n  filename=None,\n  file=DESCRIPTOR,\n  values=[\n    _descriptor.EnumValueDescriptor(\n      name='STRING', index=0, number=0,\n      serialized_options=None,\n      type=None),\n    _descriptor.EnumValueDescriptor(\n      name='INT', index=1, number=1,\n      serialized_options=None,\n      type=None),\n    _descriptor.EnumValueDescriptor(\n      name='FLOAT', index=2, number=2,\n      serialized_options=None,\n      type=None),\n  ],\n  containing_type=None,\n  serialized_options=None,\n  serialized_start=845,\n  serialized_end=890,\n)\n_sym_db.RegisterEnumDescriptor(_FEATURETYPE)\n\nFeatureType = enum_type_wrapper.EnumTypeWrapper(_FEATURETYPE)\n_ENCODERTYPE = _descriptor.EnumDescriptor(\n  name='EncoderType',\n  full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.EncoderType',\n  filename=None,\n  file=DESCRIPTOR,\n  values=[\n    _descriptor.EnumValueDescriptor(\n      name='BOW', index=0, number=0,\n      serialized_options=None,\n      type=None),\n    _descriptor.EnumValueDescriptor(\n      name='CNN', index=1, number=1,\n      serialized_options=None,\n      type=None),\n    _descriptor.EnumValueDescriptor(\n      name='LSTM', index=2, number=2,\n      serialized_options=None,\n      type=None),\n  ],\n  containing_type=None,\n  serialized_options=None,\n  serialized_start=892,\n  serialized_end=933,\n)\n_sym_db.RegisterEnumDescriptor(_ENCODERTYPE)\n\nEncoderType = enum_type_wrapper.EnumTypeWrapper(_ENCODERTYPE)\nSTRING = 0\nINT = 1\nFLOAT = 2\nBOW = 0\nCNN = 1\nLSTM = 2\n\n\n\n_FEATURE = _descriptor.Descriptor(\n  name='Feature',\n  full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.Feature',\n  filename=None,\n  file=DESCRIPTOR,\n  containing_type=None,\n  fields=[\n    _descriptor.FieldDescriptor(\n      name='feature_name', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.Feature.feature_name', index=0,\n      number=1, type=9, cpp_type=9, label=1,\n      has_default_value=False, default_value=b\"\".decode('utf-8'),\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='feature_type', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.Feature.feature_type', index=1,\n      number=2, type=14, cpp_type=8, label=1,\n      has_default_value=False, default_value=0,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='vocab_name', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.Feature.vocab_name', index=2,\n      number=3, type=9, cpp_type=9, label=1,\n      has_default_value=False, default_value=b\"\".decode('utf-8'),\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='vocab_size', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.Feature.vocab_size', index=3,\n      number=4, type=3, cpp_type=2, label=1,\n      has_default_value=False, default_value=0,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='embedding_dim', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.Feature.embedding_dim', index=4,\n      number=5, type=3, cpp_type=2, label=1,\n      has_default_value=False, default_value=0,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='feature_length', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.Feature.feature_length', index=5,\n      number=6, type=3, cpp_type=2, label=1,\n      has_default_value=False, default_value=0,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n  ],\n  extensions=[\n  ],\n  nested_types=[],\n  enum_types=[\n  ],\n  serialized_options=None,\n  is_extendable=False,\n  syntax='proto2',\n  extension_ranges=[],\n  oneofs=[\n  ],\n  serialized_start=87,\n  serialized_end=304,\n)\n\n\n_FEATUREGROUP = _descriptor.Descriptor(\n  name='FeatureGroup',\n  full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.FeatureGroup',\n  filename=None,\n  file=DESCRIPTOR,\n  containing_type=None,\n  fields=[\n    _descriptor.FieldDescriptor(\n      name='features', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.FeatureGroup.features', index=0,\n      number=1, type=11, cpp_type=10, label=3,\n      has_default_value=False, default_value=[],\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='encoder_type', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.FeatureGroup.encoder_type', index=1,\n      number=2, type=14, cpp_type=8, label=1,\n      has_default_value=False, default_value=0,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n  ],\n  extensions=[\n  ],\n  nested_types=[],\n  enum_types=[\n  ],\n  serialized_options=None,\n  is_extendable=False,\n  syntax='proto2',\n  extension_ranges=[],\n  oneofs=[\n  ],\n  serialized_start=307,\n  serialized_end=511,\n)\n\n\n_INPUTCONFIG = _descriptor.Descriptor(\n  name='InputConfig',\n  full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.InputConfig',\n  filename=None,\n  file=DESCRIPTOR,\n  containing_type=None,\n  fields=[\n    _descriptor.FieldDescriptor(\n      name='global_feature_groups', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.InputConfig.global_feature_groups', index=0,\n      number=1, type=11, cpp_type=10, label=3,\n      has_default_value=False, default_value=[],\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='activity_feature_groups', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.InputConfig.activity_feature_groups', index=1,\n      number=2, type=11, cpp_type=10, label=3,\n      has_default_value=False, default_value=[],\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n    _descriptor.FieldDescriptor(\n      name='label_feature', full_name='tensorflow_examples.lite.examples.recommendation.ml_v2.configs.InputConfig.label_feature', index=2,\n      number=3, type=11, cpp_type=10, label=1,\n      has_default_value=False, default_value=None,\n      message_type=None, enum_type=None, containing_type=None,\n      is_extension=False, extension_scope=None,\n      serialized_options=None, file=DESCRIPTOR),\n  ],\n  extensions=[\n  ],\n  nested_types=[],\n  enum_types=[\n  ],\n  serialized_options=None,\n  is_extendable=False,\n  syntax='proto2',\n  extension_ranges=[],\n  oneofs=[\n  ],\n  serialized_start=514,\n  serialized_end=843,\n)\n\n_FEATURE.fields_by_name['feature_type'].enum_type = _FEATURETYPE\n_FEATUREGROUP.fields_by_name['features'].message_type = _FEATURE\n_FEATUREGROUP.fields_by_name['encoder_type'].enum_type = _ENCODERTYPE\n_INPUTCONFIG.fields_by_name['global_feature_groups'].message_type = _FEATUREGROUP\n_INPUTCONFIG.fields_by_name['activity_feature_groups'].message_type = _FEATUREGROUP\n_INPUTCONFIG.fields_by_name['label_feature'].message_type = _FEATURE\nDESCRIPTOR.message_types_by_name['Feature'] = _FEATURE\nDESCRIPTOR.message_types_by_name['FeatureGroup'] = _FEATUREGROUP\nDESCRIPTOR.message_types_by_name['InputConfig'] = _INPUTCONFIG\nDESCRIPTOR.enum_types_by_name['FeatureType'] = _FEATURETYPE\nDESCRIPTOR.enum_types_by_name['EncoderType'] = _ENCODERTYPE\n_sym_db.RegisterFileDescriptor(DESCRIPTOR)\n\nFeature = _reflection.GeneratedProtocolMessageType('Feature', (_message.Message,), {\n  'DESCRIPTOR' : _FEATURE,\n  '__module__' : 'input_config_pb2'\n  # @@protoc_insertion_point(class_scope:tensorflow_examples.lite.examples.recommendation.ml_v2.configs.Feature)\n  })\n_sym_db.RegisterMessage(Feature)\n\nFeatureGroup = _reflection.GeneratedProtocolMessageType('FeatureGroup', (_message.Message,), {\n  'DESCRIPTOR' : _FEATUREGROUP,\n  '__module__' : 'input_config_pb2'\n  # @@protoc_insertion_point(class_scope:tensorflow_examples.lite.examples.recommendation.ml_v2.configs.FeatureGroup)\n  })\n_sym_db.RegisterMessage(FeatureGroup)\n\nInputConfig = _reflection.GeneratedProtocolMessageType('InputConfig', (_message.Message,), {\n  'DESCRIPTOR' : _INPUTCONFIG,\n  '__module__' : 'input_config_pb2'\n  # @@protoc_insertion_point(class_scope:tensorflow_examples.lite.examples.recommendation.ml_v2.configs.InputConfig)\n  })\n_sym_db.RegisterMessage(InputConfig)\n\n\n# @@protoc_insertion_point(module_scope)\n# pyformat: enable\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/configs/model_config.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Class to hold model architecture configuration.\"\"\"\nfrom typing import List\nimport attr\n\n\n@attr.s(auto_attribs=True)\nclass ModelConfig(object):\n  \"\"\"Class to hold parameters for model architecture configuration.\n\n  Attributes:\n      hidden_layer_dims: List of hidden layer dimensions.\n      eval_top_k: Top k to evaluate.\n      conv_num_filter_ratios: Number of filter ratios for the Conv1D layer.\n      conv_kernel_size: Size of the Conv1D layer kernel size.\n      lstm_num_units: Number of units for the LSTM layer.\n      num_predictions: Number of predictions to return with serving mode, which\n      has default value 10.\n  \"\"\"\n  hidden_layer_dims: List[int]\n  eval_top_k: List[int]\n  conv_num_filter_ratios: List[int]\n  conv_kernel_size: int\n  lstm_num_units: int\n  num_predictions: int = 10\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/configs/sample_input_config.pbtxt",
    "content": "activity_feature_groups {\n  features {\n    feature_name: \"context_movie_id\"\n    feature_type: INT\n    vocab_size: 3953\n    embedding_dim: 8\n    feature_length: 10\n  }\n  features {\n    feature_name: \"context_movie_rating\"\n    feature_type: FLOAT\n    feature_length: 10\n  }\n  encoder_type: CNN\n}\nactivity_feature_groups {\n  features {\n    feature_name: \"context_movie_genre\"\n    feature_type: STRING\n    vocab_name: \"movie_genre_vocab.txt\"\n    vocab_size: 19\n    embedding_dim: 4\n    feature_length: 32\n  }\n  encoder_type: CNN\n}\nlabel_feature {\n  feature_name: \"label_movie_id\"\n  feature_type: INT\n  vocab_size: 3953\n  embedding_dim: 8\n  feature_length: 1\n}\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/data/__init__.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/data/example_generation_movielens.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Prepare TF.Examples for on-device recommendation model.\n\nFollowing functions are included: 1) downloading raw data 2) processing to user\nactivity sequence and splitting to train/test data 3) convert to TF.Examples\nand write in output location.\n\nMore information about the movielens dataset can be found here:\nhttps://grouplens.org/datasets/movielens/\n\"\"\"\n\nimport collections\nimport json\nimport os\nimport random\nimport re\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\nimport pandas as pd\nimport tensorflow as tf\n\nFLAGS = flags.FLAGS\n\n# Permalinks to download movielens data.\nMOVIELENS_1M_URL = \"https://files.grouplens.org/datasets/movielens/ml-1m.zip\"\nMOVIELENS_ZIP_FILENAME = \"ml-1m.zip\"\nMOVIELENS_ZIP_HASH = \"a6898adb50b9ca05aa231689da44c217cb524e7ebd39d264c56e2832f2c54e20\"\nMOVIELENS_EXTRACTED_DIR = \"ml-1m\"\nRATINGS_FILE_NAME = \"ratings.dat\"\nMOVIES_FILE_NAME = \"movies.dat\"\nRATINGS_DATA_COLUMNS = [\"UserID\", \"MovieID\", \"Rating\", \"Timestamp\"]\nMOVIES_DATA_COLUMNS = [\"MovieID\", \"Title\", \"Genres\"]\nOUTPUT_TRAINING_DATA_FILENAME = \"train_movielens_1m.tfrecord\"\nOUTPUT_TESTING_DATA_FILENAME = \"test_movielens_1m.tfrecord\"\nOUTPUT_MOVIE_VOCAB_FILENAME = \"movie_vocab.json\"\nOUTPUT_MOVIE_YEAR_VOCAB_FILENAME = \"movie_year_vocab.txt\"\nOUTPUT_MOVIE_GENRE_VOCAB_FILENAME = \"movie_genre_vocab.txt\"\nOUTPUT_MOVIE_TITLE_UNIGRAM_VOCAB_FILENAME = \"movie_title_unigram_vocab.txt\"\nOUTPUT_MOVIE_TITLE_BIGRAM_VOCAB_FILENAME = \"movie_title_bigram_vocab.txt\"\nPAD_MOVIE_ID = 0\nPAD_RATING = 0.0\nPAD_MOVIE_YEAR = 0\nUNKNOWN_STR = \"UNK\"\nVOCAB_MOVIE_ID_INDEX = 0\nVOCAB_COUNT_INDEX = 3\n\n\ndef define_flags():\n  \"\"\"Define flags.\"\"\"\n  flags.DEFINE_string(\"data_dir\", \"/tmp\",\n                      \"Path to download and store movielens data.\")\n  flags.DEFINE_string(\"output_dir\", None,\n                      \"Path to the directory of output files.\")\n  flags.DEFINE_bool(\"build_vocabs\", True,\n                    \"If yes, generate movie feature vocabs.\")\n  flags.DEFINE_integer(\"min_timeline_length\", 3,\n                       \"The minimum timeline length to construct examples.\")\n  flags.DEFINE_integer(\"max_context_length\", 10,\n                       \"The maximum length of user context history.\")\n  flags.DEFINE_integer(\"max_context_movie_genre_length\", 10,\n                       \"The maximum length of user context history.\")\n  flags.DEFINE_integer(\n      \"min_rating\", None, \"Minimum rating of movie that will be used to in \"\n      \"training data\")\n  flags.DEFINE_float(\"train_data_fraction\", 0.9, \"Fraction of training data.\")\n\n\nclass MovieInfo(\n    collections.namedtuple(\n        \"MovieInfo\", [\"movie_id\", \"timestamp\", \"rating\", \"title\", \"genres\"])):\n  \"\"\"Data holder of basic information of a movie.\"\"\"\n  __slots__ = ()\n\n  def __new__(cls,\n              movie_id=PAD_MOVIE_ID,\n              timestamp=0,\n              rating=PAD_RATING,\n              title=\"\",\n              genres=\"\"):\n    return super(MovieInfo, cls).__new__(cls, movie_id, timestamp, rating,\n                                         title, genres)\n\n\ndef download_and_extract_data(data_directory,\n                              url=MOVIELENS_1M_URL,\n                              fname=MOVIELENS_ZIP_FILENAME,\n                              file_hash=MOVIELENS_ZIP_HASH,\n                              extracted_dir_name=MOVIELENS_EXTRACTED_DIR):\n  \"\"\"Download and extract zip containing MovieLens data to a given directory.\n\n  Args:\n    data_directory: Local path to extract dataset to.\n    url: Direct path to MovieLens dataset .zip file. See constants above for\n      examples.\n    fname: str, zip file name to download.\n    file_hash: str, SHA-256 file hash.\n    extracted_dir_name: str, extracted dir name under data_directory.\n\n  Returns:\n    Downloaded and extracted data file directory.\n  \"\"\"\n  if not tf.io.gfile.exists(data_directory):\n    tf.io.gfile.makedirs(data_directory)\n  path_to_zip = tf.keras.utils.get_file(\n      fname=fname,\n      origin=url,\n      file_hash=file_hash,\n      hash_algorithm=\"sha256\",\n      extract=True,\n      cache_dir=data_directory)\n  extracted_file_dir = os.path.join(\n      os.path.dirname(path_to_zip), extracted_dir_name)\n  return extracted_file_dir\n\n\ndef read_data(data_directory, min_rating=None):\n  \"\"\"Read movielens ratings.dat and movies.dat file into dataframe.\"\"\"\n  ratings_df = pd.read_csv(\n      os.path.join(data_directory, RATINGS_FILE_NAME),\n      sep=\"::\",\n      names=RATINGS_DATA_COLUMNS,\n      encoding=\"unicode_escape\")  # May contain unicode. Need to escape.\n  ratings_df[\"Timestamp\"] = ratings_df[\"Timestamp\"].apply(int)\n  if min_rating is not None:\n    ratings_df = ratings_df[ratings_df[\"Rating\"] >= min_rating]\n  movies_df = pd.read_csv(\n      os.path.join(data_directory, MOVIES_FILE_NAME),\n      sep=\"::\",\n      names=MOVIES_DATA_COLUMNS,\n      encoding=\"unicode_escape\")  # May contain unicode. Need to escape.\n  return ratings_df, movies_df\n\n\ndef convert_to_timelines(ratings_df):\n  \"\"\"Convert ratings data to user.\"\"\"\n  timelines = collections.defaultdict(list)\n  movie_counts = collections.Counter()\n  for user_id, movie_id, rating, timestamp in ratings_df.values:\n    timelines[user_id].append(\n        MovieInfo(movie_id=movie_id, timestamp=int(timestamp), rating=rating))\n    movie_counts[movie_id] += 1\n  # Sort per-user timeline by timestamp\n  for (user_id, context) in timelines.items():\n    context.sort(key=lambda x: x.timestamp)\n    timelines[user_id] = context\n  return timelines, movie_counts\n\n\ndef generate_movies_dict(movies_df):\n  \"\"\"Generates movies dictionary from movies dataframe.\"\"\"\n  movies_dict = {\n      movie_id: MovieInfo(movie_id=movie_id, title=title, genres=genres)\n      for movie_id, title, genres in movies_df.values\n  }\n  movies_dict[0] = MovieInfo()\n  return movies_dict\n\n\ndef extract_year_from_title(title):\n  year = re.search(r\"\\((\\d{4})\\)\", title)\n  if year:\n    return int(year.group(1))\n  return 0\n\n\ndef generate_feature_of_movie_years(movies_dict, movies):\n  \"\"\"Extracts year feature for movies from movie title.\"\"\"\n  return [\n      extract_year_from_title(movies_dict[movie.movie_id].title)\n      for movie in movies\n  ]\n\n\ndef generate_movie_genres(movies_dict, movies):\n  \"\"\"Create a feature of the genre of each movie.\n\n  Save genre as a feature for the movies.\n\n  Args:\n    movies_dict: Dict of movies, keyed by movie_id with value of (title, genre)\n    movies: list of movies to extract genres.\n\n  Returns:\n    movie_genres: list of genres of all input movies.\n  \"\"\"\n  movie_genres = []\n  for movie in movies:\n    if not movies_dict[movie.movie_id].genres:\n      continue\n    genres = [\n        tf.compat.as_bytes(genre)\n        for genre in movies_dict[movie.movie_id].genres.split(\"|\")\n    ]\n    movie_genres.extend(genres)\n\n  return movie_genres\n\n\ndef _pad_or_truncate_movie_feature(feature, max_len, pad_value):\n  feature.extend([pad_value for _ in range(max_len - len(feature))])\n  return feature[:max_len]\n\n\ndef generate_examples_from_single_timeline(timeline,\n                                           movies_dict,\n                                           max_context_len=100,\n                                           max_context_movie_genre_len=320):\n  \"\"\"Generate TF examples from a single user timeline.\n\n  Generate TF examples from a single user timeline. Timeline with length less\n  than minimum timeline length will be skipped. And if context user history\n  length is shorter than max_context_len, features will be padded with default\n  values.\n\n  Args:\n    timeline: The timeline to generate TF examples from.\n    movies_dict: Dictionary of all MovieInfos.\n    max_context_len: The maximum length of the context. If the context history\n      length is less than max_context_length, features will be padded with\n      default values.\n    max_context_movie_genre_len: The length of movie genre feature.\n\n  Returns:\n    examples: Generated examples from this single timeline.\n  \"\"\"\n  examples = []\n  for label_idx in range(1, len(timeline)):\n    start_idx = max(0, label_idx - max_context_len)\n    context = timeline[start_idx:label_idx]\n    # Pad context with out-of-vocab movie id 0.\n    while len(context) < max_context_len:\n      context.append(MovieInfo())\n    label_movie_id = int(timeline[label_idx].movie_id)\n    context_movie_id = [int(movie.movie_id) for movie in context]\n    context_movie_rating = [movie.rating for movie in context]\n    context_movie_year = generate_feature_of_movie_years(movies_dict, context)\n    context_movie_genres = generate_movie_genres(movies_dict, context)\n    context_movie_genres = _pad_or_truncate_movie_feature(\n        context_movie_genres, max_context_movie_genre_len,\n        tf.compat.as_bytes(UNKNOWN_STR))\n    feature = {\n        \"context_movie_id\":\n            tf.train.Feature(\n                int64_list=tf.train.Int64List(value=context_movie_id)),\n        \"context_movie_rating\":\n            tf.train.Feature(\n                float_list=tf.train.FloatList(value=context_movie_rating)),\n        \"context_movie_genre\":\n            tf.train.Feature(\n                bytes_list=tf.train.BytesList(value=context_movie_genres)),\n        \"context_movie_year\":\n            tf.train.Feature(\n                int64_list=tf.train.Int64List(value=context_movie_year)),\n        \"label_movie_id\":\n            tf.train.Feature(\n                int64_list=tf.train.Int64List(value=[label_movie_id]))\n    }\n    tf_example = tf.train.Example(features=tf.train.Features(feature=feature))\n    examples.append(tf_example)\n\n  return examples\n\n\ndef generate_examples_from_timelines(timelines,\n                                     movies_df,\n                                     min_timeline_len=3,\n                                     max_context_len=100,\n                                     max_context_movie_genre_len=320,\n                                     train_data_fraction=0.9,\n                                     random_seed=None,\n                                     shuffle=True):\n  \"\"\"Convert user timelines to tf examples.\n\n  Convert user timelines to tf examples by adding all possible context-label\n  pairs in the examples pool.\n\n  Args:\n    timelines: The user timelines to process.\n    movies_df: The dataframe of all movies.\n    min_timeline_len: The minimum length of timeline. If the timeline length is\n      less than min_timeline_len, empty examples list will be returned.\n    max_context_len: The maximum length of the context. If the context history\n      length is less than max_context_length, features will be padded with\n      default values.\n    max_context_movie_genre_len: The length of movie genre feature.\n    train_data_fraction: Fraction of training data.\n    random_seed: Seed for randomization.\n    shuffle: Whether to shuffle the examples before splitting train and test\n      data.\n\n  Returns:\n    train_examples: TF example list for training.\n    test_examples: TF example list for testing.\n  \"\"\"\n  examples = []\n  movies_dict = generate_movies_dict(movies_df)\n  progress_bar = tf.keras.utils.Progbar(len(timelines))\n  for timeline in timelines.values():\n    if len(timeline) < min_timeline_len:\n      progress_bar.add(1)\n      continue\n    single_timeline_examples = generate_examples_from_single_timeline(\n        timeline=timeline,\n        movies_dict=movies_dict,\n        max_context_len=max_context_len,\n        max_context_movie_genre_len=max_context_movie_genre_len)\n    examples.extend(single_timeline_examples)\n    progress_bar.add(1)\n  # Split the examples into train, test sets.\n  if shuffle:\n    random.seed(random_seed)\n    random.shuffle(examples)\n  last_train_index = round(len(examples) * train_data_fraction)\n\n  train_examples = examples[:last_train_index]\n  test_examples = examples[last_train_index:]\n  return train_examples, test_examples\n\n\ndef generate_movie_feature_vocabs(movies_df, movie_counts):\n  \"\"\"Generate vocabularies for movie features.\n\n  Generate vocabularies for movie features (movie_id, genre, year), sorted by\n  usage count. Vocab id 0 will be reserved for default padding value.\n\n  Args:\n    movies_df: Dataframe for movies.\n    movie_counts: Counts that each movie is rated.\n\n  Returns:\n    movie_id_vocab: List of all movie ids paired with movie usage count, and\n      sorted by counts.\n    movie_genre_vocab: List of all movie genres, sorted by genre usage counts.\n    movie_year_vocab: List of all movie years, sorted by year usage counts.\n  \"\"\"\n  movie_vocab = []\n  movie_genre_counter = collections.Counter()\n  movie_year_counter = collections.Counter()\n  for movie_id, title, genres in movies_df.values:\n    count = movie_counts.get(movie_id) or 0\n    movie_vocab.append([movie_id, title, genres, count])\n    year = extract_year_from_title(title)\n    movie_year_counter[year] += 1\n    for genre in genres.split(\"|\"):\n      movie_genre_counter[genre] += 1\n\n  movie_vocab.sort(key=lambda x: x[VOCAB_COUNT_INDEX], reverse=True)  # by count\n  movie_year_vocab = [0] + [x for x, _ in movie_year_counter.most_common()]\n  movie_genre_vocab = [UNKNOWN_STR\n                      ] + [x for x, _ in movie_genre_counter.most_common()]\n\n  return (movie_vocab, movie_year_vocab, movie_genre_vocab)\n\n\ndef write_tfrecords(tf_examples, filename):\n  \"\"\"Writes tf examples to tfrecord file, and returns the count.\"\"\"\n  with tf.io.TFRecordWriter(filename) as file_writer:\n    length = len(tf_examples)\n    progress_bar = tf.keras.utils.Progbar(length)\n    for example in tf_examples:\n      file_writer.write(example.SerializeToString())\n      progress_bar.add(1)\n    return length\n\n\ndef write_vocab_json(vocab, filename):\n  \"\"\"Write generated movie vocabulary to specified file.\"\"\"\n  with open(filename, \"w\", encoding=\"utf-8\") as jsonfile:\n    json.dump(vocab, jsonfile, indent=2)\n\n\ndef write_vocab_txt(vocab, filename):\n  with open(filename, \"w\", encoding=\"utf-8\") as f:\n    for item in vocab:\n      f.write(str(item) + \"\\n\")\n\n\ndef generate_datasets(extracted_data_dir,\n                      output_dir,\n                      min_timeline_length,\n                      max_context_length,\n                      max_context_movie_genre_length,\n                      min_rating=None,\n                      build_vocabs=True,\n                      train_data_fraction=0.9,\n                      train_filename=OUTPUT_TRAINING_DATA_FILENAME,\n                      test_filename=OUTPUT_TESTING_DATA_FILENAME,\n                      vocab_filename=OUTPUT_MOVIE_VOCAB_FILENAME,\n                      vocab_year_filename=OUTPUT_MOVIE_YEAR_VOCAB_FILENAME,\n                      vocab_genre_filename=OUTPUT_MOVIE_GENRE_VOCAB_FILENAME):\n  \"\"\"Generates train and test datasets as TFRecord, and returns stats.\"\"\"\n  logging.info(\"Reading data to dataframes.\")\n  ratings_df, movies_df = read_data(extracted_data_dir, min_rating=min_rating)\n  logging.info(\"Generating movie rating user timelines.\")\n  timelines, movie_counts = convert_to_timelines(ratings_df)\n  logging.info(\"Generating train and test examples.\")\n  train_examples, test_examples = generate_examples_from_timelines(\n      timelines=timelines,\n      movies_df=movies_df,\n      min_timeline_len=min_timeline_length,\n      max_context_len=max_context_length,\n      max_context_movie_genre_len=max_context_movie_genre_length,\n      train_data_fraction=train_data_fraction)\n\n  if not tf.io.gfile.exists(output_dir):\n    tf.io.gfile.makedirs(output_dir)\n  logging.info(\"Writing generated training examples.\")\n  train_file = os.path.join(output_dir, train_filename)\n  train_size = write_tfrecords(tf_examples=train_examples, filename=train_file)\n  logging.info(\"Writing generated testing examples.\")\n  test_file = os.path.join(output_dir, test_filename)\n  test_size = write_tfrecords(tf_examples=test_examples, filename=test_file)\n  stats = {\n      \"train_size\": train_size,\n      \"test_size\": test_size,\n      \"train_file\": train_file,\n      \"test_file\": test_file,\n  }\n\n  if build_vocabs:\n    (movie_vocab, movie_year_vocab, movie_genre_vocab) = (\n        generate_movie_feature_vocabs(\n            movies_df=movies_df, movie_counts=movie_counts))\n    vocab_file = os.path.join(output_dir, vocab_filename)\n    write_vocab_json(movie_vocab, filename=vocab_file)\n    stats.update({\n        \"vocab_size\": len(movie_vocab),\n        \"vocab_file\": vocab_file,\n        \"vocab_max_id\": max([arr[VOCAB_MOVIE_ID_INDEX] for arr in movie_vocab])\n    })\n\n    for vocab, filename, key in zip([movie_year_vocab, movie_genre_vocab],\n                                    [vocab_year_filename, vocab_genre_filename],\n                                    [\"year_vocab\", \"genre_vocab\"]):\n      vocab_file = os.path.join(output_dir, filename)\n      write_vocab_txt(vocab, filename=vocab_file)\n      stats.update({\n          key + \"_size\": len(vocab),\n          key + \"_file\": vocab_file,\n      })\n\n  return stats\n\n\ndef main(_):\n  logging.info(\"Downloading and extracting data.\")\n  extracted_data_dir = download_and_extract_data(data_directory=FLAGS.data_dir)\n\n  stats = generate_datasets(\n      extracted_data_dir=extracted_data_dir,\n      output_dir=FLAGS.output_dir,\n      min_timeline_length=FLAGS.min_timeline_length,\n      max_context_length=FLAGS.max_context_length,\n      max_context_movie_genre_length=FLAGS.max_context_movie_genre_length,\n      min_rating=FLAGS.min_rating,\n      build_vocabs=FLAGS.build_vocabs,\n      train_data_fraction=FLAGS.train_data_fraction,\n  )\n  logging.info(\"Generated dataset: %s\", stats)\n\n\nif __name__ == \"__main__\":\n  define_flags()\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/data/example_generation_movielens_test.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Tests for example_generation_movielens.\"\"\"\n\nimport pandas as pd\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.data import example_generation_movielens as example_gen\n\nfrom google.protobuf import text_format\n\n\nMOVIES_DF = pd.DataFrame([\n    {\n        'MovieId': int(1),\n        'Title': 'Toy Story (1995)',\n        'Genres': 'Animation|Children|Comedy'\n    },\n    {\n        'MovieId': int(2),\n        'Title': 'Four Weddings and a Funeral (1994)',\n        'Genres': 'Comedy|Romance'\n    },\n    {\n        'MovieId': int(3),\n        'Title': 'Lion King, The (1994)',\n        'Genres': 'Adventure|Animation|Children'\n    },\n    {\n        'MovieId': int(4),\n        'Title': 'Forrest Gump (1994)',\n        'Genres': 'Comedy|Drama|Romance'\n    },\n    {\n        'MovieId': int(5),\n        'Title': 'Little Women (1994)',\n        'Genres': 'Drama'\n    },\n])\n\nRATINGS_DF = pd.DataFrame([\n    {\n        'UserID': int(1),\n        'MovieId': int(1),\n        'Rating': 3.5,\n        'Timestamp': 0\n    },\n    {\n        'UserID': int(1),\n        'MovieId': int(2),\n        'Rating': 4.0,\n        'Timestamp': 1\n    },\n    {\n        'UserID': int(1),\n        'MovieId': int(3),\n        'Rating': 4.5,\n        'Timestamp': 2\n    },\n    {\n        'UserID': int(1),\n        'MovieId': int(4),\n        'Rating': 4.7,\n        'Timestamp': 3\n    },\n    {\n        'UserID': int(2),\n        'MovieId': int(5),\n        'Rating': 4.0,\n        'Timestamp': 1\n    },\n])\n\nEXAMPLE1 = text_format.Parse(\n    \"\"\"\n    features {\n        feature {\n          key: \"context_movie_id\"\n          value {\n            int64_list {\n              value: [1, 0, 0, 0, 0]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_rating\"\n          value {\n            float_list {\n              value: [3.5, 0.0, 0.0, 0.0, 0.0]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_genre\"\n          value {\n            bytes_list {\n              value: [\"Animation\", \"Children\", \"Comedy\", \"UNK\", \"UNK\", \"UNK\", \"UNK\", \"UNK\", \"UNK\", \"UNK\"]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_year\"\n          value {\n            int64_list {\n              value: [1995, 0, 0, 0, 0]\n            }\n          }\n        }\n        feature {\n          key: \"label_movie_id\"\n          value {\n            int64_list {\n              value: [2]\n            }\n          }\n        }\n      }\n      \"\"\", tf.train.Example())\n\nEXAMPLE2 = text_format.Parse(\n    \"\"\"\n    features {\n        feature {\n          key: \"context_movie_id\"\n          value {\n            int64_list {\n              value: [1, 2, 0, 0, 0]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_rating\"\n          value {\n            float_list {\n              value: [3.5, 4.0, 0.0, 0.0, 0.0]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_genre\"\n          value {\n            bytes_list {\n              value: [\n                    \"Animation\", \"Children\", \"Comedy\", \"Comedy\", \"Romance\", \"UNK\", \"UNK\", \"UNK\", \"UNK\", \"UNK\"\n                ]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_year\"\n          value {\n            int64_list {\n              value: [1995, 1994, 0, 0, 0]\n            }\n          }\n        }\n        feature {\n          key: \"label_movie_id\"\n          value {\n            int64_list {\n              value: [3]\n            }\n          }\n        }\n      }\"\"\", tf.train.Example())\n\nEXAMPLE3 = text_format.Parse(\n    \"\"\"\n    features {\n        feature {\n          key: \"context_movie_id\"\n          value {\n            int64_list {\n              value: [1, 2, 3, 0, 0]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_rating\"\n          value {\n            float_list {\n              value: [3.5, 4.0, 4.5, 0.0, 0.0]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_genre\"\n          value {\n            bytes_list {\n              value: [\n                    \"Animation\", \"Children\", \"Comedy\", \"Comedy\", \"Romance\",\n                    \"Adventure\", \"Animation\", \"Children\", \"UNK\", \"UNK\"\n                ]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_year\"\n          value {\n            int64_list {\n              value: [1995, 1994, 1994, 0, 0]\n            }\n          }\n        }\n        feature {\n          key: \"label_movie_id\"\n          value {\n            int64_list {\n              value: [4]\n            }\n          }\n        }\n      }\"\"\", tf.train.Example())\n\n\nclass ExampleGenerationMovielensTest(tf.test.TestCase):\n\n  def test_example_generation(self):\n    timelines, _ = example_gen.convert_to_timelines(RATINGS_DF)\n    train_examples, test_examples = example_gen.generate_examples_from_timelines(\n        timelines=timelines,\n        movies_df=MOVIES_DF,\n        min_timeline_len=2,\n        max_context_len=5,\n        max_context_movie_genre_len=10,\n        train_data_fraction=0.66,\n        shuffle=False)\n    self.assertLen(train_examples, 2)\n    self.assertLen(test_examples, 1)\n    self.assertProtoEquals(train_examples[0], EXAMPLE1)\n    self.assertProtoEquals(train_examples[1], EXAMPLE2)\n    self.assertProtoEquals(test_examples[0], EXAMPLE3)\n\n  def test_vocabs_generation(self):\n    _, movie_counts = example_gen.convert_to_timelines(RATINGS_DF)\n    (_, movie_year_vocab, movie_genre_vocab) = (\n        example_gen.generate_movie_feature_vocabs(\n            movies_df=MOVIES_DF, movie_counts=movie_counts))\n    self.assertAllEqual(movie_year_vocab, [0, 1994, 1995])\n    self.assertAllEqual(movie_genre_vocab, [\n        'UNK', 'Comedy', 'Animation', 'Children', 'Romance', 'Drama',\n        'Adventure'\n    ])\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/model/__init__.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/model/context_encoder.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Context encoder layer for on-device personalized recommendation model.\"\"\"\nimport math\nfrom typing import Dict\n\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.configs import input_config_pb2\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.configs import model_config as model_config_class\n\n\ndef safe_div(x, y):\n  return tf.where(tf.not_equal(y, 0), tf.divide(x, y), tf.zeros_like(x))\n\n\nclass ContextEncoder(tf.keras.layers.Layer):\n  \"\"\"Layer to encode context sequence.\n\n  This encoder layer supports three types: 1) bow: bag of words style averaging\n  sequence embeddings. 2) cnn: use convolutional neural network to encode\n  sequence. 3) rnn: use recurrent neural network to encode sequence.\n\n  This encoder should be initialized with a predefined embedding layer, encoder\n  type and necessary parameters corresponding to the encoder type.\n  \"\"\"\n\n  def __init__(self,\n               input_config: input_config_pb2.InputConfig,\n               model_config: model_config_class.ModelConfig):\n    \"\"\"Initialize ContextEncoder layer.\n\n    Initialize ContextEncoder layer according to input config and model config,\n    setting up feature group encoders and hidden layers.\n\n    Args:\n      input_config: The input config (input_config_pb2.InputConfig).\n      model_config: The model config (model_config_class.ModelConfig).\n    \"\"\"\n    super(ContextEncoder, self).__init__()\n    self._input_config = input_config\n    self._model_config = model_config\n    self._final_embedding_dim = self._input_config.label_feature.embedding_dim\n    self._feature_group_encoders = [\n        FeatureGroupEncoder(feature_group, self._model_config,\n                            self._final_embedding_dim)\n        for feature_group in input_config.global_feature_groups\n    ]\n    self._feature_group_encoders.extend([\n        FeatureGroupEncoder(feature_group, self._model_config,\n                            self._final_embedding_dim)\n        for feature_group in input_config.activity_feature_groups\n    ])\n    self._final_embedding_dim = self._input_config.label_feature.embedding_dim\n    # Set up hidden layers.\n    self._hidden_layers = []\n    for i, layer_dim in enumerate(self._model_config.hidden_layer_dims):\n      self._hidden_layers.append(\n          tf.keras.layers.Dense(\n              units=layer_dim,\n              name='hidden_layer{}'.format(i),\n              activation=tf.nn.relu))\n    # Append final top layer, dimension of which should equal final embedding\n    # dimension.\n    self._hidden_layers.append(\n        tf.keras.layers.Dense(\n            units=self._final_embedding_dim,\n            name='proj_layer',\n            activation=tf.nn.relu))\n\n  def call(self, input_context: Dict[str, tf.Tensor]) -> tf.Tensor:\n    \"\"\"Call function of the context encoder layer.\n\n    Takes in input context tensor dictionary, generates feature group\n    embeddings, concatenate them up and pass them through top hidden layers\n    to get final context embedding.\n\n    Args:\n      input_context: A dictionary mapping input context feature name to tenors.\n\n    Returns:\n      Context encoding.\n    \"\"\"\n    input_context = {\n        k: tf.expand_dims(v, 0) if v.shape.ndims == 1 else v\n        for k, v in input_context.items()\n    }\n    feature_group_embeddings = [\n        feature_group_encoder(input_context)\n        for feature_group_encoder in self._feature_group_encoders\n    ]\n    context_embedding = tf.concat(feature_group_embeddings, -1)\n    for hidden_layer in self._hidden_layers:\n      context_embedding = hidden_layer(context_embedding)\n    return context_embedding\n\n\nclass FeatureGroupEncoder(tf.keras.layers.Layer):\n  \"\"\"Layer to generate encoding for the feature group.\n\n  Layer to generate encoding for the group of features in the feature\n  group with encoder type (BOW/CNN/LSTM) specified in the feature group config.\n  Embeddings of INT or STRING type features are concatenated first, and FLOAT\n  type feature values are appended after. Embedding vector is properly masked.\n  \"\"\"\n\n  def __init__(self,\n               feature_group: input_config_pb2.FeatureGroup,\n               model_config: model_config_class.ModelConfig,\n               final_embedding_dim: int):\n    \"\"\"Initialize FeatureGroupEncoder layer.\n\n    Initialize ContextEncoder layer according to feature group.\n\n    Args:\n      feature_group: The feature group that needs to encode.\n      model_config: The model config (model_config_class.ModelConfig).\n      final_embedding_dim: Dimension of final context embedding.\n    \"\"\"\n    super(FeatureGroupEncoder, self).__init__()\n    self._feature_group = feature_group\n    self._model_config = model_config\n    self._final_embedding_dim = final_embedding_dim\n    # Prepare embedding layers.\n    self._embedding_layer_dict = {}\n    self._nonembedding_feature_names = []\n    for feature in self._feature_group.features:\n      if feature.feature_type in [\n          input_config_pb2.FeatureType.INT, input_config_pb2.FeatureType.STRING\n      ]:\n        assert feature.HasField('vocab_size')\n        assert feature.HasField('embedding_dim')\n        embedding_layer = tf.keras.layers.Embedding(\n            feature.vocab_size,\n            feature.embedding_dim,\n            embeddings_initializer=tf.keras.initializers.truncated_normal(\n                mean=0.0, stddev=1.0 / math.sqrt(feature.embedding_dim)),\n            mask_zero=True,\n            name=feature.feature_name+'embedding_layer')\n        self._embedding_layer_dict[feature.feature_name] = embedding_layer\n      else:\n        self._nonembedding_feature_names.append(feature.feature_name)\n  # Prepare CNN layers.\n    self._conv1d_layers = []\n    if self._feature_group.encoder_type == input_config_pb2.EncoderType.CNN:\n      assert self._model_config.conv_num_filter_ratios\n      assert self._model_config.conv_kernel_size\n      for ratio in self._model_config.conv_num_filter_ratios:\n        self._conv1d_layers.append(\n            tf.keras.layers.Conv1D(\n                filters=self._final_embedding_dim * ratio,\n                kernel_size=self._model_config.conv_kernel_size,\n                strides=self._model_config.conv_kernel_size,\n                padding='same',\n                activation='relu'))\n    # Prepare LSTM layer.\n    elif self._feature_group.encoder_type == input_config_pb2.EncoderType.LSTM:\n      assert self._model_config.lstm_num_units\n      self._lstm_layer = tf.keras.layers.LSTM(\n          self._model_config.lstm_num_units)\n\n  def call(self, input_context: Dict[str, tf.Tensor]) -> tf.Tensor:\n    \"\"\"Call function of the feature group encoder layer.\n\n    Takes in input context tensor dictionary, generates embedding for features\n    in the group, concatenate them up and encode them according to the\n    specified encoder type.\n\n    Feature embedding layers will have mask_zero enabled, and generated feature\n    embeddings will be masked if any of the feature value is 0. In most\n    cases, the mask of all features in the same group should be the same. This\n    function treats the input config generically.\n\n    Args:\n      input_context: A dictionary mapping input context feature name to tenors.\n\n    Returns:\n      Feature group encoding.\n    \"\"\"\n    embedding_feature_shape = []\n    embeddings = []\n    masks = []\n    for feature_name, embedding_layer in sorted(\n        self._embedding_layer_dict.items()):\n      input_feature = input_context[feature_name]\n      if isinstance(input_feature, tf.SparseTensor):\n        input_feature = tf.sparse.to_dense(input_feature)\n      if not embedding_feature_shape:\n        embedding_feature_shape = input_feature.shape.as_list()\n      assert input_feature.shape.as_list() == embedding_feature_shape\n\n      embeddings.append(embedding_layer(input_feature))\n      masks.append(embedding_layer.compute_mask(input_feature))\n    # Collect nonembedding feature values.\n    for feature_name in self._nonembedding_feature_names:\n      input_feature = input_context[feature_name]\n      if isinstance(input_feature, tf.SparseTensor):\n        input_feature = tf.sparse.to_dense(input_feature)\n      embeddings.append(tf.expand_dims(input_feature, -1))\n    embedding = tf.concat(embeddings, -1)\n\n    # Set mask_shape with batch dim = 1. Compatible to unknown batch size.\n    mask_shape = [1] + embedding_feature_shape[1:]\n    mask = tf.ones(shape=mask_shape)\n    for m in masks:\n      mask = mask * tf.cast(m, 'float32')\n    mask = tf.expand_dims(mask, -1)\n    embedding = embedding * mask\n\n    if self._feature_group.encoder_type == input_config_pb2.EncoderType.BOW:\n      weighted_summed_context_embedding = tf.keras.backend.sum(\n          embedding, axis=1)\n      total_weights = tf.ones_like(\n          weighted_summed_context_embedding) * tf.keras.backend.sum(\n              mask, axis=1)\n      embedding = safe_div(weighted_summed_context_embedding, total_weights)\n    elif self._feature_group.encoder_type == input_config_pb2.EncoderType.CNN:\n      for conv1d_layer in self._conv1d_layers:\n        embedding = conv1d_layer(embedding)\n      embedding = tf.math.reduce_max(embedding, axis=1)\n    elif self._feature_group.encoder_type == input_config_pb2.EncoderType.LSTM:\n      embedding = self._lstm_layer(embedding)\n    else:\n      raise ValueError('Unsupported encoder type %s.' %\n                       self._feature_group.encoder_type)\n\n    return embedding\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/model/context_encoder_test.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Tests for context_encoder.\"\"\"\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.configs import input_config_pb2\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.configs import model_config as model_config_class\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import context_encoder\n\n\nclass ContextEncoderTest(tf.test.TestCase):\n\n  def _create_test_feature_group(self,\n                                 encoder_type: input_config_pb2.EncoderType):\n    \"\"\"Prepare test feature group.\"\"\"\n    feature_context_movie_id = input_config_pb2.Feature(\n        feature_name='context_movie_id',\n        feature_type=input_config_pb2.FeatureType.INT,\n        vocab_size=3952,\n        embedding_dim=4)\n    feature_context_movie_rating = input_config_pb2.Feature(\n        feature_name='context_movie_rating',\n        feature_type=input_config_pb2.FeatureType.FLOAT)\n    return input_config_pb2.FeatureGroup(\n        features=[feature_context_movie_id, feature_context_movie_rating],\n        encoder_type=encoder_type)\n\n  def _create_test_input_config(self):\n    \"\"\"Generate test input_config_pb2.InputConfig proto.\"\"\"\n    feature_group_1 = self._create_test_feature_group(\n        encoder_type=input_config_pb2.EncoderType.BOW)\n\n    feature_context_movie_genre = input_config_pb2.Feature(\n        feature_name='context_movie_genre',\n        feature_type=input_config_pb2.FeatureType.STRING,\n        vocab_name='movie_genre_vocab.txt',\n        vocab_size=19,\n        embedding_dim=3)\n    feature_group_2 = input_config_pb2.FeatureGroup(\n        features=[feature_context_movie_genre],\n        encoder_type=input_config_pb2.EncoderType.BOW)\n\n    feature_label = input_config_pb2.Feature(\n        feature_name='label_movie_id',\n        feature_type=input_config_pb2.FeatureType.INT,\n        vocab_size=3952,\n        embedding_dim=4)\n\n    input_config = input_config_pb2.InputConfig(\n        activity_feature_groups=[feature_group_1, feature_group_2],\n        label_feature=feature_label)\n    return input_config\n\n  def _create_test_model_config(self):\n    return model_config_class.ModelConfig(\n        hidden_layer_dims=[8, 4],\n        eval_top_k=[1, 5],\n        conv_num_filter_ratios=[1, 2],\n        conv_kernel_size=2,\n        lstm_num_units=16)\n\n  def test_feature_group_encoder_bow(self):\n    feature_group = self._create_test_feature_group(\n        encoder_type=input_config_pb2.EncoderType.BOW)\n    model_config = self._create_test_model_config()\n    feature_group_encoder = context_encoder.FeatureGroupEncoder(\n        feature_group, model_config, final_embedding_dim=4)\n    input_context_movie_id = tf.constant([[1, 0, 0], [1, 2, 0]])\n    input_context_movie_rating = tf.constant([[1.0, 0.0, 0.0], [2.0, 3.0, 0.0]])\n    input_context = {\n        'context_movie_id': input_context_movie_id,\n        'context_movie_rating': input_context_movie_rating\n    }\n    feature_group_embedding = feature_group_encoder(input_context)\n    self.assertAllEqual([2, 5], list(feature_group_embedding.shape))\n\n  def test_feature_group_encoder_cnn(self):\n    feature_group = self._create_test_feature_group(\n        encoder_type=input_config_pb2.EncoderType.CNN)\n    model_config = self._create_test_model_config()\n    feature_group_encoder = context_encoder.FeatureGroupEncoder(\n        feature_group, model_config, final_embedding_dim=4)\n    input_context_movie_id = tf.constant([[1, 0, 0], [1, 2, 0]])\n    input_context_movie_rating = tf.constant([[1.0, 0.0, 0.0], [2.0, 3.0, 0.0]])\n    input_context = {\n        'context_movie_id': input_context_movie_id,\n        'context_movie_rating': input_context_movie_rating\n    }\n    feature_group_embedding = feature_group_encoder(input_context)\n    self.assertAllEqual([2, 8], list(feature_group_embedding.shape))\n\n  def test_feature_group_encoder_lstm(self):\n    feature_group = self._create_test_feature_group(\n        encoder_type=input_config_pb2.EncoderType.LSTM)\n    model_config = self._create_test_model_config()\n    feature_group_encoder = context_encoder.FeatureGroupEncoder(\n        feature_group, model_config, final_embedding_dim=4)\n    input_context_movie_id = tf.constant([[1, 0, 0], [1, 2, 0]])\n    input_context_movie_rating = tf.constant([[1.0, 0.0, 0.0], [2.0, 3.0, 0.0]])\n    input_context = {\n        'context_movie_id': input_context_movie_id,\n        'context_movie_rating': input_context_movie_rating\n    }\n    feature_group_embedding = feature_group_encoder(input_context)\n    self.assertAllEqual([2, 16], list(feature_group_embedding.shape))\n\n  def test_context_encoder(self):\n    input_config = self._create_test_input_config()\n    model_config = self._create_test_model_config()\n    input_context_encoder = context_encoder.ContextEncoder(\n        input_config=input_config, model_config=model_config)\n    input_context_movie_id = tf.constant([[1, 0, 0], [1, 2, 0]])\n    input_context_movie_rating = tf.constant([[1.0, 0.0, 0.0], [2.0, 3.0, 0.0]])\n    input_context_movie_genre = tf.constant([[1, 2, 2, 4, 3], [1, 1, 2, 2, 3]])\n    input_context = {\n        'context_movie_id': input_context_movie_id,\n        'context_movie_rating': input_context_movie_rating,\n        'context_movie_genre': input_context_movie_genre\n    }\n    context_embedding = input_context_encoder(input_context)\n    self.assertAllEqual([2, 4], list(context_embedding.shape))\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/model/dotproduct_similarity.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Dotproduct similarity layer.\"\"\"\nfrom typing import Optional\n\nimport tensorflow as tf\n\n\nclass DotProductSimilarity(tf.keras.layers.Layer):\n  \"\"\"Layer to comput dotproduct similarities for context/label embedding.\n\n    The top_k is an integer to represent top_k ids to compute among label ids.\n    if top_k is None, top_k computation will be ignored.\n  \"\"\"\n\n  def call(self,\n           context_embeddings: tf.Tensor,\n           label_embeddings: tf.Tensor,\n           top_k: Optional[int] = None):\n    \"\"\"Generate dotproduct similarity matrix and top values/indices.\n\n    Args:\n      context_embeddings: Context embeddings generated with input context\n        sequence. The shape of tensor should be [batch_size, embedding_dim] for\n        training mode, and [1, embedding_dim] for inference mode.\n      label_embeddings: Label embeddings generated for candidate label items.\n        The shape of tensor should be [num_items, embedding_dim].\n      top_k: The number of top values to compute. If it's not set, top values\n        and indices will not be computed.\n\n    Returns:\n      Dotproduct similarity matrix with shape [num_label_items, 1] and top\n      values/indices if top_k is set.\n    \"\"\"\n    if tf.keras.backend.ndim(label_embeddings) == 3:\n      label_embeddings = tf.squeeze(label_embeddings, 1)\n    dotproduct = tf.matmul(\n        context_embeddings, label_embeddings, transpose_b=True)\n    if top_k:\n      top_values, top_indices = tf.math.top_k(\n          tf.squeeze(dotproduct), top_k, sorted=True)\n      top_ids = tf.identity(top_indices, name='top_prediction_ids')\n      top_scores = tf.identity(\n          tf.math.sigmoid(top_values), name='top_prediction_scores')\n      return [dotproduct, top_ids, top_scores]\n    else:\n      return [dotproduct]\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/model/dotproduct_similarity_test.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Tests for dotproduct_similarity.\"\"\"\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import dotproduct_similarity\n\n\nclass DotproductSimilarityTest(tf.test.TestCase):\n\n  def test_dotproduct(self):\n    context_embeddings = tf.constant([[0.1, 0.1, 0.1, 0.1],\n                                      [0.2, 0.2, 0.2, 0.2]])\n    label_embeddings = tf.constant([[0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1]])\n    similarity_layer = dotproduct_similarity.DotProductSimilarity()\n    dotproduct = similarity_layer(context_embeddings, label_embeddings)\n    dotproduct, top_ids, top_scores = similarity_layer(context_embeddings,\n                                                       label_embeddings, 1)\n    self.assertAllClose(tf.constant([[0.04, 0.04], [0.08, 0.08]]), dotproduct)\n    self.assertAllEqual(tf.constant([[0], [0]]), top_ids)\n    self.assertAllEqual([2, 1], list(top_scores.shape))\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/model/input_pipeline.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Input pipeline for on-device recommendation model.\n\nProcess input TF example and prepare train/test datasets.\n\"\"\"\nimport collections\nimport functools\nimport os\nfrom typing import Dict, List, Tuple\n\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.configs import input_config_pb2\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import utils\n\n\nINT_DEFAULT_VALUE = 0\nSTRING_DEFAULT_VALUE = 'UNK'\nFLOAT_DEFAULT_VALUE = 0.0\n\n\nclass FeaturesAndVocabsByName(\n    collections.namedtuple(\n        'FeaturesAndVocabsByName', ['features_by_name', 'vocabs_by_name'])):\n  \"\"\"Holder for intermediate data in input processing pipeline.\"\"\"\n  __slots__ = ()\n\n  def __new__(cls, features_by_name=None, vocabs_by_name=None):\n    return super(FeaturesAndVocabsByName, cls).__new__(cls,\n                                                       features_by_name,\n                                                       vocabs_by_name)\n\n\ndef _prepare_feature_vocab_table(\n    feature: input_config_pb2.Feature,\n    vocab_file_dir: str) -> tf.lookup.StaticVocabularyTable:\n  \"\"\"Prepare vocabulary table for the feature if needed.\n\n  Prepare the vocabulary table with the specified vocab_name(vocab file name)\n  and vocab file directory. Feature type is required for the feature, as it\n  will be used to set up the vocabulary table.\n\n  Currently we assume the vocabulary file is a txt file and each line stores\n  single feature value.\n\n  Args:\n    feature: A feature config input_config_pb2.Feature proto.\n    vocab_file_dir: The directory storing vocabulary files.\n\n  Returns:\n    A vocabulary table (tf.lookup.StaticVocabularyTable) for the feature.\n  \"\"\"\n  assert feature.HasField('vocab_name')\n  assert feature.HasField('feature_type')\n  assert feature.feature_type in [input_config_pb2.FeatureType.INT,\n                                  input_config_pb2.FeatureType.STRING]\n  vocab_path = os.path.join(vocab_file_dir, feature.vocab_name)\n  num_oov_buckets = 1\n  key_type = tf.string if (\n      feature.feature_type == input_config_pb2.FeatureType.STRING) else tf.int64\n  return tf.lookup.StaticVocabularyTable(\n      tf.lookup.TextFileInitializer(\n          vocab_path,\n          key_dtype=key_type,\n          key_index=tf.lookup.TextFileIndex.WHOLE_LINE,\n          value_dtype=tf.int64,\n          value_index=tf.lookup.TextFileIndex.LINE_NUMBER,\n          delimiter='\\t'), num_oov_buckets)\n\n\ndef _get_features_vocabs_for_groups(\n    feature_groups: List[input_config_pb2.FeatureGroup],\n    vocab_file_dir: str = '') -> FeaturesAndVocabsByName:\n  \"\"\"Get feature and vocabulary dictionaries for feature groups.\n\n  Args:\n    feature_groups: A list of feature group configs(\n      input_config_pb2.FeatureGroup)s.\n    vocab_file_dir: The directory storing vocabulary files.\n\n  Returns:\n    A FeaturesAndVocabsByName object containing features and vocabs\n    dictionaries keyed feature name for features according to the feature\n    group list.\n  \"\"\"\n  features_by_name = {}\n  vocabs_by_name = {}\n  for feature_group in feature_groups:\n    for feature in feature_group.features:\n      features_by_name[feature.feature_name] = feature\n      if vocab_file_dir and feature.HasField('vocab_name'):\n        vocabs_by_name[feature.feature_name] = _prepare_feature_vocab_table(\n            feature, vocab_file_dir)\n  return FeaturesAndVocabsByName(\n      features_by_name=features_by_name, vocabs_by_name=vocabs_by_name)\n\n\ndef get_features_and_vocabs_by_name(\n    input_config: input_config_pb2.InputConfig,\n    vocab_file_dir: str = '') -> FeaturesAndVocabsByName:\n  \"\"\"Get feature and vocabulary dictionaries according to input config.\n\n  Args:\n    input_config: The input config input_config_pb2.InputConfig proto.\n    vocab_file_dir: The directory storing vocabulary files.\n\n  Returns:\n    A FeaturesAndVocabsByName object containing features and vocabs\n    dictionaries keyed feature name for all features according to input\n    config.\n  \"\"\"\n  global_features_and_vocabs_by_name = (\n      _get_features_vocabs_for_groups(list(input_config.global_feature_groups),\n                                      vocab_file_dir))\n  activity_features_and_vocabs_by_name = (\n      _get_features_vocabs_for_groups(\n          list(input_config.activity_feature_groups), vocab_file_dir))\n  features_by_name = {\n      **global_features_and_vocabs_by_name.features_by_name,\n      **activity_features_and_vocabs_by_name.features_by_name\n  }\n  vocabs_by_name = {\n      **global_features_and_vocabs_by_name.vocabs_by_name,\n      **activity_features_and_vocabs_by_name.vocabs_by_name\n  }\n  if input_config.label_feature.HasField('vocab_name'):\n    vocabs_by_name[\n        input_config.label_feature.feature_name] = _prepare_feature_vocab_table(\n            input_config.label_feature, vocab_file_dir)\n  return FeaturesAndVocabsByName(\n      features_by_name=features_by_name, vocabs_by_name=vocabs_by_name)\n\n\ndef _get_feature_spec(feature_type: input_config_pb2.FeatureType,\n                      feature_length: int) -> tf.io.FixedLenFeature:\n  \"\"\"Get feature spec based on feature type and length.\n\n  Args:\n    feature_type: The type of the feature (input_config_pb2.FeatureType).\n    feature_length: The length of the feature. The feature is expected to be\n      a sequence.\n\n  Returns:\n    The feature spec to parse serialized examples.\n\n  Raises:\n    An error occurring if the feature type is not supported.\n  \"\"\"\n  if feature_type == input_config_pb2.FeatureType.INT:\n    dtype = tf.int64\n    default_value = INT_DEFAULT_VALUE\n  elif feature_type == input_config_pb2.FeatureType.STRING:\n    dtype = tf.string\n    default_value = STRING_DEFAULT_VALUE\n  elif feature_type == input_config_pb2.FeatureType.FLOAT:\n    dtype = tf.float32\n    default_value = FLOAT_DEFAULT_VALUE\n  else:\n    raise ValueError('Unsupported feature type {}'.format(feature_type))\n\n  return tf.io.FixedLenFeature(\n      shape=[feature_length],\n      dtype=dtype,\n      default_value=[default_value] * feature_length)\n\n\ndef _get_serving_feature_spec(feature_name: str,\n                              feature_type: input_config_pb2.FeatureType,\n                              feature_length: int) -> tf.TensorSpec:\n  if feature_type == input_config_pb2.FeatureType.INT or feature_type == input_config_pb2.FeatureType.STRING:\n    dtype = tf.dtypes.int32\n  elif feature_type == input_config_pb2.FeatureType.FLOAT:\n    dtype = tf.dtypes.float32\n  else:\n    raise ValueError('Unsupported feature type {}'.format(feature_type))\n  return tf.TensorSpec(shape=[feature_length], dtype=dtype, name=feature_name)\n\n\ndef get_serving_input_specs(\n    input_config: input_config_pb2.InputConfig) -> Dict[str, tf.TensorSpec]:\n  features_by_name = get_features_and_vocabs_by_name(\n      input_config).features_by_name\n  input_specs = collections.OrderedDict()\n  for feature_name, feature in sorted(features_by_name.items()):\n    input_specs[feature_name] = _get_serving_feature_spec(\n        feature_name, feature.feature_type, feature.feature_length)\n  return input_specs\n\n\ndef decode_example(\n    serialized_proto: str, features_and_vocabs_by_name: FeaturesAndVocabsByName,\n    label_feature_name: str) -> Tuple[Dict[str, tf.Tensor], tf.Tensor]:\n  \"\"\"Decode single serialized example.\n\n  Decode single serialized example, accoring to specified features in input\n  config. Perform vocabulary lookup if vocabulary is specified.\n\n  Args:\n    serialized_proto: The serialized proto that needs to be decoded.\n    features_and_vocabs_by_name: A FeaturesAndVocabsByName object containing\n      features and vocabs dictionaries by feature names.\n    label_feature_name: Name of the label feature.\n\n  Returns:\n    features: The decoded features dictionary.\n    label_feature: The label feature.\n\n  \"\"\"\n  features_by_name = features_and_vocabs_by_name.features_by_name\n  vocabs_by_name = features_and_vocabs_by_name.vocabs_by_name\n  name_to_features = {}\n  name_to_features[label_feature_name] = tf.io.FixedLenFeature([1], tf.int64)\n  for feature_name, feature in features_by_name.items():\n    name_to_features[feature_name] = _get_feature_spec(\n        feature_type=feature.feature_type,\n        feature_length=feature.feature_length)\n  record_features = tf.io.parse_single_example(serialized_proto,\n                                               name_to_features)\n\n  features = {}\n  for feature_name, feature_value in record_features.items():\n    if feature_name in vocabs_by_name:\n      features[feature_name] = vocabs_by_name[feature_name].lookup(\n          feature_value)\n    else:\n      features[feature_name] = feature_value\n  features = {\n      k: v if v.dtype != tf.int64 else tf.cast(v, tf.int32)\n      for k, v in features.items()\n  }\n  label_feature = features[label_feature_name]\n  return features, label_feature\n\n\ndef get_input_dataset(data_filepattern: str,\n                      input_config: input_config_pb2.InputConfig,\n                      vocab_file_dir: str,\n                      batch_size: int) -> tf.data.Dataset:\n  \"\"\"An input_fn to create input datasets.\n\n  Args:\n    data_filepattern: The file pattern of the input data.\n    input_config: The input config input_config_pb2.InputConfig proto.\n    vocab_file_dir: The path to the directory storing the vocabulary files.\n    batch_size: Batch size of to-be generated dataset.\n\n  Returns:\n    A Dataset where each element is a batch of feature dicts.\n  \"\"\"\n  features_and_vocabs_by_name = get_features_and_vocabs_by_name(\n      input_config, vocab_file_dir)\n  if not input_config.HasField('label_feature'):\n    raise ValueError('Field label_feature is required.')\n  input_files = utils.GetShardFilenames(data_filepattern)\n  d = tf.data.TFRecordDataset(input_files)\n  d = d.shuffle(len(input_files))\n  d = d.repeat()\n  d = d.shuffle(buffer_size=10000)\n  d = d.map(\n      functools.partial(\n          decode_example,\n          features_and_vocabs_by_name=features_and_vocabs_by_name,\n          label_feature_name=input_config.label_feature.feature_name),\n      num_parallel_calls=8)\n  d = d.batch(batch_size, drop_remainder=True)\n  d = d.prefetch(1)\n  return d\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/model/input_pipeline_test.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Tests for input_pipeline.\"\"\"\nimport collections\nimport os\n\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.configs import input_config_pb2\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import input_pipeline\n\nfrom google.protobuf import text_format\n\nFAKE_MOVIE_GENRE_VOCAB = [\n    'UNK', 'Comedy', 'Drama', 'Romance', 'Animation', 'Children'\n]\n\nTEST_INPUT_CONFIG = text_format.Parse(\n    \"\"\"\n    activity_feature_groups {\n      features {\n        feature_name: \"context_movie_id\"\n        feature_type: INT\n        vocab_size: 3952\n        embedding_dim: 32\n        feature_length: 5\n      }\n      features {\n        feature_name: \"context_movie_rating\"\n        feature_type: FLOAT\n        feature_length: 5\n      }\n      encoder_type: BOW\n    }\n    activity_feature_groups {\n      features {\n        feature_name: \"context_movie_genre\"\n        feature_type: STRING\n        vocab_name: \"movie_genre_vocab.txt\"\n        vocab_size: 19\n        embedding_dim: 8\n        feature_length: 8\n      }\n      encoder_type: BOW\n    }\n    label_feature {\n      feature_name: \"label_movie_id\"\n      feature_type: INT\n      vocab_size: 3952\n      embedding_dim: 8\n      feature_length: 1\n    }\n    \"\"\", input_config_pb2.InputConfig())\n\nEXAMPLE1 = text_format.Parse(\n    \"\"\"\n    features {\n        feature {\n          key: \"context_movie_id\"\n          value {\n            int64_list {\n              value: [1, 2, 0, 0, 0]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_rating\"\n          value {\n            float_list {\n              value: [3.5, 4.0, 0.0, 0.0, 0.0]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_genre\"\n          value {\n            bytes_list {\n              value: [\n                    \"Animation\", \"Children\", \"Comedy\", \"Comedy\", \"Romance\", \"UNK\", \"UNK\", \"UNK\"\n                ]\n            }\n          }\n        }\n        feature {\n          key: \"label_movie_id\"\n          value {\n            int64_list {\n              value: [3]\n            }\n          }\n        }\n      }\"\"\", tf.train.Example())\n\n\nclass InputPipelineTest(tf.test.TestCase):\n\n  def _AssertSparseTensorValueEqual(self, a, b):\n    self.assertAllEqual(a.indices, b.indices)\n    self.assertAllEqual(a.values, b.values)\n    self.assertAllEqual(a.dense_shape, b.dense_shape)\n\n  def setUp(self):\n    super(InputPipelineTest, self).setUp()\n    self.tmp_dir = self.create_tempdir()\n    self.test_movie_genre_vocab_file = os.path.join(self.tmp_dir,\n                                                    'movie_genre_vocab.txt')\n    self.test_input_data_file = os.path.join(self.tmp_dir,\n                                             'test_input_data.tfrecord')\n    with open(self.test_movie_genre_vocab_file, 'w', encoding='utf-8') as f:\n      for item in FAKE_MOVIE_GENRE_VOCAB:\n        f.write(item + '\\n')\n    with tf.io.TFRecordWriter(self.test_input_data_file) as file_writer:\n      file_writer.write(EXAMPLE1.SerializeToString())\n\n  def test_features_vocabs_gen(self):\n    features_and_vocabs_by_name = (\n        input_pipeline.get_features_and_vocabs_by_name(TEST_INPUT_CONFIG,\n                                                       self.tmp_dir))\n    features_by_name = features_and_vocabs_by_name.features_by_name\n    vocabs_by_name = features_and_vocabs_by_name.vocabs_by_name\n    self.assertLen(features_by_name.keys(), 3)\n    self.assertLen(vocabs_by_name.keys(), 1)\n    self.assertAllInSet(\n        ['context_movie_id', 'context_movie_rating', 'context_movie_genre'],\n        features_by_name.keys())\n    self.assertAllInSet(['context_movie_genre'], vocabs_by_name.keys())\n\n  def test_decode_example(self):\n    features_and_vocabs_by_name = (\n        input_pipeline.get_features_and_vocabs_by_name(TEST_INPUT_CONFIG,\n                                                       self.tmp_dir))\n    features, label_feature = input_pipeline.decode_example(\n        EXAMPLE1.SerializeToString(),\n        features_and_vocabs_by_name,\n        'label_movie_id')\n    expected_context_movie_id = tf.constant([1, 2, 0, 0, 0],\n                                            dtype=tf.int32)\n    expected_context_movie_genre = tf.constant([4, 5, 1, 1, 3, 0, 0, 0],\n                                               dtype=tf.int32)\n    expected_context_movie_rating = tf.constant([3.5, 4.0, 0.0, 0.0, 0.0],\n                                                dtype=tf.float32)\n    self.assertAllEqual(features['context_movie_id'],\n                        expected_context_movie_id)\n    self.assertAllEqual(features['context_movie_genre'],\n                        expected_context_movie_genre)\n    self.assertAllEqual(features['context_movie_rating'],\n                        expected_context_movie_rating)\n    self.assertAllEqual(label_feature, tf.constant([3], dtype=tf.int32))\n\n  def test_get_input_dataset(self):\n    dataset = input_pipeline.get_input_dataset(\n        data_filepattern=self.test_input_data_file,\n        input_config=TEST_INPUT_CONFIG,\n        vocab_file_dir=self.tmp_dir,\n        batch_size=1)\n    dataset = dataset.take(1)\n    self.assertCountEqual([\n        'context_movie_id', 'context_movie_rating', 'context_movie_genre',\n        'label_movie_id'\n    ], dataset.element_spec[0].keys())\n\n  def test_get_serving_input_specs(self):\n    input_specs = input_pipeline.get_serving_input_specs(TEST_INPUT_CONFIG)\n    expected_input_specs = collections.OrderedDict()\n    expected_input_specs['context_movie_genre'] = tf.TensorSpec(\n        shape=[8], dtype=tf.dtypes.int32, name='context_movie_genre')\n    expected_input_specs['context_movie_id'] = tf.TensorSpec(\n        shape=[5], dtype=tf.dtypes.int32, name='context_movie_id')\n    expected_input_specs['context_movie_rating'] = tf.TensorSpec(\n        shape=[5], dtype=tf.dtypes.float32, name='context_movie_rating')\n    self.assertAllEqual(input_specs, expected_input_specs)\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/model/label_encoder.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Label encoder layer for on-device personalized recommendation model.\"\"\"\nimport math\nfrom typing import Dict\n\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.configs import input_config_pb2\n\n\nclass LabelEncoder(tf.keras.layers.Layer):\n  \"\"\"Layer to encode label feature.\n\n  Currently only id-based label encoder is supported. With this encoder, label\n  embedding layer will be created based on input config. And label embedding\n  will be generated by feeding input label feature to label embedding layer.\n  \"\"\"\n\n  def __init__(self,\n               input_config: input_config_pb2.InputConfig):\n    \"\"\"Initialize LabelEncoder layer based on input config.\n\n    Args:\n      input_config: The input config containing label feature configuation,\n        based on which label encoding could be generated.\n    \"\"\"\n    super(LabelEncoder, self).__init__()\n    self._input_config = input_config\n    self._label_feature = self._input_config.label_feature\n    if self._label_feature.feature_type != input_config_pb2.FeatureType.INT:\n      raise ValueError('Currently only INT type is supported for label.')\n    if not self.label_name:\n      raise ValueError('label_feature name is expected.')\n    self._label_embedding_layer = tf.keras.layers.Embedding(\n        self._label_feature.vocab_size,\n        self._label_feature.embedding_dim,\n        embeddings_initializer=tf.keras.initializers.truncated_normal(\n            mean=0.0,\n            stddev=1.0 / math.sqrt(self._label_feature.embedding_dim)),\n        mask_zero=True,\n        name=self.label_name + 'embedding_layer')\n\n  @property\n  def label_name(self):\n    return self._label_feature.feature_name\n\n  def encode(self, input_label: tf.Tensor) -> tf.Tensor:\n    \"\"\"Generates label encoding with input label tensor.\n\n    Args:\n      input_label: Input label tensor, the shape of tensor should\n        [num_label_items, 1].\n\n    Returns:\n      Label encoding.\n    \"\"\"\n    if isinstance(input_label, tf.SparseTensor):\n      input_label = tf.sparse.to_dense(input_label)\n    label_embedding = self._label_embedding_layer(input_label)\n    if tf.keras.backend.ndim(label_embedding) == 3:\n      label_embedding = tf.squeeze(label_embedding)\n    return label_embedding\n\n  def call(self, inputs: Dict[str, tf.Tensor]):\n    \"\"\"Generate label encoding with input tentor dictionary.\n\n    Args:\n      inputs: Dictionary mapping feature name to input tensors.\n\n    Returns:\n      Label encoding.\n    \"\"\"\n    input_label = inputs[self.label_name]\n    return self.encode(input_label)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/model/label_encoder_test.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Tests for label_encoder.\"\"\"\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.configs import input_config_pb2\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import label_encoder\n\n\nclass LabelEncoderTest(tf.test.TestCase):\n\n  def _create_test_input_config(self):\n    \"\"\"Generate test input_config_pb2.InputConfig proto.\"\"\"\n    feature_context_movie_id = input_config_pb2.Feature(\n        feature_name='context_movie_id',\n        feature_type=input_config_pb2.FeatureType.INT,\n        vocab_size=3952,\n        embedding_dim=4)\n    feature_context_movie_rating = input_config_pb2.Feature(\n        feature_name='context_movie_rating',\n        feature_type=input_config_pb2.FeatureType.FLOAT)\n    feature_group_1 = input_config_pb2.FeatureGroup(\n        features=[feature_context_movie_id, feature_context_movie_rating],\n        encoder_type=input_config_pb2.EncoderType.BOW)\n\n    feature_label = input_config_pb2.Feature(\n        feature_name='label_movie_id',\n        feature_type=input_config_pb2.FeatureType.INT,\n        vocab_size=3952,\n        embedding_dim=4)\n\n    input_config = input_config_pb2.InputConfig(\n        activity_feature_groups=[feature_group_1],\n        label_feature=feature_label)\n    return input_config\n\n  def test_label_encoder(self):\n    input_config = self._create_test_input_config()\n    input_label_encoder = label_encoder.LabelEncoder(\n        input_config=input_config)\n    input_label_movie_id = tf.constant([[1], [2]])\n    inputs = {\n        'label_movie_id': input_label_movie_id\n    }\n    label_embedding = input_label_encoder(inputs)\n    print(label_embedding.shape)\n    self.assertAllEqual([2, 4], list(label_embedding.shape))\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/model/losses.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Customized losses.\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow as tf\n\n\nclass BatchSoftmax(tf.keras.losses.Loss):\n  \"\"\"Compute batch softmax over batch similarities.\n\n  This softmax loss takes in-batch negatives without considering\n  negatives out of batch.\n  \"\"\"\n\n  def __init__(self, name='batch_softmax', **kwargs):\n    super(BatchSoftmax, self).__init__(name=name, **kwargs)\n\n  @tf.function\n  def call(self, y_true: tf.Tensor, y_pred: tf.Tensor):\n    \"\"\"Compute in batch softmax loss.\n\n    Args:\n      y_true: the true labels with shape [batch_size, 1].\n      y_pred: pre-calculated similarities matrix between context embedding and\n        full vocab label embeddings, y_pred is expected to be the similarities\n        with shape [batch_size,label_embedding_vocab_size].\n\n    Returns:\n      The softmax loss with in-batch negatives.\n    \"\"\"\n    logits = tf.keras.backend.cast(y_pred, 'float32')\n    logits = tf.gather(y_pred, tf.transpose(y_true)[0], axis=1)\n    full_labels = tf.eye(\n        tf.shape(logits)[0], tf.shape(logits)[1], dtype=tf.dtypes.float32)\n    batch_loss = tf.nn.softmax_cross_entropy_with_logits(\n        labels=full_labels, logits=logits)\n    loss = tf.reduce_mean(batch_loss)\n    return loss\n\n\nclass GlobalSoftmax(tf.keras.losses.Loss):\n  \"\"\"Compute softmax over similarities.\n\n  This loss fuction computes softmax over similarities between context and\n  full vocab label embeddings, considering full label vocab non-label\n  predictions as negatives. This is currently the default loss in the model.\n  \"\"\"\n\n  def __init__(self, name='global_softmax', **kwargs):\n    super(GlobalSoftmax, self).__init__(name=name, **kwargs)\n\n  @tf.function\n  def call(self, y_true: tf.Tensor, y_pred: tf.Tensor):\n    \"\"\"Compute softmax loss with full vocab labels as negatives.\n\n    Args:\n      y_true: the true labels with shape [batch_size, 1].\n      y_pred: the pre-calculated similarities matrix with shape [batch_size,\n        label_embedding_vocab_size]\n\n    Returns:\n      The softmax loss with full vocab labels as negatives.\n    \"\"\"\n    logits = tf.keras.backend.cast(y_pred, 'float32')\n    # Compose a metric to tell which column represents the similarity between\n    # the example and the label. For each row, only the item at index of the\n    # label of that example will be set as 1.\n    full_labels = tf.one_hot(\n        tf.transpose(y_true)[0], tf.shape(logits)[1], dtype=tf.dtypes.float32)\n    batch_loss = tf.nn.softmax_cross_entropy_with_logits(\n        labels=full_labels, logits=logits)\n    loss = tf.reduce_mean(batch_loss)\n    return loss\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/model/losses_test.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Tests for the keras losses.\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import losses\n\n\nclass KerasLossesTest(tf.test.TestCase):\n\n  def test_batch_softmax_loss(self):\n    batch_softmax = losses.BatchSoftmax()\n    true_label = tf.constant([[2], [0], [1]], dtype=tf.int32)\n    logits = tf.constant([\n        [0.8, 0.1, 0.2, 0.3],\n        [0.2, 0.7, 0.1, 0.5],\n        [0.5, 0.4, 0.9, 0.2]\n    ], dtype=tf.float32)\n    self.assertBetween(\n        batch_softmax.call(y_true=true_label, y_pred=logits).numpy(),\n        1.3, 1.4)\n\n  def test_global_softmax_loss(self):\n    global_softmax = losses.GlobalSoftmax()\n    true_label = tf.constant([[2], [0], [1]], dtype=tf.int32)\n    logits = tf.constant([\n        [0.8, 0.1, 0.2, 0.3],\n        [0.2, 0.7, 0.1, 0.5],\n        [0.5, 0.4, 0.9, 0.2]\n    ], dtype=tf.float32)\n    self.assertBetween(\n        global_softmax.call(y_true=true_label, y_pred=logits).numpy(), 1.5, 1.6)\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/model/metrics.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Customized metrics.\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow as tf\n\n\ndef _get_batch_similarities(batch_label, full_vocab_similarities):\n  return tf.gather(\n      full_vocab_similarities, tf.transpose(batch_label)[0], axis=1)\n\n\nclass BatchRecall(tf.keras.metrics.Recall):\n  \"\"\"Compute batch recall for top_k.\"\"\"\n\n  def __init__(self, top_k=1, name='batch_recall'):\n    super().__init__(name=name, top_k=top_k)\n\n  def update_state(self, y_true, y_pred, sample_weight=None):\n    \"\"\"Update state of the metric.\n\n    Args:\n      y_true: the true labels with shape [batch_size, 1].\n      y_pred: model output, which is the similarity matrix with shape\n        [batch_size, label_embedding_vocab_size] between context and full vocab\n        label embeddings.\n      sample_weight: Optional weighting of each example. Defaults to 1.\n    \"\"\"\n    label_indices = tf.eye(\n        tf.shape(y_pred)[0], tf.shape(y_pred)[0], dtype=tf.dtypes.float32)\n    # Since tf.keras default recall metric only accept predicted values in\n    # range [0, 1], so use tf.nn.softmax to preprocess.\n    normalized_logits = tf.nn.softmax(_get_batch_similarities(y_true, y_pred))\n    super().update_state(label_indices, normalized_logits, sample_weight)\n\n\nclass GlobalRecall(tf.keras.metrics.Recall):\n  \"\"\"Compute global recall for top_k.\"\"\"\n\n  def __init__(self, top_k=1, name='global_recall'):\n    super().__init__(name=name, top_k=top_k)\n\n  def update_state(self, y_true, y_pred, sample_weight=None):\n    \"\"\"Update state of the metric.\n\n    Args:\n      y_true: the true labels with shape [batch_size, 1].\n      y_pred: model output, which is the similarity matrix with shape\n        [batch_size, label_embedding_vocab_size] between context and full vocab\n        label embeddings.\n      sample_weight: Optional weighting of each example. Defaults to 1.\n    \"\"\"\n    label_indices = tf.one_hot(\n        tf.transpose(y_true)[0], tf.shape(y_pred)[1], dtype=tf.dtypes.float32)\n    # Since tf.keras default recall metric only accept predicted values in\n    # range [0, 1], so use tf.nn.softmax to preprocess.\n    normalized_logits = tf.nn.softmax(y_pred)\n    super().update_state(label_indices, normalized_logits, sample_weight)\n\n\nclass BatchMeanRank(tf.keras.metrics.Mean):\n  \"\"\"Keras metric computing mean rank of correct label within batch.\"\"\"\n\n  def __init__(self, name='batch_mean_rank', **kwargs):\n    super().__init__(name=name, **kwargs)\n\n  def update_state(self, y_true, y_pred, sample_weight=None):  # pytype: disable=signature-mismatch  # overriding-parameter-count-checks\n    \"\"\"Update state of the metric.\n\n    Args:\n      y_true: the true labels with shape [batch_size, 1].\n      y_pred: model output, which is the similarity matrix with shape\n        [batch_size, label_embedding_vocab_size] between context and full vocab\n        label embeddings. Hence to compute batch mean rank, the default global\n        similarities will be coverted to batch similarities first with shape\n        [batch_size, batch_size].\n      sample_weight: Optional weighting of each example. Defaults to 1.\n    \"\"\"\n    similarities = _get_batch_similarities(y_true, y_pred)\n    # Get the ranks of the correct label for each context.\n    batch_size = tf.shape(similarities)[0]\n    label_indices = tf.expand_dims(tf.range(batch_size), -1)\n\n    # Get the indices of similar labels in sorted order for each context.\n    sorted_similarities = tf.argsort(\n        similarities, axis=-1, direction='DESCENDING')\n\n    ranks = tf.where(tf.equal(sorted_similarities, label_indices))[:, -1]\n    ranks = tf.keras.backend.cast(ranks, 'float32')\n    super().update_state(ranks, sample_weight=sample_weight)\n\n\nclass GlobalMeanRank(tf.keras.metrics.Mean):\n  \"\"\"Keras metric computing mean rank of correct label globally.\"\"\"\n\n  def __init__(self, name='global_mean_rank', **kwargs):\n    super().__init__(name=name, **kwargs)\n\n  def update_state(self, y_true, y_pred, sample_weight=None):  # pytype: disable=signature-mismatch  # overriding-parameter-count-checks\n    \"\"\"Update state of the metric.\n\n    Args:\n      y_true: the true labels with shape [batch_size, 1].\n      y_pred: model output, which is the similarity matrix with shape\n        [batch_size, label_embedding_vocab_size] between context and full vocab\n        label embeddings.\n      sample_weight: Optional weighting of each example. Defaults to 1.\n    \"\"\"\n    similarities = y_pred\n    label_indices = tf.expand_dims(tf.cast(y_true, dtype=tf.int32), -1)\n\n    # Get the indices of similar labels in sorted order for each context.\n    sorted_similarities = tf.argsort(\n        similarities, axis=-1, direction='DESCENDING')\n\n    ranks = tf.where(tf.equal(sorted_similarities, label_indices))[:, -1]\n    ranks = tf.keras.backend.cast(ranks, 'float32')\n    super().update_state(ranks, sample_weight=sample_weight)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/model/metrics_test.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Tests for the keras_metrics.\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import metrics\n\n\nclass KerasMetricsTest(tf.test.TestCase):\n\n  def test_batch_recall_and_mean_rank(self):\n    batch_recall = metrics.BatchRecall(top_k=2)\n    batch_mean_rank = metrics.BatchMeanRank()\n    true_label = tf.constant([[2], [0], [1]], dtype=tf.int32)\n    logits = tf.constant([\n        [0.8, 0.1, 1.1, 0.3],\n        [0.2, 0.7, 0.1, 0.5],\n        [0.7, 0.4, 0.9, 0.2]\n    ], dtype=tf.float32)\n    batch_recall.update_state(y_true=true_label, y_pred=logits)\n    batch_mean_rank.update_state(y_true=true_label, y_pred=logits)\n    self.assertBetween(batch_recall.result().numpy(), 0.6, 0.7)\n    self.assertEqual(batch_mean_rank.result().numpy(), 1.0)\n\n  def test_global_recall_and_mean_rank(self):\n    global_recall = metrics.GlobalRecall(top_k=2)\n    global_mean_rank = metrics.GlobalMeanRank()\n    true_label = tf.constant([[2], [0], [1]], dtype=tf.int32)\n    logits = tf.constant([\n        [0.8, 0.1, 1.1, 0.3],\n        [0.2, 0.7, 0.1, 0.5],\n        [0.7, 0.4, 0.9, 0.2]\n    ], dtype=tf.float32)\n    global_recall.update_state(y_true=true_label, y_pred=logits)\n    global_mean_rank.update_state(y_true=true_label, y_pred=logits)\n    self.assertBetween(global_recall.result().numpy(), 0.3, 0.4)\n    self.assertBetween(global_mean_rank.result().numpy(), 1.3, 1.4)\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/model/recommendation_model.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"On-device personalized recommendation model.\"\"\"\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.configs import input_config_pb2\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.configs import model_config as model_config_class\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import context_encoder\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import dotproduct_similarity\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import label_encoder\n\n\nclass RecommendationModel(tf.keras.Model):\n  \"\"\"Personalized dual-encoder style recommendation model.\"\"\"\n\n  def __init__(self,\n               input_config: input_config_pb2.InputConfig,\n               model_config: model_config_class.ModelConfig):\n    \"\"\"Initializes RecommendationModel according to input and model configs.\n\n    Takes in input and model configs to initialize the recommendation model.\n    Context encoder layer, label encoder layer and dotproduct similarity layer\n    will be prepared.\n\n    Args:\n      input_config: The input config (input_config_pb2.InputConfig).\n      model_config: The model config (model_config_class.ModelConfig).\n    \"\"\"\n    super(RecommendationModel, self).__init__()\n    self._input_config = input_config\n    self._model_config = model_config\n    self._context_encoder = context_encoder.ContextEncoder(\n        input_config=self._input_config, model_config=self._model_config)\n    self._label_encoder = label_encoder.LabelEncoder(\n        input_config=self._input_config)\n    self._dotproduct_layer = dotproduct_similarity.DotProductSimilarity()\n\n  def call(self, inputs):  # pytype: disable=signature-mismatch  # overriding-parameter-count-checks\n    \"\"\"Compute outputs by passing inputs through the model.\n\n    Computes the dotproduct similarity between the context embeddings with\n    label embeddings of all items in the vocabulary, hence the output could be\n    used to compute loss with all non-label items as negatives.\n\n    Args:\n      inputs: The inputs to the model, which should be a dictionary having\n        feature names as keys and parsed inputs generated with input pipeline.\n\n    Returns:\n      Dotproduct similarity for training mode.\n    \"\"\"\n    context_embeddings = self._context_encoder(inputs)\n    # Compute the similarities between the context embedding and embeddings of\n    # all items in the vocabulary.\n    full_vocab_input_label = tf.range(\n        self._input_config.label_feature.vocab_size)\n    label = {self._label_encoder.label_name: full_vocab_input_label}\n    full_vocab_label_embeddings = self._label_encoder(label)\n    full_vocab_dotproduct = self._dotproduct_layer(\n        context_embeddings=context_embeddings,\n        label_embeddings=full_vocab_label_embeddings,\n        top_k=None)[0]\n    return full_vocab_dotproduct\n\n  @tf.function\n  def serve(self, **kwargs):\n    inputs = kwargs\n    context_embeddings = self._context_encoder(inputs)\n    full_vocab_input_label = tf.range(\n        self._input_config.label_feature.vocab_size)\n    full_vocab_label_embeddings = self._label_encoder.encode(\n        full_vocab_input_label)\n    assert self._model_config.num_predictions\n    (_, top_ids, top_scores) = self._dotproduct_layer(\n        context_embeddings=context_embeddings,\n        label_embeddings=full_vocab_label_embeddings,\n        top_k=self._model_config.num_predictions)\n    return {'top_prediction_ids': top_ids, 'top_prediction_scores': top_scores}\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/model/recommendation_model_launcher.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Personalized recommendation model runner based on Tensorflow keras API.\"\"\"\nimport os\nimport time\nfrom typing import List\n\nfrom absl import app\nfrom absl import flags\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.configs import input_config_pb2\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.configs import model_config as model_config_class\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import input_pipeline\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import losses\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import metrics\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import recommendation_model\n\nfrom google.protobuf import text_format\n\n\nFLAGS = flags.FLAGS\n\n\ndef define_flags():\n  \"\"\"Define flags.\"\"\"\n  flags.DEFINE_string('training_data_filepattern', None,\n                      'File pattern of the training data.')\n  flags.DEFINE_string('testing_data_filepattern', None,\n                      'File pattern of the training data.')\n  flags.DEFINE_string('model_dir', None, 'Directory to store checkpoints.')\n  flags.DEFINE_string('export_dir', None, 'Directory for the exported model.')\n  flags.DEFINE_integer('batch_size', 1, 'Training batch size.')\n  flags.DEFINE_float('learning_rate', 0.1, 'Learning rate.')\n  flags.DEFINE_integer('steps_per_epoch', 10,\n                       'Number of steps to run in each epoch.')\n  flags.DEFINE_integer('num_epochs', 10000, 'Number of training epochs.')\n  flags.DEFINE_integer('num_eval_steps', 1000, 'Number of eval steps.')\n  flags.DEFINE_enum('run_mode', 'train_and_eval',\n                    ['train_and_eval', 'export', 'export_tflite'],\n                    'Mode of the launcher, default value is: train_and_eval')\n  flags.DEFINE_float('gradient_clip_norm', 1.0,\n                     'gradient_clip_norm <= 0 meaning no clip.')\n  flags.DEFINE_string('vocab_dir', None,\n                      'Path of the directory storing vocabulary files.')\n  flags.DEFINE_string('input_config_file', None,\n                      'Path to the input config pbtxt'\n                      'file.')\n  flags.DEFINE_list('hidden_layer_dims', None, 'Hidden layer dimensions.')\n  flags.DEFINE_list('eval_top_k', None, 'Top k to evaluate.')\n  flags.DEFINE_list(\n      'conv_num_filter_ratios', None,\n      'Number of filter ratios for the Conv1D layer, this'\n      'flag is only required if CNN encoder type is used.')\n  flags.DEFINE_integer(\n      'conv_kernel_size', 4,\n      'Size of the Conv1D layer kernel size, this flag is only'\n      'required if CNN encoder type is used.')\n  flags.DEFINE_integer(\n      'lstm_num_units', 4, 'Number of units for the LSTM layer,'\n      'this flag is only required if LSTM encoder type is used.')\n  flags.DEFINE_integer('num_predictions', 5,\n                       'Num of top predictions to output.')\n  flags.DEFINE_string('checkpoint_path', '', 'Path to the checkpoint.')\n\n\nclass SimpleCheckpoint(tf.keras.callbacks.Callback):\n  \"\"\"Keras callback to save tf.train.Checkpoints.\"\"\"\n\n  def __init__(self, checkpoint_manager):\n    super(SimpleCheckpoint, self).__init__()\n    self.checkpoint_manager = checkpoint_manager\n\n  def on_epoch_end(self, epoch, logs=None):\n    step_counter = self.checkpoint_manager._step_counter.numpy()  # pylint: disable=protected-access\n    self.checkpoint_manager.save(checkpoint_number=step_counter)\n\n\ndef _get_optimizer(learning_rate: float, gradient_clip_norm: float):\n  \"\"\"Gets model optimizer.\"\"\"\n  kwargs = {'clipnorm': gradient_clip_norm} if gradient_clip_norm > 0 else {}\n  return tf.keras.optimizers.Adagrad(learning_rate, **kwargs)\n\n\ndef _get_metrics(eval_top_k: List[int]):\n  \"\"\"Gets model evaluation metrics of both batch samples and full vocabulary.\"\"\"\n  metrics_list = [\n      metrics.GlobalRecall(name=f'Global_Recall/Recall_{k}', top_k=k)\n      for k in eval_top_k\n  ]\n  metrics_list.append(metrics.GlobalMeanRank(name='global_mean_rank'))\n  metrics_list.extend(\n      metrics.BatchRecall(name=f'Batch_Recall/Recall_{k}', top_k=k)\n      for k in eval_top_k)\n  metrics_list.append(metrics.BatchMeanRank(name='batch_mean_rank'))\n  return metrics_list\n\n\ndef compile_model(model, eval_top_k, learning_rate, gradient_clip_norm):\n  \"\"\"Compile keras model.\"\"\"\n  model.compile(\n      optimizer=_get_optimizer(\n          learning_rate=learning_rate, gradient_clip_norm=gradient_clip_norm),\n      loss=losses.GlobalSoftmax(),\n      metrics=_get_metrics(eval_top_k))\n\n\ndef build_keras_model(input_config: input_config_pb2.InputConfig,\n                      model_config: model_config_class.ModelConfig):\n  \"\"\"Construct and compile recommendation keras model.\n\n  Construct recommendation model according to input config and model config.\n  Compile the model with optimizer, loss function and eval metrics.\n\n  Args:\n    input_config: The configuration object(input_config_pb2.InputConfig) that\n      holds parameters for model input feature processing.\n    model_config: A ModelConfig object that holds parameters to set up the\n      model architecture.\n\n  Returns:\n    The compiled keras model.\n  \"\"\"\n  model = recommendation_model.RecommendationModel(\n      input_config=input_config, model_config=model_config)\n  compile_model(model, model_config.eval_top_k, FLAGS.learning_rate,\n                FLAGS.gradient_clip_norm)\n  return model\n\n\ndef get_callbacks(keras_model: tf.keras.Model,\n                  model_dir: str):\n  \"\"\"Sets up callbacks for training and evaluation.\"\"\"\n  summary_dir = os.path.join(model_dir, 'summaries')\n  summary_callback = tf.keras.callbacks.TensorBoard(summary_dir)\n  checkpoint = tf.train.Checkpoint(\n      model=keras_model, optimizer=keras_model.optimizer)\n  checkpoint_manager = tf.train.CheckpointManager(\n      checkpoint,\n      directory=model_dir,\n      max_to_keep=None,\n      step_counter=keras_model.optimizer.iterations,\n      checkpoint_interval=0)\n  checkpoint_callback = SimpleCheckpoint(checkpoint_manager)\n  return [summary_callback, checkpoint_callback]\n\n\ndef train_and_eval(model: tf.keras.Model,\n                   model_dir: str,\n                   train_input_dataset: tf.data.Dataset,\n                   eval_input_dataset: tf.data.Dataset,\n                   steps_per_epoch: int,\n                   epochs: int,\n                   eval_steps: int):\n  \"\"\"Train and evaluate.\"\"\"\n  callbacks = get_callbacks(model, model_dir)\n  history = model.fit(\n      x=train_input_dataset,\n      validation_data=eval_input_dataset,\n      steps_per_epoch=steps_per_epoch,\n      epochs=epochs,\n      validation_steps=eval_steps,\n      callbacks=callbacks)\n  tf.get_logger().info(history)\n  return model\n\n\ndef save_model(checkpoint_path: str, export_dir: str,\n               input_config: input_config_pb2.InputConfig,\n               model_config: model_config_class.ModelConfig):\n  \"\"\"Export to savedmodel.\n\n  Args:\n    checkpoint_path: The path to the checkpoint that the model will be exported\n      based on.\n    export_dir: The directory to export models to.\n    input_config: The input config of the model.\n    model_config: The configuration to set up the model.\n  \"\"\"\n  model = recommendation_model.RecommendationModel(\n      input_config=input_config,\n      model_config=model_config)\n  checkpoint = tf.train.Checkpoint(model=model)\n  checkpoint.restore(checkpoint_path).run_restore_ops()\n  input_specs = input_pipeline.get_serving_input_specs(input_config)\n  signatures = {\n      tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY:\n          model.serve.get_concrete_function(**input_specs)\n  }\n  tf.saved_model.save(model, export_dir=export_dir, signatures=signatures)\n\n\ndef export_tflite(export_dir):\n  \"\"\"Export to TFLite model.\n\n  Args:\n    export_dir: the model exportation dir, where saved_model is located.\n  \"\"\"\n  converter = tf.lite.TFLiteConverter.from_saved_model(export_dir)\n  tflite_model = converter.convert()\n  tflite_model_path = os.path.join(export_dir, 'model.tflite')\n  with tf.io.gfile.GFile(tflite_model_path, 'wb') as f:\n    f.write(tflite_model)\n\n\ndef export(checkpoint_path: str, input_config: input_config_pb2.InputConfig,\n           model_config: model_config_class.ModelConfig, export_dir: str):\n  \"\"\"Export to tensorflow saved model and TFLite model.\n\n  Args:\n    checkpoint_path: The path to the checkpoint that the model will be exported\n      based on.\n    input_config: The input config of the model.\n    model_config: The configuration to set up the model.\n    export_dir: The directory to store the exported model, If not set, model is\n      exported to the model_dir with timestamp.\n  \"\"\"\n  logger = tf.get_logger()\n  if not export_dir:\n    export_dir = os.path.join(FLAGS.model_dir, 'export', str(int(time.time())))\n  logger.info('Exporting model to dir: {}'.format(export_dir))\n  save_model(\n      checkpoint_path=checkpoint_path,\n      export_dir=export_dir,\n      input_config=input_config,\n      model_config=model_config)\n  logger.info('Converting model to tflite model.')\n  export_tflite(export_dir)\n\n\ndef load_input_config():\n  \"\"\"Load input config.\"\"\"\n  assert FLAGS.input_config_file, 'input_config_file cannot be empty.'\n  with tf.io.gfile.GFile(FLAGS.input_config_file, 'rb') as reader:\n    return text_format.Parse(reader.read(), input_config_pb2.InputConfig())\n\n\ndef prepare_model_config():\n  \"\"\"Prepare model config.\"\"\"\n  return model_config_class.ModelConfig(\n      hidden_layer_dims=[int(x) for x in FLAGS.hidden_layer_dims],\n      eval_top_k=[int(x) for x in FLAGS.eval_top_k],\n      conv_num_filter_ratios=[int(x) for x in FLAGS.conv_num_filter_ratios],\n      conv_kernel_size=FLAGS.conv_kernel_size,\n      lstm_num_units=FLAGS.lstm_num_units,\n      num_predictions=FLAGS.num_predictions)\n\n\ndef main(_):\n  logger = tf.get_logger()\n  if not tf.io.gfile.exists(FLAGS.model_dir):\n    tf.io.gfile.mkdir(FLAGS.model_dir)\n\n  if not tf.io.gfile.exists(FLAGS.export_dir):\n    tf.io.gfile.mkdir(FLAGS.export_dir)\n\n  input_config = load_input_config()\n  model_config = prepare_model_config()\n\n  logger.info('Setting up train and eval input datasets.')\n  train_input_dataset = input_pipeline.get_input_dataset(\n      data_filepattern=FLAGS.training_data_filepattern,\n      input_config=input_config,\n      vocab_file_dir=FLAGS.vocab_dir,\n      batch_size=FLAGS.batch_size)\n  eval_input_dataset = input_pipeline.get_input_dataset(\n      data_filepattern=FLAGS.testing_data_filepattern,\n      input_config=input_config,\n      vocab_file_dir=FLAGS.vocab_dir,\n      batch_size=FLAGS.batch_size)\n\n  logger.info('Build keras model for mode: {}.'.format(FLAGS.run_mode))\n  model = build_keras_model(\n      input_config=input_config, model_config=model_config)\n\n  if FLAGS.run_mode == 'train_and_eval':\n    train_and_eval(\n        model=model,\n        model_dir=FLAGS.model_dir,\n        train_input_dataset=train_input_dataset,\n        eval_input_dataset=eval_input_dataset,\n        steps_per_epoch=FLAGS.steps_per_epoch,\n        epochs=FLAGS.num_epochs,\n        eval_steps=FLAGS.num_eval_steps)\n    latest_checkpoint_path = tf.train.latest_checkpoint(FLAGS.model_dir)\n    if latest_checkpoint_path:\n      export(\n          checkpoint_path=latest_checkpoint_path,\n          input_config=input_config,\n          model_config=model_config,\n          export_dir=FLAGS.export_dir)\n  elif FLAGS.run_mode == 'export':\n    checkpoint_path = (\n        FLAGS.checkpoint_path if FLAGS.checkpoint_path else\n        tf.train.latest_checkpoint(FLAGS.model_dir))\n    export(\n        checkpoint_path=checkpoint_path,\n        input_config=input_config,\n        model_config=model_config,\n        export_dir=FLAGS.export_dir)\n  else:\n    logger.error('Unsupported launcher run model {}.'.format(FLAGS.run_mode))\n\n\nif __name__ == '__main__':\n  define_flags()\n  app.run(main)\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/model/recommendation_model_launcher_test.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Tests for recommendation_model_launcher.\"\"\"\nimport os\nfrom absl import flags\nimport tensorflow as tf\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import input_pipeline\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import recommendation_model_launcher as launcher\nfrom google.protobuf import text_format\n# pylint: disable=g-direct-tensorflow-import\nfrom ai_edge_litert import interpreter as tfl_interpreter\n# pylint: enable=g-direct-tensorflow-import\n\nFLAGS = flags.FLAGS\n\nFAKE_MOVIE_GENRE_VOCAB = [\n    'UNK',\n    'Comedy',\n    'Drama',\n    'Romance',\n    'Animation',\n    'Children'\n]\n\nTEST_INPUT_CONFIG = \"\"\"\n    activity_feature_groups {\n      features {\n        feature_name: \"context_movie_id\"\n        feature_type: INT\n        vocab_size: 3952\n        embedding_dim: 8\n        feature_length: 5\n      }\n      features {\n        feature_name: \"context_movie_rating\"\n        feature_type: FLOAT\n        feature_length: 5\n      }\n      encoder_type: CNN\n    }\n    activity_feature_groups {\n      features {\n        feature_name: \"context_movie_genre\"\n        feature_type: STRING\n        vocab_name: \"movie_genre_vocab.txt\"\n        vocab_size: 19\n        embedding_dim: 8\n        feature_length: 8\n      }\n      encoder_type: BOW\n    }\n    label_feature {\n      feature_name: \"label_movie_id\"\n      feature_type: INT\n      vocab_size: 3952\n      embedding_dim: 8\n      feature_length: 1\n    }\n\"\"\"\n\nEXAMPLE1 = text_format.Parse(\n    \"\"\"\n    features {\n        feature {\n          key: \"context_movie_id\"\n          value {\n            int64_list {\n              value: [1, 2, 0, 0, 0]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_rating\"\n          value {\n            float_list {\n              value: [3.5, 4.0, 0.0, 0.0, 0.0]\n            }\n          }\n        }\n        feature {\n          key: \"context_movie_genre\"\n          value {\n            bytes_list {\n              value: [\n                    \"Animation\", \"Children\", \"Comedy\", \"Comedy\", \"Romance\", \"UNK\", \"UNK\", \"UNK\"\n                ]\n            }\n          }\n        }\n        feature {\n          key: \"label_movie_id\"\n          value {\n            int64_list {\n              value: [3]\n            }\n          }\n        }\n      }\"\"\", tf.train.Example())\n\n\nclass RecommendationModelLauncherTest(tf.test.TestCase):\n\n  def _AssertSparseTensorValueEqual(self, a, b):\n    self.assertAllEqual(a.indices, b.indices)\n    self.assertAllEqual(a.values, b.values)\n    self.assertAllEqual(a.dense_shape, b.dense_shape)\n\n  def _assertInputDetail(self, input_details, index, name, shape):\n    self.assertEqual(name, input_details[index]['name'])\n    self.assertEqual(shape, input_details[index]['shape'])\n\n  def setUp(self):\n    super().setUp()\n    self.tmp_dir = self.create_tempdir()\n    self.test_input_config_file = os.path.join(self.tmp_dir,\n                                               'input_config.pbtxt')\n    self.test_movie_genre_vocab_file = os.path.join(self.tmp_dir,\n                                                    'movie_genre_vocab.txt')\n    self.test_input_data_file = os.path.join(self.tmp_dir,\n                                             'test_input_data.tfrecord')\n    with open(self.test_input_config_file, 'w', encoding='utf-8') as f:\n      f.write(TEST_INPUT_CONFIG)\n    with open(self.test_movie_genre_vocab_file, 'w', encoding='utf-8') as f:\n      for item in FAKE_MOVIE_GENRE_VOCAB:\n        f.write(item + '\\n')\n    with tf.io.TFRecordWriter(self.test_input_data_file) as file_writer:\n      file_writer.write(EXAMPLE1.SerializeToString())\n\n    self.test_model_dir = os.path.join(self.tmp_dir, 'test_model_dir')\n\n    FLAGS.training_data_filepattern = self.test_input_data_file\n    FLAGS.testing_data_filepattern = self.test_input_data_file\n    FLAGS.input_config_file = self.test_input_config_file\n    FLAGS.model_dir = self.test_model_dir\n    FLAGS.hidden_layer_dims = [8, 4]\n    FLAGS.eval_top_k = [1, 5]\n    FLAGS.num_predictions = 5\n    FLAGS.conv_num_filter_ratios = [2, 4]\n    FLAGS.conv_kernel_size = 4\n    FLAGS.lstm_num_units = 16\n\n  def testModelTrainEvalExport(self):\n    \"\"\"Verifies that model can be trained and evaluated.\"\"\"\n    tf.io.gfile.mkdir(FLAGS.model_dir)\n    input_config = launcher.load_input_config()\n    model_config = launcher.prepare_model_config()\n    dataset = input_pipeline.get_input_dataset(\n        data_filepattern=self.test_input_data_file,\n        input_config=input_config,\n        vocab_file_dir=self.tmp_dir,\n        batch_size=8)\n    model = launcher.build_keras_model(input_config, model_config)\n    launcher.train_and_eval(\n        model=model,\n        model_dir=FLAGS.model_dir,\n        train_input_dataset=dataset,\n        eval_input_dataset=dataset,\n        steps_per_epoch=2,\n        epochs=2,\n        eval_steps=1)\n    self.assertTrue(os.path.exists(self.test_model_dir))\n    summaries_dir = os.path.join(self.test_model_dir, 'summaries')\n    self.assertTrue(os.path.exists(summaries_dir))\n    export_dir = os.path.join(FLAGS.model_dir, 'export')\n    latest_checkpoint = tf.train.latest_checkpoint(FLAGS.model_dir)\n    launcher.save_model(\n        checkpoint_path=latest_checkpoint,\n        export_dir=export_dir,\n        input_config=input_config,\n        model_config=model_config)\n    savedmodel_path = os.path.join(export_dir, 'saved_model.pb')\n    self.assertTrue(os.path.exists(savedmodel_path))\n    imported = tf.saved_model.load(export_dir, tags=None)\n    infer = imported.signatures['serving_default']\n    context_movie_id = tf.range(5, dtype=tf.int32)\n    context_movie_rating = tf.range(5, dtype=tf.float32)\n    context_movie_genre = tf.range(8, dtype=tf.int32)\n    predictions = infer(context_movie_id=context_movie_id,\n                        context_movie_rating=context_movie_rating,\n                        context_movie_genre=context_movie_genre)\n    self.assertAllEqual([5], predictions['top_prediction_ids'].shape)\n    self.assertAllEqual([5], predictions['top_prediction_scores'].shape)\n    launcher.export_tflite(export_dir)\n    tflite_model_path = os.path.join(export_dir, 'model.tflite')\n    self.assertTrue(os.path.exists(tflite_model_path))\n    f = open(tflite_model_path, 'rb')\n    interpreter = tfl_interpreter.Interpreter(model_content=f.read())\n    interpreter.allocate_tensors()\n    inference_signature = interpreter.get_signature_list()['serving_default']\n    self.assertAllEqual(\n        ['context_movie_genre', 'context_movie_id', 'context_movie_rating'],\n        inference_signature['inputs'])\n    self.assertAllEqual(['top_prediction_ids', 'top_prediction_scores'],\n                        inference_signature['outputs'])\n    serving_name_to_tenors = {\n        'serving_default_context_movie_id:0': context_movie_id,\n        'serving_default_context_movie_rating:0': context_movie_rating,\n        'serving_default_context_movie_genre:0': context_movie_genre\n    }\n    input_details = interpreter.get_input_details()\n    output_details = interpreter.get_output_details()\n    indice_to_tensors = {}\n    for input_detail in input_details:\n      indice_to_tensors[input_detail['index']] = serving_name_to_tenors[\n          input_detail['name']]\n    for index, tensor in indice_to_tensors.items():\n      interpreter.set_tensor(index, tensor)\n    interpreter.invoke()\n    tflite_top_predictions_ids = interpreter.get_tensor(\n        output_details[0]['index'])\n    tflite_top_prediction_scores = interpreter.get_tensor(\n        output_details[1]['index'])\n    self.assertAllEqual([5], tflite_top_predictions_ids.shape)\n    self.assertAllEqual([5], tflite_top_prediction_scores.shape)\n\nif __name__ == '__main__':\n  launcher.define_flags()\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/model/recommendation_model_test.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Tests for personalized recommendation model.\"\"\"\nimport tensorflow as tf\n\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.configs import input_config_pb2\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.configs import model_config as model_config_class\nfrom tensorflow_examples.lite.model_maker.third_party.recommendation.ml.model import recommendation_model\n\n\nclass RecommendationModelTest(tf.test.TestCase):\n\n  def _create_test_input_config(self,\n                                encoder_type: input_config_pb2.EncoderType):\n    \"\"\"Generate test input_config_pb2.InputConfig proto.\"\"\"\n    feature_context_movie_id = input_config_pb2.Feature(\n        feature_name='context_movie_id',\n        feature_type=input_config_pb2.FeatureType.INT,\n        vocab_size=20,\n        embedding_dim=4)\n    feature_context_movie_rating = input_config_pb2.Feature(\n        feature_name='context_movie_rating',\n        feature_type=input_config_pb2.FeatureType.FLOAT)\n    feature_group_1 = input_config_pb2.FeatureGroup(\n        features=[feature_context_movie_id, feature_context_movie_rating],\n        encoder_type=encoder_type)\n\n    feature_label = input_config_pb2.Feature(\n        feature_name='label_movie_id',\n        feature_type=input_config_pb2.FeatureType.INT,\n        vocab_size=20,\n        embedding_dim=4)\n\n    input_config = input_config_pb2.InputConfig(\n        activity_feature_groups=[feature_group_1],\n        label_feature=feature_label)\n    return input_config\n\n  def _create_test_model_config(self):\n    return model_config_class.ModelConfig(\n        hidden_layer_dims=[8, 4],\n        eval_top_k=[1, 5],\n        conv_num_filter_ratios=[1, 2],\n        conv_kernel_size=2,\n        lstm_num_units=16)\n\n  def test_model_train_bow(self):\n    input_config = self._create_test_input_config(\n        input_config_pb2.EncoderType.BOW)\n    model_config = self._create_test_model_config()\n    test_model = recommendation_model.RecommendationModel(\n        input_config=input_config, model_config=model_config)\n    batch_size = 4\n    input_context_movie_id = tf.keras.layers.Input(\n        shape=(10,),\n        dtype=tf.int32,\n        batch_size=batch_size,\n        name='context_movie_id')\n    input_context_movie_rating = tf.keras.layers.Input(\n        shape=(10,),\n        dtype=tf.float32,\n        batch_size=batch_size,\n        name='context_movie_rating')\n    input_label_movie_id = tf.keras.layers.Input(\n        shape=(1,),\n        dtype=tf.int32,\n        batch_size=batch_size,\n        name='label_movie_id')\n    inputs = {\n        'context_movie_id': input_context_movie_id,\n        'context_movie_rating': input_context_movie_rating,\n        'label_movie_id': input_label_movie_id\n    }\n    logits = test_model(inputs)\n    self.assertAllEqual([batch_size, 20], logits.shape.as_list())\n\n  def test_model_train_cnn(self):\n    input_config = self._create_test_input_config(\n        input_config_pb2.EncoderType.CNN)\n    model_config = self._create_test_model_config()\n    test_model = recommendation_model.RecommendationModel(\n        input_config=input_config, model_config=model_config)\n    batch_size = 4\n    input_context_movie_id = tf.keras.layers.Input(\n        shape=(10,),\n        dtype=tf.int32,\n        batch_size=batch_size,\n        name='context_movie_id')\n    input_context_movie_rating = tf.keras.layers.Input(\n        shape=(10,),\n        dtype=tf.float32,\n        batch_size=batch_size,\n        name='context_movie_rating')\n    input_label_movie_id = tf.keras.layers.Input(\n        shape=(1,),\n        dtype=tf.int32,\n        batch_size=batch_size,\n        name='label_movie_id')\n    inputs = {\n        'context_movie_id': input_context_movie_id,\n        'context_movie_rating': input_context_movie_rating,\n        'label_movie_id': input_label_movie_id\n    }\n    logits = test_model(inputs)\n    self.assertAllEqual([batch_size, 20], logits.shape.as_list())\n\n  def test_model_train_lstm(self):\n    input_config = self._create_test_input_config(\n        input_config_pb2.EncoderType.LSTM)\n    model_config = self._create_test_model_config()\n    test_model = recommendation_model.RecommendationModel(\n        input_config=input_config, model_config=model_config)\n    batch_size = 4\n    input_context_movie_id = tf.keras.layers.Input(\n        shape=(10,),\n        dtype=tf.int32,\n        batch_size=batch_size,\n        name='context_movie_id')\n    input_context_movie_rating = tf.keras.layers.Input(\n        shape=(10,),\n        dtype=tf.float32,\n        batch_size=batch_size,\n        name='context_movie_rating')\n    input_label_movie_id = tf.keras.layers.Input(\n        shape=(1,),\n        dtype=tf.int32,\n        batch_size=batch_size,\n        name='label_movie_id')\n    inputs = {\n        'context_movie_id': input_context_movie_id,\n        'context_movie_rating': input_context_movie_rating,\n        'label_movie_id': input_label_movie_id\n    }\n    logits = test_model(inputs)\n    self.assertAllEqual([batch_size, 20], logits.shape.as_list())\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/model/utils.py",
    "content": "#   Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#         http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n\"\"\"Utilities for ondevice personalized recommendations model.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nfrom typing import Iterable, Tuple, TypeVar\n\nimport tensorflow as tf\n\nTFGradient = TypeVar('TFGradient', tf.Tensor, tf.IndexedSlices)\n\nScalar = TypeVar('Scalar', tf.Variable, tf.Tensor, float, int)\n\n\ndef GetShardFilenames(filepattern):\n  \"\"\"Get a list of filenames given a pattern.\n\n  This will also check whether the files exist on the filesystem. The pattern\n  can be either of the glob form, or the 'basename@num_shards' form.\n\n  Args:\n    filepattern: File pattern.\n\n  Returns:\n    A list of shard patterns.\n\n  Raises:\n    ValueError: if using the shard pattern, if some shards don't exist.\n  \"\"\"\n  filenames = tf.io.gfile.glob(filepattern)\n  for filename in filenames:\n    if not tf.io.gfile.exists(filename):\n      raise ValueError('File not found: %s' % filename)\n  return filenames\n\n\ndef ClipGradient(\n    grads_and_vars: Iterable[Tuple[TFGradient, tf.Variable]],\n    clip_val: Scalar = 1.0,\n    include_histogram_summary: bool = False\n) -> Tuple[Tuple[TFGradient, tf.Variable], ...]:\n  \"\"\"Clips all gradients by global norm, reducing norm to clip_val.\n\n  Args:\n    grads_and_vars: Gradients and vars list input.\n    clip_val: A 0-D (scalar) `Tensor` > 0. Value to clip to.\n    include_histogram_summary: Flag indicates adding clipped gradients to\n      histogram summary.\n\n  Returns:\n    clipped_grads_and_vars: Return gradients and vars list after operation.\n  \"\"\"\n\n  grads, variables = list(zip(*grads_and_vars))\n  with tf.name_scope('gradient_clip'):\n    clipped_grads, global_norm = tf.clip_by_global_norm(grads, clip_val)\n    if include_histogram_summary:\n      tf.summary.scalar('clipped_global_norm', global_norm)\n\n  clipped_grads_and_vars = tuple(zip(clipped_grads, variables))\n\n  if include_histogram_summary:\n    with tf.name_scope('Gradient_info_clip'):\n      for g, v in clipped_grads_and_vars:\n        if not isinstance(g, tf.IndexedSlices):\n          tf.summary.scalar('%s_clip_l2_norm' % v.name.replace(':', '_'),\n                            tf.sqrt(tf.reduce_sum(tf.square(g))))\n  return clipped_grads_and_vars\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/ondevice_recommendation.ipynb",
    "content": "{\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"h2q27gKz1H20\"\n      },\n      \"source\": [\n        \"##### Copyright 2020 The TensorFlow Authors.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"TUfAcER1oUS6\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"#@title Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n\",\n        \"# you may not use this file except in compliance with the License.\\n\",\n        \"# You may obtain a copy of the License at\\n\",\n        \"#\\n\",\n        \"# https://www.apache.org/licenses/LICENSE-2.0\\n\",\n        \"#\\n\",\n        \"# Unless required by applicable law or agreed to in writing, software\\n\",\n        \"# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n\",\n        \"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n\",\n        \"# See the License for the specific language governing permissions and\\n\",\n        \"# limitations under the License.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Gb7qyhNL1yWt\"\n      },\n      \"source\": [\n        \"# On-device recommendation with TensorFlow Lite\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Fw5Y7snSuG51\"\n      },\n      \"source\": [\n        \"<table class=\\\"tfo-notebook-buttons\\\" align=\\\"left\\\">\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://colab.research.google.com/github/tensorflow/examples/blob/master/lite/examples/recommendation/ml/ondevice_recommendation.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/colab_logo_32px.png\\\" />Run in Google Colab</a>\\n\",\n        \"  </td>\\n\",\n        \"  <td>\\n\",\n        \"    <a target=\\\"_blank\\\" href=\\\"https://github.com/tensorflow/examples/blob/master/lite/examples/recommendation/ml/ondevice_recommendation.ipynb\\\"><img src=\\\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\\\" />View source on GitHub</a>\\n\",\n        \"  </td>\\n\",\n        \"</table>\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"fyYiyNxVp6mS\"\n      },\n      \"source\": [\n        \"# Overview\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"CShg7PXmqGUJ\"\n      },\n      \"source\": [\n        \"This code base provides an adaptive framework to train and serve on-device recommendation\\n\",\n        \"model. This approach personalizes recommendations by leveraging on-device data,\\n\",\n        \"and protects user privacy without having user data leave device.\\n\",\n        \"\\n\",\n        \"This Notebook shows an end-to-end example that\\n\",\n        \"\\n\",\n        \"*   prepares sequential training data \\n\",\n        \"*   trains neural-network model with various encoding techniques\\n\",\n        \"*   exports the model to TensorFlow Lite\\n\",\n        \"*   integrates in on-device ML applications to generate personalized recommendations.\\n\",\n        \"\\n\",\n        \"With this example, we demonstrate the approach with public\\n\",\n        \"[movielens](https://grouplens.org/datasets/movielens/) dataset, but you could\\n\",\n        \"adapt the data processing script for your dataset and train your own\\n\",\n        \"recommendation model.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"bcLF2PKkSbV3\"\n      },\n      \"source\": [\n        \"# Prerequisites\\n\",\n        \"\\n\",\n        \"To run this example, please clone the source code from github [repo](https://github.com/tensorflow/examples/tree/master/lite/examples/recommendation/ml), install required packages.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"6cv3K3oaksJv\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!git clone https://github.com/tensorflow/examples\\n\",\n        \"%cd examples/lite/examples/recommendation/ml/\\n\",\n        \"!pip install -r requirements.txt\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"qRBdzEu3qGFP\"\n      },\n      \"source\": [\n        \"# Model\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"m86-Nh4pMHqY\"\n      },\n      \"source\": [\n        \"We leverage a dual-encoder model architecture, with context-encoder to encode\\n\",\n        \"sequential user history and label-encoder to encode predicted recommendation\\n\",\n        \"candidate. Similarity between context and label encodings is used to represent\\n\",\n        \"the likeliness predicted candidate meets user's needs.\\n\",\n        \"\\n\",\n        \"Three different sequential user history encoding techniques are provided with\\n\",\n        \"this code base:\\n\",\n        \"\\n\",\n        \"* **Bag of words encoder (BOW)**: averaging user activities' embeddings without\\n\",\n        \"considering context order.\\n\",\n        \"* **Convolutional neural-network encoder (CNN)**: applying multiple layers of\\n\",\n        \"convolutional neural-network to generate context encoding.\\n\",\n        \"* **Recurrent neural-network encoder (RNN)**: applying recurrent neural network\\n\",\n        \"(LSTM in this example) to understand context sequence.\\n\",\n        \"\\n\",\n        \"In terms of user sequence modeling, we consider there are two approaches in general:\\n\",\n        \"* **Id-based**: putting all recommendation candidates in an embedding space to understand similarities of items. The embeddings are keyed with item Ids in the item vocabulary. Hence we call it id-based approach.\\n\",\n        \"* **Feature-based**: use more features of user activities, not only the item ids. For instance, movie genre and movie rating for movie recommendation. The model will understand the features and learns the way to understand user.\\n\",\n        \"\\n\",\n        \"This framework code base supports both id-based and feature-based recommendation models, with a configurable way.\\n\",\n        \"\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Oii_hG1843k_\"\n      },\n      \"source\": [\n        \"![Untitled drawing (2).jpg](data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAJBBdoDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3+iiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACimTSx28Ek0rBI41Lux6AAZJrgPAHiuTXNf1qGdiBM/2mBD/AAqMLj8tn60AehUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQBj+J9LvNa0KfTrO4S3efCvI4J+TuBj16fTNee+CPCN9Y+LLi4jvYcabP5Mq7T+9DLzj8/0r1quW8K/8h/xR/wBfw/8AQaAOpooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooqhqGqw2BSII891L/qrePG5vf0CjuTxQBfrLm1+ySRobbzb2dTgx2q78H0LfdU/UiqbWNzqXzatNujPSzhJEQ9mPV/xwPatCKKOCJYoo1jjUYVUGAB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eBxlXRsg1MQCCCMg9Qa5a88Kz6fdPqPhi4WxuWO6S0cZt5/qv8J9x+lAHVUVz2j+Kor27/s3Urd9N1ZRzbzHiT3RujCuhoAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACuN1OWbxfrEuh2crR6TaMBqE6HBlb/nkp/n/AJzq+LtWm0rQnNpzfXTrbWoHXzH4B/AZP4Vb0DR4dC0a3sIsEouZH7yOfvMfqaALttbQ2ltHb28SxQxqFRFGAoqWiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigDE1bwhoOt3f2rUNOSWcoI3cOyeYg/hfaRvHXhsir8OlWNvfC8htY47gW62wdBjESklUA6AAk1cooAzYdA0q3tNOtYrKNYNNcPaICcQsFKgjn0Zhz61RtPBHhqxvory20mGOaJzLH8zFVf+/tJ27ufvYz710FFAGFe+DPDuoai9/daXFJcSMryZZgkrL0LoDtcjA5YHpT9U8I6DrV59r1DTYp5ygjZiWXzFHRXAIDgdg2cVtUUAYl54P0DUILOG502Nks4hBBtZkKRgAbMqQSvA4OQalg8MaLbRWsUGnQxx2sUsMCrkCNJSDIo56EgVrUUAUF0TTVg06AWiCLTSps1yf3JVCgx9FJHPrXCH4d315q8El7aaHGkd+t5Lf2wcTz7HDgeVt2RsxChmDHPPrXpVFABRRRQAUUUUAFFFFABRRRQBnaxodhrtp9nvod+OY5F4eM+qnsa55dT1bwi4h1ovqGkZwmoouZIR2Eo7j/a/n0rsqR0WRGR1DKwwVIyCKAGW9xDdW6T28qSwyDcjochh7GpK4+4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/gtuP/iKP7Ui/wCfXUv/AAW3H/xFdVRRyIORHK/2pF/z66l/4Lbj/wCIo/tSL/n11L/wW3H/AMRXVUUciDkRxt1dfbPssMNpqBc3ls3z2MyABZkYklkAAABPJrsqKKpKw0rBRRRTGFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABXI+ILSfQdT/4SjTYy6YC6lbJ/y1jH8YH95f5fjnrqQgMCCAQeCD3oAitLuC+tIrq2kEkEqh0cdwamrjtLJ8J+IjokhI0q/YyWDHpFJ/FF/Uf/AF67GgAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigDybU/AME3jeGzk1GdjfRy3UkuwZDBs8fnXq0KNHDGjuZGVQC5GCxx1rmb/AP5KVpH/AF4zfzFdTQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAGT4j0Zdd0WW0DeXOMSW8veOReVP9PoTUfhfWW1rR1knXy72BjBdxHgpKvB49+v41tVyN3/xT3jqC8Hy2OsgQTeizr9w/iOPzoA66iiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAOWv/APkpWkf9eM38xXU1y1//AMlK0j/rxm/mK6mgAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACsbxVpR1nw5eWqD9+F8yAjqJF5XH4jH41s0UAZfh3VRrXh+y1DI3SxjzAOzjhh+YNalcp4WH9neIPEGi9I0nW7gX/AGJBkgewIxXV0AFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUVzuoa7qyeIZtK0rS7K58i1huZJbq+eD/WPKoACxPnHlEk5HUUm0ldglc6Kiua/tPxb/0AdE/8HMv/AMi0f2n4t/6AOif+DmX/AORaj2sO5XKzpaK5r+0/Fv8A0AdE/wDBzL/8i0f2n4t/6AOif+DmX/5Fo9rDuHKyO/8A+SlaR/14zfzFdTXDzweK5/EdprB0rRVa3heEQ/2rKQ27vu+z8Y9MGtP+0/Fv/QB0T/wcy/8AyLR7WHcOVnS0VzX9p+Lf+gDon/g5l/8AkWj+0/Fv/QB0T/wcy/8AyLR7WHcOVnS0VzX9p+Lf+gDon/g5l/8AkWmx6/rkGr6ZZ6no+nQw387W6y22ovMyMIZJeVaBBjEZHXuKaqRbsmLlZ09FFFWIKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAo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KKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigDlvH/wDyALf/AK/oP/Qq6muW8f8A/IAt/wDr+g/9CrqaACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKy/ENvcXWkFLaFppVnglEasoLBJUY4LEDOFPU1qUUAcx52pf9AC//wC/tv8A/HaPO1L/AKAF/wD9/bf/AOO109FRyInlRzHnal/0AL//AL+2/wD8do87Uv8AoAX/AP39t/8A47XT0UciDlRzHnal/wBAC/8A+/tv/wDHaPO1L/oAX/8A39t//jtdPRRyIOVHMedqX/QAv/8Av7b/APx2jztS/wCgBf8A/f23/wDjtdPRRyIOVHMedqX/AEAL/wD7+2//AMdo87Uv+gBf/wDf23/+O109FHIg5Ucx52pf9AC//wC/tv8A/HaPO1L/AKAF/wD9/bf/AOO109FHIg5Ucx52pf8AQAv/APv7b/8Ax2jztS/6AF//AN/bf/47XT0UciDlRzHnal/0AL//AL+2/wD8do87Uv8AoAX/AP39t/8A47XT0UciDlRzHnal/wBAC/8A+/tv/wDHaPO1L/oAX/8A39t//jtdPRRyIOVHMedqX/QAv/8Av7b/APx2jztS/wCgBf8A/f23/wDjtdPRRyIOVHJXOoXdnEJLjRL6NC6xgmW3+8zBQP8AW9yRU3nal/0AL/8A7+2//wAdrmvi3rDQQafpkLlXZ/tD4PIC8L+ufyru9C1NdY0Ky1Bcfv4gzAdm6MPwIIo5EHKjJ87Uv+gBf/8Af23/APjtHnal/wBAC/8A+/tv/wDHa6eijkQcqOY87Uv+gBf/APf23/8AjtHnal/0AL//AL+2/wD8drp6KORByo5jztS/6AF//wB/bf8A+O0edqX/AEAL/wD7+2//AMdrp6KORByo5jztS/6AF/8A9/bf/wCO0edqX/QAv/8Av7b/APx2unoo5EHKjmPO1L/oAX//AH9t/wD47R52pf8AQAv/APv7b/8Ax2unoo5EHKjmPO1L/oAX/wD39t//AI7R52pf9AC//wC/tv8A/Ha6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dBV4z+X6161XLeKv+Q/4X/6/j/6DQBo+GNLvNF0KDTry4S4eDKpIgI+TsDn06fTFbFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQBi+K7bUr7w7c2elKhubgeWSz7QqH7x/Lj8a838BeHta07xdJMqRGOzkNvdgSdmHb1xwfwr2KuW8K/8h/xR/wBfw/8AQaAOpooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAMDxv/AMiXq3/XA/zFaumf8gmz/wCuCf8AoIrK8b/8iXq3/XA/zFaumf8AIJs/+uCf+gigC1RRRQAUUUUAFFFFABRRRQAUUUUAFYHjf/kS9W/64H+YrfrA8b/8iXq3/XA/zFAGrpn/ACCbP/rgn/oIq1VXTP8AkE2f/XBP/QRVqgAooooAKKKKACiiigAooooAKKKKACiiigAooooAK5a//wCSlaR/14zfzFdTXLX/APyUrSP+vGb+YoA6miiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA5bQ/l8e+Kl7MLRh/37NdTXLaV8vxE8QD+9b2zfkpFdTQAUUUUAFFFFABRRRQAUUUUAFFFFAFLWLBdU0a8sWx+/haMZ7Ejg/nis7wZfNqHhDTpn/1ixeU+euUO3n8s1vVyvgz/AEa41/Tugt9SkdB6I4BA/nQB1VFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABVTU9Ot9W024sLpd0M6FG9vQj3B5/CrdFAHN+Dr+4ksbjSb9t1/pcn2eQ/30/gf8R/Kukrk9UH9kePdL1Fflh1JGsp/TeOYz9T0/CusoAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA5bxV/yH/C/wD1/H/0GuprlvFX/If8L/8AX8f/AEGupoAKKKKACiiigAooooAKKKKACiiigArlvFX/ACH/AAv/ANfx/wDQa6muW8Vf8h/wv/1/H/0GgDqaKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArlvCv/ACH/ABR/1/D/ANBrqa5bwr/yH/FH/X8P/QaAOpoo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7LVJIZLpJS8IZUaKeSIgNjIyjDIO0dfSqi7O4mrqxj0HpV3/AIRXS/8Ap+/8GNx/8XR/wiul/wDT9/4Mbj/4ut/brsZ+zZ5fbeCr2XUtOa502wtFs7wXclxb3crpIVJYCKBvli3HGcH16101x4O0S5vZLmW2kPmyiaWBbiQQyyDB3NEG2MeB1HPeuq/4RXS/+n7/AMGNx/8AF0f8Irpf/T9/4Mbj/wCLqVViug+R9zDOjWBGojyP+Ql/x9/O37z5Anrx8oA4xS2ej2Fhcm4toNkpgjti29j+7jzsXk9tx56881t/8Irpf/T9/wCDG4/+Lo/4RXS/+n7/AMGNx/8AF0/bR7C9m+5ysPg7RLe9juY7aQeVKZ4oDcSGCOTk71iLbAeT0HFaWpaZZ6vZNZ30IlhYhsZKlWByCCMEEHoQc1sf8Irpf/T9/wCDG4/+Lo/4RXS/+n7/AMGNx/8AF0e2j2D2b7mBpWhWGjGZ7VJWmnIM088zzSyY4GXckkDsOgrSq7/wiul/9P3/AIMbj/4uj/hFdL/6fv8AwY3H/wAXT9uuwezZSrS8M/8AIqaP/wBeMH/oAqL/AIRXS/8Ap+/8GNx/8XWrbW0Vnaw2sCbIYUWONck7VAwBz7Cs6k+exUY8pLRRRWRYUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB/9k=)\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"jZpnkJ9X_RfA\"\n      },\n      \"source\": [\n        \"# Data Adaptivity\\n\",\n        \"\\n\",\n        \"The framework can support model training or customization with various kinds of data, for which we provide the way to configurate the input data and encoder architecture.\\n\",\n        \"\\n\",\n        \"With the input config, you can specify the input features' information, such as data type, shapde, vocab, embedding dimension. You can also freely group features in a feature group to encode together, for instance movie_id and movie rating in above diagram.  We support 3 feature types INT/STRING/FLOAT, for string and integer categorical features we will map them in embedding spaces, for float feature we suggest to concatenate directly together with other features' embeddings.\\n\",\n        \"\\n\",\n        \"Please check out the step-to-step example session below for more details about setting up the input config.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"5zCIjZgoSpNZ\"\n      },\n      \"source\": [\n        \"# Training Data Preparation\\n\",\n        \"\\n\",\n        \"This notebook makes use of public dataset [movielens](https://grouplens.org/datasets/movielens/) to demonstrate training of on-device recommendation model.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"KpQlunkiSwio\"\n      },\n      \"source\": [\n        \"## Examples Generation\\n\",\n        \"The examples generation process performs the following steps:\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"*   Downloads [movielens](https://grouplens.org/datasets/movielens/) dataset\\n\",\n        \"*   Groups movie rating records by user, and orders per-user movie rating records by timestamp.\\n\",\n        \"*   Generates TensorFlow examples with features: 1) `context_movie_id`: \\n\",\n        \"time-ordered sequential movie IDs 2) `context_movie_rating`: time-ordered sequential rating numbers 3) `context_movie_genre`: time-ordered sequential movie genres 4) `context_movie_year`: time-ordered sequential movie years. 5) `label_movie_id`: the next movie ID user rated.\\n\",\n        \"\\n\",\n        \"There's case that one user activity will have multiple values for a single feature. For example, the movie genre feature in movielens dataset, each movie can have multiple genres. For this case, we suggest to concatenate all movies' genres for the activity sequence. Let's look at one example, if the user activity sequence is\\n\",\n        \"```\\n\",\n        \"Star Wars: Episode IV - A New Hope (1977), Genres: Action|Adventure|Fantasy\\n\",\n        \"Terminator 2: Judgment Day (1991), Genres: Action|Sci-Fi|Thriller\\n\",\n        \"Jurassic Park (1993), Genres: Action|Adventure|Sci-Fi\\n\",\n        \"```\\n\",\n        \"The context_movie_genre feature will be\\n\",\n        \"```\\n\",\n        \"\\\"Action, Adventure, Fantasy, Action, Sci-Fi, Thriller, Action, Adventure, Sci-Fi\\\"\\n\",\n        \"```\\n\",\n        \"Since TFLite input tensors should be fixed length, so we suggest to pad features to fixed length.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"Wff0kYZKS05g\"\n      },\n      \"source\": [\n        \"## Vocabularies Generation\\n\",\n        \"For String and Integer type features, we would suggest to create an embedding space for each of them, for which vocabularies will be needed. This framework supports txt file based vocabulary setup, for which you can puch vocab iten line-by-line in a txt file. And in the training input pipeline vocabularies will be formed as:\\n\",\n        \"\\n\",\n        \"```\\n\",\n        \"tf.lookup.StaticVocabularyTable(\\n\",\n        \"      tf.lookup.TextFileInitializer(\\n\",\n        \"          vocab_path,\\n\",\n        \"          key_dtype=key_type,\\n\",\n        \"          key_index=tf.lookup.TextFileIndex.WHOLE_LINE,\\n\",\n        \"          value_dtype=tf.int64,\\n\",\n        \"          value_index=tf.lookup.TextFileIndex.LINE_NUMBER,\\n\",\n        \"          delimiter='\\\\t'),\\n\",\n        \"      num_oov_buckets)\\n\",\n        \"```\\n\",\n        \"And vocab_path is the path to the genreated vocabulary txt file.\\n\",\n        \"\\n\",\n        \"The `data/example_generation_movielens.py` script, generates vocabularies with \\\"--build_vocabs\\\" set as true.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"eY0T_skNS4Uw\"\n      },\n      \"source\": [\n        \"## Try out data preparation\\n\",\n        \"Please try out the movielens training examples and vocabs generation script below.\\n\",\n        \"\\n\",\n        \"Note: If you would like to use your own data, please adapt the data processing script for your specific case.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"FQvryCfGtCQX\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!python -m data.example_generation_movielens \\\\\\n\",\n        \"  --data_dir=data/raw \\\\\\n\",\n        \"  --output_dir=data/examples \\\\\\n\",\n        \"  --min_timeline_length=3 \\\\\\n\",\n        \"  --max_context_length=10 \\\\\\n\",\n        \"  --max_context_movie_genre_length=32 \\\\\\n\",\n        \"  --min_rating=2 \\\\\\n\",\n        \"  --train_data_fraction=0.9 \\\\\\n\",\n        \"  --build_vocabs=True\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"8zcEXFkgCz8g\"\n      },\n      \"source\": [\n        \"Raw movielens ratings.dat data is in the following format:\\n\",\n        \"UserID::MovieID::Rating::Timestamp\\n\",\n        \"\\n\",\n        \"*   UserIDs range between 1 and 6040\\n\",\n        \"*   MovieIDs range between 1 and 3952\\n\",\n        \"*   Ratings are made on a 5-star scale (whole-star ratings only)\\n\",\n        \"*   Timestamp is represented in seconds since the epoch as returned by time(2)\\n\",\n        \"*   Each user has at least 20 ratings\\n\",\n        \"\\n\",\n        \"Ref:[movielens readme.txt](http://files.grouplens.org/datasets/movielens/ml-1m-README.txt)\\n\",\n        \"\\n\",\n        \"In this example, we consider each rating as a movie watch by the users, and construct user movie watch history with rated movie IDs ordering by time.\\n\",\n        \"\\n\",\n        \"Sample generated training example with max user history as 10:\\n\",\n        \"```\\n\",\n        \"0 : {   # (tensorflow.Example)\\n\",\n        \"  features: {   # (tensorflow.Features)\\n\",\n        \"    feature: {\\n\",\n        \"      key  : \\\"context_movie_id\\\"\\n\",\n        \"      value: {\\n\",\n        \"        int64_list: {\\n\",\n        \"          value: [ 3476, 3264, 2120, 1717, 382, 1644, 2328, 2461, 2064, 3679 ]\\n\",\n        \"        }\\n\",\n        \"      }\\n\",\n        \"    }\\n\",\n        \"    feature: {\\n\",\n        \"      key  : \\\"context_movie_year\\\"\\n\",\n        \"      value: {\\n\",\n        \"        int64_list: {\\n\",\n        \"          value: [ 1990, 1992, 1993, 1997, 1994, 1997, 1998, 1990, 1989, 1981 ]\\n\",\n        \"        }\\n\",\n        \"      }\\n\",\n        \"    }\\n\",\n        \"    feature: {\\n\",\n        \"      key  : \\\"context_movie_genre\\\"\\n\",\n        \"      value: {\\n\",\n        \"        bytes_list: {\\n\",\n        \"          value: [ \\\"Horror\\\", \\\"Mystery\\\", \\\"Thriller\\\", \\\"Comedy\\\", \\\"Horror\\\", \\\"Drama\\\", \\\"Horror\\\", \\\"Horror\\\", \\\"Thriller\\\", \\\"Drama\\\", \\\"Horror\\\", \\\"Horror\\\", \\\"Mystery\\\", \\\"Thriller\\\", \\\"Horror\\\", \\\"Horror\\\", \\\"Comedy\\\", \\\"Documentary\\\", \\\"Documentary\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\", \\\"UNK\\\" ]\\n\",\n        \"        }\\n\",\n        \"      }\\n\",\n        \"    }\\n\",\n        \"    feature: {\\n\",\n        \"      key  : \\\"context_movie_rating\\\"\\n\",\n        \"      value: {\\n\",\n        \"        float_list: {\\n\",\n        \"          value: [ 4.0, 4.0, 3.0, 1.0, 3.0, 3.0, 1.0, 4.0, 3.0, 4.0 ]\\n\",\n        \"        }\\n\",\n        \"      }\\n\",\n        \"    }\\n\",\n        \"    feature: {\\n\",\n        \"      key  : \\\"label_movie_id\\\"\\n\",\n        \"      value: {\\n\",\n        \"        int64_list: {\\n\",\n        \"          value: [ 1361 ]\\n\",\n        \"        }\\n\",\n        \"      }\\n\",\n        \"    }\\n\",\n        \"  }\\n\",\n        \"}\\n\",\n        \"```\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"GApKDT6gRebW\"\n      },\n      \"source\": [\n        \"# Model Configuration\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ubHdVNyJXkVE\"\n      },\n      \"source\": [\n        \"##Input Configuration\\n\",\n        \"\\n\",\n        \"Trainer code will prepare tf datasets and set up model according to input config. You can configurate the following:\\n\",\n        \"\\n\",\n        \"*   Feature: name, data type, feature length, vocab name, vocab size, embedding dimension.\\n\",\n        \"*   Feature group: features to encode together, encoder type.\\n\",\n        \"*   Global feature groups: global features, e.g. user age, profession etc.\\n\",\n        \"*   Activity feature groups: features to represent activities.\\n\",\n        \"*   Label feature: the feature used as label.\\n\",\n        \"\\n\",\n        \"Both input data processing and model architecture setup will be based on the input configuration.\\n\",\n        \"\\n\",\n        \"Please check example input config with command below:\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"Mk7hpSlCta95\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!cat configs/sample_input_config.pbtxt\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"XgjrV1aRqaOp\"\n      },\n      \"source\": [\n        \"You can also see different model graph generated based on different input configs in the appendix section.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"XQcQ6AssuBN8\"\n      },\n      \"source\": [\n        \"# Train model\\n\",\n        \"\\n\",\n        \"The training launcher script uses TensorFlow keras compile/fit APIs and performs\\n\",\n        \"the following steps to kick off training and evaluation process:\\n\",\n        \"\\n\",\n        \"*   Set up both train and eval dataset input function.\\n\",\n        \"*   Construct keras model according to provided configs, please refer to sample.config file in the source code to config your model architecture, such as embedding dimension, convolutional neural network params, LSTM units etc.\\n\",\n        \"*   Setup loss function. In this code base, we leverages customized batch softmax loss function.\\n\",\n        \"*   Setup optimizer, with flag specified learning rate and gradient clip if needed.\\n\",\n        \"*   Setup evaluation metrics, we provided recall@k metrics by default.\\n\",\n        \"*   Compile model with loss function, optimizer and defined metrics.\\n\",\n        \"*   Setup callbacks for tensorboard and checkpoint manager.\\n\",\n        \"*   Run model.fit with compiled model, where you could specify number of epochs to train, number of train steps in each epoch and number of eval steps in each epoch.\\n\",\n        \"\\n\",\n        \"To start training please execute command:\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"3gPKz5InxEbF\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!python -m model.recommendation_model_launcher \\\\\\n\",\n        \"  --training_data_filepattern \\\"data/examples/train_movielens_1m.tfrecord\\\" \\\\\\n\",\n        \"  --testing_data_filepattern \\\"data/examples/test_movielens_1m.tfrecord\\\" \\\\\\n\",\n        \"  --model_dir \\\"model/model_dir\\\" \\\\\\n\",\n        \"  --export_dir \\\"model/model_dir/export_m1\\\" \\\\\\n\",\n        \"  --vocab_dir \\\"data/examples\\\" \\\\\\n\",\n        \"  --input_config_file \\\"configs/sample_input_config.pbtxt\\\" \\\\\\n\",\n        \"  --batch_size 32 \\\\\\n\",\n        \"  --learning_rate 0.01 \\\\\\n\",\n        \"  --steps_per_epoch 2 \\\\\\n\",\n        \"  --num_epochs 2 \\\\\\n\",\n        \"  --num_eval_steps 2 \\\\\\n\",\n        \"  --run_mode \\\"train_and_eval\\\" \\\\\\n\",\n        \"  --gradient_clip_norm 1.0 \\\\\\n\",\n        \"  --num_predictions 10 \\\\\\n\",\n        \"  --hidden_layer_dims \\\"32,32\\\" \\\\\\n\",\n        \"  --eval_top_k \\\"1,5\\\" \\\\\\n\",\n        \"  --conv_num_filter_ratios \\\"2,4\\\" \\\\\\n\",\n        \"  --conv_kernel_size 4 \\\\\\n\",\n        \"  --lstm_num_units 16\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"ObH_mcGcxS96\"\n      },\n      \"source\": [\n        \"# Export model\\n\",\n        \"\\n\",\n        \"Inside launcher script we also provide model exportation functionality.\\n\",\n        \"\\n\",\n        \"In serve model, the model takes in user context history, for the example case the input is a vector of movie IDs you interacted with. With context encoder, model computes the context embedding vector, at the same time generate candidate embedding vector for all movie candidates in the vocab. By dotproduct and top-k ranking, top-k candidates will be served as the predicted candidates.\\n\",\n        \"\\n\",\n        \"At model exportation step, you could specify number of predictions you want to get from the output of the model.\\n\",\n        \"\\n\",\n        \"This step includes:\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"*   Export the model to saved_model with tf.saved_model.save.\\n\",\n        \"*   Convert the saved_model to TensorFlow lite with tf.lite.TFLiteConverter.from_saved_model, and save it to the export directory wanted.\\n\",\n        \"\\n\",\n        \"To export the model, please execute command:\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"SH5r6AxHzGrS\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"!python -m model.recommendation_model_launcher \\\\\\n\",\n        \"  --training_data_filepattern \\\"data/examples/train_movielens_1m.tfrecord\\\" \\\\\\n\",\n        \"  --testing_data_filepattern \\\"data/examples/test_movielens_1m.tfrecord\\\" \\\\\\n\",\n        \"  --input_config_file \\\"configs/sample_input_config.pbtxt\\\" \\\\\\n\",\n        \"  --model_dir \\\"model/model_dir\\\" \\\\\\n\",\n        \"  --export_dir \\\"model/model_dir/export_m2\\\" \\\\\\n\",\n        \"  --vocab_dir \\\"data/examples\\\" \\\\\\n\",\n        \"  --run_mode \\\"export\\\" \\\\\\n\",\n        \"  --checkpoint_path \\\"model/model_dir/ckpt-4\\\" \\\\\\n\",\n        \"  --num_predictions 10 \\\\\\n\",\n        \"  --hidden_layer_dims \\\"32,32\\\" \\\\\\n\",\n        \"  --eval_top_k \\\"1,5\\\" \\\\\\n\",\n        \"  --conv_num_filter_ratios \\\"2,4\\\" \\\\\\n\",\n        \"  --conv_kernel_size 4 \\\\\\n\",\n        \"  --lstm_num_units 16\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"qXMQ5D5JzSgv\"\n      },\n      \"source\": [\n        \"# Model inference\\n\",\n        \"\\n\",\n        \"You could verify your model's performance by running inference with test examples.\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"execution_count\": null,\n      \"metadata\": {\n        \"id\": \"og0qkYavz3Nt\"\n      },\n      \"outputs\": [],\n      \"source\": [\n        \"import os\\n\",\n        \"import tensorflow as tf\\n\",\n        \"\\n\",\n        \"# Use [0, 1, ... 9] as example input to represent 10 movies that user interacted with.\\n\",\n        \"context = tf.range(10)\\n\",\n        \"# Path to exported TensorFlow Lite model.\\n\",\n        \"tflite_model_path = 'model/model_dir/export/export_m2/model.tflite'  #@param {type:\\\"string\\\"}\\n\",\n        \"\\n\",\n        \"# Create TFLite interpreter.\\n\",\n        \"interpreter = tf.lite.Interpreter(tflite_model_path)\\n\",\n        \"interpreter.allocate_tensors()\\n\",\n        \"input_details = interpreter.get_input_details()\\n\",\n        \"output_details = interpreter.get_output_details()\\n\",\n        \"print('Display inputs and outputs:')\\n\",\n        \"print(input_details)\\n\",\n        \"print(output_details)\\n\",\n        \"\\n\",\n        \"# Find indices.\\n\",\n        \"names = [\\n\",\n        \"  'serving_default_context_movie_id:0',\\n\",\n        \"  'serving_default_context_movie_genre:0',\\n\",\n        \"  'serving_default_context_movie_rating:0',\\n\",\n        \"]\\n\",\n        \"indices = {i['name']: i['index'] for i in input_details}\\n\",\n        \"\\n\",\n        \"# Fake inputs for illustration. Please change to the real data.\\n\",\n        \"# Use [0, 1, ... 9] to represent 10 movies that user interacted with.\\n\",\n        \"ids = tf.range(10)\\n\",\n        \"interpreter.set_tensor(indices[names[0]], ids)\\n\",\n        \"# Use [0, 1, ..., 31] to represent 32 movie genres.\\n\",\n        \"genres = tf.range(32)\\n\",\n        \"interpreter.set_tensor(indices[names[1]], genres)\\n\",\n        \"# Use [1.0, 1.0, ..., 1.0] to represent 10 movie ratings.\\n\",\n        \"ratings = tf.ones(10)\\n\",\n        \"interpreter.set_tensor(indices[names[2]], ratings)\\n\",\n        \"\\n\",\n        \"# Run inference.\\n\",\n        \"interpreter.invoke()\\n\",\n        \"\\n\",\n        \"# Get outputs.\\n\",\n        \"top_prediction_ids = interpreter.get_tensor(output_details[0]['index'])\\n\",\n        \"top_prediction_scores = interpreter.get_tensor(output_details[1]['index'])\\n\",\n        \"print('Predicted results:')\\n\",\n        \"print('Top ids: {}'.format(top_prediction_ids))\\n\",\n        \"print('Top scores: {}'.format(top_prediction_scores))\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"A_omMjoT035u\"\n      },\n      \"source\": [\n        \"# Integrate in your application\\n\",\n        \"\\n\",\n        \"We also open source an Android reference app to run inference with TF Lite.\\n\",\n        \"**Please follow [`android/app/README.md`](https://github.com/tensorflow/examples/blob/master/lite/examples/recommendation/android/README.md)** to install required developer tools and build Android app.\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"N83Ev6nSwsUW\"\n      },\n      \"source\": [\n        \"The app uses one pretrained model to illustrate how to run TFLite. If you want to replace the existing model with the one you just trained above, please copy the respective TF Lite model to `assets` folder, and adapt its file name accordingly. If you directly train and export your model in this notebook, your\\n\",\n        \"exported model should be located at \\\"model/model_dir/export/model.tflite\\\".\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"hUaXjqGBvnFP\"\n      },\n      \"source\": [\n        \"```shell\\n\",\n        \"cp path/to/your/model.tflite ../android/app/src/main/assets/\\n\",\n        \"```\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"bwr7pigOB7pT\"\n      },\n      \"source\": [\n        \"The app uses the json file `config.json` to load one model and control how to consume IDs and scores predicted by the TF Lite recommendation model on device. `Config` definition can be found in [`android/app/src/main/java/org/tensorflow/lite/examples/recommendation/Config.java`](../android/app/src/main/java/org/tensorflow/lite/examples/recommendation/Config.java).\\n\",\n        \"\\n\",\n        \"A sample json is presented below for the built-in model, and you may need to *adapt* it and related code to handle your own trained model.\\n\",\n        \"\\n\",\n        \"``` json\\n\",\n        \"{\\n\",\n        \"  \\\"model\\\": \\\"<your_model>.tflite\\\",\\n\",\n        \"  \\\"inputs\\\": [\\n\",\n        \"    {\\\"name\\\": \\\"movieFeature\\\", \\\"index\\\": 0, \\\"inputLength\\\": 10},\\n\",\n        \"    {\\\"name\\\": \\\"genreFeature\\\", \\\"index\\\": 1, \\\"inputLength\\\": 32}\\n\",\n        \"  ],\\n\",\n        \"  \\\"movieList\\\": \\\"sorted_movie_vocab.json\\\",\\n\",\n        \"  \\\"genreList\\\": \\\"movie_genre_vocab.txt\\\",\\n\",\n        \"  \\\"topK\\\": 10,\\n\",\n        \"  \\\"outputLength\\\": 10,\\n\",\n        \"  \\\"outputIdsIndex\\\": 0,\\n\",\n        \"  \\\"outputScoresIndex\\\": 1\\n\",\n        \"}\\n\",\n        \"```\\n\",\n        \"# Further reading\\n\",\n        \"If you want to read more about the technology related to on-device recommendation, please check out our [blogpost](https://blog.tensorflow.org/2021/04/adaptive-framework-for-on-device-recommendation.html).\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"2egsu0GMsjyB\"\n      },\n      \"source\": [\n        \"# Appendix\\n\",\n        \"\\n\",\n        \"> Model A: ID-based bag-of-words\\n\",\n        \"\\n\",\n        \"```\\n\",\n        \"activity_feature_groups {\\n\",\n        \"  features {\\n\",\n        \"    feature_name: \\\"context_movie_id\\\"\\n\",\n        \"    feature_type: INT\\n\",\n        \"    vocab_size: 3953\\n\",\n        \"    embedding_dim: 8\\n\",\n        \"    feature_length: 10\\n\",\n        \"  }\\n\",\n        \"  encoder_type: BOW\\n\",\n        \"}\\n\",\n        \"label_feature {\\n\",\n        \"  feature_name: \\\"label_movie_id\\\"\\n\",\n        \"  feature_type: INT\\n\",\n        \"  vocab_size: 3953\\n\",\n        \"  embedding_dim: 8\\n\",\n        \"  feature_length: 1\\n\",\n        \"}\\n\",\n        \"```\\n\",\n        \"![Screen Shot 2021-03-15 at 4.12.22 PM.png](data:image/png;base64,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hg0r7mVfbVfwed9996WkTqn8fOh9escdd9j5559vEydOtFatWtl5552XM18QUgJKIQjkuACBbI53INVPn0BBQYF9//33tnDhwmIPojwacVy9enWRPD///LNpRE5p7ty57qrmtWvXunu1R2fW/j/99FN4s253qbKVdIp+5cqV7rFOq6pOxSXto1P53kidytU+yaTNmze7e8nH2+/XX391I6FePXUMjTzq6m0FcA0aNHCPo4O4VatW2ZdffmkbN24sVK2tW7e6/Dp+ZJKjDGIlmem1TZs2uX1VdqJJ9dZIrkZ0i0vy08h4rBHVyP7VeySy/1Se9lUeJZlEv67tsQzloP2ifdQ2lal6qzy9rveTHv/2228qrsQUWV+N5i5atCicX3302Wefhd9n4RciHuhiyVhTTOQX3ceRdVQRO++8s1WqVCmitN8fxmp/kUwxNkR+PryXE33fevk1leDaa6+1Sy65xAWyu+++u/3zn/+0Fi1a2I033uhl4zcCCPhcgEDW5x1E9bIjMHPmTOvUqZMdfvjhdsghh7hbUE6ZMqVQZXSv9d12282NuOmP39/+9jeLnGd3+umnu1PrBxxwgO2///727LPPuruKtWvXrkjwdNZZZ9ltt93mytdp+fbt24fL0ul5nZrVH13tq3odeeSRNnXq1EL1efPNN61t27Z26KGH2l/+8hfr06ePK/OKK64olK+kJ/fee6/7Q96tWzd3rJEjRxbJrgD32GOPdVMHunTpYm3atAnf1UgB91577eVO/T/++OPu8X/+8x9Xhm7nqXI15UC/GzdubI8++mi4/B9//NG1O3qawNlnn22qV6x05plnulFJfZnQcW+55ZZY2YpsU91U78MOO8y1U2bz588P51Mbe/To4Sz0HlD/qh8ik/r3oYcecm3RLUrVZ9rmBayqs0b6lPQeUnleKslQQaBGHXv16uVld1+m1Pea96mgMdr4tddeC+ct7oHq9uCDD5r67KCDDnLL0N1000327rvvutH/o446ylq2bFlkCoCmRug9p7VS99tvP9tjjz0KTTXRCOaAAQMKHXby5MmmwNCzOOecc+z//u//wnlKan84UzEPoj8fypbI+1af14cffjhcqoJ5BdJ6f3mpTJkydsYZZ5jWmyYhgEBuCBDI5kY/UcsMC+i2k5o3p9FP/Zxyyik2ZMiQ8MicTkNef/317o+zRrY0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G812p9Y+JLBkyRInFI4//vhE6e644w579dVXbebMmfbVV1/ZQQcd5CwzXoS1117bdt55Z5NllgCBahJ46aWX3HXYpEmTamaTkvall17qHuxSdub5MWLECDvllFOsadOf1/vRQ94+++xjw4YNs4ULF9p//vMfe+CBB+wf//iHS+mLL76w/fbbzy644AL7+uuv7eWXX7brrrvOnn322UROH3zwgZ188sn2yiuvJD7J1ul8/TSRUI6NI4880k488URbsGCB4zx+/Hi77777Emf89re/NT3oEiAAgVQCCNlUHvwKOAHdaPbaay/7xS9+YQcffLA9+uijDWqkG9dhhx1mvXr1cjes5Nd1Y8aMsaOOOsqdv/vuu6fczHSjUZq6we2www62yy672F133ZWSvm6SegX4y1/+0g444AB75plnUo7n+iHL0xFHHGEtWrRIRNt0003t9ttvt1atWjkrrcoti3Lyq8dzzjnHbr755sQ5bECgGgQk5AYOHOiSlnvOjjvuaHI3kGWye/fu7m3HO++8k8g6X3+RSFUan332mTtHfUe/db0rHHfccTZ37lzT634JzRdeeMHtz/VHr+DvueceO/XUUxPR5EOuMsqiqbDBBhu4/itxqqB0VX71e70JUZ+TaLzpppvccf3RilbdunVz5+p8fdZaa63E8UL66TfffJOIr43k3xLbH3/8sR1yyCEujtLeY489bNasWYlzZBFWPuPGjUvsYwMCEDBDyHIVhIaAblj65y+LxoUXXmjff/+9ybo5Z86clDqeffbZ1rhxY/v8889t6NChJvGq8NFHHzkhqZvnMcccY7rR7b///i49HdeNViJWlhgJ1RkzZjjLz9ixY3XY3Yj23ntvmzp1qrPe6GavG79eW+YLEqZ33nlnAx9EpdejRw93M9fgFfn+/frXv064HijdLbbYwvTqUZZbAgSqRUCv571rbPny5c4H9ZZbbnHCSq/r5X8uq6YX8vUXvWWQH6sGMil8++237rfXX9u0aWOy/kpctm3b1po1a+YlnfX7kUcecQ+yOtcLOlcifOXKle5/wv/+7//aa6+95h5mFUf/C1Sf5KC47733XmKXhKysuRLn6pPpltF8/VTuQHr49QagyWXiV7/6VeKBVPU87bTTnMuDBnA+/fTT7n+N8ksO5513XorATj7GNgQiSyB+AyVAwNcE5s2bF+vdu3cs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aptxGbl5AlbWZAIEIBBeAgjZ8LatL2v2m9/8xh577LGalG327Nm2xx572AknnFCT/MgEAmElIDGohQ023njjQFRRgltWZC3som8CBCAQXgII2fC2rS9r9uqrr7plK6tduEcffdR22WUXW2ONNdy8lNXOj/QhEGYCEoOXX365cy/o2bOnr90MJGK7du1qEt+sUBjmq5K6QeBnAghZroSSCCxdutRuv/12O+yww2zIkCFurkgvoUsuucQmTpzo/bQ//OEPNn78ePf7xhtvNM0t6QXt1xySgwYNsscff9zbXfb366+/7sowcODAstMiAQhAwJxl86mnnrLOnTtbt27dnO+pX1wN5Avbvn17VyZPxA4fPpxmgwAEIkCA6bci0MjVqOKll15qn376qclVQFbWXXfd1T7//HNbffXVbaeddrKTTjrJ3nnnHZs0aZLdd999dvbZZ7tiPP/8826+SM0tqeMSsXfffbetttpqduaZZ1q7du2sf//+DYo8Y8YMe//99xvs32233ax169YN9l933XVuX7KgbhCJHRCAQFEEZJmVQBw7dqyNGDGiqHNrEVl+sQMGDMASWwvY5AEBnxBAyPqkIYJWjOuvv96aNGniii0xOWrUKHvrrbds++23t3333df23ntv+5//+R+bMGGCPfDAA07gptdRwlcidOutt7Z11103xaqbHler/Tz55JPpu92yl5mEbIOI7IAABCpCwPM91Wt7rcCltx/lhCuuuML53uqtTKlBZWJQV6n0OA8CwSaAkA12+9Wt9FpuUuJVQvXHH3+0Tz75JGU1nhtuuMHdnI4++uisNxgNxNp///1t8803t+7du9uvf/1rO/bYYzPW6dBDDzV9CBCAgH8ISEDqU04466yznBVVllQCBCAAgWIJ4CNbLDHi26pVq+yII45wAnXKlCn2wQcfWJcuXVLIyIVAN7gxY8bYN998k3LM+9G4cWOT4J03b54bSHLTTTc5v1vvePL36NGjnciV0E3+SEATIAABCEAAAhCIJgEsstFs97JqvXjxYtPUVjvssIObFUCidebMmYk0NU+srCzjxo1zVltty082PWjgiAZ+XXnllbbnnnu6WQbmzp2bHs391kjpTCsLtWnTJmN8dkIAAhCAAAQgEH4CCNnwt3HFayifVE3FowFbGincr18/5xvrZXTaaafZKaec4qbAGTZsmG2x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Fu177LHHbPHixYljErGffvqpu3kmdsY3nnjiCRs6dGjyLrYhEFkC6jMtW7a09dZbL4XBH//4R9thhx0S+/R71KhRid/a2GWXXVwfk6BVuOiii+zee+91K7BJrL7zzjs2ceJE96bkrbfecgtazJ4925577jlbffXV3Tn8gQAEIBAkAgjZILWWD8t655132nHHHZfiPtC2bVs76qij7I477vBhiSkSBPxNQA+AetuhBRTkTrDVVltZo0aNSi70HnvsYdOnT3d9dOedd7btt9/eJk+ebOeff74TzIcccoj96le/sv3226/kPDgRAhCAQL0IIGTrRT4k+Y4bN84WLFhgGtjlhUGDBtmHH35oL7/8sreLbwhAoEACcrGRa46spPJBf/PNN92r/yuuuMJatGhRYCr/jab+efPNNzs3hVdeecWef/5554P77LPP2k8//eTeiMyaNcu5CP33LLYgAAEIBIMAQjYY7eTbUsZiMfvLX/5ip512WqKM2pallgABCJRGYMaMGe5NR7t27ax///42adIk5yZQyluOuXPnphRCrgtffvllyj79btqUIRMpUPgBAQgEggBCNhDN5O9C3nPPPdatWzfr27ev7b777tapUyfTFFrZgubGTA7pv5OPsQ2BKBPQoEj5tB599NFukNcRRxyRMq9sussBfSnKVwt1h0A0CaQqimgyoNZlEvjqq6/s4YcfdoO+NMjr/vvvdyOh05P95ptv3GhpTSfkBd2IN9tsM+8n3xCIPAEN4JIrQHrQzAUaxOVZTtXvttxyy5RomoWAAAEIQCBKBHiXFKXWrmJd9cpTPrGyCPXq1StjTkuWLHFT/0jsyidP03BdcMEF7pyMJ7ATAhEkoH4kMav+oRkHJFg1W8H1119v48ePt6+//tpRee2111z/0XzNo0ePdj6uZ555ZgSJUWUIQCDKBLDIRrn1K1h33VTffvttNxo6fU7Z5GyOP/54N62QbtYaST1//nwGhSUDYjvyBOSqc8YZZziRqunqli5dahqYpVkMNMOAFzS7gXzRr732Wvvkk0/shhtusMsvv9w7zDcEIACBSBBoFB+sE4tETalkQQS23XZbJy4/++yzguKXGkmLIGh0tqYZIgSXQO/evW3w4MHuE9xa1KbkAwYMsNdff90K7Vtyu+nSpYvziX3vvfds+fLlGQuqhRK0OIIsuITwEJALlizs6l8ECEAgOwFcC7Kz4UgVCSxcuLCKqZM0BIJPQDaGjz76KG9FJHARsXkxEQECEAgpAVwLQtqwVAsCEIAABCAAAQiEnQBCNuwtTP0gAAEIQAACEIBASAkgZEPasFQLAhCAAAQgAAEIhJ0AQjbsLUz9IAABCEAAAhCAQEgJIGRD2rBUCwIQgAAEIAABCISdALMWhL2FS6hfq1atSjiLUyAAgXwE6Fv5CHEcAhCAQHEEELLF8YpE7K5du0ainlSyfAKFzolafk7hSIG+FY52rEUt6Fu1oEweYSCAkA1DK1a4Dpq4nUm4Kww1pMlpAQ1C4QToW4WzinpMXSsECEAgPwF8ZPMzIgYEIAABCEAAAhCAgA8JIGR92CgUCQIQgAAEIAABCEAgPwGEbH5GxIAABCAAAQhAAAIQ8CEBhKwPG4UiQQACEIAABCAAAQjkJ4CQzc+IGBCAAAQgAAEIQAACPiSAkPVho0SpSJ9++ql98sknVa/yF198YR9++GHV8yEDCEAAAhCAAARqRwAhWzvW5JSBwNVXX22TJ0/OcKT0XT/88EODk99//30777zzGuxnBwTCQuCjjz6yadOmhaU61AMCEIBAQQQQsgVhIlI6gdNPP906d+6c+Gy55ZZ2yimn2JdffpkeNevvRYsW2YQJE+zQQw9NxFmyZImdeuqptummmyb2FbKxatUqu/jii61Dhw628cYb29Zbb21vvPFG4tRddtnFPv/8c/vggw8S+9iAQJgI3HbbbaYHw1qE2bNn2x577GEnnHBCLbIjDwhAAAJZCSBks6LhQC4CCxcudC4B++23nw0aNMjatm1rd911lx1//PG5Tks5NmLECCd+mzb9eV2Od955x3r37m0Ss3Pnzk2Jm+/H448/bvrIIrVgwQJnfT344INTTjv77LPtlltuSdnHDwiEhcCf/vQne/DBB6tenUcffdT0YLjGGmuYXHYIEIAABOpJACFbT/ohyPu0006zK6+80saNG2ctW7Y0idFCwooVK+yee+5x1lcvvlwM/vznP2e0KsldYNmyZV5U9/3tt98mfs+aNct23XVXW3vttd0+iVgt8bh06dJEnCOOOMKV85tvvknsYwMCfiOga/b222+3ww47zIYMGWJvv/12ooiXXHKJTZw4MfH7D3/4g40fP979Hjt2bIqQ1X49WOpBUw95lQqvv/66K8PAgQMrlSTpQAACECiZAEvUloyOE0VAvqcSmM8884wTjRdddFFBYB555BHba6+9rE2bNon43rK48vVLDw888IA9+eST9thjj1nz5s3t3HPPNbkTeBZWuSfoxqo4v/zlL50QOOmkk5y49tJabbXVTPtkCb7gggu83XxDwFcELr30UtMgyN/85jf26quvugc0ucWsvvrqttNOO7lrWA+MkyZNsvvuu8/0pkFB++bPn+9e92tbIvbuu+82XfdnnnmmtWvXzvr37+/iJv+ZMWOG68fJ+7S92267WevWrdN323XXXef2JQvqBpHYAQEIQKBGBBCyNQId1myOPPLIRNV22GEHJ04TO3Js/OMf/zD52RYa5DerG64E6yabbGISuxK1Xthwww1t//33t6OOOspZZRs1amR///vfvcOJb4ndk08+GSGbIMKG3whcf/311qRJE1csiclRo0bZW2+9Zdtvv73tu+++tvfee9v//M//OP9yPeBJ4KYHCV+JUPmKr7vuuilW3fS406dPdw+A6ft79eqVUcimx+M3BCAAgXoSQMjWk34I8p4yZYr17NnT3SiPOeYYZ/F57733bKONNspZOwnOhx56yHbfffec8ZIPygdwm222cbMcfPzxx9asWbPE4fPPP98N5pLbgCy2U6dONfnvymq0+eabJ+Ipz6OPPjrxmw0I+I2A/LwlXjUQ8scff3S+6MluNTfccIMb0KjrWD7lmYIGYunBTtd+9+7d7de//rUde+yxmaK6h8PkAZcZI7ETAhCAgE8J4CPr04YJSrFkDZJvrKyxO++8s3MvkA9dviDL6CuvvGIaNFZokFjt1KmTe2Uq0bx8+fLEqXJtkD+hRKzCtttu60SvxIAXJAokZOVeQICAHwnIXUa+3BKoekjULBtdunRJKerzzz/v+sGYMWMsm79348aNTYJ33rx5dvnll9tNN93k3G1SEvq/H6NHj3YiV0I3+VOL+Z0zlYd9EIAABIohgEW2GFrEbUBALgKyfsonTzdEzUAgUZsv6NWp3AXuvPNOu+yyy/JFt5EjRzo/Pg1akSVWc8Lq9aoGhylIuMqVQJYo3cS1+MFrr71mV111VSJtjeg+6KCDbM0110zsYwMCfiKwePFi09RW6kOaFUCidebMmYki6sHvrLPOcoMWZbXVtvxk08NTTz3lrn8NxNxzzz3dLAPZZgLRGxXvATA5nWT/9eT9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ciuQdVEfQmkExC55IJissyyiUBpLzspNwBOy9NfcnDgKAQjkJoCQzc2HowEiIMFVbetOLBazpUuXBohK8UWVsEi2ztZjEYXly5fbihUrii88ZwSGgOfPXo8CL1myxNSXCRCAQPAJIGSD34bUIE6gkNkKHnroIevWrZtbGKBYaLrxnXDCCdamTRtba621rEePHqYbcZhDJutsvpkNJD6HDh1qm2yyib377rtF45k1a5btuuuu1rp1a/fZc8897dNPPy06HU7wPwE9eBZrjV24cKEdcsghtvXWW5dUwWeeecY222wz14/btWtnQ4YMsWXLlpWUFidBAAL+IICQ9Uc7UIoKEchkkf3++++dCJXA0va3335bdG5nn322ffDBB/bOO++4NM444wz71a9+ZV9++WXRaQXphGTrrDfvbLbBYOKz4447OoH/0UcfmayqxYa9997bPSR88cUXNn/+fFt//fXtwAMPLDYZ4geAgFwLill979lnn7Utt9zSPv/8c/PcEoqpps4ZMGCAe9DSg+nUqVPt1Vdfdb+LSYe4EICAvwggZP3VHpSmRAKaPkpCK1N48MEHndXlrbfesg022CBTlLz7XnzxRfvtb39rG220ka2++uomIdu2bVubMmVK3nPDECFZ0Ko+meadlbV24MCB9txzz5VUZQlXWWQvueQSZ40V3wsvvND+85//2OLFi0tKk5P8SaCUgZnnn3++jRgxwl0fpdRq0qRJbkYOzWrSrFkz99Zg8ODBpr5NgAAEgksAIRvctqPkSQT0mjKbdefII4+0hx9+2LkEJJ2S2NSN7bXXXkv8fu+99+ywww6zVatWJfbtvPPONn78+MRvWWa//vpr22GHHRL7orAhQZs+GMxzN/jzn/9sF198sTVu3PDfilgddNBBKRbsRx99NGXO2k6dOjmhkcxZ27LCrbnmmlHAG5k6zpkzx9W1GNeCl156yVlUM0HSA+Wpp56a0mclfJ9++ulE9J122snmzp3rHpa0Uz6y+r+h/QQIQCC4BBrecYJbF0oeUQL5rDuy7OUKEq3777+/E7MSsfLL1OvsZEEmkfbdd9/ZFltsYbvvvrvtt99+TtCtvfbauZIO5bFk62yyu4H4ZAtqA7HbY489nJiViJWFW/6OyeH555+3P/7xj05c6CFBlvZ//vOfyVHYDgEBvebP9gYlW/Vy9eOtttrK5M6i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Zvyst956KXFr8WOTTTax0aNH21prrVWL7MgDAiUT8PxowzLTgSzOqgsBAhCAQD0INK1HpuRZPQK6Se6+++42ceJEGzVqVEpGa6+9tm200UYp+wr5cccdd9ibb75p559/fiK6xOqKFSvsuuuuS+zzNphdwSPBNwSyE/AErXxMNYWVrJr6SBhqXzbrpvxSsx3LnlvljqjcXpB7ASubeTT4hgAE6kEAIVsP6lXO89NPP7WbbrrJveqbNWtW3ty0Rvrmm29uX3zxhX355ZeJ+NrfqVMn97peS6Cuv/769vXXX9vSpUtdHA1Cy2b9TSQS32jWrJn16NHDVJZvv/3WJIIXL15sS5YssSZNmliHDh1c3hLGXlhnnXVs2bJl9t1333m73HebNm1MN9L33nvPHU85yA8IBJCArmeJQYlXiVSJWvmbevslbJODXuXrNb6OZwqasWLs2LHukKZhq/QMFpq1oGfPnm455KlTp9qAAQMaFEP/N/TZZpttMh5vcAI7IAABCJRIACFbIjg/n/aXv/zF9tprLycyd9llF/vpp5+yFvfUU0+1a665xr2SX2211ew///mPHXzwwfbJJ5/YGmusYR999JFpf9++fd2NVjfVhx56KGt66QeGDh1qF110kROzK1eutLPPPtvOOeccu/POO+322293IlbCe+utt7a33norcfoTTzxhL730kul8hY4dO9pjjz1m2267rauPBLDSkbWYAIEwEJAwVf/SR5ZZCVqJVg2k0j7P8ql4ErqZXudrBUB9FDTTSfKDaaUZ5Up72rRprs9rDmyVZ/jw4U7YVroMpAcBCEAAIRvCa2DVqlV2wgkn2Ntvv+2E4NVXX52xlvvuu68TlKeffroTve3bt3fuCM8884z17t3bWU01eEp+t+muBUpQQlf+s8lBVlRv5PJBBx3kRPKQIUPsvvvucxZdfWsKomLDpZdeahK8KvOPP/5ov/3tb50Qls/v559/XmxyxIeArwlIuOqjviQhq1f4ErfaJyEry61ErgaPeUH9zLPA+mUpa72NkauRyibL7eDBg73i8g0BCECgIgQY7FURjP5LRK//zjzzTLvsssucKM1UwuOOO86efPJJGzlypBOH8+bNcwJ4s802s+222y7TKSn7dthhB5sxY0bKx3ulqYjHHHOM/eMf/3DpS+DKunvyySdby5YtU9Ip5McZZ5xhRxxx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)\\n\",\n        \"\\n\",\n        \"> Model B: Feature-based CNN\\n\",\n        \"\\n\",\n        \"```\\n\",\n        \"activity_feature_groups {\\n\",\n        \"  features {\\n\",\n        \"    feature_name: \\\"context_movie_id\\\"\\n\",\n        \"    feature_type: INT\\n\",\n        \"    vocab_size: 3953\\n\",\n        \"    embedding_dim: 8\\n\",\n        \"    feature_length: 10\\n\",\n        \"  }\\n\",\n        \"  features {\\n\",\n        \"    feature_name: \\\"context_movie_rating\\\"\\n\",\n        \"    feature_type: FLOAT\\n\",\n        \"    feature_length: 10\\n\",\n        \"  }\\n\",\n        \"  encoder_type: CNN\\n\",\n        \"}\\n\",\n        \"activity_feature_groups {\\n\",\n        \"  features {\\n\",\n        \"    feature_name: \\\"context_movie_genre\\\"\\n\",\n        \"    feature_type: STRING\\n\",\n        \"    vocab_name: \\\"movie_genre_vocab.txt\\\"\\n\",\n        \"    vocab_size: 19\\n\",\n        \"    embedding_dim: 4\\n\",\n        \"    feature_length: 32\\n\",\n        \"  }\\n\",\n        \"  encoder_type: CNN\\n\",\n        \"}\\n\",\n        \"label_feature {\\n\",\n        \"  feature_name: \\\"label_movie_id\\\"\\n\",\n        \"  feature_type: INT\\n\",\n        \"  vocab_size: 3953\\n\",\n        \"  embedding_dim: 8\\n\",\n        \"  feature_length: 1\\n\",\n        \"}\\n\",\n        \"```\\n\",\n        \"![Screen Shot 2021-03-15 at 4.23.46 PM.png](data:image/png;base64,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hTXs33rrLX/Zunfvbi+88IItWLDAdtllF3eedM8991y3s/Bee+1lJ598crRzs3jxYjvmmGPsgAMOsP3228+uueYat0t3NME4B1hw2F/iwAMPtP/85z9FOqrkhdFv9sagbLgJ4Hpw1llnORcQysNI/2+//WZHHnmk+9txxx3tnXfecXciLCy8fPvtty7f/juf06ZNczskMzpIesnczW699VaDGeWGle/U/fLLLy7/3IvrbECI4LJA/i+66CJr3769S/+7775z10444QTXYaDc3loSr54Y0d5tt91cPonIcp0PPPCA20yQz2HDhtmZZ57p0oz33+WXX+7qGIYoGHTKyTv5oU4pP0LHgqV8qTvK4Ec2H3roIVcvLlDkvyeeeMIpi3ynbL/++qu7BIODDz7YDj/8cFf3vpzuYsx/yZ4Tnw/y+49//MOeeuqpaGzyddttt7nnoW3btu7Z5CKcCXv66ac7xqSRKD/z58+3K6+8MqrAMbp79tlnu3tUr17dTjrppGh9RG+cowclaRN4Xvhr166ds0Sm0iYke85Bh/WU9HheeK4OOeSQpETTbRNI7OijjzaeK54/3n86u7wXtAs77bST9e3b190zlTaBgPHeT5dAzH/ZbhPitYGlbRNow8855xzXFtL2MvhDOXh/t9lmm2j7SVHhxnvH7wc8UQKxSNFOeJkyZYoLw3fafAYzkETvoLsY8x9ufRdeeGH0LMrP7rvv7p6ZoGtyIb2/URg6EAERCDUBKS0lqB4afX5w3n77bWcV4YcNoQP9zTffuFHIzz77zK14hNXECyOmbFBHhwIFhJ21EX6cPv30U9cZ4Lsf2WYElPB0JseMGWM1atSIdtJvuukm90PD/egM4qqQaLScNOns9OzZ0+X5yy+/dJ0LFCSEH6cbbrjBzXHB5YCOTq9evWzbbbd1G4PxY0tnH4sLHRGu8Z18+Q4K6fh8c4zE5qdp06ZF0gtabv6O8ff/7777rn344YeuTCg/zZo1i671jxLFjzXlxWXqsssuiyo/WIX4MaZ8uCANHTrUJUjHoWbNmvbRRx+5jnaietpss81c2ehIYlXBjeK8885zHY4LLrjAunTpEk0zmF9/THlhyi7QPBt02FF8qCM6KeQXYZSTMn311VeuHPiQU15cp4jnub366quuHMThnD9PJ6hPnz42YsQIu//++12niDDxJNlzgnsM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dEIEREAEREAEREAEREAERCBMBKS0hKk2lBcREAEREAEREAEREAEREIENCEhp2QCJToiACIiACIiACIiACIiACISJgJSWMNWG8iICIiACIiACIiACIiACIrABASktGyDRCREQAREQAREQAREQAREQgTARkNISptpQXkRABERABERABERABERABDYgIKVlAyQ6IQIiIAIiIAIiIAIiIAIiECYCUlrCVBvKiwiIgAiIgAiIgAiIgAiIwAYEpLRsgEQnREAEREAEREAEREAEREAEwkRASkuYakN5EQEREAEREAEREAEREAER2ICAlJYNkOiECIiACIiACIiACIiACIhAmAhIaQlTbSgvIiACIiACIiACIiACIiACGxCQ0rIBEp0QAREQAREQAREQAREQAREIEwEpLWGqDeVFBERABERABERABERABERgAwJSWjZAohMiIAIiIAIiIAIiIAIiIAJhIiClJUy1obyUOYGvvvrKHnzwQfv000+L3HvFihX26quv2pAhQ2zOnDlFrumLCIhA7hEYO3asTZkyJZrx9evX28iRI+2JJ56wP/74I3peByIgAiIgAuEkIKUlnPWiXJUBgXvvvdduu+02q169ut100012zz33RO/asWNHGz16tC1evNjat29vv//+e/SaDkRABHKHwOrVq+3aa6+1ww47zF588cVoxvv162d9+/a1uXPn2gEHHGDz5s2LXtOBCIiACIhA+AhUDl+WlCMRKBsCS5YssSeffNJq1apl7dq1swsuuMB69Ohhs2fPtlatWlmfPn1cRn766Sf76KOP7NRTTy2bjOkuIiACGSPw8ssvW6NGjeyaa66xVatWuXSXLl1qgwcPth9++MGqVatmderUsdtvv93uuOOOjN1XCYmACIiACGSWgCwtmeWp1HKIwPXXX29r1661CRMm2P333x9VSho2bGj9+/d3JcGFBLeS1q1b51DJlFUREAFPoHPnznbxxRf7r+5z8uTJ1qZNG6ewcGLvvfe2iRMnFgmjLyIgAiIgAuEiIKUlXPWh3JQxgVGjRlnPnj1t3LhxtuOOO25w96uvvtq5h9HBkYiACOQHAVzCNtlkk2hhOJYLaBSHDkRABEQglATkHhbKalGmyorAgQceaPwx2Z7R1p9//tkqVvxbl7/zzjttwYIF9uijj5ZVdnQfERCBMiDQrFkzmzlzZvROs2bNsqZNm0a/60AEREAERCB8BGRpCV+dKEdlRIAJ9n/++ae7G5PxmbC7bt069/2xxx5zbmGDBg0qo9zoNiIgAmVFoHnz5jZt2jRDWUFeeOEF23fffcvq9rqPCIiACIhACQjI0lICaIqSHwTwc6ej0rJlSzch9+6777bKlSvbL7/8Yt26dbMWLVrYrrvu6grbtWtXu+yyy/Kj4CqFCBQ4gSpVqtiAAQPsiCOOsAYNGliNGjXslltuKXAqKr4IiIAIhJtAhchE4/XhzqJyJwIlI9C7d283mspeK4mEifi4iWy11VZRt7BEYXVeBEQgnARw9xo+fHjaLl68/wsXLrRNN900nAVTrkRABERABKIEZGmJotBBIRKoVKmSbb311oVYdJVZBAqeAO+/FJaCfwwEQAREIEcIaE5LjlSUsikCIiACIiACIiACIiAChUpASkuh1rzKLQIiIAIiIAIiIAIiIAI5QkBKS45UlLIpAiIgAiIgAiIgAiIgAoVKQEpLoda8yi0CIiACIiACIiACIiACOUJASkuOVJSyKQIiIAIiIAIiIAIiIAKFSkBKS6HWvMotAiIgAiIgAiIgAiIgAjlCQEpLjlSUsikCIiACIiACIiACIiAChUpASkuh1rzKLQIiIAIiIAIiIAIiIAI5QkBKS45UlLIpAiIgAiIgAiIgAiIgAoVKQEpLoda8yi0CIiACOUxg2rRpcXOf6HzcwDopAiIgAiKQMwSktORMVSmjqRLo2rWrxXZchg4dmmp0hRMBEcgBAk2bNrXevXsXySnv/YgRI4qc0xcREAEREIH8ICClJT/qUaUIEOjZs6d16NDBKS50YjimgyMRARHILwK8315xGTZsmDVr1szOPPPM/CqkSiMCIiACIuAIVFgfEbEQgXwjgKLiR1zbt29vw4cPz7ciqjwiUPAEUFpQVLwMGTJESouHoU8REAERyDMCUlryrEJVnL8JBDszKCwoLhIREIH8I4A7qHf/1Bhc/tWvSiQCIiACnoCUFk9CnwkJzJo1K+G1MF/wc1ty1crSqFGjMONV3vKQQC6+69OnT3eDEr169bIuXbrkXK3oPc+5KlOGRUAEyomAlJZyAh/229J5GTRokL3xxhtWrZKmPpVHfW3SYHOjQ9OtWzdr27ZteWRB9ywAAo888oiNHj3a/eldL/sKX7l2nTVs2NA6duzo3vWyz4HuKAIiIAK5QUBKS27UU5nmEkWFya1b1a1pjSN/kvIhsHLNWlu8crWtrb2RHXLEkerQlE815PVdu3fvbhPGfmvbblLH6larktdlDXPheNfn/rXS1taqZ1iMNEgR5tpS3kRABMqLgJSW8iIf0vsy4kpHptVm9dSJCUkd0aGZOG+R3ffAg+rMhKRO8iEbDEx8/P677l3Ph/LkQxnmLVthy6rXcRbufCiPyiACIiACmSQgv59M0syDtHAV2Xbj2lJYQlSX1SpXciPhjMBKRCATBHD/xKLK4IQkPAQ2q1Xd1ixaYLTDEhEQAREQgaIEpLQU5VHw37C0yE0kfI8BdbJg7u+WixOlw0dTOeI936xmNYEIIYEGEcWF+pGIgAiIgAgUJSClpSiPgv7mO8SM7EvCR4B6mT17dvgyphzlHIExY8ZYPc1hCWW9sRiCb4tDmUFlSgREQATKiYCUlnICH9bbavWgsNbM3/lSZybc9aPciYAIiIAIiIAIZIeAlJbscM3LVCtUqGCVKlV2fxUrlv7R2XrbFtZgy63ykpUKJQK5TKBipUrRd533vjRCWrvte0BpklBcERABERABEbDKYiACqRI45MTOdvDxJ9myJUusarXqtmTRn/bCwwNsyg8TU02iSLjd9t3f/pgzx+bOnFHkvL6IgAiUL4Hzb+hjjZo0s1UrV1i1GjVs1rSp9nj/O2zxwgVpZ6xylSp20rnn25jPPk47riKIgAiIgAiIgCdQ+uFyn5I+C4LAp++8Zb3P62rXdT3V3nvhaTvnmhstdiS2StWqcVlUrZZ44m/CONWrx02Lk1UTXMMKVKmy9PGE4HRBBFIg8MS9d7h3/Yazz7BZv06zThHFIyi8s7HvPtd597CuxJNE7znpJLrmrLsJ3udEceLdW+dEQAREQARym4B6drldf+Wa+++++tL+eelVVqNWbftr6RLbac+97ajT/mnr1q1z1phHb7vZli9bao232dZOu6iHrVyxwlAonh5wj8357VeX9y22bmKX33aPbbRJfftpwnf2xL13uvOtdt/DjjzljP/vyFSwQbfeZPNmzbS2+x1gO+7WzjberIHV2WgjW7d2rQ2+tY/Nmz3LxTvqtH/Zznvt7Y5/njjenn94oDvWfyIgAiUjsHbNGhv3xWfWqftFLgEUhTOvuMY22Wxzq1y1in34yov21Yfvu2snd7/QmrZoaWvXrrEpEyfYq8MGu/MVrIId1+Uc22HX3ZxSw3v50/hx7tphnU61Xf6xr7Po/D7jNxtyZ9+IhWelXT9wsH3x/ju210GHWvWaNe3r4f+1N58a5uJs1rCRdbn8386FrUKkTXnyvjttxi9T3DX9JwIiIAIikJ8EpLTkZ71mrVS4eqCkVKtezf5x8OE2a/o0p7DUrlvPju96rt397x7ObezAY0+wY7ucZc8+cJ8deOyJ9sGLz9mYzz+xrZpvY3Q4vNLSoFEju+eay42Ox5W332vMc/n158kRJWZTe6Rvb1u04A876LiT7IAjj7EXBz8YKVcFa91uL7utxwW2YN5c2/2AA+30S66w/tdeYa133zPSYdrBbr3sAlu/fr2de23PiJLT3kZ/OiJrPJSwCOQrAdzCeNfrbryxdTjmBPvx27+X4T369C7225SfbVC/m9z1q++63yaN+zaiwFS17drsbLdc1M0hIU71SBrrIu9i7Xr1bPzXXzolZud/7GMHRdoElBYUoNWrVtkdV13iBiDOu/4ma7lrWxv31RfOilMvMpjR95LuLtyVd9zr4kz6bqx1vfJae+nRh23K9xMigyLbRZSoa+3mC8/J16pQuURABERABCIEpLToMUiLwB7tD4p0KnazWnXqRRSK+Taw13UufpMW27vOxz6HHem+00lptn1Ldzzhm5FGR2eLxltHOiOfRzovX0XvOXH0N07BWB+xmPz8/Xhr8v9Ky9cj/mvb77Sr7XXwobZV0+aRMNEoNvm7b53Cwhn85DtHRoBxIdm2dRtjVPjQk05xgXE5abZDSykt/0OnIxFImcDxXbvZqhXLI1bNzd179vqTQ1zcbVu1sWmTfrTDTz7NfV+9aqVtvV0LGz/yS1u9cpWdffV17h3/7N03XZuAGycWVz/3bfK4sdb5vIv/P+4qG/nRB7b7/h2s/uZbRJSbjSJK0ibRPH714XvRcN9+/qk1b9nKpv80yS3gsV3rnYw/ZOP6m0bapLoRC+9i913/iYAIiIAI5B8BKS35V6dZLdEXH7xrb0Q6L6z6dVnfOyPKxDp3vwoVKtpfkY7J9IiVxAujpQiWjikRhaTNHv+w0y++3MaN/MLee/4Zd23t6jXuk//WrVmLIcWNsF4Rsbp8F1FwSI/zjKYWJ+Rhwbzfo3kg7uIF6U8cLu4+ui4ChUDg2QfutR/HjolYVA9zFk1cMRHeM+a4YOlEeM/m/DrduYXeceXFtv3Ou0asoXvaUZGBitt6XGhr1qyO/P3vPcd1DHcxZKP69e2yfnfbR6+9FFGEfrDNI+1KIllv6/+OFxmMIC/Btmbw7Tfb6tWrEkXVeREQAREQgTwgoIn4eVCJ5VEEVvz6LqKUHNbp79FWRj83icwzmTn1F/thzChbvnSp+07e9jviaKtcuap9+s6bzqWj5S5tk2Z5o003s7qR+SrvvvCMTRz1tRuBDUZoEbHAMKcF2W2f/W3mtF/+34d+fMSa08QmRTpa5KFW7TrOPSUYV8ciIALpEcASUnejjZ1bJjGxiG5cfzP3jvGuNW6+rXv/GMjY+5AjnKLz4qAHbX5knhnuoMmEd/m3KT/ZJ2+9bsxB23yrxkWC73XQYe47bmS77r2/s9as+Osv+2Pu75HBjDUuDzOm/OzcQldH5sFIREAEREAE8peALC35W7dZL9nbzz5p193/iH369hs2//fZ9vJjj9jFfW51LiK4ZjE5Fvnzj/l2fs+b7c/586xWZO7Li4MeSJq3hZER3O8jSseNDzzq3D1wRQnKL5Ells+66j8RhaRmZHT374n4XP8u4jPfdPsd7Nr7HnIjsQvnz7dhd98WjKpjERCBNAmwsMbrTzwWmUh/duS9/MbefHJoZE7Jf+zf9wx07yAun4sXLoy4gC2LKDZ72D8OOdxZYBnYYDJ+pSqJf2YYlNj/yI527b0PRlzRVhoT8YvKertuwCOROXQ13ET8yZH5LMiQO/vZmZdfEzla7+bVvBHJE/PYJCIgAiIgAvlLoEKkoVdLn7/1m1bJ2G290/HH2W4N/+dTnlYC/x+Yybcrli/fIGqNmrVs+V/LNjif6AS+8LiP4U7ihYn1LXbaxZ4Z2N+tKMSoa6z8vTFeJedPH3stl79PnLfIelzzH+vYsWMuF0N5DwGB3r172w+fDbfNaiVeUry4bLKE+ZrVq51bWDAsVhEUHeaXpSqsDrYy0mYEf45uiAxa3H/D1bZ08WJ3Pl56idqaVO8bxnArI+6wsytUtzfeeCOM2VOeREAERKDcCCQeAiu3LOnGuU4gnsJCmdJRWAi/KrJEcjKJp7AQHn9373+fLL6uiYAIlJwAyxLHE1YDS1cSvcukg2KUSBK1NYnC67wIiIAIiEDuEpDSkrt1V5A5Z1K/ljAuyKpXoQuMQJ8Lzi6wEqu4IiACIiACyQhoIn4yOromAiIgAiIgAiIgAiIgAiJQ7gSktJR7FSgDIiACIiACIiACIiACIiACyQjIPSwZnQK9Nm9Z8rkkBYql3IvNBF2JCGSKwKKVieeKZOoeSid9AivWRva+KsUCCenfUTFEQAREIDcISGnJjXoqs1yujPxgtty3Q5ndTzdKncCyUaNSD6yQIlAMga1btrZGjRoVE0qXy4PAKL3r5YFd9xQBEQg5ASktIa+gss5ew4YNrWfPnmV9W90vBQLdu3dPIZSCiEBqBI4++mgtn50aqjINxdLzUlrKFLluJgIikCMENKclRypK2RQBERABERABERABERCBQiUgpaVQa17lFgEREAEREAEREAEREIEcISClJUcqStkUAREQAREQAREQAREQgUIlIKWlUGte5RYBERABERABERABERCBHCEgpSVHKkrZ3JDA+++/b9OnT9/wwv+f+fTTT+2HH35IeF0XREAEwk9g/fr1Nnjw4KQZHTp0qK1erSWck0LSRREQARHIcQJSWnK8Ass7+//9739t0003tXXrInsLlLE88sgjtuWWW7q7orxcdNFF1rVrV/v222/duebNm9uAAQPKOFe6nQjkJ4HevXuXy2pj7777rq1d+789ih577DE75ZRT7Pbbb4+er1mzpr3wwgv5CV6lEgEREAERcASktOhBSIsAo56LFi2Kxlm1apX98ccf7vvy5cuj5/0Bo59//fWX/2rE938rVmy4ieWyZcuiHZFopDgHX3zxhe2xxx5WufLfq3b/85//tPPPP9+uv/56O+OMM1waKDQrV660+fPnx0lBp0RABJIRWLp0aZF3kXfTv/vx3l1/zafp3/M1a9bEtYIsXrzYB036+fjjj9u//vUvF+bll192gxIMWCxcuDA6KHHCCScY1yQiIAIiIAL5S0BKS/7WbcZLNnz4cNt8881t5513tp122imqrHAjLBy1a9e2gw8+OGp1ueKKK2yLLbawjTbayLp16+byc+GFF9pee+1lLVu2tDp16hjfETo2nTt3tsaNG9tmm21mjz76qDuf6L9BgwZF06RzxKhrq1atbJtttnHpekXpvPPOs4ceeihRMjovAiIQQ4D36fjjj3fvEu/jXXfdFQ3x559/Wtu2ba1evXpugIALuGBuv/32zurJPk9jx4514XkfjzvuOGeJrV+/vqF8IOxDsuuuu1rTpk1t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165dbZtttokGI8XYaQAAQABJREFUoV35+eefo+0AqxdmQ9j76L777stG0kqznAho3lo5gddtRUAEyoWA9mlJE/sdd9xhn3zyif36668JY9I5WL16ta1bty4aZvfdd3fWjvfffz96buDAgfaf//wn+j32oFatWkaHJh2pUKGC60wtX748nWgKm8cEGIlV5yazFdy8eXPr27evXX755UkTrlGjhsW+i8cff7xRJ1OmTInG3W677aIbWEZP/v8Blh3alNh0YsPFfifOmjVrbO3atbGX9F0EQkUAdz8WV9C8xVBVizKTpwTYrJeB7LZt2+ZcCaW0pFll7BfCLsQHHnigrV+/vkhsOijsI7D99ts7t68nnnjCbrrpJuvUqZP16NHDuZS0atXKTjrppCLxYr/stNNO9txzz7n0WSnnr7/+ssGDB9s777zjNrojfb/KGZ/sGI2wEd6JJ57ovv/444/OFYS4ksIlIGUlO3X/0EMP2ZFHHmlvvvmmffTRRxvc5LjjjrNbb73VKQ0oGyeffLJNnTrVncPqusMOO9iQIUOsX79+G8QNnvjnP/9pN998sy1dutTeeust904Td+utt7Ynn3zS2rVr54IzKHL77be7dgk3sGeeecYpqvw4cZ8bbrghmKyORSA0BNhq4LXXXrMtttjC+O2TiIAIZJfAl19+ab///rvNnTvX8MTJJeVFSkuaz8agQYPsiCOOcCOsd911V5HYt9xyi3PzYCSVHZw/++wz+/rrr+2FF16wxo0bO0tLcDdnFB/cy7ygmKBs0Bm58sorXSdlxx13tG+++cYpLYTDkhIU/x2FacWKFbbHHnu4jhKdqcMOO8xeeeWVYHAdi4AIZIAAFtB//etf9uyzz9puu+1WZACjQYMGhhV1r732st9++81QYFAi+I5rGe/0PffcY8OHD4/m5LbbbotaRLDi4saFwoEiwjtNOmeeeaZTVnyk4Bw42gHfFuB69vLLL7t74sJGXO6nVbc8ufz6xGrXNLJnSy5K9+7d3e8Vv3kSERCBsiWA5w8DcOedd17OKC5SWkrwjJxzzjnG8shBVy+SYQQVf3WE1YewlnDu3Xffdedi/8MUjkLiZdGiRVa3bl1ntmNUFfn+++9t5MiRPkjCT0Zzhw4daqeddpo1a9bM6DjR6ZGIQC53asJcewxI8M498MADdv7550ezipLx1VdfOWWB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aapKTwTyk0D9+vXzs2AqVVYJaE5LVvEqcREQAREQAREQAREQAREQgdISkNJSWoKKLwIiIAIiIAIiIAIiIAIikFUCUlqyileJi4AIiIAIiIAIiIAIiIAIlJaAlJbSElR8ERABERABERABERABERCBrBKQ0pJVvEpcBERABERABERABERABESgtASktJSWoOKLgAiIgAiIgAiIgAiIgAhklYCUlqziVeIiIAIiIAIiIAIiIAIiIAKlJSClpbQEFV8EREAEREAEREAEREAERCCrBKS0ZBWvEhcBERABERABERABERABESgtASktpSWo+CIgAiIgAiIgAiJQIASmTZsWt6SJzscNrJMiUAICUlpKAE1RREAEREAERKC8CHTt2tWCHcShQ4cafxIRKCsCvXv3jt6KZ5FnsmnTptFzOhCBbBConI1ElaYIiIAIiIAIiEB2CHTp0sU6dOjgFBc6iiNGjLCpU6dm52ZKVQRiCPhnjucOadasmQ0ZMsQd6z8RyCYBKS3ZpKu0RUAEREAERCDDBNq3b+9GtRnhxsKiDmOGASu5Ygn07NnTKc4ERIk588wzi42jACJQWgJyDystQcUXAREQAREQgTImQKcRUYexjMHrdo4AijN/iH8W3Rf9JwJZJCBLSxbhKmkREAEREIHwEpg1a5bNnj3b+MxFadu2res4vvHGG7mYfWvUqJFRhkIWnr3Ro0fnJALqbvz48Va/fn3LxWeQ569hw4buOczJCijATEtpKcBKV5FFQAREoJAJ0FF88803bcCAAQ7DkiVLchbHSy+9ZPzlolStWtVatGhhHTt2tG7duuViEUqcZxSV7t27W7VKFa1utSolTqe8I7bbcXt74I5byzsbJbr/yrXrbPHK1e75O/fcc6W8lIhi2UaS0lK2vHU3ERABERCBciZAZ5FOI1YWSfkSYF7O9OnTXSYKRXHBKnHrzX2s1Wb1clphKd8nJzN3X7lmrX378YfWfdQoe/jhh6W4ZAZr1lLRnJasoVXCIiACIiACYSOAwjJhwgQpLCGpmFWrVtmkSZOctShX3aTSQYmVj+WCpbCkQy17YatVrmSN69a0SssWuXrJ3p2UciYISGnJBEWlIQIiIAIikBME6Bgzui8JDwEUF+rlkUceCU+mspSTQYMG2bYb1zY6y5LwEGhQs5r9NOG7nJ1fFB6S2c2JlJbs8i3z1BP9GCc6X+YZ1A1FQATKhIDfQyF4s0JvB3DL+eOPP4JIdBwSAswrytUFEdJBOCrihpTLc1jSKWsuhfVKZCE8g7lUL7F5ldISSyQPvrPpmBc6KXxnWUyJCIhAYREI7lqNEhP8Xlgk/i6t5rCEt9axthRC/RRCGcP7lCXPGcqk6ic5o/K+qon45V0DGb4/ygl/7FDrP7XxWIYhKzkRyAECvP9du3Z17QCDF2xCqF3Tc6DilEUREAEREIG4BGRpiYslt0/6jZ68K4h2qs3t+lTuRaAkBFBaaAuwsNAW0A5wTlI8gQoVKth2221ntWrVKj5wghC77767bb/99gmu6rQIbEiA565SpcrRvw1DpHdmo/qb2rat2qQXSaFFIMQEpLSEuHJKmjU6Jl5RkZWlpBQVTwRynwA7VtMeIH4ww33RfwkJ9OjRw3799Vd7/PHHbeLEifb0009b7dq1E4b3F/bff3879NBD/Vfr3LmzHXjggdHvOhCB4gg03b6l3fnsK3b9wEHW86HHrNcjw2z/IzsWFy3h9S2bNbf9ShE/YcK6IALlREDuYeUEvrjbMhkM38rS7CVAZ4X170vix77bbrtpt+LiKknXRaAMCNAW+HagJP7WtAP8DRs2rES5LaS24OSTT3Yb/u2yyy5uwn6lSpXsoYcesn79+tnFF18c5VetWjVbs2aNrV27NnoOy0q9evXs/fffj57zB9WrV7cVK1b4r9HPGjVq2PLly6PfgwektWjRouApHRcAgd9++dnu/ncPV9LNGjayq+8e6Fa1mv3r9Gjpq1Staqsjc4BipWrkuVy1cmXsafe9JHFKkl7cm+ukCGSIgJSWDIHMZDJ0UNhLgNVUSrtTM5sllUT4Ufa7FWvDpZIQVBwRKD0BloBl13bagZUJOiOp3oW9MEoitAV16tSxiy66KO93LWdzw8suuyy6whhKyZVXXmkHHXSQQ1e5cmV75plnnBLYsGFDw5J9ww03WKdOnQwLDUpOq1at7KSTTnLhOf7iiy9syy23dG56Xbp0cee33XZbe+qpp6xKlSouDpbxb7/91k455RSXFq5pkydPjqZTknpTnNwnMG/2LJv+0yRr0GhLQ2lBiely+b+d+1iFihXtyfvutBm/TLGatevYWVdfZ5UjzxPP1EevvWSjP/3YAageUYy7/aeXbbFVY1u0cIENvetWW7TgD9tkswb2r0haVatWs7obb2zPPXS/jf/6q8jxJnZBz5tt2qQfrXnLVlY1onC/8tjDNu6rL1x6O+25tx112j9t3bp1tizSLj162822fNnS3IetEuQEASktIasmFBYmz06L+KCXVmHJRNHIAwpUr169rG3btplIUmmIgAikQID3jvkodF7LWxjAoA1gyWD+8lGYT0Abx5K0CJ0/rB3IJ5984j632WYbe/nll53igvXkt99+s3vuucdeeOEFa9y4sQsfdMND+dh3332tYqSDOXLkSMMaQ/rPPvusU3I+/fRTd08UoR122MHIA1Yewi1cuNDdU/8VFgGelRq1artnZttWra3Jti3ssQnjHYSuV15rLz36sE35foI13mY7O/OKa+3mC8+xXfbe1+bOnGHPPzwgosDUtnbt/1ayibRVs22t36XdbenixXbSuefb7vt3sA9ffdEabLmVvTbsUZv64/e2deQep13UwyktxNmyaXN76+nH7dkH73MK02X97rJJ3421ypWr2PFdz3WWoCWL/rQDjz3Bju1ylj37wH1Ek4hA1glIack64vRuQEclLAoLOccd5csvv4x2WNIrjUKLgAiUhACDF7x3YVBYyL9fjpb8YP3BIpFvsn79euMPxQFp06ZNdLNDlBk6k1irsL5gddp8883d8RZbbGELFiyIi+Ptt992I9KMSqP4tGvXzqXBBP32kflG/CFbbbWV1a9f3x1//PHHUlgcicL8r+HWTa1HvzutSsQCUqtuXev/nyvsr6VLDIsJisZ2rXdyf9DZODLRvladuvbzxPF26ImdrVO3C23CN1/ZJ2+9HoU3bfIPTmHhxORxY63tAR3ctUnjvrUmLba3g4/vZHU22sjqRSwsXlBwsLogc2fNtNnTp0WUn22sWiQPuKXtc9iR7lrtiFLfLDIPRyICZUWgYlndSPcpnoDf+CwMFpZgbtmMjc4KHSmJCIhA9gm8+eabodxojwGVfLW0UKtjxoxxVo7g8QknnGA///yzU2hw+3ryySfdXJPhw4fbn3/+mfRhQNnzsnr1aqcQoRQxH+abb76J/pGun/MSnCfj4+qzcAjMnPaL9b3kvIgF5VxbFlEesLo4iTw36yIK8/SfJ0f/Bt9+s61evcpZWfpe0t1ZTfY57Ci7sFffKDCeNS9r166xCpF/yEnnXmAHHHWszZszyyZFlJlksv7/L1aoUNH+iriC+TxM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LglgtOQSGMUhAAEIQAACEIAABCAAgYIlgNFSsLxpDQIQgAAEIAABCEAAAhDIJQGMllwCozgEIAABCEAAAhCAAAQgULAEMFoKljetQQACEIAABCAAAQhAAAK5JIDRkktgFIcABCAAAQhAAAIQgAAECpYARkvB8qY1CEAAAhCAAAQgAAEIQCCXBDBacgmM4hCAAAQgAAEIQAACEIBAwRLAaClY3rQGAQhAAAIQgAAEIAABCOSSAEZLLoFRHAIQgAAEIAABCEAAAhAoWAIYLQXLm9YgAAEIQAACEIAABCAAgVwSwGjJJTCKQwACEIAABCAAAQhAAAIFSwCjpWB50xoEIAABCEAAAhCAAAQgkEsCGC25BEbx5CKwdOlS++6773LcqdmzZ9vChQtzVH7fvn02ZswYGzBggP300085ukeFtm3bZuPHj89QfsuWLfb+++/b8OHDbcOGDRmucQIBCEAg1gRiqSvXrVtnI0aMsGHDhtnatWtz3JVwMs2dO9fp3GnTpuW4HgpCAAKJQQCjJTHGCSljQOCtt96yli1b2kMPPZRt7bt377b77rvPWrdube+991625VWgY8eONnbsWCtevLh16tTJPv/882zvmzlzpp122mnWvn37YFkZLC1atLAff/zRfv/9dzvllFOcYRMswAEEIACBGBKIpa5cv369nXHGGbZ69WpbtWqV028bN27MtjfhZNIE0a233urufeCBB5zxkm1FFIAABBKGQHrCSIqgEIgigSVLltikSZPczN6jjz4arHnPnj2Wnv73/xbe+QcffGBVqlSxe++913bt2pVteRWYPHmyMzJ0XKpUKed1adWqlU7Nqzfz8XPPPWdvv/22NWvWzJXTn6efftoZQLfccovLa9y4sfO2lCxZMliGAwhAAAKxIBBrXan67777buvQoYMT/9tvv7UZM2a4yZtQPamL3nk4mbZu3WrSn7NmzTLpxosuusi++uqrWCChTghAIE4E8LTECTzNxpdAjRo1bNCgQe7hFirJ0KFD7cEHH3RZMlS6devmjvVA9YyG0PLXX3+986Yo784777SRI0cGL1944YX21FNPOeNIoQ96iCrJa6OZxUWLFpketGeffbY71rU333zTatasqcNg0kO4YsWKdsEFF7g6ihQp4gyoYAEOIAABCMSIQKx15fHHH+8MlsWLFztdKm9LkyZNXG8i6eNw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QQABuwqcPXtW0ms+xrVr12TChAlpRjd+/Pg0OxcnQgABBBAIHgGCluCpS64EAQRsKqBPvGrXrl28pR84cKA0b97c6z4bN26UkydPet2W0Mr58+dLdHS0e7cvvvhCnnjiCendu7f8/fff7vW+Fg4cOCB9+vSRtm3byrJly+Ls9t///le+++479/otW7bI/v373e9ZQAABBBBAIDECBC2JUWIfBBBAIBEC2muhP1aylmOvv3DhQoxAYdCgQTJ9+nTrMImKipLIyEiTl5WHHnPmzBmzz6VLl9z76kLdunVl3rx57nV6jLWve6WPhU8//dQdMK1bt06mTp1qel4aNmwoTz/9tI+jrq/WMrZv314eeOABGTp0qLz22mvy119/uY/57LPP5Mcff5Sff/7Zva5Hjx4yatQo93sWEEAAAQQQSIwAQUtilNgHAQQQSITAzJkzJUeOHCYg0Z6K3Llzm+VevXpJkyZNTDDSqlUrKVasmBQoUEAmTpxoctWeiscee8wsa/CSP39+ueGGG+TBBx+UChUquM98+vRpqVGjhsn39ddfN+s1YLh48aJ07tzZBBtLliyRQoUKSdWqVaVKlSrx9sBs3bpVSpcuLVmzZjV5aY9L8eLFJXv27FK+fHlTXt2gAdCcOXPMPho8zZo1yyz/8ssvUqlSJSlYsKDs27dPli5dasquG7Xn55NPPpGXX37Z7Gv90nwPHTok58+ft1bxigACCCCAQIICBC0JErEDAgggkDiBRo0aSUREhGzbtk2WL18uOXPmFB26tX79emncuLF8/vnnptdhz5495g967XXQ3grP1LNnT3nyySdFh13F7i3RP/a//vpr6du3r7z//vty5coVE0Bky5ZNRo8eLR06dDA9NhUrVpTNmzfL4MGDRQMdX2nSpEnSqVMn9+bbb7/dBBvVq1cX7WmxvqgxV65cMnfuXBNktW7dWooWLWqO2bFjh2jvzMcff2yCmnvuuUcuX75str3yyisyZMgQyZgxozt/a6Fly5by1VdfWW95RQABBBBAIEEBgpYEidgBAQQQSJyA9o5Uq1ZNtLfjxIkTJojQZQ1cNGj57bffTCDSoEEDExDccsstcvDgQXfm2vugx+kf/9pLU79+ffc2XShbtqzpCalXr57pqTh37pxkyZLF7JM5c2YJCwuTZ5991gRC2lszYsSIOEGRZ4YamCxevNi9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)\"\n      ]\n    }\n  ],\n  \"metadata\": {\n    \"colab\": {\n      \"collapsed_sections\": [],\n      \"name\": \"ondevice_recommendation.ipynb\",\n      \"provenance\": [],\n      \"toc_visible\": true\n    },\n    \"kernelspec\": {\n      \"display_name\": \"Python 3\",\n      \"name\": \"python3\"\n    }\n  },\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "tensorflow_examples/lite/model_maker/third_party/recommendation/ml/requirements.txt",
    "content": "pandas>=1.0.5\ntensorflow>=2.2.0\nabsl-py>=0.1.6\n"
  },
  {
    "path": "tensorflow_examples/models/__init__.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Some example models written in TensorFlow 2.0.\"\"\"\n"
  },
  {
    "path": "tensorflow_examples/models/dcgan/__init__.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n"
  },
  {
    "path": "tensorflow_examples/models/dcgan/dcgan.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"DCGAN.\n\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nimport time\nfrom absl import app\nfrom absl import flags\nimport tensorflow as tf\nimport tensorflow_datasets as tfds\n\nFLAGS = flags.FLAGS\n\nflags.DEFINE_integer('buffer_size', 10000, 'Shuffle buffer size')\nflags.DEFINE_integer('batch_size', 64, 'Batch Size')\nflags.DEFINE_integer('epochs', 1, 'Number of epochs')\nflags.DEFINE_boolean('enable_function', True, 'Enable Function?')\n\nAUTOTUNE = tf.data.experimental.AUTOTUNE\n\n\ndef scale(image, label):\n  image = tf.cast(image, tf.float32)\n  image = (image - 127.5) / 127.5\n\n  return image, label\n\n\ndef create_dataset(buffer_size, batch_size):\n  train_dataset = tfds.load(\n      'mnist', split='train', as_supervised=True, shuffle_files=True)\n  train_dataset = train_dataset.map(scale, num_parallel_calls=AUTOTUNE)\n  train_dataset = train_dataset.shuffle(buffer_size).batch(batch_size)\n  return train_dataset\n\n\ndef make_generator_model():\n  \"\"\"Generator.\n\n  Returns:\n    Keras Sequential model\n  \"\"\"\n  model = tf.keras.Sequential([\n      tf.keras.layers.Dense(7*7*256, use_bias=False),\n      tf.keras.layers.BatchNormalization(),\n      tf.keras.layers.LeakyReLU(),\n      tf.keras.layers.Reshape((7, 7, 256)),\n      tf.keras.layers.Conv2DTranspose(128, 5, strides=(1, 1),\n                                      padding='same', use_bias=False),\n      tf.keras.layers.BatchNormalization(),\n      tf.keras.layers.LeakyReLU(),\n      tf.keras.layers.Conv2DTranspose(64, 5, strides=(2, 2),\n                                      padding='same', use_bias=False),\n      tf.keras.layers.BatchNormalization(),\n      tf.keras.layers.LeakyReLU(),\n      tf.keras.layers.Conv2DTranspose(1, 5, strides=(2, 2),\n                                      padding='same', use_bias=False,\n                                      activation='tanh')\n  ])\n\n  return model\n\n\ndef make_discriminator_model():\n  \"\"\"Discriminator.\n\n  Returns:\n    Keras Sequential model\n  \"\"\"\n  model = tf.keras.Sequential([\n      tf.keras.layers.Conv2D(64, 5, strides=(2, 2), padding='same'),\n      tf.keras.layers.LeakyReLU(),\n      tf.keras.layers.Dropout(0.3),\n      tf.keras.layers.Conv2D(128, 5, strides=(2, 2), padding='same'),\n      tf.keras.layers.LeakyReLU(),\n      tf.keras.layers.Dropout(0.3),\n      tf.keras.layers.Flatten(),\n      tf.keras.layers.Dense(1)\n  ])\n\n  return model\n\n\ndef get_checkpoint_prefix():\n  checkpoint_dir = './training_checkpoints'\n  checkpoint_prefix = os.path.join(checkpoint_dir, 'ckpt')\n\n  return checkpoint_prefix\n\n\nclass Dcgan(object):\n  \"\"\"Dcgan class.\n\n  Args:\n    epochs: Number of epochs.\n    enable_function: If true, train step is decorated with tf.function.\n    batch_size: Batch size.\n  \"\"\"\n\n  def __init__(self, epochs, enable_function, batch_size):\n    self.epochs = epochs\n    self.enable_function = enable_function\n    self.batch_size = batch_size\n    self.noise_dim = 100\n    self.loss_object = tf.keras.losses.BinaryCrossentropy(from_logits=True)\n    self.generator_optimizer = tf.keras.optimizers.Adam(1e-4)\n    self.discriminator_optimizer = tf.keras.optimizers.Adam(1e-4)\n    self.generator = make_generator_model()\n    self.discriminator = make_discriminator_model()\n    self.checkpoint = tf.train.Checkpoint(\n        generator_optimizer=self.generator_optimizer,\n        discriminator_optimizer=self.discriminator_optimizer,\n        generator=self.generator,\n        discriminator=self.discriminator)\n\n  def generator_loss(self, generated_output):\n    return self.loss_object(tf.ones_like(generated_output), generated_output)\n\n  def discriminator_loss(self, real_output, generated_output):\n    real_loss = self.loss_object(tf.ones_like(real_output), real_output)\n    generated_loss = self.loss_object(\n        tf.zeros_like(generated_output), generated_output)\n\n    total_loss = real_loss + generated_loss\n\n    return total_loss\n\n  def train_step(self, image):\n    \"\"\"One train step over the generator and discriminator model.\n\n    Args:\n      image: Input image.\n\n    Returns:\n      generator loss, discriminator loss.\n    \"\"\"\n    noise = tf.random.normal([self.batch_size, self.noise_dim])\n\n    with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:\n      generated_images = self.generator(noise, training=True)\n\n      real_output = self.discriminator(image, training=True)\n      generated_output = self.discriminator(generated_images, training=True)\n\n      gen_loss = self.generator_loss(generated_output)\n      disc_loss = self.discriminator_loss(real_output, generated_output)\n\n    gradients_of_generator = gen_tape.gradient(\n        gen_loss, self.generator.trainable_variables)\n    gradients_of_discriminator = disc_tape.gradient(\n        disc_loss, self.discriminator.trainable_variables)\n\n    self.generator_optimizer.apply_gradients(zip(\n        gradients_of_generator, self.generator.trainable_variables))\n    self.discriminator_optimizer.apply_gradients(zip(\n        gradients_of_discriminator, self.discriminator.trainable_variables))\n\n    return gen_loss, disc_loss\n\n  def train(self, dataset, checkpoint_pr):\n    \"\"\"Train the GAN for x number of epochs.\n\n    Args:\n      dataset: train dataset.\n      checkpoint_pr: prefix in which the checkpoints are stored.\n\n    Returns:\n      Time for each epoch.\n    \"\"\"\n    time_list = []\n    if self.enable_function:\n      self.train_step = tf.function(self.train_step)\n\n    for epoch in range(self.epochs):\n      start_time = time.time()\n      for image, _ in dataset:\n        gen_loss, disc_loss = self.train_step(image)\n\n      wall_time_sec = time.time() - start_time\n      time_list.append(wall_time_sec)\n\n      # saving (checkpoint) the model every 15 epochs\n      if (epoch + 1) % 15 == 0:\n        self.checkpoint.save(file_prefix=checkpoint_pr)\n\n      template = 'Epoch {}, Generator loss {}, Discriminator Loss {}'\n      print (template.format(epoch, gen_loss, disc_loss))\n\n    return time_list\n\n\ndef run_main(argv):\n  del argv\n  kwargs = {'epochs': FLAGS.epochs, 'enable_function': FLAGS.enable_function,\n            'buffer_size': FLAGS.buffer_size, 'batch_size': FLAGS.batch_size}\n  main(**kwargs)\n\n\ndef main(epochs, enable_function, buffer_size, batch_size):\n  train_dataset = create_dataset(buffer_size, batch_size)\n  checkpoint_pr = get_checkpoint_prefix()\n\n  dcgan_obj = Dcgan(epochs, enable_function, batch_size)\n  print ('Training ...')\n  return dcgan_obj.train(train_dataset, checkpoint_pr)\n\nif __name__ == '__main__':\n  app.run(run_main)\n"
  },
  {
    "path": "tensorflow_examples/models/dcgan/dcgan_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"DCGAN tests.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nfrom absl import flags\nimport tensorflow as tf\nfrom tensorflow_examples.models.dcgan import dcgan\n\nFLAGS = flags.FLAGS\n\n\nclass DcganTest(tf.test.TestCase):\n\n  def test_one_epoch_with_function(self):\n    epochs = 1\n    batch_size = 1\n    enable_function = True\n\n    input_image = tf.random.uniform((28, 28, 1))\n    label = tf.zeros((1,))\n    train_dataset = tf.data.Dataset.from_tensors(\n        (input_image, label)).batch(batch_size)\n    checkpoint_pr = dcgan.get_checkpoint_prefix()\n\n    dcgan_obj = dcgan.Dcgan(epochs, enable_function, batch_size)\n    dcgan_obj.train(train_dataset, checkpoint_pr)\n\n  def test_one_epoch_without_function(self):\n    epochs = 1\n    batch_size = 1\n    enable_function = False\n\n    input_image = tf.random.uniform((28, 28, 1))\n    label = tf.zeros((1,))\n    train_dataset = tf.data.Dataset.from_tensors(\n        (input_image, label)).batch(batch_size)\n    checkpoint_pr = dcgan.get_checkpoint_prefix()\n\n    dcgan_obj = dcgan.Dcgan(epochs, enable_function, batch_size)\n    dcgan_obj.train(train_dataset, checkpoint_pr)\n\n\nclass DCGANBenchmark(tf.test.Benchmark):\n\n  def __init__(self, output_dir=None, **kwargs):\n    self.output_dir = output_dir\n\n  def benchmark_with_function(self):\n    kwargs = {\"epochs\": 6, \"enable_function\": True,\n              \"buffer_size\": 10000, \"batch_size\": 64}\n    self._run_and_report_benchmark(**kwargs)\n\n  def benchmark_without_function(self):\n    kwargs = {\"epochs\": 6, \"enable_function\": False,\n              \"buffer_size\": 10000, \"batch_size\": 64}\n    self._run_and_report_benchmark(**kwargs)\n\n  def _run_and_report_benchmark(self, **kwargs):\n    time_list = dcgan.main(**kwargs)\n    # 1st epoch is the warmup epoch hence skipping it for calculating time.\n    self.report_benchmark(wall_time=tf.reduce_mean(time_list[1:]))\n\nif __name__ == \"__main__\":\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/models/densenet/README.md",
    "content": "# Densely Connected Convolutional Networks.\n\nImage Classification using the Densenet model as described in [Densely Connected Convolutional Networks](https://arxiv.org/abs/1608.06993)\n\n## Content\n*  `densenet.py`: Model definition.\n*  `train.py`: Training the model with custom loop and keras fit.\n*  `densenet_test.py`: Unit tests and benchmarks.\n*  `distributed_train.py`: Training the model using tf.distribute.Strategy with custom loops.\n*  `densenet_distributed_test.py`: Unit tests and benchmarks using multiple GPUs.\n*  `utils.py`: Configuration and dataset preprocessing.\n\n## Benchmarks\n\nEach of these benchmarks were performed on the CIFAR10 dataset with image augmentation (C10+ as described in the [paper](https://arxiv.org/abs/1608.06993)) with a batch size of 64 per replica. All of the benchmarks were performed on the\nNVIDIA V100 GPUs.\n\nModel                 | Benchmark name                                          | accuracy   | seconds/epoch |\n--------------------- | ------------------------------------------------------  | ---------  | ------------- |\nDensenet (L=40, k=12) | benchmark_with_keras_fit_300_epochs                     |     94.67% |            51 |\nDensenet (L=40, k=12) | benchmark_with_function_custom_loops_300_epochs         |     94.75% |            42 |\nDensenet (L=40, k=12) | benchmark_with_function_custom_loops_300_epochs_2_gpus  |     94.69% |            23 |\n\n\nTo build (and run benchmarks) from source:\n\n```python\nbazel run -c opt --config=cuda :densenet_test -- --benchmarks=.\n\nbazel run -c opt --config=cuda :densenet_distributed_test -- --benchmarks=.\n```\n\n## Reference\n\n```\n@article{DBLP:journals/corr/HuangLW16a,\n  author    = {Gao Huang and\n               Zhuang Liu and\n               Kilian Q. Weinberger},\n  title     = {Densely Connected Convolutional Networks},\n  journal   = {CoRR},\n  volume    = {abs/1608.06993},\n  year      = {2016},\n  url       = {http://arxiv.org/abs/1608.06993},\n  archivePrefix = {arXiv},\n  eprint    = {1608.06993},\n  timestamp = {Mon, 10 Sep 2018 15:49:32 +0200},\n  biburl    = {https://dblp.org/rec/bib/journals/corr/HuangLW16a},\n  bibsource = {dblp computer science bibliography, https://dblp.org}\n}\n```\n"
  },
  {
    "path": "tensorflow_examples/models/densenet/__init__.py",
    "content": "# Copyright 2017 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n"
  },
  {
    "path": "tensorflow_examples/models/densenet/densenet.py",
    "content": "# Copyright 2017 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Densely Connected Convolutional Networks.\n\nReference [\nDensely Connected Convolutional Networks](https://arxiv.org/abs/1608.06993)\n\n\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow as tf\n\nl2 = tf.keras.regularizers.l2\n\n\ndef calc_from_depth(depth, num_blocks, bottleneck):\n  \"\"\"Calculate number of layers in each block from the depth.\n\n  Args:\n    depth: Depth of model\n    num_blocks: Number of dense blocks\n    bottleneck: If True, num_layers will be halved\n\n  Returns:\n    Number of layers in each block as a list\n\n  Raises:\n    ValueError: If depth or num_blocks is None and num_blocks is not 3.\n  \"\"\"\n\n  if depth is None or num_blocks is None:\n    raise ValueError(\"For 'from_depth' mode, you need to specify the depth \"\n                     \"and number of blocks.\")\n\n  if num_blocks != 3:\n    raise ValueError(\n        \"Number of blocks must be 3 if mode is 'from_depth'.\")\n\n  if (depth - 4) % 3 == 0:\n    num_layers = (depth - 4) / 3\n    if bottleneck:\n      num_layers //= 2\n    return [num_layers] * num_blocks\n  else:\n    raise ValueError(\"Depth must be 3N+4 if mode is 'from_depth'.\")\n\n\ndef calc_from_list(depth, num_blocks, layers_per_block):\n  \"\"\"Calculate number of layers in each block.\n\n  Args:\n    depth: Depth of model\n    num_blocks: Number of dense blocks\n    layers_per_block: Number of layers per block as a list or tuple\n\n  Returns:\n    Number of layers in each block as a list\n\n  Raises:\n    ValueError: If depth or num_blocks is not None and\n                layers_per_block is None or not a list or tuple\n  \"\"\"\n  if depth is not None or num_blocks is not None:\n    raise ValueError(\"You don't have to specify the depth and number of \"\n                     \"blocks when mode is 'from_list'\")\n\n  if layers_per_block is None or not isinstance(\n      layers_per_block, list) or not isinstance(layers_per_block, tuple):\n    raise ValueError(\"You must pass list or tuple when using 'from_list' mode.\")\n\n  if isinstance(layers_per_block, list) or isinstance(layers_per_block, tuple):\n    return list(layers_per_block)\n\n\ndef calc_from_integer(depth, num_blocks, layers_per_block):\n  \"\"\"Calculate number of layers in each block.\n\n  Args:\n    depth: Depth of model\n    num_blocks: Number of dense blocks\n    layers_per_block: Number of layers per block as an integer.\n\n  Returns:\n    Number of layers in each block as a list\n\n  Raises:\n    ValueError: If depth is not None and\n                num_blocks is None or layer_per_block is not an integer.\n  \"\"\"\n  if depth is not None:\n    raise ValueError(\"You don't have to specify the depth \"\n                     \"when mode is 'from_integer'\")\n\n  if num_blocks is None or not isinstance(layers_per_block, int):\n    raise ValueError(\"You must pass number of blocks or an integer to \"\n                     \"layers in each block\")\n\n  return [layers_per_block] * num_blocks\n\n\nclass ConvBlock(tf.keras.Model):\n  \"\"\"Convolutional Block consisting of (batchnorm->relu->conv).\n\n  Arguments:\n    num_filters: number of filters passed to a convolutional layer.\n    data_format: \"channels_first\" or \"channels_last\"\n    bottleneck: if True, then a 1x1 Conv is performed followed by 3x3 Conv.\n    weight_decay: weight decay\n    dropout_rate: dropout rate.\n  \"\"\"\n\n  def __init__(self, num_filters, data_format, bottleneck, weight_decay=1e-4,\n               dropout_rate=0):\n    super(ConvBlock, self).__init__()\n    self.bottleneck = bottleneck\n\n    axis = -1 if data_format == \"channels_last\" else 1\n    inter_filter = num_filters * 4\n    # don't forget to set use_bias=False when using batchnorm\n    self.conv2 = tf.keras.layers.Conv2D(num_filters,\n                                        (3, 3),\n                                        padding=\"same\",\n                                        use_bias=False,\n                                        data_format=data_format,\n                                        kernel_initializer=\"he_normal\",\n                                        kernel_regularizer=l2(weight_decay))\n    self.batchnorm1 = tf.keras.layers.BatchNormalization(axis=axis)\n    self.dropout = tf.keras.layers.Dropout(dropout_rate)\n\n    if self.bottleneck:\n      self.conv1 = tf.keras.layers.Conv2D(inter_filter,\n                                          (1, 1),\n                                          padding=\"same\",\n                                          use_bias=False,\n                                          data_format=data_format,\n                                          kernel_initializer=\"he_normal\",\n                                          kernel_regularizer=l2(weight_decay))\n      self.batchnorm2 = tf.keras.layers.BatchNormalization(axis=axis)\n\n  def call(self, x, training=True):\n    output = self.batchnorm1(x, training=training)\n\n    if self.bottleneck:\n      output = self.conv1(tf.nn.relu(output))\n      output = self.batchnorm2(output, training=training)\n\n    output = self.conv2(tf.nn.relu(output))\n    output = self.dropout(output, training=training)\n\n    return output\n\n\nclass TransitionBlock(tf.keras.Model):\n  \"\"\"Transition Block to reduce the number of features.\n\n  Arguments:\n    num_filters: number of filters passed to a convolutional layer.\n    data_format: \"channels_first\" or \"channels_last\"\n    weight_decay: weight decay\n    dropout_rate: dropout rate.\n  \"\"\"\n\n  def __init__(self, num_filters, data_format,\n               weight_decay=1e-4, dropout_rate=0):\n    super(TransitionBlock, self).__init__()\n    axis = -1 if data_format == \"channels_last\" else 1\n\n    self.batchnorm = tf.keras.layers.BatchNormalization(axis=axis)\n    self.conv = tf.keras.layers.Conv2D(num_filters,\n                                       (1, 1),\n                                       padding=\"same\",\n                                       use_bias=False,\n                                       data_format=data_format,\n                                       kernel_initializer=\"he_normal\",\n                                       kernel_regularizer=l2(weight_decay))\n    self.avg_pool = tf.keras.layers.AveragePooling2D(data_format=data_format)\n\n  def call(self, x, training=True):\n    output = self.batchnorm(x, training=training)\n    output = self.conv(tf.nn.relu(output))\n    output = self.avg_pool(output)\n    return output\n\n\nclass DenseBlock(tf.keras.Model):\n  \"\"\"Dense Block.\n\n  It consists of ConvBlocks where each block's output is concatenated\n  with its input.\n\n  Arguments:\n    num_layers: Number of layers in each block.\n    growth_rate: number of filters to add per conv block.\n    data_format: \"channels_first\" or \"channels_last\"\n    bottleneck: boolean, that decides which part of ConvBlock to call.\n    weight_decay: weight decay\n    dropout_rate: dropout rate.\n  \"\"\"\n\n  def __init__(self, num_layers, growth_rate, data_format, bottleneck,\n               weight_decay=1e-4, dropout_rate=0):\n    super(DenseBlock, self).__init__()\n    self.num_layers = num_layers\n    self.axis = -1 if data_format == \"channels_last\" else 1\n\n    self.blocks = []\n    for _ in range(int(self.num_layers)):\n      self.blocks.append(ConvBlock(growth_rate,\n                                   data_format,\n                                   bottleneck,\n                                   weight_decay,\n                                   dropout_rate))\n\n  def call(self, x, training=True):\n    for i in range(int(self.num_layers)):\n      output = self.blocks[i](x, training=training)\n      x = tf.concat([x, output], axis=self.axis)\n\n    return x\n\n\nclass DenseNet(tf.keras.Model):\n  \"\"\"Creating the Densenet Architecture.\n\n  Arguments:\n    mode: mode could be:\n        - from_depth: num_layers_in_each_block will be calculated from the depth\n                      and number of blocks.\n        - from_list: pass num_layers_in_each_block as a list. depth and number\n                     of blocks should not be specified\n        - from_integer: pass num_layers_in_each_block as an integer. Number of\n                        layers in each block will be calculated using\n                        num_of_blocks * num_layers_in_each_block\n    depth_of_model: number of layers in the model.\n    growth_rate: number of filters to add per conv block.\n    num_of_blocks: number of dense blocks.\n    output_classes: number of output classes.\n    num_layers_in_each_block: number of layers in each block.\n                              If -1, then we calculate this by (depth-3)/4.\n                              If positive integer, then it is used as the\n                                number of layers per block.\n                              If list or tuple, then this list is used directly.\n    data_format: \"channels_first\" or \"channels_last\"\n    bottleneck: boolean, to decide which part of conv block to call.\n    compression: reducing the number of inputs(filters) to the transition block.\n    weight_decay: weight decay\n    rate: dropout rate.\n    pool_initial: If True add a 7x7 conv with stride 2 followed by 3x3 maxpool\n                  else, do a 3x3 conv with stride 1.\n    include_top: If true, GlobalAveragePooling Layer and Dense layer are\n                 included.\n  \"\"\"\n\n  def __init__(self, mode, growth_rate, output_classes, depth_of_model=None,\n               num_of_blocks=None, num_layers_in_each_block=None,\n               data_format=\"channels_last\", bottleneck=True, compression=0.5,\n               weight_decay=1e-4, dropout_rate=0., pool_initial=False,\n               include_top=True):\n    super(DenseNet, self).__init__()\n    self.mode = mode\n    self.depth_of_model = depth_of_model\n    self.growth_rate = growth_rate\n    self.num_of_blocks = num_of_blocks\n    self.output_classes = output_classes\n    self.num_layers_in_each_block = num_layers_in_each_block\n    self.data_format = data_format\n    self.bottleneck = bottleneck\n    self.compression = compression\n    self.weight_decay = weight_decay\n    self.dropout_rate = dropout_rate\n    self.pool_initial = pool_initial\n    self.include_top = include_top\n\n    # deciding number of layers in each block\n    if mode == \"from_depth\":\n      self.num_layers_in_each_block = calc_from_depth(\n          self.depth_of_model, self.num_of_blocks, self.bottleneck)\n    elif mode == \"from_list\":\n      self.num_layers_in_each_block = calc_from_list(\n          self.depth_of_model, self.num_of_blocks,\n          self.num_layers_in_each_block)\n    elif mode == \"from_integer\":\n      self.num_layers_in_each_block = calc_from_integer(\n          self.depth_of_model, self.num_of_blocks,\n          self.num_layers_in_each_block)\n\n    axis = -1 if self.data_format == \"channels_last\" else 1\n\n    # setting the filters and stride of the initial conv layer.\n    if self.pool_initial:\n      init_filters = (7, 7)\n      stride = (2, 2)\n    else:\n      init_filters = (3, 3)\n      stride = (1, 1)\n\n    self.num_filters = 2 * self.growth_rate\n\n    # first conv and pool layer\n    self.conv1 = tf.keras.layers.Conv2D(self.num_filters,\n                                        init_filters,\n                                        strides=stride,\n                                        padding=\"same\",\n                                        use_bias=False,\n                                        data_format=self.data_format,\n                                        kernel_initializer=\"he_normal\",\n                                        kernel_regularizer=l2(\n                                            self.weight_decay))\n    if self.pool_initial:\n      self.pool1 = tf.keras.layers.MaxPooling2D(pool_size=(3, 3),\n                                                strides=(2, 2),\n                                                padding=\"same\",\n                                                data_format=self.data_format)\n      self.batchnorm1 = tf.keras.layers.BatchNormalization(axis=axis)\n\n    self.batchnorm2 = tf.keras.layers.BatchNormalization(axis=axis)\n\n    # calculating the number of filters after each block\n    num_filters_after_each_block = [self.num_filters]\n    for i in range(1, self.num_of_blocks):\n      temp_num_filters = num_filters_after_each_block[i-1] + (\n          self.growth_rate * self.num_layers_in_each_block[i-1])\n      # using compression to reduce the number of inputs to the\n      # transition block\n      temp_num_filters = int(temp_num_filters * compression)\n      num_filters_after_each_block.append(temp_num_filters)\n\n    # dense block initialization\n    self.dense_blocks = []\n    self.transition_blocks = []\n    for i in range(self.num_of_blocks):\n      self.dense_blocks.append(DenseBlock(self.num_layers_in_each_block[i],\n                                          self.growth_rate,\n                                          self.data_format,\n                                          self.bottleneck,\n                                          self.weight_decay,\n                                          self.dropout_rate))\n      if i+1 < self.num_of_blocks:\n        self.transition_blocks.append(\n            TransitionBlock(num_filters_after_each_block[i+1],\n                            self.data_format,\n                            self.weight_decay,\n                            self.dropout_rate))\n\n    # last pooling and fc layer\n    if self.include_top:\n      self.last_pool = tf.keras.layers.GlobalAveragePooling2D(\n          data_format=self.data_format)\n      self.classifier = tf.keras.layers.Dense(self.output_classes)\n\n  def call(self, x, training=True):\n    output = self.conv1(x)\n\n    if self.pool_initial:\n      output = self.batchnorm1(output, training=training)\n      output = tf.nn.relu(output)\n      output = self.pool1(output)\n\n    for i in range(self.num_of_blocks - 1):\n      output = self.dense_blocks[i](output, training=training)\n      output = self.transition_blocks[i](output, training=training)\n\n    output = self.dense_blocks[\n        self.num_of_blocks - 1](output, training=training)\n    output = self.batchnorm2(output, training=training)\n    output = tf.nn.relu(output)\n\n    if self.include_top:\n      output = self.last_pool(output)\n      output = self.classifier(output)\n\n    return output\n"
  },
  {
    "path": "tensorflow_examples/models/densenet/densenet_distributed_test.py",
    "content": "# Copyright 2017 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Densely Connected Convolutional Networks.\n\nReference [\nDensely Connected Convolutional Networks](https://arxiv.org/abs/1608.06993)\n\n\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport time\nimport tensorflow as tf\nfrom tensorflow_examples.models.densenet import distributed_train\nfrom tensorflow_examples.models.densenet import utils\n\n\nclass DenseNetDistributedBenchmark(tf.test.Benchmark):\n\n  def __init__(self, output_dir=None, **kwargs):\n    self.output_dir = output_dir\n\n  def benchmark_with_function_custom_loops(self):\n    kwargs = utils.get_cifar10_kwargs()\n    self._run_and_report_benchmark(**kwargs)\n\n  def benchmark_with_function_custom_loops_300_epochs_2_gpus(self):\n    kwargs = utils.get_cifar10_kwargs()\n    kwargs.update({'epochs': 300, 'data_format': 'channels_first',\n                   'bottleneck': False, 'compression': 1., 'num_gpu': 2,\n                   'batch_size': 128})\n\n    self._run_and_report_benchmark(**kwargs)\n\n  def benchmark_with_function_custom_loops_300_epochs_8_gpus(self):\n    kwargs = utils.get_cifar10_kwargs()\n    kwargs.update({'epochs': 300, 'data_format': 'channels_first',\n                   'bottleneck': False, 'compression': 1., 'num_gpu': 8,\n                   'batch_size': 512})\n\n    self._run_and_report_benchmark(**kwargs)\n\n  def _run_and_report_benchmark(self, top_1_min=.944, top_1_max=.949, **kwargs):\n    \"\"\"Run the benchmark and report metrics.report.\n\n    Args:\n      top_1_min: Min value for top_1 accuracy.  Default range is SOTA.\n      top_1_max: Max value for top_1 accuracy.\n      **kwargs: All args passed to the test.\n    \"\"\"\n    start_time_sec = time.time()\n    train_loss, train_acc, _, test_acc = distributed_train.main(**kwargs)\n    wall_time_sec = time.time() - start_time_sec\n\n    metrics = []\n    metrics.append({'name': 'accuracy_top_1',\n                    'value': test_acc,\n                    'min_value': top_1_min,\n                    'max_value': top_1_max})\n\n    metrics.append({'name': 'training_accuracy_top_1',\n                    'value': train_acc})\n\n    metrics.append({'name': 'train_loss',\n                    'value': train_loss})\n\n    self.report_benchmark(wall_time=wall_time_sec, metrics=metrics)\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/models/densenet/densenet_test.py",
    "content": "# Copyright 2017 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Densely Connected Convolutional Networks.\n\nReference [\nDensely Connected Convolutional Networks](https://arxiv.org/abs/1608.06993)\n\n\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport time\nimport tensorflow as tf\nfrom tensorflow_examples.models.densenet import densenet\nfrom tensorflow_examples.models.densenet import train\nfrom tensorflow_examples.models.densenet import utils\n\n\ndef create_sample_dataset(batch_size):\n  input_image = tf.random.uniform((32, 32, 3))\n  label = tf.zeros((1,))\n  dataset = tf.data.Dataset.from_tensors(\n      (input_image, label)).batch(batch_size)\n  return dataset\n\n\nclass DensenetTest(tf.test.TestCase):\n\n  def test_one_epoch_with_function_custom_loop(self):\n    epochs = 1\n    enable_function = True\n    depth_of_model = 7\n    growth_rate = 2\n    num_of_blocks = 3\n    output_classes = 10\n    mode = 'from_depth'\n    data_format = 'channels_last'\n\n    train_dataset = create_sample_dataset(batch_size=1)\n    test_dataset = create_sample_dataset(batch_size=1)\n\n    model = densenet.DenseNet(\n        mode, growth_rate, output_classes, depth_of_model, num_of_blocks,\n        data_format)\n    train_obj = train.Train(epochs, enable_function, model)\n    train_obj.custom_loop(train_dataset, test_dataset)\n\n  def test_one_epoch_with_keras_fit(self):\n    epochs = 1\n    enable_function = True\n    depth_of_model = 7\n    growth_rate = 2\n    num_of_blocks = 3\n    output_classes = 10\n    mode = 'from_depth'\n    data_format = 'channels_last'\n\n    train_dataset = create_sample_dataset(batch_size=1)\n    test_dataset = create_sample_dataset(batch_size=1)\n\n    model = densenet.DenseNet(\n        mode, growth_rate, output_classes, depth_of_model, num_of_blocks,\n        data_format)\n    train_obj = train.Train(epochs, enable_function, model)\n    train_obj.keras_fit(train_dataset, test_dataset)\n\n\nclass DenseNetBenchmark(tf.test.Benchmark):\n\n  def __init__(self, output_dir=None, **kwargs):\n    self.output_dir = output_dir\n\n  def benchmark_with_function_custom_loops(self):\n    kwargs = utils.get_cifar10_kwargs()\n    self._run_and_report_benchmark(**kwargs)\n\n  def benchmark_without_function_custom_loops(self):\n    kwargs = utils.get_cifar10_kwargs()\n    kwargs.update({'enable_function': False})\n    self._run_and_report_benchmark(**kwargs)\n\n  def benchmark_with_keras_fit(self):\n    kwargs = utils.get_cifar10_kwargs()\n    kwargs.update({'train_mode': 'keras_fit'})\n    self._run_and_report_benchmark(**kwargs)\n\n  def benchmark_with_function_custom_loops_300_epochs(self):\n    kwargs = utils.get_cifar10_kwargs()\n    kwargs.update({'epochs': 300, 'data_format': 'channels_first',\n                   'bottleneck': False, 'compression': 1.})\n    self._run_and_report_benchmark(**kwargs)\n\n  def benchmark_with_keras_fit_300_epochs(self):\n    kwargs = utils.get_cifar10_kwargs()\n    kwargs.update({'epochs': 300, 'data_format': 'channels_first',\n                   'train_mode': 'keras_fit', 'bottleneck': False,\n                   'compression': 1.})\n    self._run_and_report_benchmark(**kwargs)\n\n  def _run_and_report_benchmark(self, **kwargs):\n    \"\"\"Run the benchmark and report metrics.report.\n\n    Args:\n      **kwargs: All args passed to the test.\n    \"\"\"\n    start_time_sec = time.time()\n    train_loss, train_acc, _, test_acc = train.main(**kwargs)\n    wall_time_sec = time.time() - start_time_sec\n\n    metrics = []\n    metrics.append({'name': 'accuracy_top_1',\n                    'value': test_acc,\n                    'min_value': .944,\n                    'max_value': .949})\n\n    metrics.append({'name': 'training_accuracy_top_1',\n                    'value': train_acc})\n\n    metrics.append({'name': 'train_loss',\n                    'value': train_loss})\n\n    self.report_benchmark(wall_time=wall_time_sec, metrics=metrics)\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/models/densenet/distributed_train.py",
    "content": "# Copyright 2017 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Densenet Training.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nfrom absl import app\nfrom absl import flags\nimport tensorflow as tf\nfrom tensorflow_examples.models.densenet import densenet\nfrom tensorflow_examples.models.densenet import utils\n\nFLAGS = flags.FLAGS\n\n# if additional flags are needed, define it here.\nflags.DEFINE_integer('num_gpu', 1, 'Number of GPUs to use')\n\n\nclass Train(object):\n  \"\"\"Train class.\n\n  Args:\n    epochs: Number of epochs\n    enable_function: If True, wraps the train_step and test_step in tf.function\n    model: Densenet model.\n    batch_size: Batch size.\n    strategy: Distribution strategy in use.\n  \"\"\"\n\n  def __init__(self, epochs, enable_function, model, batch_size, strategy):\n    self.epochs = epochs\n    self.batch_size = batch_size\n    self.enable_function = enable_function\n    self.strategy = strategy\n    self.loss_object = tf.keras.losses.SparseCategoricalCrossentropy(\n        from_logits=True, reduction=tf.keras.losses.Reduction.NONE)\n    self.optimizer = tf.keras.optimizers.SGD(learning_rate=0.1,\n                                             momentum=0.9, nesterov=True)\n    self.train_acc_metric = tf.keras.metrics.SparseCategoricalAccuracy(\n        name='train_accuracy')\n    self.test_acc_metric = tf.keras.metrics.SparseCategoricalAccuracy(\n        name='test_accuracy')\n    self.test_loss_metric = tf.keras.metrics.Sum(name='test_loss')\n    self.model = model\n\n  def decay(self, epoch):\n    if epoch < 150:\n      return 0.1\n    if epoch >= 150 and epoch < 225:\n      return 0.01\n    if epoch >= 225:\n      return 0.001\n\n  def compute_loss(self, label, predictions):\n    loss = tf.reduce_sum(self.loss_object(label, predictions)) * (\n        1. / self.batch_size)\n    loss += (sum(self.model.losses) * 1. / self.strategy.num_replicas_in_sync)\n    return loss\n\n  def train_step(self, inputs):\n    \"\"\"One train step.\n\n    Args:\n      inputs: one batch input.\n\n    Returns:\n      loss: Scaled loss.\n    \"\"\"\n\n    image, label = inputs\n    with tf.GradientTape() as tape:\n      predictions = self.model(image, training=True)\n      loss = self.compute_loss(label, predictions)\n    gradients = tape.gradient(loss, self.model.trainable_variables)\n    self.optimizer.apply_gradients(zip(gradients,\n                                       self.model.trainable_variables))\n\n    self.train_acc_metric(label, predictions)\n    return loss\n\n  def test_step(self, inputs):\n    \"\"\"One test step.\n\n    Args:\n      inputs: one batch input.\n    \"\"\"\n    image, label = inputs\n    predictions = self.model(image, training=False)\n\n    unscaled_test_loss = self.loss_object(label, predictions) + sum(\n        self.model.losses)\n\n    self.test_acc_metric(label, predictions)\n    self.test_loss_metric(unscaled_test_loss)\n\n  def custom_loop(self, train_dist_dataset, test_dist_dataset,\n                  strategy):\n    \"\"\"Custom training and testing loop.\n\n    Args:\n      train_dist_dataset: Training dataset created using strategy.\n      test_dist_dataset: Testing dataset created using strategy.\n      strategy: Distribution strategy.\n\n    Returns:\n      train_loss, train_accuracy, test_loss, test_accuracy\n    \"\"\"\n\n    def distributed_train_epoch(ds):\n      total_loss = 0.0\n      num_train_batches = 0.0\n      for one_batch in ds:\n        per_replica_loss = strategy.run(self.train_step, args=(one_batch,))\n        total_loss += strategy.reduce(\n            tf.distribute.ReduceOp.SUM, per_replica_loss, axis=None)\n        num_train_batches += 1\n      return total_loss, num_train_batches\n\n    def distributed_test_epoch(ds):\n      num_test_batches = 0.0\n      for one_batch in ds:\n        strategy.run(self.test_step, args=(one_batch,))\n        num_test_batches += 1\n      return self.test_loss_metric.result(), num_test_batches\n\n    if self.enable_function:\n      distributed_train_epoch = tf.function(distributed_train_epoch)\n      distributed_test_epoch = tf.function(distributed_test_epoch)\n\n    for epoch in range(self.epochs):\n      self.optimizer.learning_rate = self.decay(epoch)\n\n      train_total_loss, num_train_batches = distributed_train_epoch(\n          train_dist_dataset)\n      test_total_loss, num_test_batches = distributed_test_epoch(\n          test_dist_dataset)\n\n      template = ('Epoch: {}, Train Loss: {}, Train Accuracy: {}, '\n                  'Test Loss: {}, Test Accuracy: {}')\n\n      print(\n          template.format(epoch,\n                          train_total_loss / num_train_batches,\n                          self.train_acc_metric.result(),\n                          test_total_loss / num_test_batches,\n                          self.test_acc_metric.result()))\n\n      if epoch != self.epochs - 1:\n        self.train_acc_metric.reset_states()\n        self.test_acc_metric.reset_states()\n\n    return (train_total_loss / num_train_batches,\n            self.train_acc_metric.result().numpy(),\n            test_total_loss / num_test_batches,\n            self.test_acc_metric.result().numpy())\n\n\ndef run_main(argv):\n  \"\"\"Passes the flags to main.\n\n  Args:\n    argv: argv\n  \"\"\"\n  del argv\n  kwargs = utils.flags_dict()\n  kwargs.update({'num_gpu': FLAGS.num_gpu})\n  main(**kwargs)\n\n\ndef main(epochs,\n         enable_function,\n         buffer_size,\n         batch_size,\n         mode,\n         growth_rate,\n         output_classes,\n         depth_of_model=None,\n         num_of_blocks=None,\n         num_layers_in_each_block=None,\n         data_format='channels_last',\n         bottleneck=True,\n         compression=0.5,\n         weight_decay=1e-4,\n         dropout_rate=0.,\n         pool_initial=False,\n         include_top=True,\n         train_mode='custom_loop',\n         data_dir=None,\n         num_gpu=1):\n\n  devices = ['/device:GPU:{}'.format(i) for i in range(num_gpu)]\n  strategy = tf.distribute.MirroredStrategy(devices)\n\n  train_dataset, test_dataset, _ = utils.create_dataset(\n      buffer_size, batch_size, data_format, data_dir)\n\n  with strategy.scope():\n    model = densenet.DenseNet(\n        mode, growth_rate, output_classes, depth_of_model, num_of_blocks,\n        num_layers_in_each_block, data_format, bottleneck, compression,\n        weight_decay, dropout_rate, pool_initial, include_top)\n\n    trainer = Train(epochs, enable_function, model, batch_size, strategy)\n\n    train_dist_dataset = strategy.experimental_distribute_dataset(train_dataset)\n    test_dist_dataset = strategy.experimental_distribute_dataset(test_dataset)\n\n    print('Training...')\n    if train_mode == 'custom_loop':\n      return trainer.custom_loop(train_dist_dataset,\n                                 test_dist_dataset,\n                                 strategy)\n    elif train_mode == 'keras_fit':\n      raise ValueError(\n          '`tf.distribute.Strategy` does not support subclassed models yet.')\n    else:\n      raise ValueError(\n          'Please enter either \"keras_fit\" or \"custom_loop\" as the argument.')\n\n\nif __name__ == '__main__':\n  utils.define_densenet_flags()\n  app.run(run_main)\n"
  },
  {
    "path": "tensorflow_examples/models/densenet/train.py",
    "content": "# Copyright 2017 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Densenet Training.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nfrom absl import app\nimport tensorflow as tf\nfrom tensorflow_examples.models.densenet import densenet\nfrom tensorflow_examples.models.densenet import utils\n\n\nclass Train(object):\n  \"\"\"Train class.\n\n  Args:\n    epochs: Number of epochs\n    enable_function: If True, wraps the train_step and test_step in tf.function\n    model: Densenet model.\n  \"\"\"\n\n  def __init__(self, epochs, enable_function, model):\n    self.epochs = epochs\n    self.enable_function = enable_function\n    self.autotune = tf.data.experimental.AUTOTUNE\n    self.loss_object = tf.keras.losses.SparseCategoricalCrossentropy(\n        from_logits=True)\n    self.optimizer = tf.keras.optimizers.SGD(learning_rate=0.1,\n                                             momentum=0.9, nesterov=True)\n    self.train_loss_metric = tf.keras.metrics.Mean(name='train_loss')\n    self.train_acc_metric = tf.keras.metrics.SparseCategoricalAccuracy(\n        name='train_accuracy')\n    self.test_loss_metric = tf.keras.metrics.Mean(name='test_loss')\n    self.test_acc_metric = tf.keras.metrics.SparseCategoricalAccuracy(\n        name='test_accuracy')\n    self.model = model\n\n  def decay(self, epoch):\n    if epoch < 150:\n      return 0.1\n    if epoch >= 150 and epoch < 225:\n      return 0.01\n    if epoch >= 225:\n      return 0.001\n\n  def keras_fit(self, train_dataset, test_dataset):\n    self.model.compile(\n        optimizer=self.optimizer, loss=self.loss_object, metrics=['accuracy'])\n    history = self.model.fit(\n        train_dataset, epochs=self.epochs, validation_data=test_dataset,\n        verbose=2, callbacks=[tf.keras.callbacks.LearningRateScheduler(\n            self.decay)])\n    return (history.history['loss'][-1],\n            history.history['accuracy'][-1],\n            history.history['val_loss'][-1],\n            history.history['val_accuracy'][-1])\n\n  def train_step(self, image, label):\n    \"\"\"One train step.\n\n    Args:\n      image: Batch of images.\n      label: corresponding label for the batch of images.\n    \"\"\"\n\n    with tf.GradientTape() as tape:\n      predictions = self.model(image, training=True)\n      loss = self.loss_object(label, predictions)\n      loss += sum(self.model.losses)\n    gradients = tape.gradient(loss, self.model.trainable_variables)\n    self.optimizer.apply_gradients(\n        zip(gradients, self.model.trainable_variables))\n\n    self.train_loss_metric(loss)\n    self.train_acc_metric(label, predictions)\n\n  def test_step(self, image, label):\n    \"\"\"One test step.\n\n    Args:\n      image: Batch of images.\n      label: corresponding label for the batch of images.\n    \"\"\"\n\n    predictions = self.model(image, training=False)\n    loss = self.loss_object(label, predictions)\n\n    self.test_loss_metric(loss)\n    self.test_acc_metric(label, predictions)\n\n  def custom_loop(self, train_dataset, test_dataset):\n    \"\"\"Custom training and testing loop.\n\n    Args:\n      train_dataset: Training dataset\n      test_dataset: Testing dataset\n\n    Returns:\n      train_loss, train_accuracy, test_loss, test_accuracy\n    \"\"\"\n    if self.enable_function:\n      self.train_step = tf.function(self.train_step)\n      self.test_step = tf.function(self.test_step)\n\n    for epoch in range(self.epochs):\n      self.optimizer.learning_rate = self.decay(epoch)\n\n      for image, label in train_dataset:\n        self.train_step(image, label)\n\n      for test_image, test_label in test_dataset:\n        self.test_step(test_image, test_label)\n\n      template = ('Epoch: {}, Train Loss: {}, Train Accuracy: {}, '\n                  'Test Loss: {}, Test Accuracy: {}')\n\n      print(\n          template.format(epoch, self.train_loss_metric.result(),\n                          self.train_acc_metric.result(),\n                          self.test_loss_metric.result(),\n                          self.test_acc_metric.result()))\n\n      if epoch != self.epochs - 1:\n        self.train_loss_metric.reset_states()\n        self.train_acc_metric.reset_states()\n        self.test_loss_metric.reset_states()\n        self.test_acc_metric.reset_states()\n\n    return (self.train_loss_metric.result().numpy(),\n            self.train_acc_metric.result().numpy(),\n            self.test_loss_metric.result().numpy(),\n            self.test_acc_metric.result().numpy())\n\n\ndef run_main(argv):\n  \"\"\"Passes the flags to main.\n\n  Args:\n    argv: argv\n  \"\"\"\n  del argv\n  kwargs = utils.flags_dict()\n  main(**kwargs)\n\n\ndef main(epochs,\n         enable_function,\n         buffer_size,\n         batch_size,\n         mode,\n         growth_rate,\n         output_classes,\n         depth_of_model=None,\n         num_of_blocks=None,\n         num_layers_in_each_block=None,\n         data_format='channels_last',\n         bottleneck=True,\n         compression=0.5,\n         weight_decay=1e-4,\n         dropout_rate=0.,\n         pool_initial=False,\n         include_top=True,\n         train_mode='custom_loop',\n         data_dir=None):\n\n  model = densenet.DenseNet(mode, growth_rate, output_classes, depth_of_model,\n                            num_of_blocks, num_layers_in_each_block,\n                            data_format, bottleneck, compression, weight_decay,\n                            dropout_rate, pool_initial, include_top)\n  train_obj = Train(epochs, enable_function, model)\n  train_dataset, test_dataset, _ = utils.create_dataset(\n      buffer_size, batch_size, data_format, data_dir)\n\n  print('Training...')\n  if train_mode == 'custom_loop':\n    return train_obj.custom_loop(train_dataset, test_dataset)\n  elif train_mode == 'keras_fit':\n    return train_obj.keras_fit(train_dataset, test_dataset)\n\n\nif __name__ == '__main__':\n  utils.define_densenet_flags()\n  app.run(run_main)\n"
  },
  {
    "path": "tensorflow_examples/models/densenet/utils.py",
    "content": "# Copyright 2017 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Densenet utils.\n\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nfrom absl import flags\nimport tensorflow as tf\nimport tensorflow_datasets as tfds\n\nFLAGS = flags.FLAGS\n\n\ndef define_densenet_flags():\n  \"\"\"Defining all the necessary flags.\"\"\"\n  flags.DEFINE_integer('buffer_size', 50000, 'Shuffle buffer size')\n  flags.DEFINE_integer('batch_size', 64, 'Batch Size')\n  flags.DEFINE_integer('epochs', 1, 'Number of epochs')\n  flags.DEFINE_boolean('enable_function', True, 'Enable Function?')\n  flags.DEFINE_string('data_dir', None, 'Directory to store the dataset')\n  flags.DEFINE_string('mode', 'from_depth', 'Deciding how to build the model')\n  flags.DEFINE_integer('depth_of_model', 7, 'Number of layers in the model')\n  flags.DEFINE_integer('growth_rate', 12, 'Filters to add per dense block')\n  flags.DEFINE_integer('num_of_blocks', 3, 'Number of dense blocks')\n  flags.DEFINE_integer('output_classes', 10, 'Number of classes in the dataset')\n  flags.DEFINE_integer('num_layers_in_each_block', -1,\n                       'Number of layers in each dense block')\n  flags.DEFINE_string('data_format', 'channels_last',\n                      'channels_last or channels_first')\n  flags.DEFINE_boolean('bottleneck', True,\n                       'Add bottleneck blocks between layers')\n  flags.DEFINE_float(\n      'compression', 0.5,\n      'reducing the number of inputs(filters) to the transition block.')\n  flags.DEFINE_float('weight_decay', 1e-4, 'weight decay')\n  flags.DEFINE_float('dropout_rate', 0., 'dropout rate')\n  flags.DEFINE_boolean(\n      'pool_initial', False,\n      'If True add a conv => maxpool block at the start. Used for Imagenet')\n  flags.DEFINE_boolean('include_top', True, 'Include the classifier layer')\n  flags.DEFINE_string('train_mode', 'custom_loop',\n                      'Use either \"keras_fit\" or \"custom_loop\"')\n\nAUTOTUNE = tf.data.experimental.AUTOTUNE\n\nCIFAR_MEAN = [125.3, 123.0, 113.9]\nCIFAR_STD = [63.0, 62.1, 66.7]\n\nHEIGHT = 32\nWIDTH = 32\n\n\nclass Preprocess(object):\n  \"\"\"Preprocess images.\n\n  Args:\n    data_format: channels_first or channels_last\n  \"\"\"\n\n  def __init__(self, data_format, train):\n    self._data_format = data_format\n    self._train = train\n\n  def __call__(self, image, label):\n    image = tf.cast(image, tf.float32)\n\n    if self._train:\n      image = tf.image.random_flip_left_right(image)\n      image = self.random_jitter(image)\n\n    image = (image - CIFAR_MEAN) / CIFAR_STD\n\n    if self._data_format == 'channels_first':\n      image = tf.transpose(image, [2, 0, 1])\n\n    return image, label\n\n  def random_jitter(self, image):\n    # add 4 pixels on each side; image_size == (36 x 36)\n    image = tf.image.resize_with_crop_or_pad(\n        image, HEIGHT + 8, WIDTH + 8)\n\n    image = tf.image.random_crop(image, size=[HEIGHT, WIDTH, 3])\n\n    return image\n\n\ndef create_dataset(buffer_size, batch_size, data_format, data_dir=None):\n  \"\"\"Creates a tf.data Dataset.\n\n  Args:\n    buffer_size: Shuffle buffer size.\n    batch_size: Batch size\n    data_format: channels_first or channels_last\n    data_dir: directory to store the dataset.\n\n  Returns:\n    train dataset, test dataset, metadata\n  \"\"\"\n\n  preprocess_train = Preprocess(data_format, train=True)\n  preprocess_test = Preprocess(data_format, train=False)\n\n  train_dataset, metadata = tfds.load(\n      'cifar10',\n      split='train',\n      data_dir=data_dir,\n      as_supervised=True,\n      shuffle_files=True,\n      with_info=True)\n  test_dataset = tfds.load(\n      'cifar10', split='test', data_dir=data_dir, as_supervised=True)\n\n  train_dataset = train_dataset.map(\n      preprocess_train, num_parallel_calls=AUTOTUNE)\n  train_dataset = train_dataset.cache()\n  train_dataset = train_dataset.shuffle(buffer_size).batch(batch_size)\n  train_dataset = train_dataset.prefetch(buffer_size=AUTOTUNE)\n\n  test_dataset = test_dataset.map(preprocess_test, num_parallel_calls=AUTOTUNE)\n  test_dataset = test_dataset.cache().batch(batch_size)\n  test_dataset = test_dataset.prefetch(buffer_size=AUTOTUNE)\n\n  return train_dataset, test_dataset, metadata\n\n\ndef flags_dict():\n  \"\"\"Define the flags.\n\n  Returns:\n    Command line arguments as Flags.\n  \"\"\"\n\n  kwargs = {\n      'epochs': FLAGS.epochs,\n      'enable_function': FLAGS.enable_function,\n      'buffer_size': FLAGS.buffer_size,\n      'batch_size': FLAGS.batch_size,\n      'mode': FLAGS.mode,\n      'depth_of_model': FLAGS.depth_of_model,\n      'growth_rate': FLAGS.growth_rate,\n      'num_of_blocks': FLAGS.num_of_blocks,\n      'output_classes': FLAGS.output_classes,\n      'num_layers_in_each_block': FLAGS.num_layers_in_each_block,\n      'data_format': FLAGS.data_format,\n      'bottleneck': FLAGS.bottleneck,\n      'compression': FLAGS.compression,\n      'weight_decay': FLAGS.weight_decay,\n      'dropout_rate': FLAGS.dropout_rate,\n      'pool_initial': FLAGS.pool_initial,\n      'include_top': FLAGS.include_top,\n      'train_mode': FLAGS.train_mode\n  }\n  return kwargs\n\n\ndef get_cifar10_kwargs():\n  return {'epochs': 1, 'enable_function': True, 'buffer_size': 50000,\n          'batch_size': 64, 'depth_of_model': 40, 'growth_rate': 12,\n          'num_of_blocks': 3, 'output_classes': 10, 'mode': 'from_depth',\n          'data_format': 'channels_last', 'dropout_rate': 0.}\n"
  },
  {
    "path": "tensorflow_examples/models/nmt_with_attention/__init__.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"See the tutorial: https://www.tensorflow.org/tutorials/text/nmt_with_attention\"\n"
  },
  {
    "path": "tensorflow_examples/models/nmt_with_attention/distributed_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Tests for distributed nmt_with_attention.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport time\nimport tensorflow as tf\nfrom tensorflow_examples.models.nmt_with_attention import distributed_train\nfrom tensorflow_examples.models.nmt_with_attention import utils\n\n\nclass NmtDistributedTest(tf.test.TestCase):\n\n  def test_one_epoch_multi_device(self):\n    if tf.test.is_gpu_available():\n      print('Using 2 virtual GPUs.')\n      device = tf.config.experimental.list_physical_devices('GPU')[0]\n      tf.config.experimental.set_virtual_device_configuration(\n          device, [\n              tf.config.experimental.VirtualDeviceConfiguration(\n                  memory_limit=8192),\n              tf.config.experimental.VirtualDeviceConfiguration(\n                  memory_limit=8192)\n          ])\n\n    kwargs = utils.get_common_kwargs()\n    kwargs.update({\n        'epochs': 1,\n        'batch_size': 16,\n        'num_examples': 10,\n        'embedding_dim': 4,\n        'enc_units': 4,\n        'dec_units': 4\n    })\n\n    distributed_train.main(**kwargs)\n\n\nclass NmtDistributedBenchmark(tf.test.Benchmark):\n\n  def __init__(self, output_dir=None, **kwargs):\n    self.output_dir = output_dir\n\n  def benchmark_one_epoch_1_gpu(self):\n    kwargs = utils.get_common_kwargs()\n    kwargs.update({'enable_function': False})\n    self._run_and_report_benchmark(**kwargs)\n\n  def benchmark_one_epoch_1_gpu_function(self):\n    kwargs = utils.get_common_kwargs()\n    self._run_and_report_benchmark(**kwargs)\n\n  def benchmark_ten_epochs_2_gpus(self):\n    kwargs = utils.get_common_kwargs()\n    kwargs.update({'epochs': 10, 'batch_size': 128})\n    self._run_and_report_benchmark(**kwargs)\n\n  def _run_and_report_benchmark(self, **kwargs):\n    start_time_sec = time.time()\n    train_loss, test_loss = distributed_train.main(**kwargs)\n    wall_time_sec = time.time() - start_time_sec\n\n    extras = {'train_loss': train_loss,\n              'test_loss': test_loss}\n\n    self.report_benchmark(\n        wall_time=wall_time_sec, extras=extras)\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/models/nmt_with_attention/distributed_train.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Distributed Train.\n\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nfrom absl import app\nfrom absl import flags\n\nimport tensorflow as tf\nfrom tensorflow_examples.models.nmt_with_attention import nmt\nfrom tensorflow_examples.models.nmt_with_attention import utils\nfrom tensorflow_examples.models.nmt_with_attention.train import Train\n\n\nFLAGS = flags.FLAGS\n\n\nclass DistributedTrain(Train):\n  \"\"\"Distributed Train class.\n\n  Attributes:\n    epochs: Number of epochs.\n    enable_function: Decorate function with tf.function.\n    encoder: Encoder.\n    decoder: Decoder.\n    inp_lang: Input language tokenizer.\n    targ_lang: Target language tokenizer.\n    batch_size: Batch size.\n    per_replica_batch_size: Batch size per replica for sync replicas.\n  \"\"\"\n\n  def __init__(self, epochs, enable_function, encoder, decoder, inp_lang,\n               targ_lang, batch_size, per_replica_batch_size):\n    Train.__init__(\n        self, epochs, enable_function, encoder, decoder, inp_lang, targ_lang,\n        batch_size, per_replica_batch_size)\n\n  def training_loop(self, train_iterator, test_iterator,\n                    num_train_steps_per_epoch, num_test_steps_per_epoch,\n                    strategy):\n    \"\"\"Custom training and testing loop.\n\n    Args:\n      train_iterator: Training iterator created using strategy\n      test_iterator: Testing iterator created using strategy\n      num_train_steps_per_epoch: number of training steps in an epoch.\n      num_test_steps_per_epoch: number of test steps in an epoch.\n      strategy: Distribution strategy\n\n    Returns:\n      train_loss, test_loss\n    \"\"\"\n\n    # this code is expected to change.\n    def distributed_train():\n      return strategy.experimental_run(self.train_step, train_iterator)\n\n    def distributed_test():\n      return strategy.experimental_run(self.test_step, test_iterator)\n\n    if self.enable_function:\n      distributed_train = tf.function(distributed_train)\n      distributed_test = tf.function(distributed_test)\n\n    template = 'Epoch: {}, Train Loss: {}, Test Loss: {}'\n\n    for epoch in range(self.epochs):\n      self.train_loss_metric.reset_states()\n      self.test_loss_metric.reset_states()\n\n      train_iterator.initialize()\n      for _ in range(num_train_steps_per_epoch):\n        distributed_train()\n\n      test_iterator.initialize()\n      for _ in range(num_test_steps_per_epoch):\n        distributed_test()\n\n      print (template.format(epoch,\n                             self.train_loss_metric.result().numpy(),\n                             self.test_loss_metric.result().numpy()))\n\n    return (self.train_loss_metric.result().numpy(),\n            self.test_loss_metric.result().numpy())\n\n\ndef run_main(argv):\n  del argv\n  kwargs = utils.flags_dict()\n  main(**kwargs)\n\n\ndef main(epochs, enable_function, buffer_size, batch_size, download_path,\n         num_examples=70000, embedding_dim=256, enc_units=1024, dec_units=1024):\n\n  strategy = tf.distribute.MirroredStrategy()\n  num_replicas = strategy.num_replicas_in_sync\n\n  file_path = utils.download(download_path)\n  train_ds, test_ds, inp_lang, targ_lang = utils.create_dataset(\n      file_path, num_examples, buffer_size, batch_size)\n\n  with strategy.scope():\n    vocab_inp_size = len(inp_lang.word_index) + 1\n    vocab_tar_size = len(targ_lang.word_index) + 1\n\n    num_train_steps_per_epoch = train_ds.cardinality()\n    num_test_steps_per_epoch = test_ds.cardinality()\n\n    train_iterator = strategy.make_dataset_iterator(train_ds)\n    test_iterator = strategy.make_dataset_iterator(test_ds)\n\n    local_batch_size, remainder = divmod(batch_size, num_replicas)\n\n    template = ('Batch size ({}) must be divisible by the '\n                'number of replicas ({})')\n    if remainder:\n      raise ValueError(template.format(batch_size, num_replicas))\n\n    encoder = nmt.Encoder(vocab_inp_size, embedding_dim, enc_units,\n                          local_batch_size)\n    decoder = nmt.Decoder(vocab_tar_size, embedding_dim, dec_units)\n\n    train_obj = DistributedTrain(epochs, enable_function, encoder, decoder,\n                                 inp_lang, targ_lang, batch_size,\n                                 local_batch_size)\n    print ('Training ...')\n    return train_obj.training_loop(train_iterator,\n                                   test_iterator,\n                                   num_train_steps_per_epoch,\n                                   num_test_steps_per_epoch,\n                                   strategy)\n\nif __name__ == '__main__':\n  utils.nmt_flags()\n  app.run(run_main)\n"
  },
  {
    "path": "tensorflow_examples/models/nmt_with_attention/nmt.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Neural Machine Translation with Attention.\n\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow as tf\n\n\nclass Encoder(tf.keras.Model):\n  \"\"\"Encoder.\n\n  Args:\n    vocab_size: Vocabulary size.\n    embedding_dim: Embedding dimension.\n    enc_units: Number of encoder units.\n    batch_sz: Batch size.\n  \"\"\"\n\n  def __init__(self, vocab_size, embedding_dim, enc_units, batch_sz):\n    super(Encoder, self).__init__()\n    self.batch_sz = batch_sz\n    self.enc_units = enc_units\n    self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)\n    self.gru = tf.keras.layers.GRU(self.enc_units,\n                                   return_sequences=True,\n                                   return_state=True,\n                                   recurrent_initializer='glorot_uniform')\n\n  def call(self, x, hidden):\n    x = self.embedding(x)\n    output, state = self.gru(x, initial_state=hidden)\n    return output, state\n\n  def initialize_hidden_state(self):\n    return tf.zeros((self.batch_sz, self.enc_units))\n\n\nclass BahdanauAttention(tf.keras.Model):\n  \"\"\"Bahdanau Attention.\n\n  Args:\n    units: Number of dense units.\n  \"\"\"\n\n  def __init__(self, units):\n    super(BahdanauAttention, self).__init__()\n    self.w1 = tf.keras.layers.Dense(units)\n    self.w2 = tf.keras.layers.Dense(units)\n    self.v = tf.keras.layers.Dense(1)\n\n  def call(self, query, values):\n    # hidden shape == (batch_size, hidden size)\n    # hidden_with_time_axis shape == (batch_size, 1, hidden size)\n    # we are doing this to perform addition to calculate the score\n    hidden_with_time_axis = tf.expand_dims(query, 1)\n\n    # score shape == (batch_size, max_length, hidden_size)\n    score = self.v(tf.nn.tanh(\n        self.w1(values) + self.w2(hidden_with_time_axis)))\n\n    # attention_weights shape == (batch_size, max_length, 1)\n    # we get 1 at the last axis because we are applying score to self.V\n    attention_weights = tf.nn.softmax(score, axis=1)\n\n    # context_vector shape after sum == (batch_size, hidden_size)\n    context_vector = attention_weights * values\n    context_vector = tf.reduce_sum(context_vector, axis=1)\n\n    return context_vector, attention_weights\n\n\nclass Decoder(tf.keras.Model):\n  \"\"\"Decoder.\n\n  Args:\n    vocab_size: Vocabulary size.\n    embedding_dim: Embedding dimension.\n    dec_units: Number of decoder units.\n  \"\"\"\n\n  def __init__(self, vocab_size, embedding_dim, dec_units):\n    super(Decoder, self).__init__()\n    self.dec_units = dec_units\n    self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)\n    self.gru = tf.keras.layers.GRU(self.dec_units,\n                                   return_sequences=True,\n                                   return_state=True,\n                                   recurrent_initializer='glorot_uniform')\n    self.fc = tf.keras.layers.Dense(vocab_size)\n\n    # used for attention\n    self.attention = BahdanauAttention(self.dec_units)\n\n  def call(self, x, hidden, enc_output):\n    # enc_output shape == (batch_size, max_length, hidden_size)\n    context_vector, attention_weights = self.attention(hidden, enc_output)\n\n    # x shape after passing through embedding == (batch_size, 1, embedding_dim)\n    x = self.embedding(x)\n\n    # x shape after concatenation == (batch_size, 1, embedding_dim+hidden_size)\n    x = tf.concat([tf.expand_dims(context_vector, 1), x], axis=-1)\n\n    # passing the concatenated vector to the GRU\n    output, state = self.gru(x)\n\n    # output shape == (batch_size * 1, hidden_size)\n    output = tf.reshape(output, (-1, output.shape[2]))\n\n    # output shape == (batch_size, vocab)\n    x = self.fc(output)\n\n    return x, state, attention_weights\n"
  },
  {
    "path": "tensorflow_examples/models/nmt_with_attention/nmt_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Tests for nmt_with_attention.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport time\n\nimport tensorflow as tf\nfrom tensorflow_examples.models.nmt_with_attention import train\nfrom tensorflow_examples.models.nmt_with_attention import utils\n\n\nclass NmtTest(tf.test.TestCase):\n\n  def test_one_epoch(self):\n    num_examples = 10\n    buffer_size = 10\n    batch_size = 1\n    embedding_dim = 4\n    enc_units = 4\n    dec_units = 4\n    epochs = 1\n\n    train.main(epochs, True, buffer_size, batch_size, 'datasets', num_examples,\n               embedding_dim, enc_units, dec_units)\n\n\nclass NmtBenchmark(tf.test.Benchmark):\n\n  def __init__(self, output_dir=None, **kwargs):\n    self.output_dir = output_dir\n\n  def benchmark_one_epoch(self):\n    kwargs = utils.get_common_kwargs()\n    self._run_and_report_benchmark(**kwargs)\n\n  def benchmark_ten_epochs(self):\n    kwargs = utils.get_common_kwargs()\n    kwargs.update({'epochs': 10})\n    self._run_and_report_benchmark(**kwargs)\n\n  def _run_and_report_benchmark(self, **kwargs):\n    start_time_sec = time.time()\n    train_loss, test_loss = train.main(**kwargs)\n    wall_time_sec = time.time() - start_time_sec\n\n    extras = {'train_loss': train_loss,\n              'test_loss': test_loss}\n\n    self.report_benchmark(\n        wall_time=wall_time_sec, extras=extras)\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/models/nmt_with_attention/train.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Train.\n\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nfrom absl import app\n\nimport tensorflow as tf\nfrom tensorflow_examples.models.nmt_with_attention import nmt\nfrom tensorflow_examples.models.nmt_with_attention import utils\n\n\nclass Train(object):\n  \"\"\"Train class.\n\n  Attributes:\n    epochs: Number of epochs.\n    enable_function: Decorate function with tf.function.\n    encoder: Encoder.\n    decoder: Decoder.\n    inp_lang: Input language tokenizer.\n    targ_lang: Target language tokenizer.\n    batch_size: Batch size.\n    per_replica_batch_size: Batch size per replica for sync replicas. Same as\n      batch_size for non distributed training.\n    optimizer: Optimizer.\n    loss_object: Object of the loss class.\n    train_loss_metric: Mean metric to keep track of the train loss value.\n    test_loss_metric: Mean metric to keep track of the test loss value.\n  \"\"\"\n\n  def __init__(self, epochs, enable_function, encoder, decoder, inp_lang,\n               targ_lang, batch_size, per_replica_batch_size):\n    self.epochs = epochs\n    self.enable_function = enable_function\n    self.encoder = encoder\n    self.decoder = decoder\n    self.inp_lang = inp_lang\n    self.targ_lang = targ_lang\n    self.batch_size = batch_size\n    self.per_replica_batch_size = per_replica_batch_size\n    self.optimizer = tf.keras.optimizers.Adam()\n    self.loss_object = tf.keras.losses.SparseCategoricalCrossentropy(\n        from_logits=True, reduction=tf.keras.losses.Reduction.NONE)\n    self.train_loss_metric = tf.keras.metrics.Mean(name='train_loss')\n    self.test_loss_metric = tf.keras.metrics.Mean(name='test_loss')\n\n  def loss_function(self, real, pred):\n    mask = tf.math.logical_not(tf.math.equal(real, 0))\n    loss_ = self.loss_object(real, pred)\n\n    mask = tf.cast(mask, dtype=loss_.dtype)\n    loss_ *= mask\n\n    return tf.reduce_sum(loss_) * 1. / self.batch_size\n\n  def train_step(self, inputs):\n    \"\"\"One train step.\n\n    Args:\n      inputs: tuple of input tensor, target tensor.\n    \"\"\"\n\n    loss = 0\n    enc_hidden = self.encoder.initialize_hidden_state()\n\n    inp, targ = inputs\n\n    with tf.GradientTape() as tape:\n      enc_output, enc_hidden = self.encoder(inp, enc_hidden)\n      dec_hidden = enc_hidden\n      dec_input = tf.expand_dims(\n          [self.targ_lang.word_index['<start>']] * self.per_replica_batch_size,\n          1)\n\n      for t in range(1, targ.shape[1]):\n        # passing enc_output to the decoder\n        predictions, dec_hidden, _ = self.decoder(\n            dec_input, dec_hidden, enc_output)\n        loss += self.loss_function(targ[:, t], predictions)\n        # using teacher forcing\n        dec_input = tf.expand_dims(targ[:, t], 1)\n\n    batch_loss = (loss / int(targ.shape[1]))\n    variables = (self.encoder.trainable_variables +\n                 self.decoder.trainable_variables)\n    gradients = tape.gradient(loss, variables)\n    self.optimizer.apply_gradients(zip(gradients, variables))\n\n    self.train_loss_metric(batch_loss)\n\n  def test_step(self, inputs_test):\n    \"\"\"One test step.\n\n    Args:\n      inputs_test: tuple of input tensor, target tensor.\n    \"\"\"\n\n    loss = 0\n    enc_hidden = self.encoder.initialize_hidden_state()\n\n    inp_test, targ_test = inputs_test\n\n    enc_output, enc_hidden = self.encoder(inp_test, enc_hidden)\n    dec_hidden = enc_hidden\n    dec_input = tf.expand_dims(\n        [self.targ_lang.word_index['<start>']] * self.per_replica_batch_size,\n        1)\n\n    for t in range(1, targ_test.shape[1]):\n      predictions, dec_hidden, _ = self.decoder(\n          dec_input, dec_hidden, enc_output)\n      loss += self.loss_function(targ_test[:, t], predictions)\n\n      prediction_id = tf.argmax(predictions, axis=1)\n      # passing the predictions back to the model as the input.\n      dec_input = tf.expand_dims(prediction_id, 1)\n\n    batch_loss = (loss / int(targ_test.shape[1]))\n\n    self.test_loss_metric(batch_loss)\n\n  def training_loop(self, train_ds, test_ds):\n    \"\"\"Custom training and testing loop.\n\n    Args:\n      train_ds: Training dataset\n      test_ds: Testing dataset\n\n    Returns:\n      train_loss, test_loss\n    \"\"\"\n\n    if self.enable_function:\n      self.train_step = tf.function(self.train_step)\n      self.test_step = tf.function(self.test_step)\n\n    template = 'Epoch: {}, Train Loss: {}, Test Loss: {}'\n\n    for epoch in range(self.epochs):\n      self.train_loss_metric.reset_states()\n      self.test_loss_metric.reset_states()\n\n      for inp, targ in train_ds:\n        self.train_step((inp, targ))\n\n      for inp_test, targ_test in test_ds:\n        self.test_step((inp_test, targ_test))\n\n      print (template.format(epoch,\n                             self.train_loss_metric.result().numpy(),\n                             self.test_loss_metric.result().numpy()))\n\n    return (self.train_loss_metric.result().numpy(),\n            self.test_loss_metric.result().numpy())\n\n\ndef run_main(argv):\n  del argv\n  kwargs = utils.flags_dict()\n  main(**kwargs)\n\n\ndef main(epochs, enable_function, buffer_size, batch_size, download_path,\n         num_examples=70000, embedding_dim=256, enc_units=1024, dec_units=1024):\n  file_path = utils.download(download_path)\n  train_ds, test_ds, inp_lang, targ_lang = utils.create_dataset(\n      file_path, num_examples, buffer_size, batch_size)\n  vocab_inp_size = len(inp_lang.word_index) + 1\n  vocab_tar_size = len(targ_lang.word_index) + 1\n\n  encoder = nmt.Encoder(vocab_inp_size, embedding_dim, enc_units, batch_size)\n  decoder = nmt.Decoder(vocab_tar_size, embedding_dim, dec_units)\n\n  train_obj = Train(epochs, enable_function, encoder, decoder,\n                    inp_lang, targ_lang, batch_size, batch_size)\n  print ('Training ...')\n  return train_obj.training_loop(train_ds, test_ds)\n\nif __name__ == '__main__':\n  utils.nmt_flags()\n  app.run(run_main)\n"
  },
  {
    "path": "tensorflow_examples/models/nmt_with_attention/utils.py",
    "content": "# coding=utf-8\n\n# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Utils.\n\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport io\nimport os\nimport re\nimport unicodedata\nfrom absl import flags\nfrom sklearn.model_selection import train_test_split\n\nimport tensorflow as tf\n\nFLAGS = flags.FLAGS\n\n_URL = 'http://storage.googleapis.com/download.tensorflow.org/data/spa-eng.zip'\n\n\ndef nmt_flags():\n  flags.DEFINE_string('download_path', 'datasets', 'Download folder')\n  flags.DEFINE_integer('buffer_size', 70000, 'Shuffle buffer size')\n  flags.DEFINE_integer('batch_size', 64, 'Batch Size')\n  flags.DEFINE_integer('epochs', 1, 'Number of epochs')\n  flags.DEFINE_integer('embedding_dim', 256, 'Embedding dimension')\n  flags.DEFINE_integer('enc_units', 1024, 'Encoder GRU units')\n  flags.DEFINE_integer('dec_units', 1024, 'Decoder GRU units')\n  flags.DEFINE_boolean('enable_function', True, 'Enable Function?')\n  flags.DEFINE_integer('num_examples', 70000, 'Number of examples from dataset')\n\n\ndef download(download_path):\n  path_to_zip = tf.keras.utils.get_file(\n      'spa-eng.zip', origin=_URL, cache_subdir=download_path,\n      extract=True)\n  path_to_file = os.path.join(os.path.dirname(path_to_zip), 'spa-eng/spa.txt')\n\n  return path_to_file\n\n\ndef unicode_to_ascii(s):\n  return ''.join(c for c in unicodedata.normalize('NFD', s)\n                 if unicodedata.category(c) != 'Mn')\n\n\ndef preprocess_sentence(w):\n  \"\"\"Preprocessing words in a sentence.\n\n  Args:\n    w: Word.\n\n  Returns:\n    Preprocessed words.\n  \"\"\"\n\n  w = unicode_to_ascii(w.lower().strip())\n\n  # creating a space between a word and the punctuation following it\n  w = re.sub(r'([?.!,¿])', r' \\1 ', w)\n  w = re.sub(r'[\" \"]+', ' ', w)\n\n  # replacing everything with space except (a-z, A-Z, \".\", \"?\", \"!\", \",\")\n  w = re.sub(r'[^a-zA-Z?.!,¿]+', ' ', w)\n\n  w = w.rstrip().strip()\n\n  # adding a start and an end token to the sentence\n  # so that the model know when to start and stop predicting.\n  w = '<start> ' + w + ' <end>'\n  return w\n\n\ndef create_word_pairs(path, num_examples):\n  lines = io.open(path, encoding='UTF-8').read().strip().split('\\n')\n\n  word_pairs = [[preprocess_sentence(w) for w in l.split('\\t')]  # pylint: disable=g-complex-comprehension\n                for l in lines[:num_examples]]\n\n  return zip(*word_pairs)\n\n\ndef max_length(tensor):\n  return max(len(t) for t in tensor)\n\n\ndef tokenize(lang):\n  \"\"\"Tokenize the languages.\n\n  Args:\n    lang: Language to be tokenized.\n\n  Returns:\n    tensor: Tensors generated after tokenization.\n    lang_tokenizer: tokenizer.\n  \"\"\"\n\n  lang_tokenizer = tf.keras.preprocessing.text.Tokenizer(\n      filters='')\n  lang_tokenizer.fit_on_texts(lang)\n\n  tensor = lang_tokenizer.texts_to_sequences(lang)\n\n  tensor = tf.keras.preprocessing.sequence.pad_sequences(tensor,\n                                                         padding='post')\n\n  return tensor, lang_tokenizer\n\n\ndef load_dataset(path, num_examples):\n  # creating cleaned input, output pairs\n  targ_lang, inp_lang = create_word_pairs(path, num_examples)\n\n  input_tensor, inp_lang_tokenizer = tokenize(inp_lang)\n  target_tensor, targ_lang_tokenizer = tokenize(targ_lang)\n\n  return input_tensor, target_tensor, inp_lang_tokenizer, targ_lang_tokenizer\n\n\ndef create_dataset(path_to_file, num_examples, buffer_size, batch_size):\n  \"\"\"Create a tf.data Dataset.\n\n  Args:\n    path_to_file: Path to the file to load the text from.\n    num_examples: Number of examples to sample.\n    buffer_size: Shuffle buffer size.\n    batch_size: Batch size.\n\n  Returns:\n    train_dataset: Training dataset.\n    test_dataset: Test dataset.\n    inp_lang: Input language tokenizer.\n    targ_lang: Target language tokenizer.\n  \"\"\"\n\n  input_tensor, target_tensor, inp_lang, targ_lang = load_dataset(\n      path_to_file, num_examples)\n\n  # Creating training and validation sets using an 80-20 split\n  inp_train, inp_val, target_train, target_val = train_test_split(\n      input_tensor, target_tensor, test_size=0.2)\n\n  # Create a tf.data dataset\n  train_dataset = tf.data.Dataset.from_tensor_slices(\n      (inp_train, target_train)).shuffle(buffer_size)\n  train_dataset = train_dataset.batch(batch_size, drop_remainder=True)\n\n  test_dataset = tf.data.Dataset.from_tensor_slices((inp_val, target_val))\n  test_dataset = test_dataset.batch(batch_size, drop_remainder=True)\n\n  return train_dataset, test_dataset, inp_lang, targ_lang\n\n\ndef get_common_kwargs():\n  return {'epochs': 1, 'enable_function': True, 'buffer_size': 70000,\n          'batch_size': 64, 'download_path': 'datasets'}\n\n\ndef flags_dict():\n  \"\"\"Define the flags.\n\n  Returns:\n    Command line arguments as Flags.\n  \"\"\"\n\n  kwargs = {\n      'epochs': FLAGS.epochs,\n      'enable_function': FLAGS.enable_function,\n      'buffer_size': FLAGS.buffer_size,\n      'batch_size': FLAGS.batch_size,\n      'download_path': FLAGS.download_path,\n      'num_examples': FLAGS.num_examples,\n      'embedding_dim': FLAGS.embedding_dim,\n      'enc_units': FLAGS.enc_units,\n      'dec_units': FLAGS.dec_units\n  }\n\n  return kwargs\n"
  },
  {
    "path": "tensorflow_examples/models/pix2pix/__init__.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n"
  },
  {
    "path": "tensorflow_examples/models/pix2pix/data_download.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Download facades data.\n\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nfrom absl import app\nfrom absl import flags\n\nimport tensorflow as tf\n\nFLAGS = flags.FLAGS\n\nflags.DEFINE_string('download_path', 'datasets', 'Download folder')\n\n_URL = 'https://people.eecs.berkeley.edu/~tinghuiz/projects/pix2pix/datasets/facades.tar.gz'\n\n\ndef _main(argv):\n  del argv\n  download_path = FLAGS.download_path\n  main(download_path)\n\n\ndef main(download_path):\n  path_to_zip = tf.keras.utils.get_file(\n      'facades.tar.gz', cache_subdir=download_path,\n      origin=_URL, extract=True)\n\n  path_to_folder = os.path.join(os.path.dirname(path_to_zip), 'facades/')\n\n  return path_to_folder\n\nif __name__ == '__main__':\n  app.run(_main)\n"
  },
  {
    "path": "tensorflow_examples/models/pix2pix/pix2pix.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Pix2pix.\n\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nimport time\nfrom absl import app\nfrom absl import flags\n\nimport tensorflow as tf\n\nFLAGS = flags.FLAGS\n\nflags.DEFINE_integer('buffer_size', 400, 'Shuffle buffer size')\nflags.DEFINE_integer('batch_size', 1, 'Batch Size')\nflags.DEFINE_integer('epochs', 1, 'Number of epochs')\nflags.DEFINE_string('path', None, 'Path to the data folder')\nflags.DEFINE_boolean('enable_function', True, 'Enable Function?')\n\nIMG_WIDTH = 256\nIMG_HEIGHT = 256\nAUTOTUNE = tf.data.experimental.AUTOTUNE\n\n\ndef load(image_file):\n  \"\"\"Loads the image and generates input and target image.\n\n  Args:\n    image_file: .jpeg file\n\n  Returns:\n    Input image, target image\n  \"\"\"\n  image = tf.io.read_file(image_file)\n  image = tf.image.decode_jpeg(image)\n\n  w = tf.shape(image)[1]\n\n  w = w // 2\n  real_image = image[:, :w, :]\n  input_image = image[:, w:, :]\n\n  input_image = tf.cast(input_image, tf.float32)\n  real_image = tf.cast(real_image, tf.float32)\n\n  return input_image, real_image\n\n\ndef resize(input_image, real_image, height, width):\n  input_image = tf.image.resize(input_image, [height, width],\n                                method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)\n  real_image = tf.image.resize(real_image, [height, width],\n                               method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)\n\n  return input_image, real_image\n\n\ndef random_crop(input_image, real_image):\n  stacked_image = tf.stack([input_image, real_image], axis=0)\n  cropped_image = tf.image.random_crop(\n      stacked_image, size=[2, IMG_HEIGHT, IMG_WIDTH, 3])\n\n  return cropped_image[0], cropped_image[1]\n\n\ndef normalize(input_image, real_image):\n  input_image = (input_image / 127.5) - 1\n  real_image = (real_image / 127.5) - 1\n\n  return input_image, real_image\n\n\n@tf.function\ndef random_jitter(input_image, real_image):\n  \"\"\"Random jittering.\n\n  Resizes to 286 x 286 and then randomly crops to IMG_HEIGHT x IMG_WIDTH.\n\n  Args:\n    input_image: Input Image\n    real_image: Real Image\n\n  Returns:\n    Input Image, real image\n  \"\"\"\n  # resizing to 286 x 286 x 3\n  input_image, real_image = resize(input_image, real_image, 286, 286)\n\n  # randomly cropping to 256 x 256 x 3\n  input_image, real_image = random_crop(input_image, real_image)\n\n  if tf.random.uniform(()) > 0.5:\n    # random mirroring\n    input_image = tf.image.flip_left_right(input_image)\n    real_image = tf.image.flip_left_right(real_image)\n\n  return input_image, real_image\n\n\ndef load_image_train(image_file):\n  input_image, real_image = load(image_file)\n  input_image, real_image = random_jitter(input_image, real_image)\n  input_image, real_image = normalize(input_image, real_image)\n\n  return input_image, real_image\n\n\ndef load_image_test(image_file):\n  input_image, real_image = load(image_file)\n  input_image, real_image = resize(input_image, real_image,\n                                   IMG_HEIGHT, IMG_WIDTH)\n  input_image, real_image = normalize(input_image, real_image)\n\n  return input_image, real_image\n\n\ndef create_dataset(path_to_train_images, path_to_test_images, buffer_size,\n                   batch_size):\n  \"\"\"Creates a tf.data Dataset.\n\n  Args:\n    path_to_train_images: Path to train images folder.\n    path_to_test_images: Path to test images folder.\n    buffer_size: Shuffle buffer size.\n    batch_size: Batch size\n\n  Returns:\n    train dataset, test dataset\n  \"\"\"\n  train_dataset = tf.data.Dataset.list_files(path_to_train_images)\n  train_dataset = train_dataset.shuffle(buffer_size)\n  train_dataset = train_dataset.map(\n      load_image_train, num_parallel_calls=AUTOTUNE)\n  train_dataset = train_dataset.batch(batch_size)\n\n  test_dataset = tf.data.Dataset.list_files(path_to_test_images)\n  test_dataset = test_dataset.map(\n      load_image_test, num_parallel_calls=AUTOTUNE)\n  test_dataset = test_dataset.batch(batch_size)\n\n  return train_dataset, test_dataset\n\n\nclass InstanceNormalization(tf.keras.layers.Layer):\n  \"\"\"Instance Normalization Layer (https://arxiv.org/abs/1607.08022).\"\"\"\n\n  def __init__(self, epsilon=1e-5):\n    super(InstanceNormalization, self).__init__()\n    self.epsilon = epsilon\n\n  def build(self, input_shape):\n    self.scale = self.add_weight(\n        name='scale',\n        shape=input_shape[-1:],\n        initializer=tf.random_normal_initializer(1., 0.02),\n        trainable=True)\n\n    self.offset = self.add_weight(\n        name='offset',\n        shape=input_shape[-1:],\n        initializer='zeros',\n        trainable=True)\n\n  def call(self, x):\n    mean, variance = tf.nn.moments(x, axes=[1, 2], keepdims=True)\n    inv = tf.math.rsqrt(variance + self.epsilon)\n    normalized = (x - mean) * inv\n    return self.scale * normalized + self.offset\n\n\ndef downsample(filters, size, norm_type='batchnorm', apply_norm=True):\n  \"\"\"Downsamples an input.\n\n  Conv2D => Batchnorm => LeakyRelu\n\n  Args:\n    filters: number of filters\n    size: filter size\n    norm_type: Normalization type; either 'batchnorm' or 'instancenorm'.\n    apply_norm: If True, adds the batchnorm layer\n\n  Returns:\n    Downsample Sequential Model\n  \"\"\"\n  initializer = tf.random_normal_initializer(0., 0.02)\n\n  result = tf.keras.Sequential()\n  result.add(\n      tf.keras.layers.Conv2D(filters, size, strides=2, padding='same',\n                             kernel_initializer=initializer, use_bias=False))\n\n  if apply_norm:\n    if norm_type.lower() == 'batchnorm':\n      result.add(tf.keras.layers.BatchNormalization())\n    elif norm_type.lower() == 'instancenorm':\n      result.add(InstanceNormalization())\n\n  result.add(tf.keras.layers.LeakyReLU())\n\n  return result\n\n\ndef upsample(filters, size, norm_type='batchnorm', apply_dropout=False):\n  \"\"\"Upsamples an input.\n\n  Conv2DTranspose => Batchnorm => Dropout => Relu\n\n  Args:\n    filters: number of filters\n    size: filter size\n    norm_type: Normalization type; either 'batchnorm' or 'instancenorm'.\n    apply_dropout: If True, adds the dropout layer\n\n  Returns:\n    Upsample Sequential Model\n  \"\"\"\n\n  initializer = tf.random_normal_initializer(0., 0.02)\n\n  result = tf.keras.Sequential()\n  result.add(\n      tf.keras.layers.Conv2DTranspose(filters, size, strides=2,\n                                      padding='same',\n                                      kernel_initializer=initializer,\n                                      use_bias=False))\n\n  if norm_type.lower() == 'batchnorm':\n    result.add(tf.keras.layers.BatchNormalization())\n  elif norm_type.lower() == 'instancenorm':\n    result.add(InstanceNormalization())\n\n  if apply_dropout:\n    result.add(tf.keras.layers.Dropout(0.5))\n\n  result.add(tf.keras.layers.ReLU())\n\n  return result\n\n\ndef unet_generator(output_channels, norm_type='batchnorm'):\n  \"\"\"Modified u-net generator model (https://arxiv.org/abs/1611.07004).\n\n  Args:\n    output_channels: Output channels\n    norm_type: Type of normalization. Either 'batchnorm' or 'instancenorm'.\n\n  Returns:\n    Generator model\n  \"\"\"\n\n  down_stack = [\n      downsample(64, 4, norm_type, apply_norm=False),  # (bs, 128, 128, 64)\n      downsample(128, 4, norm_type),  # (bs, 64, 64, 128)\n      downsample(256, 4, norm_type),  # (bs, 32, 32, 256)\n      downsample(512, 4, norm_type),  # (bs, 16, 16, 512)\n      downsample(512, 4, norm_type),  # (bs, 8, 8, 512)\n      downsample(512, 4, norm_type),  # (bs, 4, 4, 512)\n      downsample(512, 4, norm_type),  # (bs, 2, 2, 512)\n      downsample(512, 4, norm_type),  # (bs, 1, 1, 512)\n  ]\n\n  up_stack = [\n      upsample(512, 4, norm_type, apply_dropout=True),  # (bs, 2, 2, 1024)\n      upsample(512, 4, norm_type, apply_dropout=True),  # (bs, 4, 4, 1024)\n      upsample(512, 4, norm_type, apply_dropout=True),  # (bs, 8, 8, 1024)\n      upsample(512, 4, norm_type),  # (bs, 16, 16, 1024)\n      upsample(256, 4, norm_type),  # (bs, 32, 32, 512)\n      upsample(128, 4, norm_type),  # (bs, 64, 64, 256)\n      upsample(64, 4, norm_type),  # (bs, 128, 128, 128)\n  ]\n\n  initializer = tf.random_normal_initializer(0., 0.02)\n  last = tf.keras.layers.Conv2DTranspose(\n      output_channels, 4, strides=2,\n      padding='same', kernel_initializer=initializer,\n      activation='tanh')  # (bs, 256, 256, 3)\n\n  concat = tf.keras.layers.Concatenate()\n\n  inputs = tf.keras.layers.Input(shape=[None, None, 3])\n  x = inputs\n\n  # Downsampling through the model\n  skips = []\n  for down in down_stack:\n    x = down(x)\n    skips.append(x)\n\n  skips = reversed(skips[:-1])\n\n  # Upsampling and establishing the skip connections\n  for up, skip in zip(up_stack, skips):\n    x = up(x)\n    x = concat([x, skip])\n\n  x = last(x)\n\n  return tf.keras.Model(inputs=inputs, outputs=x)\n\n\ndef discriminator(norm_type='batchnorm', target=True):\n  \"\"\"PatchGan discriminator model (https://arxiv.org/abs/1611.07004).\n\n  Args:\n    norm_type: Type of normalization. Either 'batchnorm' or 'instancenorm'.\n    target: Bool, indicating whether target image is an input or not.\n\n  Returns:\n    Discriminator model\n  \"\"\"\n\n  initializer = tf.random_normal_initializer(0., 0.02)\n\n  inp = tf.keras.layers.Input(shape=[None, None, 3], name='input_image')\n  x = inp\n\n  if target:\n    tar = tf.keras.layers.Input(shape=[None, None, 3], name='target_image')\n    x = tf.keras.layers.concatenate([inp, tar])  # (bs, 256, 256, channels*2)\n\n  down1 = downsample(64, 4, norm_type, False)(x)  # (bs, 128, 128, 64)\n  down2 = downsample(128, 4, norm_type)(down1)  # (bs, 64, 64, 128)\n  down3 = downsample(256, 4, norm_type)(down2)  # (bs, 32, 32, 256)\n\n  zero_pad1 = tf.keras.layers.ZeroPadding2D()(down3)  # (bs, 34, 34, 256)\n  conv = tf.keras.layers.Conv2D(\n      512, 4, strides=1, kernel_initializer=initializer,\n      use_bias=False)(zero_pad1)  # (bs, 31, 31, 512)\n\n  if norm_type.lower() == 'batchnorm':\n    norm1 = tf.keras.layers.BatchNormalization()(conv)\n  elif norm_type.lower() == 'instancenorm':\n    norm1 = InstanceNormalization()(conv)\n\n  leaky_relu = tf.keras.layers.LeakyReLU()(norm1)\n\n  zero_pad2 = tf.keras.layers.ZeroPadding2D()(leaky_relu)  # (bs, 33, 33, 512)\n\n  last = tf.keras.layers.Conv2D(\n      1, 4, strides=1,\n      kernel_initializer=initializer)(zero_pad2)  # (bs, 30, 30, 1)\n\n  if target:\n    return tf.keras.Model(inputs=[inp, tar], outputs=last)\n  else:\n    return tf.keras.Model(inputs=inp, outputs=last)\n\n\ndef get_checkpoint_prefix():\n  checkpoint_dir = './training_checkpoints'\n  checkpoint_prefix = os.path.join(checkpoint_dir, 'ckpt')\n\n  return checkpoint_prefix\n\n\nclass Pix2pix(object):\n  \"\"\"Pix2pix class.\n\n  Args:\n    epochs: Number of epochs.\n    enable_function: If true, train step is decorated with tf.function.\n    buffer_size: Shuffle buffer size..\n    batch_size: Batch size.\n  \"\"\"\n\n  def __init__(self, epochs, enable_function):\n    self.epochs = epochs\n    self.enable_function = enable_function\n    self.lambda_value = 100\n    self.loss_object = tf.keras.losses.BinaryCrossentropy(from_logits=True)\n    self.generator_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)\n    self.discriminator_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)\n    self.generator = unet_generator(output_channels=3)\n    self.discriminator = discriminator()\n    self.checkpoint = tf.train.Checkpoint(\n        generator_optimizer=self.generator_optimizer,\n        discriminator_optimizer=self.discriminator_optimizer,\n        generator=self.generator,\n        discriminator=self.discriminator)\n\n  def discriminator_loss(self, disc_real_output, disc_generated_output):\n    real_loss = self.loss_object(\n        tf.ones_like(disc_real_output), disc_real_output)\n\n    generated_loss = self.loss_object(tf.zeros_like(\n        disc_generated_output), disc_generated_output)\n\n    total_disc_loss = real_loss + generated_loss\n\n    return total_disc_loss\n\n  def generator_loss(self, disc_generated_output, gen_output, target):\n    gan_loss = self.loss_object(tf.ones_like(\n        disc_generated_output), disc_generated_output)\n\n    # mean absolute error\n    l1_loss = tf.reduce_mean(tf.abs(target - gen_output))\n    total_gen_loss = gan_loss + (self.lambda_value * l1_loss)\n    return total_gen_loss\n\n  def train_step(self, input_image, target_image):\n    \"\"\"One train step over the generator and discriminator model.\n\n    Args:\n      input_image: Input Image.\n      target_image: Target image.\n\n    Returns:\n      generator loss, discriminator loss.\n    \"\"\"\n    with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:\n      gen_output = self.generator(input_image, training=True)\n\n      disc_real_output = self.discriminator(\n          [input_image, target_image], training=True)\n      disc_generated_output = self.discriminator(\n          [input_image, gen_output], training=True)\n\n      gen_loss = self.generator_loss(\n          disc_generated_output, gen_output, target_image)\n      disc_loss = self.discriminator_loss(\n          disc_real_output, disc_generated_output)\n\n    generator_gradients = gen_tape.gradient(\n        gen_loss, self.generator.trainable_variables)\n    discriminator_gradients = disc_tape.gradient(\n        disc_loss, self.discriminator.trainable_variables)\n\n    self.generator_optimizer.apply_gradients(zip(\n        generator_gradients, self.generator.trainable_variables))\n    self.discriminator_optimizer.apply_gradients(zip(\n        discriminator_gradients, self.discriminator.trainable_variables))\n\n    return gen_loss, disc_loss\n\n  def train(self, dataset, checkpoint_pr):\n    \"\"\"Train the GAN for x number of epochs.\n\n    Args:\n      dataset: train dataset.\n      checkpoint_pr: prefix in which the checkpoints are stored.\n\n    Returns:\n      Time for each epoch.\n    \"\"\"\n    time_list = []\n    if self.enable_function:\n      self.train_step = tf.function(self.train_step)\n\n    for epoch in range(self.epochs):\n      start_time = time.time()\n      for input_image, target_image in dataset:\n        gen_loss, disc_loss = self.train_step(input_image, target_image)\n\n      wall_time_sec = time.time() - start_time\n      time_list.append(wall_time_sec)\n\n      # saving (checkpoint) the model every 20 epochs\n      if (epoch + 1) % 20 == 0:\n        self.checkpoint.save(file_prefix=checkpoint_pr)\n\n      template = 'Epoch {}, Generator loss {}, Discriminator Loss {}'\n      print (template.format(epoch, gen_loss, disc_loss))\n\n    return time_list\n\n\ndef run_main(argv):\n  del argv\n  kwargs = {'epochs': FLAGS.epochs, 'enable_function': FLAGS.enable_function,\n            'path': FLAGS.path, 'buffer_size': FLAGS.buffer_size,\n            'batch_size': FLAGS.batch_size}\n  main(**kwargs)\n\n\ndef main(epochs, enable_function, path, buffer_size, batch_size):\n  path_to_folder = path\n\n  pix2pix_object = Pix2pix(epochs, enable_function)\n\n  train_dataset, _ = create_dataset(\n      os.path.join(path_to_folder, 'train/*.jpg'),\n      os.path.join(path_to_folder, 'test/*.jpg'),\n      buffer_size, batch_size)\n  checkpoint_pr = get_checkpoint_prefix()\n  print ('Training ...')\n  return pix2pix_object.train(train_dataset, checkpoint_pr)\n\n\nif __name__ == '__main__':\n  app.run(run_main)\n"
  },
  {
    "path": "tensorflow_examples/models/pix2pix/pix2pix_test.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Tests for Pix2Pix.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nfrom absl import flags\n\nimport tensorflow as tf\nfrom tensorflow_examples.models.pix2pix import data_download\nfrom tensorflow_examples.models.pix2pix import pix2pix\n\nFLAGS = flags.FLAGS\n\n\nclass Pix2PixTest(tf.test.TestCase):\n\n  def test_one_step_with_function(self):\n    epochs = 1\n    batch_size = 1\n    enable_function = True\n\n    input_image = tf.random.uniform((256, 256, 3))\n    target_image = tf.random.uniform((256, 256, 3))\n\n    train_dataset = tf.data.Dataset.from_tensors(\n        (input_image, target_image)).map(pix2pix.random_jitter).batch(\n            batch_size)\n    checkpoint_pr = pix2pix.get_checkpoint_prefix()\n\n    pix2pix_obj = pix2pix.Pix2pix(epochs, enable_function)\n    pix2pix_obj.train(train_dataset, checkpoint_pr)\n\n  def test_one_step_without_function(self):\n    epochs = 1\n    batch_size = 1\n    enable_function = False\n\n    input_image = tf.random.uniform((256, 256, 3))\n    target_image = tf.random.uniform((256, 256, 3))\n\n    train_dataset = tf.data.Dataset.from_tensors(\n        (input_image, target_image)).map(pix2pix.random_jitter).batch(\n            batch_size)\n\n    pix2pix_obj = pix2pix.Pix2pix(epochs, enable_function)\n\n    checkpoint_pr = pix2pix.get_checkpoint_prefix()\n    pix2pix_obj.train(train_dataset, checkpoint_pr)\n\n\nclass Pix2PixBenchmark(tf.test.Benchmark):\n\n  def __init__(self, output_dir=None, **kwargs):\n    self.output_dir = output_dir\n\n  def benchmark_with_function(self):\n    path = data_download.main(\"datasets\")\n    kwargs = {\"epochs\": 6, \"enable_function\": True, \"path\": path,\n              \"buffer_size\": 400, \"batch_size\": 1}\n    self._run_and_report_benchmark(**kwargs)\n\n  def benchmark_without_function(self):\n    path = data_download.main(\"datasets\")\n    kwargs = {\"epochs\": 6, \"enable_function\": False, \"path\": path,\n              \"buffer_size\": 400, \"batch_size\": 1}\n    self._run_and_report_benchmark(**kwargs)\n\n  def _run_and_report_benchmark(self, **kwargs):\n    time_list = pix2pix.main(**kwargs)\n    # 1st epoch is the warmup epoch hence skipping it for calculating time.\n    self.report_benchmark(wall_time=tf.reduce_mean(time_list[1:]))\n\nif __name__ == \"__main__\":\n  tf.test.main()\n"
  },
  {
    "path": "tensorflow_examples/profiling/imagenet_preprocessing_ineffecient_input_pipeline.py",
    "content": "# Copyright 2016 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"NOTE!\n\nThis is an unoptimized data input pipeline created by deoptimizing the input\npipeline for ResNet50, created for profiling purposes.\n\n---------------------------------------\nDO NOT USE FOR NON-PROFILING USE CASES\n---------------------------------------\n\nProvides utilities to preprocess images.\n\nTraining images are sampled using the provided bounding boxes, and subsequently\ncropped to the sampled bounding box. Images are additionally flipped randomly,\nthen resized to the target output size (without aspect-ratio preservation).\n\nImages used during evaluation are resized (with aspect-ratio preservation) and\ncentrally cropped.\n\nAll images undergo mean color subtraction.\n\nNote that these steps are colloquially referred to as \"ResNet preprocessing,\"\nand they differ from \"VGG preprocessing,\" which does not use bounding boxes\nand instead does an aspect-preserving resize followed by random crop during\ntraining. (These both differ from \"Inception preprocessing,\" which introduces\ncolor distortion steps.)\n\n\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nfrom absl import logging\nimport tensorflow as tf\n\nDEFAULT_IMAGE_SIZE = 224\nNUM_CHANNELS = 3\nNUM_CLASSES = 1001\n\nNUM_IMAGES = {\n    'train': 1281167,\n    'validation': 50000,\n}\n\n_NUM_TRAIN_FILES = 1024\n_SHUFFLE_BUFFER = 10000\n\n_R_MEAN = 123.68\n_G_MEAN = 116.78\n_B_MEAN = 103.94\nCHANNEL_MEANS = [_R_MEAN, _G_MEAN, _B_MEAN]\n\n# The lower bound for the smallest side of the image for aspect-preserving\n# resizing. For example, if an image is 500 x 1000, it will be resized to\n# _RESIZE_MIN x (_RESIZE_MIN * 2).\n_RESIZE_MIN = 256\n\n\ndef process_record_dataset(dataset,\n                           is_training,\n                           batch_size,\n                           shuffle_buffer,\n                           parse_record_fn,\n                           num_epochs=1,\n                           dtype=tf.float32,\n                           datasets_num_private_threads=None,\n                           drop_remainder=False,\n                           tf_data_experimental_slack=False):\n  \"\"\"Given a Dataset with raw records, return an iterator over the records.\n\n  Args:\n    dataset: A Dataset representing raw records\n    is_training: A boolean denoting whether the input is for training.\n    batch_size: The number of samples per batch.\n    shuffle_buffer: The buffer size to use when shuffling records. A larger\n      value results in better randomness, but smaller values reduce startup\n      time and use less memory.\n    parse_record_fn: A function that takes a raw record and returns the\n      corresponding (image, label) pair.\n    num_epochs: The number of epochs to repeat the dataset.\n    dtype: Data type to use for images/features.\n    datasets_num_private_threads: Number of threads for a private\n      threadpool created for all datasets computation.\n    drop_remainder: A boolean indicates whether to drop the remainder of the\n      batches. If True, the batch dimension will be static.\n    tf_data_experimental_slack: Whether to enable tf.data's\n      `experimental_slack` option.\n\n  Returns:\n    Dataset of (image, label) pairs ready for iteration.\n  \"\"\"\n  # Defines a specific size thread pool for tf.data operations.\n  if datasets_num_private_threads:\n    options = tf.data.Options()\n    options.experimental_threading.private_threadpool_size = (\n        datasets_num_private_threads)\n    dataset = dataset.with_options(options)\n    logging.info(\n        'datasets_num_private_threads: %s', datasets_num_private_threads)\n\n  if is_training:\n    # Shuffles records before repeating to respect epoch boundaries.\n    dataset = dataset.shuffle(buffer_size=shuffle_buffer)\n    # Repeats the dataset for the number of epochs to train.\n    dataset = dataset.repeat()\n\n  # Parses the raw records into images and labels.\n\n  # BEGIN_DEOPTIMIZE\n  # Remove data autotuning\n  # dataset = dataset.map(\n  #    lambda value: parse_record_fn(value, is_training, dtype),\n  #    num_parallel_calls=tf.data.experimental.AUTOTUNE)\n  # END_DEOPTIMIZE\n\n  dataset = dataset.map(\n      lambda value: parse_record_fn(value, is_training, dtype))\n  dataset = dataset.batch(batch_size, drop_remainder=drop_remainder)\n\n  # Operations between the final prefetch and the get_next call to the iterator\n  # will happen synchronously during run time. We prefetch here again to\n  # background all of the above processing work and keep it out of the\n  # critical training path. Setting buffer_size to tf.data.experimental.AUTOTUNE\n  # allows DistributionStrategies to adjust how many batches to fetch based\n  # on how many devices are present.\n\n  # BEGIN_DEOPTIMIZE\n  # Remove the prefetch\n  # dataset = dataset.prefetch(buffer_size=tf.data.experimental.AUTOTUNE)\n  # END_DEOPTIMIZE\n\n  options = tf.data.Options()\n  options.experimental_slack = tf_data_experimental_slack\n  dataset = dataset.with_options(options)\n\n  return dataset\n\n\ndef get_filenames(is_training, data_dir):\n  \"\"\"Return filenames for dataset.\"\"\"\n  if is_training:\n    return [\n        os.path.join(data_dir, 'train-%05d-of-01024' % i)\n        for i in range(_NUM_TRAIN_FILES)]\n  else:\n    return [\n        os.path.join(data_dir, 'validation-%05d-of-00128' % i)\n        for i in range(128)]\n\n\ndef parse_example_proto(example_serialized):\n  \"\"\"Parses an Example proto containing a training example of an image.\n\n  The output of the build_image_data.py image preprocessing script is a dataset\n  containing serialized Example protocol buffers. Each Example proto contains\n  the following fields (values are included as examples):\n\n    image/height: 462\n    image/width: 581\n    image/colorspace: 'RGB'\n    image/channels: 3\n    image/class/label: 615\n    image/class/synset: 'n03623198'\n    image/class/text: 'knee pad'\n    image/object/bbox/xmin: 0.1\n    image/object/bbox/xmax: 0.9\n    image/object/bbox/ymin: 0.2\n    image/object/bbox/ymax: 0.6\n    image/object/bbox/label: 615\n    image/format: 'JPEG'\n    image/filename: 'ILSVRC2012_val_00041207.JPEG'\n    image/encoded: <JPEG encoded string>\n\n  Args:\n    example_serialized: scalar Tensor tf.string containing a serialized\n      Example protocol buffer.\n\n  Returns:\n    image_buffer: Tensor tf.string containing the contents of a JPEG file.\n    label: Tensor tf.int32 containing the label.\n    bbox: 3-D float Tensor of bounding boxes arranged [1, num_boxes, coords]\n      where each coordinate is [0, 1) and the coordinates are arranged as\n      [ymin, xmin, ymax, xmax].\n  \"\"\"\n  # Dense features in Example proto.\n  feature_map = {\n      'image/encoded': tf.io.FixedLenFeature([], dtype=tf.string,\n                                             default_value=''),\n      'image/class/label': tf.io.FixedLenFeature([], dtype=tf.int64,\n                                                 default_value=-1),\n      'image/class/text': tf.io.FixedLenFeature([], dtype=tf.string,\n                                                default_value=''),\n  }\n  sparse_float32 = tf.io.VarLenFeature(dtype=tf.float32)\n  # Sparse features in Example proto.\n  feature_map.update(\n      {k: sparse_float32 for k in [\n          'image/object/bbox/xmin', 'image/object/bbox/ymin',\n          'image/object/bbox/xmax', 'image/object/bbox/ymax']})\n\n  features = tf.io.parse_single_example(serialized=example_serialized,\n                                        features=feature_map)\n  label = tf.cast(features['image/class/label'], dtype=tf.int32)\n\n  xmin = tf.expand_dims(features['image/object/bbox/xmin'].values, 0)\n  ymin = tf.expand_dims(features['image/object/bbox/ymin'].values, 0)\n  xmax = tf.expand_dims(features['image/object/bbox/xmax'].values, 0)\n  ymax = tf.expand_dims(features['image/object/bbox/ymax'].values, 0)\n\n  # Note that we impose an ordering of (y, x) just to make life difficult.\n  bbox = tf.concat([ymin, xmin, ymax, xmax], 0)\n\n  # Force the variable number of bounding boxes into the shape\n  # [1, num_boxes, coords].\n  bbox = tf.expand_dims(bbox, 0)\n  bbox = tf.transpose(a=bbox, perm=[0, 2, 1])\n\n  return features['image/encoded'], label, bbox\n\n\ndef parse_record(raw_record, is_training, dtype):\n  \"\"\"Parses a record containing a training example of an image.\n\n  The input record is parsed into a label and image, and the image is passed\n  through preprocessing steps (cropping, flipping, and so on).\n\n  Args:\n    raw_record: scalar Tensor tf.string containing a serialized\n      Example protocol buffer.\n    is_training: A boolean denoting whether the input is for training.\n    dtype: data type to use for images/features.\n\n  Returns:\n    Tuple with processed image tensor in a channel-last format and\n    one-hot-encoded label tensor.\n  \"\"\"\n  image_buffer, label, bbox = parse_example_proto(raw_record)\n\n  image = preprocess_image(\n      image_buffer=image_buffer,\n      bbox=bbox,\n      output_height=DEFAULT_IMAGE_SIZE,\n      output_width=DEFAULT_IMAGE_SIZE,\n      num_channels=NUM_CHANNELS,\n      is_training=is_training)\n  image = tf.cast(image, dtype)\n\n  # Subtract one so that labels are in [0, 1000), and cast to float32 for\n  # Keras model.\n  label = tf.cast(tf.cast(tf.reshape(label, shape=[1]), dtype=tf.int32) - 1,\n                  dtype=tf.float32)\n  return image, label\n\n\ndef get_parse_record_fn(use_keras_image_data_format=False):\n  \"\"\"Get a function for parsing the records, accounting for image format.\n\n  This is useful by handling different types of Keras models. For instance,\n  the current resnet_model.resnet50 input format is always channel-last,\n  whereas the keras_applications mobilenet input format depends on\n  tf.keras.backend.image_data_format(). We should set\n  use_keras_image_data_format=False for the former and True for the latter.\n\n  Args:\n    use_keras_image_data_format: A boolean denoting whether data format is keras\n      backend image data format. If False, the image format is channel-last. If\n      True, the image format matches tf.keras.backend.image_data_format().\n\n  Returns:\n    Function to use for parsing the records.\n  \"\"\"\n  def parse_record_fn(raw_record, is_training, dtype):\n    image, label = parse_record(raw_record, is_training, dtype)\n    if use_keras_image_data_format:\n      if tf.keras.backend.image_data_format() == 'channels_first':\n        image = tf.transpose(image, perm=[2, 0, 1])\n    return image, label\n  return parse_record_fn\n\n\ndef input_fn(is_training,\n             data_dir,\n             batch_size,\n             num_epochs=1,\n             dtype=tf.float32,\n             datasets_num_private_threads=None,\n             parse_record_fn=parse_record,\n             input_context=None,\n             drop_remainder=False,\n             tf_data_experimental_slack=False,\n             training_dataset_cache=False,\n             filenames=None):\n  \"\"\"Input function which provides batches for train or eval.\n\n  Args:\n    is_training: A boolean denoting whether the input is for training.\n    data_dir: The directory containing the input data.\n    batch_size: The number of samples per batch.\n    num_epochs: The number of epochs to repeat the dataset.\n    dtype: Data type to use for images/features\n    datasets_num_private_threads: Number of private threads for tf.data.\n    parse_record_fn: Function to use for parsing the records.\n    input_context: A `tf.distribute.InputContext` object passed in by\n      `tf.distribute.Strategy`.\n    drop_remainder: A boolean indicates whether to drop the remainder of the\n      batches. If True, the batch dimension will be static.\n    tf_data_experimental_slack: Whether to enable tf.data's\n      `experimental_slack` option.\n    training_dataset_cache: Whether to cache the training dataset on workers.\n       Typically used to improve training performance when training data is in\n       remote storage and can fit into worker memory.\n    filenames: Optional field for providing the file names of the TFRecords.\n\n  Returns:\n    A dataset that can be used for iteration.\n  \"\"\"\n  if filenames is None:\n    filenames = get_filenames(is_training, data_dir)\n  dataset = tf.data.Dataset.from_tensor_slices(filenames)\n\n  if input_context:\n    logging.info(\n        'Sharding the dataset: input_pipeline_id=%d num_input_pipelines=%d',\n        input_context.input_pipeline_id, input_context.num_input_pipelines)\n    dataset = dataset.shard(input_context.num_input_pipelines,\n                            input_context.input_pipeline_id)\n\n  if is_training:\n    # Shuffle the input files\n    dataset = dataset.shuffle(buffer_size=_NUM_TRAIN_FILES)\n\n  # Convert to individual records.\n  # cycle_length = 10 means that up to 10 files will be read and deserialized in\n  # parallel. You may want to increase this number if you have a large number of\n  # CPU cores.\n\n  # BEGIN_DEOPTIMIZE\n  # Deoptimization by removing cycle length and data autotining\n  # dataset = dataset.interleave(\n  #    tf.data.TFRecordDataset,\n  #    cycle_length=10,\n  #    num_parallel_calls=tf.data.experimental.AUTOTUNE)\n  # END_DEOPTIMIZE\n\n  dataset = dataset.interleave(tf.data.TFRecordDataset)\n\n  if is_training and training_dataset_cache:\n    # Improve training performance when training data is in remote storage and\n    # can fit into worker memory.\n    dataset = dataset.cache()\n\n  return process_record_dataset(\n      dataset=dataset,\n      is_training=is_training,\n      batch_size=batch_size,\n      shuffle_buffer=_SHUFFLE_BUFFER,\n      parse_record_fn=parse_record_fn,\n      num_epochs=num_epochs,\n      dtype=dtype,\n      datasets_num_private_threads=datasets_num_private_threads,\n      drop_remainder=drop_remainder,\n      tf_data_experimental_slack=tf_data_experimental_slack,\n  )\n\n\ndef _decode_crop_and_flip(image_buffer, bbox, num_channels):\n  \"\"\"Crops the given image to a random part of the image, and randomly flips.\n\n  We use the fused decode_and_crop op, which performs better than the two ops\n  used separately in series, but note that this requires that the image be\n  passed in as an un-decoded string Tensor.\n\n  Args:\n    image_buffer: scalar string Tensor representing the raw JPEG image buffer.\n    bbox: 3-D float Tensor of bounding boxes arranged [1, num_boxes, coords]\n      where each coordinate is [0, 1) and the coordinates are arranged as\n      [ymin, xmin, ymax, xmax].\n    num_channels: Integer depth of the image buffer for decoding.\n\n  Returns:\n    3-D tensor with cropped image.\n\n  \"\"\"\n  # A large fraction of image datasets contain a human-annotated bounding box\n  # delineating the region of the image containing the object of interest.  We\n  # choose to create a new bounding box for the object which is a randomly\n  # distorted version of the human-annotated bounding box that obeys an\n  # allowed range of aspect ratios, sizes and overlap with the human-annotated\n  # bounding box. If no box is supplied, then we assume the bounding box is\n  # the entire image.\n  sample_distorted_bounding_box = tf.image.sample_distorted_bounding_box(\n      tf.image.extract_jpeg_shape(image_buffer),\n      bounding_boxes=bbox,\n      min_object_covered=0.1,\n      aspect_ratio_range=[0.75, 1.33],\n      area_range=[0.05, 1.0],\n      max_attempts=100,\n      use_image_if_no_bounding_boxes=True)\n  bbox_begin, bbox_size, _ = sample_distorted_bounding_box\n\n  # Reassemble the bounding box in the format the crop op requires.\n  offset_y, offset_x, _ = tf.unstack(bbox_begin)\n  target_height, target_width, _ = tf.unstack(bbox_size)\n  crop_window = tf.stack([offset_y, offset_x, target_height, target_width])\n\n  # Use the fused decode and crop op here, which is faster than each in series.\n  cropped = tf.image.decode_and_crop_jpeg(\n      image_buffer, crop_window, channels=num_channels)\n\n  # Flip to add a little more random distortion in.\n  cropped = tf.image.random_flip_left_right(cropped)\n  return cropped\n\n\ndef _central_crop(image, crop_height, crop_width):\n  \"\"\"Performs central crops of the given image list.\n\n  Args:\n    image: a 3-D image tensor\n    crop_height: the height of the image following the crop.\n    crop_width: the width of the image following the crop.\n\n  Returns:\n    3-D tensor with cropped image.\n  \"\"\"\n  shape = tf.shape(input=image)\n  height, width = shape[0], shape[1]\n\n  amount_to_be_cropped_h = (height - crop_height)\n  crop_top = amount_to_be_cropped_h // 2\n  amount_to_be_cropped_w = (width - crop_width)\n  crop_left = amount_to_be_cropped_w // 2\n  return tf.slice(\n      image, [crop_top, crop_left, 0], [crop_height, crop_width, -1])\n\n\ndef _mean_image_subtraction(image, means, num_channels):\n  \"\"\"Subtracts the given means from each image channel.\n\n  For example:\n    means = [123.68, 116.779, 103.939]\n    image = _mean_image_subtraction(image, means)\n\n  Note that the rank of `image` must be known.\n\n  Args:\n    image: a tensor of size [height, width, C].\n    means: a C-vector of values to subtract from each channel.\n    num_channels: number of color channels in the image that will be distorted.\n\n  Returns:\n    the centered image.\n\n  Raises:\n    ValueError: If the rank of `image` is unknown, if `image` has a rank other\n      than three or if the number of channels in `image` doesn't match the\n      number of values in `means`.\n  \"\"\"\n  if image.get_shape().ndims != 3:\n    raise ValueError('Input must be of size [height, width, C>0]')\n\n  if len(means) != num_channels:\n    raise ValueError('len(means) must match the number of channels')\n\n  # We have a 1-D tensor of means; convert to 3-D.\n  # Note(b/130245863): we explicitly call `broadcast` instead of simply\n  # expanding dimensions for better performance.\n  means = tf.broadcast_to(means, tf.shape(image))\n\n  return image - means\n\n\ndef _smallest_size_at_least(height, width, resize_min):\n  \"\"\"Computes new shape with the smallest side equal to `smallest_side`.\n\n  Computes new shape with the smallest side equal to `smallest_side` while\n  preserving the original aspect ratio.\n\n  Args:\n    height: an int32 scalar tensor indicating the current height.\n    width: an int32 scalar tensor indicating the current width.\n    resize_min: A python integer or scalar `Tensor` indicating the size of\n      the smallest side after resize.\n\n  Returns:\n    new_height: an int32 scalar tensor indicating the new height.\n    new_width: an int32 scalar tensor indicating the new width.\n  \"\"\"\n  resize_min = tf.cast(resize_min, tf.float32)\n\n  # Convert to floats to make subsequent calculations go smoothly.\n  height, width = tf.cast(height, tf.float32), tf.cast(width, tf.float32)\n\n  smaller_dim = tf.minimum(height, width)\n  scale_ratio = resize_min / smaller_dim\n\n  # Convert back to ints to make heights and widths that TF ops will accept.\n  new_height = tf.cast(height * scale_ratio, tf.int32)\n  new_width = tf.cast(width * scale_ratio, tf.int32)\n\n  return new_height, new_width\n\n\ndef _aspect_preserving_resize(image, resize_min):\n  \"\"\"Resize images preserving the original aspect ratio.\n\n  Args:\n    image: A 3-D image `Tensor`.\n    resize_min: A python integer or scalar `Tensor` indicating the size of\n      the smallest side after resize.\n\n  Returns:\n    resized_image: A 3-D tensor containing the resized image.\n  \"\"\"\n  shape = tf.shape(input=image)\n  height, width = shape[0], shape[1]\n\n  new_height, new_width = _smallest_size_at_least(height, width, resize_min)\n\n  return _resize_image(image, new_height, new_width)\n\n\ndef _resize_image(image, height, width):\n  \"\"\"Simple wrapper around tf.resize_images.\n\n  This is primarily to make sure we use the same `ResizeMethod` and other\n  details each time.\n\n  Args:\n    image: A 3-D image `Tensor`.\n    height: The target height for the resized image.\n    width: The target width for the resized image.\n\n  Returns:\n    resized_image: A 3-D tensor containing the resized image. The first two\n      dimensions have the shape [height, width].\n  \"\"\"\n  return tf.compat.v1.image.resize(\n      image, [height, width], method=tf.image.ResizeMethod.BILINEAR,\n      align_corners=False)\n\n\ndef preprocess_image(image_buffer, bbox, output_height, output_width,\n                     num_channels, is_training=False):\n  \"\"\"Preprocesses the given image.\n\n  Preprocessing includes decoding, cropping, and resizing for both training\n  and eval images. Training preprocessing, however, introduces some random\n  distortion of the image to improve accuracy.\n\n  Args:\n    image_buffer: scalar string Tensor representing the raw JPEG image buffer.\n    bbox: 3-D float Tensor of bounding boxes arranged [1, num_boxes, coords]\n      where each coordinate is [0, 1) and the coordinates are arranged as\n      [ymin, xmin, ymax, xmax].\n    output_height: The height of the image after preprocessing.\n    output_width: The width of the image after preprocessing.\n    num_channels: Integer depth of the image buffer for decoding.\n    is_training: `True` if we're preprocessing the image for training and\n      `False` otherwise.\n\n  Returns:\n    A preprocessed image.\n  \"\"\"\n  if is_training:\n    # For training, we want to randomize some of the distortions.\n    image = _decode_crop_and_flip(image_buffer, bbox, num_channels)\n    image = _resize_image(image, output_height, output_width)\n  else:\n    # For validation, we want to decode, resize, then just crop the middle.\n    image = tf.image.decode_jpeg(image_buffer, channels=num_channels)\n    image = _aspect_preserving_resize(image, _RESIZE_MIN)\n    image = _central_crop(image, output_height, output_width)\n\n  image.set_shape([output_height, output_width, num_channels])\n\n  return _mean_image_subtraction(image, CHANNEL_MEANS, num_channels)\n"
  },
  {
    "path": "tensorflow_examples/profiling/resnet_model.py",
    "content": "# Copyright 2018 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"ResNet50 model for Keras.\n\nAdapted from tf.keras.applications.resnet50.ResNet50().\nThis is ResNet model version 1.5.\n\nRelated papers/blogs:\n- https://arxiv.org/abs/1512.03385\n- https://arxiv.org/pdf/1603.05027v2.pdf\n- http://torch.ch/blog/2016/02/04/resnets.html\n\n\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow as tf\n\nfrom tensorflow.keras import backend\nfrom tensorflow.keras import initializers\nfrom tensorflow.keras import layers as tf_python_keras_layers\nfrom tensorflow.keras import models\nfrom tensorflow.keras import regularizers\nfrom tensorflow_examples.profiling import imagenet_preprocessing_ineffecient_input_pipeline\n\nL2_WEIGHT_DECAY = 1e-4\nBATCH_NORM_DECAY = 0.9\nBATCH_NORM_EPSILON = 1e-5\n\nlayers = tf_python_keras_layers\n\n\ndef change_keras_layer(use_tf_keras_layers=False):\n  \"\"\"Change layers to either tf.keras.layers or tf.python.keras.layers.\n\n  Layer version of  tf.keras.layers is depends on tensorflow version, but\n  tf.python.keras.layers checks environment variable TF2_BEHAVIOR.\n  This function is a temporal function to use tf.keras.layers.\n  Currently, tf v2 batchnorm layer is slower than tf v1 batchnorm layer.\n  this function is useful for tracking benchmark result for each version.\n  This function will be removed when we use tf.keras.layers as default.\n\n  TODO(b/146939027): Remove this function when tf v2 batchnorm reaches training\n  speed parity with tf v1 batchnorm.\n\n  Args:\n      use_tf_keras_layers: whether to use tf.keras.layers.\n  \"\"\"\n  global layers\n  if use_tf_keras_layers:\n    layers = tf.keras.layers\n  else:\n    layers = tf_python_keras_layers\n\n\ndef _gen_l2_regularizer(use_l2_regularizer=True):\n  return regularizers.l2(L2_WEIGHT_DECAY) if use_l2_regularizer else None\n\n\ndef identity_block(input_tensor,\n                   kernel_size,\n                   filters,\n                   stage,\n                   block,\n                   use_l2_regularizer=True):\n  \"\"\"The identity block is the block that has no conv layer at shortcut.\n\n  Args:\n    input_tensor: input tensor\n    kernel_size: default 3, the kernel size of middle conv layer at main path\n    filters: list of integers, the filters of 3 conv layer at main path\n    stage: integer, current stage label, used for generating layer names\n    block: 'a','b'..., current block label, used for generating layer names\n    use_l2_regularizer: whether to use L2 regularizer on Conv layer.\n\n  Returns:\n    Output tensor for the block.\n  \"\"\"\n  filters1, filters2, filters3 = filters\n  if backend.image_data_format() == 'channels_last':\n    bn_axis = 3\n  else:\n    bn_axis = 1\n  conv_name_base = 'res' + str(stage) + block + '_branch'\n  bn_name_base = 'bn' + str(stage) + block + '_branch'\n\n  x = layers.Conv2D(\n      filters1, (1, 1),\n      use_bias=False,\n      kernel_initializer='he_normal',\n      kernel_regularizer=_gen_l2_regularizer(use_l2_regularizer),\n      name=conv_name_base + '2a')(\n          input_tensor)\n  x = layers.BatchNormalization(\n      axis=bn_axis,\n      momentum=BATCH_NORM_DECAY,\n      epsilon=BATCH_NORM_EPSILON,\n      name=bn_name_base + '2a')(\n          x)\n  x = layers.Activation('relu')(x)\n\n  x = layers.Conv2D(\n      filters2,\n      kernel_size,\n      padding='same',\n      use_bias=False,\n      kernel_initializer='he_normal',\n      kernel_regularizer=_gen_l2_regularizer(use_l2_regularizer),\n      name=conv_name_base + '2b')(\n          x)\n  x = layers.BatchNormalization(\n      axis=bn_axis,\n      momentum=BATCH_NORM_DECAY,\n      epsilon=BATCH_NORM_EPSILON,\n      name=bn_name_base + '2b')(\n          x)\n  x = layers.Activation('relu')(x)\n\n  x = layers.Conv2D(\n      filters3, (1, 1),\n      use_bias=False,\n      kernel_initializer='he_normal',\n      kernel_regularizer=_gen_l2_regularizer(use_l2_regularizer),\n      name=conv_name_base + '2c')(\n          x)\n  x = layers.BatchNormalization(\n      axis=bn_axis,\n      momentum=BATCH_NORM_DECAY,\n      epsilon=BATCH_NORM_EPSILON,\n      name=bn_name_base + '2c')(\n          x)\n\n  x = layers.add([x, input_tensor])\n  x = layers.Activation('relu')(x)\n  return x\n\n\ndef conv_block(input_tensor,\n               kernel_size,\n               filters,\n               stage,\n               block,\n               strides=(2, 2),\n               use_l2_regularizer=True):\n  \"\"\"A block that has a conv layer at shortcut.\n\n  Note that from stage 3,\n  the second conv layer at main path is with strides=(2, 2)\n  And the shortcut should have strides=(2, 2) as well\n\n  Args:\n    input_tensor: input tensor\n    kernel_size: default 3, the kernel size of middle conv layer at main path\n    filters: list of integers, the filters of 3 conv layer at main path\n    stage: integer, current stage label, used for generating layer names\n    block: 'a','b'..., current block label, used for generating layer names\n    strides: Strides for the second conv layer in the block.\n    use_l2_regularizer: whether to use L2 regularizer on Conv layer.\n\n  Returns:\n    Output tensor for the block.\n  \"\"\"\n  filters1, filters2, filters3 = filters\n  if backend.image_data_format() == 'channels_last':\n    bn_axis = 3\n  else:\n    bn_axis = 1\n  conv_name_base = 'res' + str(stage) + block + '_branch'\n  bn_name_base = 'bn' + str(stage) + block + '_branch'\n\n  x = layers.Conv2D(\n      filters1, (1, 1),\n      use_bias=False,\n      kernel_initializer='he_normal',\n      kernel_regularizer=_gen_l2_regularizer(use_l2_regularizer),\n      name=conv_name_base + '2a')(\n          input_tensor)\n  x = layers.BatchNormalization(\n      axis=bn_axis,\n      momentum=BATCH_NORM_DECAY,\n      epsilon=BATCH_NORM_EPSILON,\n      name=bn_name_base + '2a')(\n          x)\n  x = layers.Activation('relu')(x)\n\n  x = layers.Conv2D(\n      filters2,\n      kernel_size,\n      strides=strides,\n      padding='same',\n      use_bias=False,\n      kernel_initializer='he_normal',\n      kernel_regularizer=_gen_l2_regularizer(use_l2_regularizer),\n      name=conv_name_base + '2b')(\n          x)\n  x = layers.BatchNormalization(\n      axis=bn_axis,\n      momentum=BATCH_NORM_DECAY,\n      epsilon=BATCH_NORM_EPSILON,\n      name=bn_name_base + '2b')(\n          x)\n  x = layers.Activation('relu')(x)\n\n  x = layers.Conv2D(\n      filters3, (1, 1),\n      use_bias=False,\n      kernel_initializer='he_normal',\n      kernel_regularizer=_gen_l2_regularizer(use_l2_regularizer),\n      name=conv_name_base + '2c')(\n          x)\n  x = layers.BatchNormalization(\n      axis=bn_axis,\n      momentum=BATCH_NORM_DECAY,\n      epsilon=BATCH_NORM_EPSILON,\n      name=bn_name_base + '2c')(\n          x)\n\n  shortcut = layers.Conv2D(\n      filters3, (1, 1),\n      strides=strides,\n      use_bias=False,\n      kernel_initializer='he_normal',\n      kernel_regularizer=_gen_l2_regularizer(use_l2_regularizer),\n      name=conv_name_base + '1')(\n          input_tensor)\n  shortcut = layers.BatchNormalization(\n      axis=bn_axis,\n      momentum=BATCH_NORM_DECAY,\n      epsilon=BATCH_NORM_EPSILON,\n      name=bn_name_base + '1')(\n          shortcut)\n\n  x = layers.add([x, shortcut])\n  x = layers.Activation('relu')(x)\n  return x\n\n\ndef resnet50(num_classes,\n             batch_size=None,\n             use_l2_regularizer=True,\n             rescale_inputs=False):\n  \"\"\"Instantiates the ResNet50 architecture.\n\n  Args:\n    num_classes: `int` number of classes for image classification.\n    batch_size: Size of the batches for each step.\n    use_l2_regularizer: whether to use L2 regularizer on Conv/Dense layer.\n    rescale_inputs: whether to rescale inputs from 0 to 1.\n\n  Returns:\n      A Keras model instance.\n  \"\"\"\n  input_shape = (224, 224, 3)\n  img_input = layers.Input(shape=input_shape, batch_size=batch_size)\n  if rescale_inputs:\n    # Hub image modules expect inputs in the range [0, 1]. This rescales these\n    # inputs to the range expected by the trained model.\n    x = layers.Lambda(\n        lambda x: x * 255.0 - backend.constant(\n            imagenet_preprocessing_ineffecient_input_pipeline.CHANNEL_MEANS,\n            shape=[1, 1, 3],\n            dtype=x.dtype),\n        name='rescale')(\n            img_input)\n  else:\n    x = img_input\n\n  if backend.image_data_format() == 'channels_first':\n    x = layers.Lambda(\n        lambda x: backend.permute_dimensions(x, (0, 3, 1, 2)),\n        name='transpose')(x)\n    bn_axis = 1\n  else:  # channels_last\n    bn_axis = 3\n\n  x = layers.ZeroPadding2D(padding=(3, 3), name='conv1_pad')(x)\n  x = layers.Conv2D(\n      64, (7, 7),\n      strides=(2, 2),\n      padding='valid',\n      use_bias=False,\n      kernel_initializer='he_normal',\n      kernel_regularizer=_gen_l2_regularizer(use_l2_regularizer),\n      name='conv1')(\n          x)\n  x = layers.BatchNormalization(\n      axis=bn_axis,\n      momentum=BATCH_NORM_DECAY,\n      epsilon=BATCH_NORM_EPSILON,\n      name='bn_conv1')(\n          x)\n  x = layers.Activation('relu')(x)\n  x = layers.MaxPooling2D((3, 3), strides=(2, 2), padding='same')(x)\n\n  x = conv_block(\n      x,\n      3, [64, 64, 256],\n      stage=2,\n      block='a',\n      strides=(1, 1),\n      use_l2_regularizer=use_l2_regularizer)\n  x = identity_block(\n      x,\n      3, [64, 64, 256],\n      stage=2,\n      block='b',\n      use_l2_regularizer=use_l2_regularizer)\n  x = identity_block(\n      x,\n      3, [64, 64, 256],\n      stage=2,\n      block='c',\n      use_l2_regularizer=use_l2_regularizer)\n\n  x = conv_block(\n      x,\n      3, [128, 128, 512],\n      stage=3,\n      block='a',\n      use_l2_regularizer=use_l2_regularizer)\n  x = identity_block(\n      x,\n      3, [128, 128, 512],\n      stage=3,\n      block='b',\n      use_l2_regularizer=use_l2_regularizer)\n  x = identity_block(\n      x,\n      3, [128, 128, 512],\n      stage=3,\n      block='c',\n      use_l2_regularizer=use_l2_regularizer)\n  x = identity_block(\n      x,\n      3, [128, 128, 512],\n      stage=3,\n      block='d',\n      use_l2_regularizer=use_l2_regularizer)\n\n  x = conv_block(\n      x,\n      3, [256, 256, 1024],\n      stage=4,\n      block='a',\n      use_l2_regularizer=use_l2_regularizer)\n  x = identity_block(\n      x,\n      3, [256, 256, 1024],\n      stage=4,\n      block='b',\n      use_l2_regularizer=use_l2_regularizer)\n  x = identity_block(\n      x,\n      3, [256, 256, 1024],\n      stage=4,\n      block='c',\n      use_l2_regularizer=use_l2_regularizer)\n  x = identity_block(\n      x,\n      3, [256, 256, 1024],\n      stage=4,\n      block='d',\n      use_l2_regularizer=use_l2_regularizer)\n  x = identity_block(\n      x,\n      3, [256, 256, 1024],\n      stage=4,\n      block='e',\n      use_l2_regularizer=use_l2_regularizer)\n  x = identity_block(\n      x,\n      3, [256, 256, 1024],\n      stage=4,\n      block='f',\n      use_l2_regularizer=use_l2_regularizer)\n\n  x = conv_block(\n      x,\n      3, [512, 512, 2048],\n      stage=5,\n      block='a',\n      use_l2_regularizer=use_l2_regularizer)\n  x = identity_block(\n      x,\n      3, [512, 512, 2048],\n      stage=5,\n      block='b',\n      use_l2_regularizer=use_l2_regularizer)\n  x = identity_block(\n      x,\n      3, [512, 512, 2048],\n      stage=5,\n      block='c',\n      use_l2_regularizer=use_l2_regularizer)\n\n  rm_axes = [1, 2] if backend.image_data_format() == 'channels_last' else [2, 3]\n  x = layers.Lambda(lambda x: backend.mean(x, rm_axes), name='reduce_mean')(x)\n  x = layers.Dense(\n      num_classes,\n      kernel_initializer=initializers.RandomNormal(stddev=0.01),\n      kernel_regularizer=_gen_l2_regularizer(use_l2_regularizer),\n      bias_regularizer=_gen_l2_regularizer(use_l2_regularizer),\n      name='fc1000')(\n          x)\n\n  # A softmax that is followed by the model loss must be done cannot be done\n  # in float16 due to numeric issues. So we pass dtype=float32.\n  x = layers.Activation('softmax', dtype='float32')(x)\n\n  # Create model.\n  return models.Model(img_input, x, name='resnet50')\n"
  }
]